Merge remote-tracking branch 'offical/bill/raddebugger-custom-section'

This commit is contained in:
ed
2025-05-25 09:25:29 -04:00
266 changed files with 20392 additions and 4511 deletions
+416 -211
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -1,10 +1,10 @@
#!/usr/bin/env bash
set -eu
VERSION="3.0.0"
VERSION="3.1.0"
RELEASE="https://github.com/erincatto/box2d/archive/refs/tags/v$VERSION.tar.gz"
cd "$(odin root)"/vendor/box2d
cd "$(dirname "$0")"
curl -O -L "$RELEASE"
tar -xzvf "v$VERSION.tar.gz"
@@ -58,7 +58,7 @@ Darwin)
*)
rm -rf build
mkdir build
cmake $FLAGS -DCMAKE_OSX_ARCHITECTURES=arm64 -S . -B build
cmake $FLAGS -S . -B build
cmake --build build
cp build/src/libbox2d.a ../lib/box2d_other.a
;;
@@ -73,7 +73,8 @@ make -f wasm.Makefile
if [[ $? -ne 0 ]]; then
printf "\e[30;43mwarning:\e[0m Native Box2D libraries were built successfully, the WASM build failed, likely because your default C compiler and/or linker doesn't support WASM, you can set the CC and LD environment variables to point to a compiler and linker that support it\n"
fi
make -f wasm.Makefile clean
set -e
rm -rf v3.0.0.tar.gz
rm -rf box2d-3.0.0
rm -rf "v$VERSION.tar.gz"
rm -rf box2d-"$VERSION"
+143 -126
View File
@@ -5,9 +5,9 @@ import "core:c"
// The maximum number of vertices on a convex polygon. Changing this affects performance even if you
// don't use more vertices.
maxPolygonVertices :: 8
MAX_POLYGON_VERTICES :: 8
// Low level ray-cast input data
// Low level ray cast input data
RayCastInput :: struct {
// Start point of the ray cast
origin: Vec2,
@@ -19,27 +19,37 @@ RayCastInput :: struct {
maxFraction: f32,
}
// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
// You can provide between 1 and MAX_POLYGON_VERTICES and a radius.
ShapeProxy :: struct {
// The point cloud
points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points. Must be greater than 0.
count: c.int,
// The external radius of the point cloud. May be zero.
radius: f32,
}
// Low level shape cast input in generic form. This allows casting an arbitrary point
// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
ShapeCastInput :: struct {
// A point cloud to cast
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
// The radius around the point cloud
radius: f32,
// A generic shape
proxy: ShapeProxy,
// The translation of the shape cast
translation: Vec2,
// The maximum fraction of the translation to consider, typically 1
maxFraction: f32,
// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero.
canEncroach: bool,
}
// Low level ray-cast or shape-cast output data
// Low level ray cast or shape-cast output data
CastOutput :: struct {
// The surface normal at the hit point
normal: Vec2,
@@ -51,7 +61,7 @@ CastOutput :: struct {
fraction: f32,
// The number of iterations used
iterations: i32,
iterations: c.int,
// Did the cast hit?
hit: bool,
@@ -93,16 +103,16 @@ Capsule :: struct {
// A solid convex polygon. It is assumed that the interior of the polygon is to
// the left of each edge.
// Polygons have a maximum number of vertices equal to maxPolygonVertices.
// Polygons have a maximum number of vertices equal to MAX_POLYGON_VERTICES.
// In most cases you should not need many vertices for a convex polygon.
// @warning DO NOT fill this out manually, instead use a helper function like
// b2MakePolygon or b2MakeBox.
// @warning DO NOT fill this out manually, instead use a helper function like
// b2MakePolygon or b2MakeBox.
Polygon :: struct {
// The polygon vertices
vertices: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
vertices: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The outward normal vectors of the polygon sides
normals: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
normals: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The centroid of the polygon
centroid: Vec2,
@@ -111,7 +121,7 @@ Polygon :: struct {
radius: f32,
// The number of polygon vertices
count: i32,
count: c.int,
}
// A line segment with two-sided collision.
@@ -123,10 +133,10 @@ Segment :: struct {
point2: Vec2,
}
// A smooth line segment with one-sided collision. Only collides on the right side.
// A line segment with one-sided collision. Only collides on the right side.
// Several of these are generated for a chain shape.
// ghost1 -> point1 -> point2 -> ghost2
SmoothSegment :: struct {
ChainSegment :: struct {
// The tail ghost vertex
ghost1: Vec2,
@@ -137,7 +147,7 @@ SmoothSegment :: struct {
ghost2: Vec2,
// The owning chain shape index (internal usage only)
chainId: i32,
chainId: c.int,
}
@@ -145,10 +155,10 @@ SmoothSegment :: struct {
// @warning Do not modify these values directly, instead use b2ComputeHull()
Hull :: struct {
// The final points of the hull
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
count: c.int,
}
/**
@@ -178,21 +188,11 @@ SegmentDistanceResult :: struct {
distanceSquared: f32,
}
// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
DistanceProxy :: struct {
// The point cloud
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
// The external radius of the point cloud
radius: f32,
}
// Used to warm start b2Distance. Set count to zero on first call or
// use zero initialization.
DistanceCache :: struct {
// Used to warm start the GJK simplex. If you call this function multiple times with nearby
// transforms this might improve performance. Otherwise you can zero initialize this.
// The distance cache must be initialized to zero on the first call.
// Users should generally just zero initialize this structure for each call.
SimplexCache :: struct {
// The number of stored simplex points
count: u16,
@@ -203,15 +203,15 @@ DistanceCache :: struct {
indexB: [3]u8 `fmt:"v,count"`,
}
emptyDistanceCache :: DistanceCache{}
emptySimplexCache :: SimplexCache{}
// Input for b2ShapeDistance
DistanceInput :: struct {
// The proxy for shape A
proxyA: DistanceProxy,
proxyA: ShapeProxy,
// The proxy for shape B
proxyB: DistanceProxy,
proxyB: ShapeProxy,
// The world transform for shape A
transformA: Transform,
@@ -227,6 +227,7 @@ DistanceInput :: struct {
DistanceOutput :: struct {
pointA: Vec2, // Closest point on shapeA
pointB: Vec2, // Closest point on shapeB
normal: Vec2, // Normal vector that points from A to B
distance: f32, // The final distance, zero if overlapped
iterations: i32, // Number of GJK iterations used
simplexCount: i32, // The number of simplexes stored in the simplex array
@@ -234,28 +235,29 @@ DistanceOutput :: struct {
// Simplex vertex for debugging the GJK algorithm
SimplexVertex :: struct {
wA: Vec2, // support point in proxyA
wB: Vec2, // support point in proxyB
w: Vec2, // wB - wA
a: f32, // barycentric coordinate for closest point
indexA: i32, // wA index
indexB: i32, // wB index
wA: Vec2, // support point in proxyA
wB: Vec2, // support point in proxyB
w: Vec2, // wB - wA
a: f32, // barycentric coordinate for closest point
indexA: c.int, // wA index
indexB: c.int, // wB index
}
// Simplex from the GJK algorithm
Simplex :: struct {
v1, v2, v3: SimplexVertex `fmt:"v,count"`, // vertices
count: i32, // number of valid vertices
count: c.int, // number of valid vertices
}
// Input parameters for b2ShapeCast
ShapeCastPairInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B
proxyA: ShapeProxy, // The proxy for shape A
proxyB: ShapeProxy, // The proxy for shape B
transformA: Transform, // The world transform for shape A
transformB: Transform, // The world transform for shape B
translationB: Vec2, // The translation of shape B
maxFraction: f32, // The fraction of the translation to consider, typically 1
canEncroach: bool, // Allows shapes with a radius to move slightly closer if already touching
}
@@ -272,11 +274,11 @@ Sweep :: struct {
// Input parameters for b2TimeOfImpact
TOIInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B
sweepA: Sweep, // The movement of shape A
sweepB: Sweep, // The movement of shape B
tMax: f32, // Defines the sweep interval [0, tMax]
proxyA: ShapeProxy, // The proxy for shape A
proxyB: ShapeProxy, // The proxy for shape B
sweepA: Sweep, // The movement of shape A
sweepB: Sweep, // The movement of shape B
maxFraction: f32, // Defines the sweep interval [0, maxFraction]
}
// Describes the TOI output
@@ -290,8 +292,8 @@ TOIState :: enum c.int {
// Output parameters for b2TimeOfImpact.
TOIOutput :: struct {
state: TOIState, // The type of result
t: f32, // The time of the collision
state: TOIState, // The type of result
fraction: f32, // The sweep time of the collision
}
@@ -301,26 +303,30 @@ TOIOutput :: struct {
* @brief Functions for colliding pairs of shapes
*/
// A manifold point is a contact point belonging to a contact
// manifold. It holds details related to the geometry and dynamics
// of the contact points.
// A manifold point is a contact point belonging to a contact manifold.
// It holds details related to the geometry and dynamics of the contact points.
// Box2D uses speculative collision so some contact points may be separated.
// You may use the totalNormalImpulse to determine if there was an interaction during
// the time step.
ManifoldPoint :: struct {
// Location of the contact point in world space. Subject to precision loss at large coordinates.
// @note Should only be used for debugging.
point: Vec2,
// Location of the contact point relative to bodyA's origin in world space
// @note When used internally to the Box2D solver, these are relative to the center of mass.
// Location of the contact point relative to shapeA's origin in world space
// @note When used internally to the Box2D solver, this is relative to the body center of mass.
anchorA: Vec2,
// Location of the contact point relative to bodyB's origin in world space
// Location of the contact point relative to shapeB's origin in world space
// @note When used internally to the Box2D solver, this is relative to the body center of mass.
anchorB: Vec2,
// The separation of the contact point, negative if penetrating
separation: f32,
// The impulse along the manifold normal vector.
normalImpulse: f32,
// The total normal impulse applied across sub-stepping and restitution. This is important
// to identify speculative contact points that had an interaction in the time step.
totalNormalImpulse: f32,
// The friction impulse
tangentImpulse: f32,
@@ -340,16 +346,21 @@ ManifoldPoint :: struct {
persisted: bool,
}
// A contact manifold describes the contact points between colliding shapes
// A contact manifold describes the contact points between colliding shapes.
// @note Box2D uses speculative collision so some contact points may be separated.
Manifold :: struct {
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The unit normal vector in world space, points from shape A to bodyB
normal: Vec2,
normal: Vec2,
// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
rollingImpulse: f32,
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The number of contacts points, will be 0, 1, or 2
pointCount: i32,
pointCount: c.int,
}
@@ -364,63 +375,17 @@ Manifold :: struct {
* A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
* A dynamic tree arranges data in a binary tree to accelerate
* queries such as AABB queries and ray casts. Leaf nodes are proxies
* with an AABB. These are used to hold a user collision object, such as a reference to a b2Shape.
* with an AABB. These are used to hold a user collision object.
* Nodes are pooled and relocatable, so I use node indices rather than pointers.
* The dynamic tree is made available for advanced users that would like to use it to organize
* spatial game data besides rigid bodies.
*
* @note This is an advanced feature and normally not used by applications directly.
*/
// The default category bit for a tree proxy. Used for collision filtering.
defaultCategoryBits :: 0x00000001
// Convenience mask bits to use when you don't need collision filtering and just want
// all results.
defaultMaskBits :: 0xFFFFFFFF
// A node in the dynamic tree. This is private data placed here for performance reasons.
// 16 + 16 + 8 + pad(8)
TreeNode :: struct {
// The node bounding box
aabb: AABB, // 16
// Category bits for collision filtering
categoryBits: u32, // 4
using _: struct #raw_union {
// The node parent index
parent: i32,
// The node freelist next index
next: i32,
}, // 4
// Child 1 index
child1: i32, // 4
// Child 2 index
child2: i32, // 4
// User data
// todo could be union with child index
userData: i32, // 4
// Leaf = 0, free node = -1
height: i16, // 2
// Has the AABB been enlarged?
enlarged: bool, // 1
// Padding for clarity
_: [9]byte,
}
// The dynamic tree structure. This should be considered private data.
// It is placed here for performance reasons.
DynamicTree :: struct {
// The tree nodes
nodes: [^]TreeNode `fmt"v,nodeCount"`,
nodes: rawptr,
// The root index
root: i32,
@@ -453,16 +418,25 @@ DynamicTree :: struct {
rebuildCapacity: i32,
}
// These are performance results returned by dynamic tree queries.
TreeStats :: struct {
// Number of internal nodes visited during the query
nodeVisits: c.int,
// Number of leaf nodes visited during the query
leafVisits: c.int,
}
// This function receives proxies found in the AABB query.
// @return true if the query should continue
TreeQueryCallbackFcn :: #type proc "c" (proxyId: i32, userData: i32, ctx: rawptr) -> bool
TreeQueryCallbackFcn :: #type proc "c" (proxyId: i32, userData: u64, ctx: rawptr) -> bool
// This function receives clipped ray-cast input for a proxy. The function
// This function receives clipped ray cast input for a proxy. The function
// returns the new ray fraction.
// - return a value of 0 to terminate the ray-cast
// - return a value of 0 to terminate the ray cast
// - return a value less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping
TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32
TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32
// This function receives clipped raycast input for a proxy. The function
@@ -470,4 +444,47 @@ TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxy
// - return a value of 0 to terminate the ray cast
// - return a value less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping
TreeRayCastCallbackFcn :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32
TreeRayCastCallbackFcn :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32
/**@}*/
/**
* @defgroup character Character mover
* Character movement solver
* @{
*/
/// These are the collision planes returned from b2World_CollideMover
PlaneResult :: struct {
// The collision plane between the mover and convex shape
plane: Plane,
// Did the collision register a hit? If not this plane should be ignored.
hit: bool,
}
// These are collision planes that can be fed to b2SolvePlanes. Normally
// this is assembled by the user from plane results in b2PlaneResult
CollisionPlane :: struct {
// The collision plane between the mover and some shape
plane: Plane,
// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can
// make the plane collision soft. Usually in meters.
pushLimit: f32,
// The push on the mover determined by b2SolvePlanes. Usually in meters.
push: f32,
// Indicates if b2ClipVector should clip against this plane. Should be false for soft collision.
clipVelocity: bool,
}
// Result returned by b2SolvePlanes
PlaneSolverResult :: struct {
// The final position of the mover
position: Vec2,
// The number of iterations used by the plane solver. For diagnostics.
iterationCount: i32,
}
+22 -21
View File
@@ -23,45 +23,46 @@ import "base:intrinsics"
/// World id references a world instance. This should be treated as an opaque handle.
WorldId :: struct {
index1: u16,
revision: u16,
index1: u16,
generation: u16,
}
/// Body id references a body instance. This should be treated as an opaque handle.
BodyId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Shape id references a shape instance. This should be treated as an opaque handle.
ShapeId :: struct {
index1: i32,
world0: u16,
revision: u16,
}
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Chain id references a chain instances. This should be treated as an opaque handle.
ChainId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Use these to make your identifiers null.
/// You may also use zero initialization to get null.
nullWorldId :: WorldId{}
nullBodyId :: BodyId{}
nullShapeId :: ShapeId{}
nullJointId :: JointId{}
nullChainId :: ChainId{}
nullJointId :: JointId{}
/// Macro to determine if any id is null.
IS_NULL :: #force_inline proc "c" (id: $T) -> bool
@@ -82,6 +83,6 @@ ID_EQUALS :: #force_inline proc "c" (id1, id2: $T) -> bool
where intrinsics.type_is_struct(T),
intrinsics.type_has_field(T, "index1"),
intrinsics.type_has_field(T, "world0"),
intrinsics.type_has_field(T, "revision") {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision
intrinsics.type_has_field(T, "generation") {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+211 -106
View File
@@ -3,9 +3,18 @@ package vendor_box2d
import "core:c"
import "core:math"
pi :: 3.14159265359
EPSILON :: 1e-23
Vec2 :: [2]f32
// Cosine and sine pair
// This uses a custom implementation designed for cross-platform determinism
CosSin :: struct {
// cosine and sine
cosine: f32,
sine: f32,
}
Rot :: struct {
c, s: f32, // cosine and sine
}
@@ -21,11 +30,43 @@ AABB :: struct {
upperBound: Vec2,
}
// separation = dot(normal, point) - offset
Plane :: struct {
normal: Vec2,
offset: f32,
}
PI :: math.PI
Vec2_zero :: Vec2{0, 0}
Rot_identity :: Rot{1, 0}
Transform_identity :: Transform{{0, 0}, {1, 0}}
Mat22_zero :: Mat22{0, 0, 0, 0}
// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
}
// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
}
// @return the minimum of two floats
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
@@ -51,28 +92,15 @@ ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 {
return clamp(a, lower, upper)
}
// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
@(require_results)
Atan2 :: proc "c" (y, x: f32) -> f32 {
return math.atan2(y, x)
}
// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
@(require_results)
ComputeCosSin :: proc "c" (radians: f32) -> (res: CosSin) {
res.sine, res.cosine = math.sincos(radians)
return
}
// Vector dot product
@@ -198,12 +226,6 @@ Length :: proc "c" (v: Vec2) -> f32 {
return math.sqrt(v.x * v.x + v.y * v.y)
}
// Get the length squared of this vector
@(require_results)
LengthSquared :: proc "c" (v: Vec2) -> f32 {
return v.x * v.x + v.y * v.y
}
// Get the distance between two points
@(require_results)
Distance :: proc "c" (a, b: Vec2) -> f32 {
@@ -212,45 +234,41 @@ Distance :: proc "c" (a, b: Vec2) -> f32 {
return math.sqrt(dx * dx + dy * dy)
}
// Get the distance squared between points
@(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
c := Vec2{b.x - a.x, b.y - a.y}
return c.x * c.x + c.y * c.y
Normalize :: proc "c" (v: Vec2) -> Vec2 {
length := Length(v)
if length < EPSILON {
return Vec2_zero
}
invLength := 1 / length
return invLength * v
}
// Make a rotation using an angle in radians
@(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot {
// todo determinism
return {math.cos(angle), math.sin(angle)}
IsNormalized :: proc "c" (v: Vec2) -> bool {
aa := Dot(v, v)
return abs(1. - aa) < 10. * EPSILON
}
// Normalize rotation
@(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot {
mag := math.sqrt(q.s * q.s + q.c * q.c)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0)
return {q.c * invMag, q.s * invMag}
NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
panic("zero-length Vec2")
}
invLength := 1 / length
return invLength * v
}
// Is this rotation normalized?
@(require_results)
IsNormalized :: proc "c" (q: Rot) -> bool {
// larger tolerance due to failure on mingw 32-bit
qq := q.s * q.s + q.c * q.c
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006
}
// Normalized linear interpolation
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
length = Length(v)
if length < 1e-23 {
return
}
invLength := 1 / length
vn = invLength * v
return
}
// Integration rotation from angular velocity
@@ -268,6 +286,63 @@ IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot {
return {q2.c * invMag, q2.s * invMag}
}
// Get the length squared of this vector
@(require_results)
LengthSquared :: proc "c" (v: Vec2) -> f32 {
return v.x * v.x + v.y * v.y
}
// Get the distance squared between points
@(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
c := Vec2{b.x - a.x, b.y - a.y}
return c.x * c.x + c.y * c.y
}
// Make a rotation using an angle in radians
@(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot {
cs := ComputeCosSin(angle)
return Rot{c=cs.cosine, s=cs.sine}
}
// Compute the rotation between two unit vectors
@(require_results)
ComputeRotationBetweenUnitVectors :: proc(v1, v2: Vec2) -> Rot {
return NormalizeRot({
c = Dot(v1, v2),
s = Cross(v1, v2),
})
}
// Is this rotation normalized?
@(require_results)
IsNormalizedRot :: proc "c" (q: Rot) -> bool {
// larger tolerance due to failure on mingw 32-bit
qq := q.s * q.s + q.c * q.c
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006
}
// Normalize rotation
@(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot {
mag := math.sqrt(q.s * q.s + q.c * q.c)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0)
return {q.c * invMag, q.s * invMag}
}
// Normalized linear interpolation
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
}
// Compute the angular velocity necessary to rotate between two rotations over a give time
// @param q1 initial rotation
// @param q2 final rotation
@@ -291,8 +366,7 @@ ComputeAngularVelocity :: proc "c" (q1: Rot, q2: Rot, inv_h: f32) -> f32 {
// Get the angle in radians in the range [-pi, pi]
@(require_results)
Rot_GetAngle :: proc "c" (q: Rot) -> f32 {
// todo determinism
return math.atan2(q.s, q.c)
return Atan2(q.s, q.c)
}
// Get the x-axis
@@ -338,18 +412,34 @@ RelativeAngle :: proc "c" (b, a: Rot) -> f32 {
// cos(b - a) = bc * ac + bs * as
s := b.s * a.c - b.c * a.s
c := b.c * a.c + b.s * a.s
return math.atan2(s, c)
return Atan2(s, c)
}
// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi]
@(require_results)
UnwindAngle :: proc "c" (angle: f32) -> f32 {
if angle < -pi {
return angle + 2.0 * pi
} else if angle > pi {
return angle - 2.0 * pi
UnwindAngle :: proc "c" (radians: f32) -> f32 {
if radians < -PI {
return radians + 2.0 * PI
} else if radians > PI {
return radians - 2.0 * PI
}
return angle
return radians
}
// Convert any into the range [-pi, pi] (slow)
@(require_results)
UnwindLargeAngle :: proc "c" (radians: f32) -> f32 {
radians := radians
for radians > PI {
radians -= 2. * PI
}
for radians < -PI {
radians += 2. * PI
}
return radians
}
// Rotate a vector
@@ -380,6 +470,9 @@ InvTransformPoint :: proc "c" (t: Transform, p: Vec2) -> Vec2 {
return {t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy}
}
// Multiply two transforms. If the result is applied to a point p local to frame B,
// the transform would first convert p to a point local to frame A, then into a point
// in the world frame.
// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
@(require_results)
@@ -389,6 +482,7 @@ MulTransforms :: proc "c" (A, B: Transform) -> (C: Transform) {
return
}
// Creates a transform that converts a local point in frame B to a local point in frame A.
// v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
@(require_results)
@@ -469,54 +563,65 @@ AABB_Union :: proc "c" (a, b: AABB) -> (c: AABB) {
return
}
// Compute the bounding box of an array of circles
@(require_results)
Float_IsValid :: proc "c" (a: f32) -> bool {
math.is_nan(a) or_return
math.is_inf(a) or_return
MakeAABB :: proc "c" (points: []Vec2, radius: f32) -> AABB {
a := AABB{points[0], points[0]}
for point in points {
a.lowerBound = Min(a.lowerBound, point)
a.upperBound = Max(a.upperBound, point)
}
r := Vec2{radius, radius}
a.lowerBound = a.lowerBound - r
a.upperBound = a.upperBound + r
return a
}
// Signed separation of a point from a plane
@(require_results)
PlaneSeparation :: proc "c" (plane: Plane, point: Vec2) -> f32 {
return Dot(plane.normal, point) - plane.offset
}
@(require_results)
IsValidFloat :: proc "c" (a: f32) -> bool {
#partial switch math.classify(a) {
case .NaN, .Inf, .Neg_Inf: return false
case: return true
}
}
@(require_results)
IsValidVec2 :: proc "c" (v: Vec2) -> bool {
IsValidFloat(v.x) or_return
IsValidFloat(v.y) or_return
return true
}
@(require_results)
Vec2_IsValid :: proc "c" (v: Vec2) -> bool {
(math.is_nan(v.x) || math.is_nan(v.y)) or_return
(math.is_inf(v.x) || math.is_inf(v.y)) or_return
IsValidRotation :: proc "c" (q: Rot) -> bool {
IsValidFloat(q.s) or_return
IsValidFloat(q.c) or_return
return IsNormalizedRot(q)
}
// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
@(require_results)
IsValidAABB :: proc "c" (aabb: AABB) -> bool {
IsValidVec2(aabb.lowerBound) or_return
IsValidVec2(aabb.upperBound) or_return
(aabb.upperBound.x >= aabb.lowerBound.x) or_return
(aabb.upperBound.y >= aabb.lowerBound.y) or_return
return true
}
// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.
@(require_results)
Rot_IsValid :: proc "c" (q: Rot) -> bool {
(math.is_nan(q.s) || math.is_nan(q.c)) or_return
(math.is_inf(q.s) || math.is_inf(q.c)) or_return
return IsNormalized(q)
}
@(require_results)
Normalize :: proc "c" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
return Vec2_zero
}
invLength := 1 / length
return invLength * v
}
@(require_results)
NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
panic("zero-length Vec2")
}
invLength := 1 / length
return invLength * v
}
@(require_results)
GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
length = Length(v)
if length < 1e-23 {
return
}
invLength := 1 / length
vn = invLength * v
return
IsValidPlane :: proc "c" (plane: Plane) -> bool {
IsValidFloat(plane.offset) or_return
IsValidVec2(plane.normal) or_return
IsNormalized(plane.normal) or_return
return true
}
+352 -231
View File
@@ -37,14 +37,28 @@ EnqueueTaskCallback :: #type proc "c" (task: TaskCallback, itemCount: i32, minRa
// @ingroup world
FinishTaskCallback :: #type proc "c" (userTask: rawptr, userContext: rawptr)
// Optional friction mixing callback. This intentionally provides no context objects because this is called
// from a worker thread.
// @warning This function should not attempt to modify Box2D state or user application state.
// @ingroup world
FrictionCallback :: #type proc "c" (frictionA: f32, userMaterialIdA: i32, frictionB: f32, userMaterialIdB: i32)
// Optional restitution mixing callback. This intentionally provides no context objects because this is called
// from a worker thread.
// @warning This function should not attempt to modify Box2D state or user application state.
// @ingroup world
RestitutionCallback :: #type proc "c" (restitutionA: f32, userMaterialIdA: i32, restitutuionB: f32, userMaterialIdB: i32)
// Result from b2World_RayCastClosest
// @ingroup world
RayResult :: struct {
shapeId: ShapeId,
point: Vec2,
normal: Vec2,
fraction: f32,
hit: bool,
shapeId: ShapeId,
point: Vec2,
normal: Vec2,
fraction: f32,
nodeVisits: i32,
leafVisits: i32,
hit: bool,
}
// World definition used to create a simulation world.
@@ -54,37 +68,53 @@ WorldDef :: struct {
// Gravity vector. Box2D has no up-vector defined.
gravity: Vec2,
// Restitution velocity threshold, usually in m/s. Collisions above this
// Restitution speed threshold, usually in m/s. Collisions above this
// speed have restitution applied (will bounce).
restitutionThreshold: f32,
// This parameter controls how fast overlap is resolved and has units of meters per second
contactPushoutVelocity: f32,
// Threshold velocity for hit events. Usually meters per second.
// Threshold speed for hit events. Usually meters per second.
hitEventThreshold: f32,
// Contact stiffness. Cycles per second.
// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter.
contactHertz: f32,
// Contact bounciness. Non-dimensional.
// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with
// the trade-off that overlap resolution becomes more energetic.
contactDampingRatio: f32,
// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only
// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or
// decreasing the damping ratio.
maxContactPushSpeed: f32,
// Joint stiffness. Cycles per second.
jointHertz: f32,
// Joint bounciness. Non-dimensional.
jointDampingRatio: f32,
// Maximum linear speed. Usually meters per second.
maximumLinearSpeed: f32,
// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB).
frictionCallback: FrictionCallback,
// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB).
restitutionCallback: RestitutionCallback,
// Can bodies go to sleep to improve performance
enableSleep: bool,
// Enable continuous collision
enableContinous: bool,
enableContinuous: bool,
// Number of workers to use with the provided task system. Box2D performs best when using only
// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide
// little benefit and may even harm performance.
// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide
// little benefit and may even harm performance.
// @note Box2D does not create threads. This is the number of threads your applications has created
// that you are allocating to b2World_Step.
// @warning Do not modify the default value unless you are also providing a task system and providing
// task callbacks (enqueueTask and finishTask).
workerCount: i32,
// Function to spawn tasks
@@ -96,6 +126,9 @@ WorldDef :: struct {
// User context that is provided to enqueueTask and finishTask
userTaskContext: rawptr,
// User data
userData: rawptr,
// Used internally to detect a valid definition. DO NOT SET.
internalValue: i32,
}
@@ -138,20 +171,20 @@ BodyDef :: struct {
// The initial world rotation of the body. Use b2MakeRot() if you have an angle.
rotation: Rot,
// The initial linear velocity of the body's origin. Typically in meters per second.
// The initial linear velocity of the body's origin. Usually in meters per second.
linearVelocity: Vec2,
// The initial angular velocity of the body. Radians per second.
angularVelocity: f32,
// Linear damping is use to reduce the linear velocity. The damping parameter
// Linear damping is used to reduce the linear velocity. The damping parameter
// can be larger than 1 but the damping effect becomes sensitive to the
// time step when the damping parameter is large.
// Generally linear damping is undesirable because it makes objects move slowly
// as if they are f32ing.
linearDamping: f32,
// Angular damping is use to reduce the angular velocity. The damping parameter
// Angular damping is used to reduce the angular velocity. The damping parameter
// can be larger than 1.0f but the damping effect becomes sensitive to the
// time step when the damping parameter is large.
// Angular damping can be use slow down rotating bodies.
@@ -160,9 +193,12 @@ BodyDef :: struct {
// Scale the gravity applied to this body. Non-dimensional.
gravityScale: f32,
// Sleep velocity threshold, default is 0.05 meter per second
// Sleep speed threshold, default is 0.05 meters per second
sleepThreshold: f32,
// Optional body name for debugging. Up to 32 characters (excluding null termination)
name: cstring,
// Use this to store application specific body data.
userData: rawptr,
@@ -184,9 +220,9 @@ BodyDef :: struct {
// Used to disable a body. A disabled body does not move or collide.
isEnabled: bool,
// Automatically compute mass and related properties on this body from shapes.
// Triggers whenever a shape is add/removed/changed. Default is true.
automaticMass: bool,
// This allows this body to bypass rotational speed limits. Should only be used
// for circular objects, like wheels.
allowFastRotation: bool,
// Used internally to detect a valid definition. DO NOT SET.
internalValue: i32,
@@ -200,7 +236,7 @@ Filter :: struct {
// The collision category bits. Normally you would just set one bit. The category bits should
// represent your application object types. For example:
// @code{.odin}
// My_Categories :: enum u32 {
// My_Categories :: enum u64 {
// Static = 0x00000001,
// Dynamic = 0x00000002,
// Debris = 0x00000004,
@@ -209,16 +245,16 @@ Filter :: struct {
// };
// @endcode
// Or use a bit_set.
categoryBits: u32,
categoryBits: u64,
// The collision mask bits. This states the categories that this
// shape would accept for collision.
// For example, you may want your player to only collide with static objects
// and other players.
// @code{.odin}
// maskBits = u32(My_Categories.Static | My_Categories.Player);
// maskBits = u64(My_Categories.Static | My_Categories.Player);
// @endcode
maskBits: u32,
maskBits: u64,
// Collision groups allow a certain group of objects to never collide (negative)
// or always collide (positive). A group index of zero has no effect. Non-zero group filtering
@@ -236,11 +272,11 @@ Filter :: struct {
// @ingroup shape
QueryFilter :: struct {
// The collision category bits of this query. Normally you would just set one bit.
categoryBits: u32,
categoryBits: u64,
// The collision mask bits. This states the shape categories that this
// query would accept for collision.
maskBits: u32,
maskBits: u64,
}
@@ -259,13 +295,37 @@ ShapeType :: enum c.int {
// A convex polygon
polygonShape,
// A smooth segment owned by a chain shape
smoothSegmentShape,
// A line segment owned by a chain shape
chainSegmentShape,
}
// The number of shape types
shapeTypeCount :: len(ShapeType)
// Surface materials allow chain shapes to have per segment surface properties.
// @ingroup shape
SurfaceMaterial :: struct {
// The Coulomb (dry) friction coefficient, usually in the range [0,1].
friction: f32,
// The coefficient of restitution (bounce) usually in the range [0,1].
// https://en.wikipedia.org/wiki/Coefficient_of_restitution
restitution: f32,
// The rolling resistance usually in the range [0,1].
rollingResistance: f32,
// The tangent speed for conveyor belts
tangentSpeed: f32,
// User material identifier. This is passed with query results and to friction and restitution
// combining functions. It is not used internally.
userMaterialId: i32,
// Custom debug draw color.
customColor: u32,
}
// Used to create a shape.
// This is a temporary object used to bundle shape creation parameters. You may use
// the same shape definition to create multiple shapes.
@@ -275,58 +335,61 @@ ShapeDef :: struct {
// Use this to store application specific shape data.
userData: rawptr,
// The Coulomb (dry) friction coefficient, usually in the range [0,1].
friction: f32,
// The restitution (bounce) usually in the range [0,1].
restitution: f32,
// The surface material for this shape.
material: SurfaceMaterial,
// The density, usually in kg/m^2.
// This is not part of the surface material because this is for the interior, which may have
// other considerations, such as being hollow. For example a wood barrel may be hollow or full of water.
density: f32,
// Collision filtering data.
filter: Filter,
// Custom debug draw color.
customColor: u32,
// A sensor shape generates overlap events but never generates a collision response.
// Sensors do not have continuous collision. Instead, use a ray or shape cast for those scenarios.
// Sensors still contribute to the body mass if they have non-zero density.
// @note Sensor events are disabled by default.
// @see enableSensorEvents
isSensor: bool,
// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors.
enableSensorEvents: bool,
// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.
enableContactEvents: bool,
// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors.
// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.
enableHitEvents: bool,
// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive
// and must be carefully handled due to threading. Ignored for sensors.
enablePreSolveEvents: bool,
// Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides
// that behavior and causes contact creation. This significantly slows down static body creation which can be important
// when there are many static shapes.
forceContactCreation: bool,
// When shapes are created they will scan the environment for collision the next time step. This can significantly slow down
// static body creation when there are many static shapes.
// This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation.
invokeContactCreation: bool,
// Should the body update the mass properties when this shape is created. Default is true.
updateBodyMass: bool,
// Used internally to detect a valid definition. DO NOT SET.
internalValue: i32,
}
// Used to create a chain of edges. This is designed to eliminate ghost collisions with some limitations.
// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations.
// - chains are one-sided
// - chains have no mass and should be used on static bodies
// - chains have a counter-clockwise winding order
// - chains have a counter-clockwise winding order (normal points right of segment direction)
// - chains are either a loop or open
// - a chain must have at least 4 points
// - the distance between any two points must be greater than b2_linearSlop
// - the distance between any two points must be greater than B2_LINEAR_SLOP
// - a chain shape should not self intersect (this is not validated)
// - an open chain shape has NO COLLISION on the first and final edge
// - you may overlap two open chains on their first three and/or last three points to get smooth collision
// - a chain shape creates multiple smooth edges shapes on the body
// - a chain shape creates multiple line segment shapes on the body
// https://en.wikipedia.org/wiki/Polygonal_chain
// Must be initialized using b2DefaultChainDef().
// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature.
@@ -341,11 +404,12 @@ ChainDef :: struct {
// The point count, must be 4 or more.
count: i32,
// The friction coefficient, usually in the range [0,1].
friction: f32,
// Surface materials for each segment. These are cloned.
materials: [^]SurfaceMaterial `fmt:"v,materialCount"`,
// The restitution (elasticity) usually in the range [0,1].
restitution: f32,
// The material count. Must be 1 or count. This allows you to provide one
// material for all segments or a unique material per segment.
materialCount: i32,
// Contact filtering data.
filter: Filter,
@@ -353,6 +417,9 @@ ChainDef :: struct {
// Indicates a closed chain formed by connecting the first and last points
isLoop: bool,
// Enable sensors to detect this chain. False by default.
enableSensorEvents: bool,
// Used internally to detect a valid definition. DO NOT SET.
internalValue: i32,
}
@@ -365,29 +432,28 @@ Profile :: struct {
pairs: f32,
collide: f32,
solve: f32,
buildIslands: f32,
mergeIslands: f32,
prepareStages: f32,
solveConstraints: f32,
prepareTasks: f32,
solverTasks: f32,
prepareConstraints: f32,
integrateVelocities: f32,
warmStart: f32,
solveVelocities: f32,
solveImpulses: f32,
integratePositions: f32,
relaxVelocities: f32,
relaxImpulses: f32,
applyRestitution: f32,
storeImpulses: f32,
finalizeBodies: f32,
splitIslands: f32,
sleepIslands: f32,
transforms: f32,
hitEvents: f32,
broadphase: f32,
continuous: f32,
refit: f32,
bullets: f32,
sleepIslands: f32,
sensors: f32,
}
// Counters that give details of the simulation size.
Counters :: struct {
staticBodyCount: i32,
bodyCount: i32,
shapeCount: i32,
contactCount: i32,
@@ -409,6 +475,7 @@ Counters :: struct {
// @ingroup joint
JointType :: enum c.int {
distanceJoint,
filterJoint,
motorJoint,
mouseJoint,
prismaticJoint,
@@ -522,7 +589,7 @@ MotorJointDef :: struct {
// applying huge forces. This also applies rotation constraint heuristic to improve control.
// @ingroup mouse_joint
MouseJointDef :: struct {
// The first attached body.
// The first attached body. This is assumed to be static.
bodyIdA: BodyId,
// The second attached body.
@@ -550,6 +617,22 @@ MouseJointDef :: struct {
internalValue: i32,
}
// A filter joint is used to disable collision between two specific bodies.
//
// @ingroup filter_joint
FilterJointDef :: struct {
/// The first attached body.
bodyIdA: BodyId,
/// The second attached body.
bodyIdB: BodyId,
/// User data pointer
userData: rawptr,
/// Used internally to detect a valid definition. DO NOT SET.
internalValue: i32,
}
// Prismatic joint definition
//
@@ -656,10 +739,10 @@ RevoluteJointDef :: struct {
// A flag to enable joint limits
enableLimit: bool,
// The lower angle for the joint limit in radians
// The lower angle for the joint limit in radians. Minimum of -0.95*pi radians.
lowerAngle: f32,
// The upper angle for the joint limit in radians
// The upper angle for the joint limit in radians. Maximum of 0.95*pi radians.
upperAngle: f32,
// A flag to enable the joint motor
@@ -790,6 +873,27 @@ WheelJointDef :: struct {
internalValue: i32,
}
// The explosion definition is used to configure options for explosions. Explosions
// consider shape geometry when computing the impulse.
// @ingroup world
ExplosionDef :: struct {
/// Mask bits to filter shapes
maskBits: u64,
/// The center of the explosion in world space
position: Vec2,
/// The radius of the explosion
radius: f32,
/// The falloff distance beyond the radius. Impulse is reduced to zero at this distance.
falloff: f32,
/// Impulse per unit length. This applies an impulse according to the shape perimeter that
/// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This
/// may be negative for implosions.
impulsePerLength: f32,
}
/**
* @defgroup events Events
@@ -818,11 +922,18 @@ SensorBeginTouchEvent :: struct {
}
// An end touch event is generated when a shape stops overlapping a sensor shape.
// These include things like setting the transform, destroying a body or shape, or changing
// a filter. You will also get an end event if the sensor or visitor are destroyed.
// Therefore you should always confirm the shape id is valid using b2Shape_IsValid.
SensorEndTouchEvent :: struct {
// The id of the sensor shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
sensorShapeId: ShapeId,
// The id of the dynamic shape that stopped touching the sensor shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
visitorShapeId: ShapeId,
}
@@ -850,14 +961,25 @@ ContactBeginTouchEvent :: struct {
// Id of the second shape
shapeIdB: ShapeId,
// The initial contact manifold. This is recorded before the solver is called,
// so all the impulses will be zero.
manifold: Manifold,
}
// An end touch event is generated when two shapes stop touching.
// You will get an end event if you do anything that destroys contacts previous to the last
// world step. These include things like setting the transform, destroying a body
// or shape, or changing a filter or body type.
ContactEndTouchEvent :: struct {
// Id of the first shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
shapeIdA: ShapeId,
// Id of the second shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
shapeIdB: ShapeId,
}
@@ -994,201 +1116,191 @@ OverlapResultFcn :: #type proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool
// @ingroup world
CastResultFcn :: #type proc "c" (shapeId: ShapeId, point: Vec2, normal: Vec2, fraction: f32, ctx: rawptr) -> f32
// These colors are used for debug draw.
// See https://www.rapidtables.com/web/color/index.html
// Used to collect collision planes for character movers.
// Return true to continue gathering planes.
PlaneResultFcn :: #type proc "c" (shapeId: ShapeId, plane: ^PlaneResult, ctx: rawptr) -> bool
// These colors are used for debug draw and mostly match the named SVG colors.
// See https://www.rapidtables.com/web/color/index.html
// https://johndecember.com/html/spec/colorsvg.html
// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg
HexColor :: enum c.int {
AliceBlue = 0xf0f8ff,
AntiqueWhite = 0xfaebd7,
Aqua = 0x00ffff,
Aquamarine = 0x7fffd4,
Azure = 0xf0ffff,
Beige = 0xf5f5dc,
Bisque = 0xffe4c4,
AliceBlue = 0xF0F8FF,
AntiqueWhite = 0xFAEBD7,
Aqua = 0x00FFFF,
Aquamarine = 0x7FFFD4,
Azure = 0xF0FFFF,
Beige = 0xF5F5DC,
Bisque = 0xFFE4C4,
Black = 0x000000,
BlanchedAlmond = 0xffebcd,
Blue = 0x0000ff,
BlueViolet = 0x8a2be2,
Brown = 0xa52a2a,
Burlywood = 0xdeb887,
CadetBlue = 0x5f9ea0,
Chartreuse = 0x7fff00,
Chocolate = 0xd2691e,
Coral = 0xff7f50,
CornflowerBlue = 0x6495ed,
Cornsilk = 0xfff8dc,
Crimson = 0xdc143c,
Cyan = 0x00ffff,
DarkBlue = 0x00008b,
DarkCyan = 0x008b8b,
DarkGoldenrod = 0xb8860b,
DarkGray = 0xa9a9a9,
BlanchedAlmond = 0xFFEBCD,
Blue = 0x0000FF,
BlueViolet = 0x8A2BE2,
Brown = 0xA52A2A,
Burlywood = 0xDEB887,
CadetBlue = 0x5F9EA0,
Chartreuse = 0x7FFF00,
Chocolate = 0xD2691E,
Coral = 0xFF7F50,
CornflowerBlue = 0x6495ED,
Cornsilk = 0xFFF8DC,
Crimson = 0xDC143C,
Cyan = 0x00FFFF,
DarkBlue = 0x00008B,
DarkCyan = 0x008B8B,
DarkGoldenRod = 0xB8860B,
DarkGray = 0xA9A9A9,
DarkGreen = 0x006400,
DarkKhaki = 0xbdb76b,
DarkMagenta = 0x8b008b,
DarkOliveGreen = 0x556b2f,
DarkOrange = 0xff8c00,
DarkOrchid = 0x9932cc,
DarkRed = 0x8b0000,
DarkSalmon = 0xe9967a,
DarkSeaGreen = 0x8fbc8f,
DarkSlateBlue = 0x483d8b,
DarkSlateGray = 0x2f4f4f,
DarkTurquoise = 0x00ced1,
DarkViolet = 0x9400d3,
DeepPink = 0xff1493,
DeepSkyBlue = 0x00bfff,
DarkKhaki = 0xBDB76B,
DarkMagenta = 0x8B008B,
DarkOliveGreen = 0x556B2F,
DarkOrange = 0xFF8C00,
DarkOrchid = 0x9932CC,
DarkRed = 0x8B0000,
DarkSalmon = 0xE9967A,
DarkSeaGreen = 0x8FBC8F,
DarkSlateBlue = 0x483D8B,
DarkSlateGray = 0x2F4F4F,
DarkTurquoise = 0x00CED1,
DarkViolet = 0x9400D3,
DeepPink = 0xFF1493,
DeepSkyBlue = 0x00BFFF,
DimGray = 0x696969,
DodgerBlue = 0x1e90ff,
Firebrick = 0xb22222,
FloralWhite = 0xfffaf0,
ForestGreen = 0x228b22,
Fuchsia = 0xff00ff,
Gainsboro = 0xdcdcdc,
GhostWhite = 0xf8f8ff,
Gold = 0xffd700,
Goldenrod = 0xdaa520,
Gray = 0xbebebe,
Gray1 = 0x1a1a1a,
Gray2 = 0x333333,
Gray3 = 0x4d4d4d,
Gray4 = 0x666666,
Gray5 = 0x7f7f7f,
Gray6 = 0x999999,
Gray7 = 0xb3b3b3,
Gray8 = 0xcccccc,
Gray9 = 0xe5e5e5,
Green = 0x00ff00,
GreenYellow = 0xadff2f,
Honeydew = 0xf0fff0,
HotPink = 0xff69b4,
IndianRed = 0xcd5c5c,
Indigo = 0x4b0082,
Ivory = 0xfffff0,
Khaki = 0xf0e68c,
Lavender = 0xe6e6fa,
LavenderBlush = 0xfff0f5,
LawnGreen = 0x7cfc00,
LemonChiffon = 0xfffacd,
LightBlue = 0xadd8e6,
LightCoral = 0xf08080,
LightCyan = 0xe0ffff,
LightGoldenrod = 0xeedd82,
LightGoldenrodYellow = 0xfafad2,
LightGray = 0xd3d3d3,
LightGreen = 0x90ee90,
LightPink = 0xffb6c1,
LightSalmon = 0xffa07a,
LightSeaGreen = 0x20b2aa,
LightSkyBlue = 0x87cefa,
LightSlateBlue = 0x8470ff,
DodgerBlue = 0x1E90FF,
FireBrick = 0xB22222,
FloralWhite = 0xFFFAF0,
ForestGreen = 0x228B22,
Fuchsia = 0xFF00FF,
Gainsboro = 0xDCDCDC,
GhostWhite = 0xF8F8FF,
Gold = 0xFFD700,
GoldenRod = 0xDAA520,
Gray = 0x808080,
Green = 0x008000,
GreenYellow = 0xADFF2F,
HoneyDew = 0xF0FFF0,
HotPink = 0xFF69B4,
IndianRed = 0xCD5C5C,
Indigo = 0x4B0082,
Ivory = 0xFFFFF0,
Khaki = 0xF0E68C,
Lavender = 0xE6E6FA,
LavenderBlush = 0xFFF0F5,
LawnGreen = 0x7CFC00,
LemonChiffon = 0xFFFACD,
LightBlue = 0xADD8E6,
LightCoral = 0xF08080,
LightCyan = 0xE0FFFF,
LightGoldenRodYellow = 0xFAFAD2,
LightGray = 0xD3D3D3,
LightGreen = 0x90EE90,
LightPink = 0xFFB6C1,
LightSalmon = 0xFFA07A,
LightSeaGreen = 0x20B2AA,
LightSkyBlue = 0x87CEFA,
LightSlateGray = 0x778899,
LightSteelBlue = 0xb0c4de,
LightYellow = 0xffffe0,
Lime = 0x00ff00,
LimeGreen = 0x32cd32,
Linen = 0xfaf0e6,
Magenta = 0xff00ff,
Maroon = 0xb03060,
MediumAquamarine = 0x66cdaa,
MediumBlue = 0x0000cd,
MediumOrchid = 0xba55d3,
MediumPurple = 0x9370db,
MediumSeaGreen = 0x3cb371,
MediumSlateBlue = 0x7b68ee,
MediumSpringGreen = 0x00fa9a,
MediumTurquoise = 0x48d1cc,
MediumVioletRed = 0xc71585,
LightSteelBlue = 0xB0C4DE,
LightYellow = 0xFFFFE0,
Lime = 0x00FF00,
LimeGreen = 0x32CD32,
Linen = 0xFAF0E6,
Magenta = 0xFF00FF,
Maroon = 0x800000,
MediumAquaMarine = 0x66CDAA,
MediumBlue = 0x0000CD,
MediumOrchid = 0xBA55D3,
MediumPurple = 0x9370DB,
MediumSeaGreen = 0x3CB371,
MediumSlateBlue = 0x7B68EE,
MediumSpringGreen = 0x00FA9A,
MediumTurquoise = 0x48D1CC,
MediumVioletRed = 0xC71585,
MidnightBlue = 0x191970,
MintCream = 0xf5fffa,
MistyRose = 0xffe4e1,
Moccasin = 0xffe4b5,
NavajoWhite = 0xffdead,
MintCream = 0xF5FFFA,
MistyRose = 0xFFE4E1,
Moccasin = 0xFFE4B5,
NavajoWhite = 0xFFDEAD,
Navy = 0x000080,
NavyBlue = 0x000080,
OldLace = 0xfdf5e6,
OldLace = 0xFDF5E6,
Olive = 0x808000,
OliveDrab = 0x6b8e23,
Orange = 0xffa500,
OrangeRed = 0xff4500,
Orchid = 0xda70d6,
PaleGoldenrod = 0xeee8aa,
PaleGreen = 0x98fb98,
PaleTurquoise = 0xafeeee,
PaleVioletRed = 0xdb7093,
PapayaWhip = 0xffefd5,
PeachPuff = 0xffdab9,
Peru = 0xcd853f,
Pink = 0xffc0cb,
Plum = 0xdda0dd,
PowderBlue = 0xb0e0e6,
Purple = 0xa020f0,
OliveDrab = 0x6B8E23,
Orange = 0xFFA500,
OrangeRed = 0xFF4500,
Orchid = 0xDA70D6,
PaleGoldenRod = 0xEEE8AA,
PaleGreen = 0x98FB98,
PaleTurquoise = 0xAFEEEE,
PaleVioletRed = 0xDB7093,
PapayaWhip = 0xFFEFD5,
PeachPuff = 0xFFDAB9,
Peru = 0xCD853F,
Pink = 0xFFC0CB,
Plum = 0xDDA0DD,
PowderBlue = 0xB0E0E6,
Purple = 0x800080,
RebeccaPurple = 0x663399,
Red = 0xff0000,
RosyBrown = 0xbc8f8f,
RoyalBlue = 0x4169e1,
SaddleBrown = 0x8b4513,
Salmon = 0xfa8072,
SandyBrown = 0xf4a460,
SeaGreen = 0x2e8b57,
Seashell = 0xfff5ee,
Sienna = 0xa0522d,
Silver = 0xc0c0c0,
SkyBlue = 0x87ceeb,
SlateBlue = 0x6a5acd,
Red = 0xFF0000,
RosyBrown = 0xBC8F8F,
RoyalBlue = 0x4169E1,
SaddleBrown = 0x8B4513,
Salmon = 0xFA8072,
SandyBrown = 0xF4A460,
SeaGreen = 0x2E8B57,
SeaShell = 0xFFF5EE,
Sienna = 0xA0522D,
Silver = 0xC0C0C0,
SkyBlue = 0x87CEEB,
SlateBlue = 0x6A5ACD,
SlateGray = 0x708090,
Snow = 0xfffafa,
SpringGreen = 0x00ff7f,
SteelBlue = 0x4682b4,
Tan = 0xd2b48c,
Snow = 0xFFFAFA,
SpringGreen = 0x00FF7F,
SteelBlue = 0x4682B4,
Tan = 0xD2B48C,
Teal = 0x008080,
Thistle = 0xd8bfd8,
Tomato = 0xff6347,
Turquoise = 0x40e0d0,
Violet = 0xee82ee,
VioletRed = 0xd02090,
Wheat = 0xf5deb3,
White = 0xffffff,
WhiteSmoke = 0xf5f5f5,
Yellow = 0xffff00,
YellowGreen = 0x9acd32,
Box2DRed = 0xdc3132,
Box2DBlue = 0x30aebf,
Box2DGreen = 0x8cc924,
Box2DYellow = 0xffee8c,
Thistle = 0xD8BFD8,
Tomato = 0xFF6347,
Turquoise = 0x40E0D0,
Violet = 0xEE82EE,
Wheat = 0xF5DEB3,
White = 0xFFFFFF,
WhiteSmoke = 0xF5F5F5,
Yellow = 0xFFFF00,
YellowGreen = 0x9ACD32,
Box2DRed = 0xDC3132,
Box2DBlue = 0x30AEBF,
Box2DGreen = 0x8CC924,
Box2DYellow = 0xFFEE8C,
}
// This struct holds callbacks you can implement to draw a Box2D world.
// @ingroup world
DebugDraw :: struct {
// Draw a closed polygon provided in CCW order.
DrawPolygon: proc "c" (vertices: [^]Vec2, vertexCount: c.int, color: HexColor, ctx: rawptr),
DrawPolygonFcn: proc "c" (vertices: [^]Vec2, vertexCount: c.int, color: HexColor, ctx: rawptr),
// Draw a solid closed polygon provided in CCW order.
DrawSolidPolygon: proc "c" (transform: Transform, vertices: [^]Vec2, vertexCount: c.int, radius: f32, colr: HexColor, ctx: rawptr ),
DrawSolidPolygonFcn: proc "c" (transform: Transform, vertices: [^]Vec2, vertexCount: c.int, radius: f32, colr: HexColor, ctx: rawptr ),
// Draw a circle.
DrawCircle: proc "c" (center: Vec2, radius: f32, color: HexColor, ctx: rawptr),
DrawCircleFcn: proc "c" (center: Vec2, radius: f32, color: HexColor, ctx: rawptr),
// Draw a solid circle.
DrawSolidCircle: proc "c" (transform: Transform, radius: f32, color: HexColor, ctx: rawptr),
// Draw a capsule.
DrawCapsule: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr),
DrawSolidCircleFcn: proc "c" (transform: Transform, radius: f32, color: HexColor, ctx: rawptr),
// Draw a solid capsule.
DrawSolidCapsule: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr),
DrawSolidCapsuleFcn: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr),
// Draw a line segment.
DrawSegment: proc "c" (p1, p2: Vec2, color: HexColor, ctx: rawptr),
DrawSegmentFcn: proc "c" (p1, p2: Vec2, color: HexColor, ctx: rawptr),
// Draw a transform. Choose your own length scale.
DrawTransform: proc "c" (transform: Transform, ctx: rawptr),
DrawTransformFcn: proc "c" (transform: Transform, ctx: rawptr),
// Draw a point.
DrawPoint: proc "c" (p: Vec2, size: f32, color: HexColor, ctx: rawptr),
DrawPointFcn: proc "c" (p: Vec2, size: f32, color: HexColor, ctx: rawptr),
// Draw a string.
DrawString: proc "c" (p: Vec2, s: cstring, ctx: rawptr),
// Draw a string in world space.
DrawStringFcn: proc "c" (p: Vec2, s: cstring, color: HexColor, ctx: rawptr),
// Bounds to use if restricting drawing to a rectangular region
drawingBounds: AABB,
@@ -1206,11 +1318,14 @@ DebugDraw :: struct {
drawJointExtras: bool,
// Option to draw the bounding boxes for shapes
drawAABBs: bool,
drawBounds: bool,
// Option to draw the mass and center of mass of dynamic bodies
drawMass: bool,
// Option to draw body names
drawBodyNames: bool,
// Option to draw contact points
drawContacts: bool,
@@ -1223,9 +1338,15 @@ DebugDraw :: struct {
// Option to draw contact normal impulses
drawContactImpulses: bool,
// Option to draw contact feature ids
drawContactFeatures: bool,
// Option to draw contact friction impulses
drawFrictionImpulses: bool,
// Option to draw islands as bounding boxes
drawIslands: bool,
// User context that is passed as an argument to drawing callback functions
userContext: rawptr,
}
}
+5 -5
View File
@@ -7,20 +7,20 @@
# CC = $(shell brew --prefix llvm)/bin/clang
# LD = $(shell brew --prefix llvm)/bin/wasm-ld
VERSION = 3.0.0
VERSION = 3.1.0
SRCS = $(wildcard box2d-$(VERSION)/src/*.c)
OBJS_SIMD = $(SRCS:.c=_simd.o)
OBJS = $(SRCS:.c=.o)
SYSROOT = $(shell odin root)/vendor/libc
CFLAGS = -Ibox2d-$(VERSION)/include -Ibox2d-$(VERSION)/extern/simde --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT)
CFLAGS = -Ibox2d-$(VERSION)/include --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT)
all: lib/box2d_wasm.o lib/box2d_wasm_simd.o clean
all: lib/box2d_wasm.o lib/box2d_wasm_simd.o
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD $< -o $@
%_simd.o: %.c
$(CC) -c $(CFLAGS) -msimd128 $< -o $@
$(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD -msimd128 $< -o $@
lib/box2d_wasm.o: $(OBJS)
$(LD) -r -o lib/box2d_wasm.o $(OBJS)
+84 -5
View File
@@ -837,6 +837,16 @@ FEATURE :: enum i32 {
OPTIONS8 = 36,
OPTIONS9 = 37,
WAVE_MMA = 38,
OPTIONS10 = 39,
OPTIONS11 = 40,
OPTIONS12 = 41,
OPTIONS13 = 42,
OPTIONS14 = 43,
OPTIONS15 = 44,
OPTIONS16 = 45,
OPTIONS17 = 46,
OPTIONS18 = 47,
OPTIONS19 = 48,
}
SHADER_MIN_PRECISION_SUPPORT :: enum i32 {
@@ -1195,6 +1205,74 @@ FEATURE_DATA_OPTIONS9 :: struct {
WaveMMATier: WAVE_MMA_TIER,
}
FEATURE_DATA_OPTIONS10 :: struct {
VariableRateShadingSumCombinerSupported: BOOL,
MeshShaderPerPrimitiveShadingRateSupported: BOOL,
}
FEATURE_DATA_OPTIONS11 :: struct {
AtomicInt64OnDescriptorHeapResourceSupported: BOOL,
}
TRI_STATE :: enum i32 {
UNKNOWN = -1,
FALSE = 0,
TRUE = 1,
}
FEATURE_DATA_OPTIONS12 :: struct {
MSPrimitivesPipelineStatisticIncludesCulledPrimitives: TRI_STATE,
EnhancedBarriersSupported: BOOL,
RelaxedFormatCastingSupported: BOOL,
}
FEATURE_DATA_OPTIONS13 :: struct {
UnrestrictedBufferTextureCopyPitchSupported: BOOL,
UnrestrictedVertexElementAlignmentSupported: BOOL,
InvertedViewportHeightFlipsYSupported: BOOL,
InvertedViewportDepthFlipsZSupported: BOOL,
TextureCopyBetweenDimensionsSupported: BOOL,
AlphaBlendFactorSupported: BOOL,
}
FEATURE_DATA_OPTIONS14 :: struct {
AdvancedTextureOpsSupported: BOOL,
WriteableMSAATexturesSupported: BOOL,
IndependentFrontAndBackStencilRefMaskSupported: BOOL,
}
FEATURE_DATA_OPTIONS15 :: struct {
TriangleFanSupported: BOOL,
DynamicIndexBufferStripCutSupported: BOOL,
}
FEATURE_DATA_OPTIONS16 :: struct {
DynamicDepthBiasSupported: BOOL,
GPUUploadHeapSupported: BOOL,
}
FEATURE_DATA_OPTIONS17 :: struct {
NonNormalizedCoordinateSamplersSupported: BOOL,
ManualWriteTrackingResourceSupported: BOOL,
}
FEATURE_DATA_OPTIONS18 :: struct {
RenderPassesValid: BOOL,
}
FEATURE_DATA_OPTIONS19 :: struct {
MismatchingOutputDimensionsSupported: BOOL,
SupportedSampleCountsWithNoOutputs: u32,
PointSamplingAddressesNeverRoundUp: BOOL,
RasterizerDesc2Supported: BOOL,
NarrowQuadrilateralLinesSupported: BOOL,
AnisoFilterWithPointMipSupported: BOOL,
MaxSamplerDescriptorHeapSize: u32,
MaxSamplerDescriptorHeapSizeWithStaticSamplers: u32,
MaxViewDescriptorHeapSize: u32,
ComputeOnlyCustomHeapSupported: BOOL,
}
WAVE_MMA_INPUT_DATATYPE :: enum i32 {
INVALID = 0,
BYTE = 1,
@@ -1238,10 +1316,11 @@ RESOURCE_ALLOCATION_INFO1 :: struct {
}
HEAP_TYPE :: enum i32 {
DEFAULT = 1,
UPLOAD = 2,
READBACK = 3,
CUSTOM = 4,
DEFAULT = 1,
UPLOAD = 2,
READBACK = 3,
CUSTOM = 4,
GPU_UPLOAD = 5,
}
CPU_PAGE_PROPERTY :: enum i32 {
@@ -1473,7 +1552,7 @@ RESOURCE_STATE_GENERIC_READ :: RESOURCE_STATES{
.VERTEX_AND_CONSTANT_BUFFER, .INDEX_BUFFER, .NON_PIXEL_SHADER_RESOURCE, .PIXEL_SHADER_RESOURCE, .INDIRECT_ARGUMENT, .COPY_SOURCE,
}
RESOURCE_STATE_ALL_SHADER_RESOURCE :: RESOURCE_STATES{
.SHADING_RATE_SOURCE, .INDEX_BUFFER,
.NON_PIXEL_SHADER_RESOURCE, .PIXEL_SHADER_RESOURCE,
}
RESOURCE_BARRIER_TYPE :: enum i32 {
+3 -2
View File
@@ -193,7 +193,6 @@ foreign glfw {
SetWindowPosCallback :: proc(window: WindowHandle, cbfun: WindowPosProc) -> WindowPosProc ---
SetFramebufferSizeCallback :: proc(window: WindowHandle, cbfun: FramebufferSizeProc) -> FramebufferSizeProc ---
SetDropCallback :: proc(window: WindowHandle, cbfun: DropProc) -> DropProc ---
SetMonitorCallback :: proc(window: WindowHandle, cbfun: MonitorProc) -> MonitorProc ---
SetWindowMaximizeCallback :: proc(window: WindowHandle, cbfun: WindowMaximizeProc) -> WindowMaximizeProc ---
SetWindowContentScaleCallback :: proc(window: WindowHandle, cbfun: WindowContentScaleProc) -> WindowContentScaleProc ---
@@ -204,7 +203,9 @@ foreign glfw {
SetCharCallback :: proc(window: WindowHandle, cbfun: CharProc) -> CharProc ---
SetCharModsCallback :: proc(window: WindowHandle, cbfun: CharModsProc) -> CharModsProc ---
SetCursorEnterCallback :: proc(window: WindowHandle, cbfun: CursorEnterProc) -> CursorEnterProc ---
SetJoystickCallback :: proc(cbfun: JoystickProc) -> JoystickProc ---
SetMonitorCallback :: proc(cbfun: MonitorProc) -> MonitorProc ---
SetJoystickCallback :: proc(cbfun: JoystickProc) -> JoystickProc ---
SetErrorCallback :: proc(cbfun: ErrorProc) -> ErrorProc ---
+1 -1
View File
@@ -48,7 +48,7 @@ WindowMaximizeProc :: #type proc "c" (window: WindowHandle, iconified: c.int
WindowContentScaleProc :: #type proc "c" (window: WindowHandle, xscale, yscale: f32)
FramebufferSizeProc :: #type proc "c" (window: WindowHandle, width, height: c.int)
DropProc :: #type proc "c" (window: WindowHandle, count: c.int, paths: [^]cstring)
MonitorProc :: #type proc "c" (window: WindowHandle, event: c.int)
MonitorProc :: #type proc "c" (monitor: MonitorHandle, event: c.int)
KeyProc :: #type proc "c" (window: WindowHandle, key, scancode, action, mods: c.int)
MouseButtonProc :: #type proc "c" (window: WindowHandle, button, action, mods: c.int)
+3 -3
View File
@@ -47,19 +47,19 @@ bool __isnanf(float);
bool __isnand(double);
#define isnan(x) \
( sizeof(x) == sizeof(float) ? __isnanf((float)(x)) \
: : __isnand((double)(x)))
: __isnand((double)(x)))
bool __isinff(float);
bool __isinfd(double);
#define isinf(x) \
( sizeof(x) == sizeof(float) ? __isinff((float)(x)) \
: : __isinfd((double)(x)))
: __isinfd((double)(x)))
bool __isfinitef(float);
bool __isfinited(double);
#define isfinite(x) \
( sizeof(x) == sizeof(float) ? __isfinitef((float)(x)) \
: : __isfinited((double)(x)))
: __isfinited((double)(x)))
#ifdef __cplusplus
}
+23
View File
@@ -0,0 +1,23 @@
#ifdef __cplusplus
extern "C" {
#endif
#pragma once
#include <stdint.h>
#define CLOCK_MONOTONIC 1
struct timespec
{
int64_t tv_sec;
int64_t tv_nsec;
};
int clock_gettime(int clockid, struct timespec *tp);
int sched_yield();
#ifdef __cplusplus
}
#endif
+35
View File
@@ -0,0 +1,35 @@
package odin_libc
import "core:time"
import "core:thread"
Clock :: enum i32 {
Monotonic = 1,
}
Time_Spec :: struct {
tv_sec: i64,
tv_nsec: i64,
}
@(require, linkage="strong", link_name="clock_gettime")
clock_gettine :: proc "c" (clockid: Clock, tp: ^Time_Spec) -> i32 {
switch clockid {
case .Monotonic:
tick := time.tick_now()
tp.tv_sec = tick._nsec/1e9
tp.tv_nsec = tick._nsec%1e9/1000
return 0
case: return -1
}
}
@(require, linkage="strong", link_name="sched_yield")
sched_yield :: proc "c" () -> i32 {
when thread.IS_SUPPORTED {
context = g_ctx
thread.yield()
}
return 0
}
+2 -2
View File
@@ -20,9 +20,9 @@ foreign import lib { LIB }
BINDINGS_VERSION_MAJOR :: 0
BINDINGS_VERSION_MINOR :: 11
BINDINGS_VERSION_REVISION :: 21
BINDINGS_VERSION_REVISION :: 22
BINDINGS_VERSION :: [3]u32{BINDINGS_VERSION_MAJOR, BINDINGS_VERSION_MINOR, BINDINGS_VERSION_REVISION}
BINDINGS_VERSION_STRING :: "0.11.21"
BINDINGS_VERSION_STRING :: "0.11.22"
@(init)
version_check :: proc() {
+2 -2
View File
@@ -194,7 +194,7 @@ foreign lib {
resampler_get_expected_output_frame_count :: proc(pResampler: ^resampler, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result ---
/*
Resets the resampler's timer and clears it's internal cache.
Resets the resampler's timer and clears its internal cache.
*/
resampler_reset :: proc(pResampler: ^resampler) -> result ---
}
@@ -421,7 +421,7 @@ foreign lib {
/*
Copies a channel map.
Both input and output channel map buffers must have a capacity of at at least `channels`.
Both input and output channel map buffers must have a capacity of at least `channels`.
*/
channel_map_copy :: proc(pOut: [^]channel, pIn: [^]channel, channels: u32) ---
+2 -2
View File
@@ -71,7 +71,7 @@ decoder :: struct {
pInputCache: rawptr, /* In input format. Can be null if it's not needed. */
inputCacheCap: u64, /* The capacity of the input cache. */
inputCacheConsumed: u64, /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
inputCacheRemaining: u64, /* The number of valid frames remaining in the cahce. */
inputCacheRemaining: u64, /* The number of valid frames remaining in the cache. */
allocationCallbacks: allocation_callbacks,
data: struct #raw_union {
vfs: struct {
@@ -111,7 +111,7 @@ foreign lib {
decoder_read_pcm_frames :: proc(pDecoder: ^decoder, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
/*
Seeks to a PCM frame based on it's absolute index.
Seeks to a PCM frame based on its absolute index.
This is not thread safe without your own synchronization.
*/
+13 -5
View File
@@ -12,6 +12,8 @@ foreign lib {
device_job_thread_uninit :: proc(pJobThread: ^device_job_thread, pAllocationCallbacks: ^allocation_callbacks) ---
device_job_thread_post :: proc(pJobThread: ^device_job_thread, pJob: ^job) -> result ---
device_job_thread_next :: proc(pJobThread: ^device_job_thread, pJob: ^job) -> result ---
device_id_equal :: proc(pA: ^device_id, pB: ^device_id) -> b32 ---
/*
Initializes a `ma_context_config` object.
@@ -370,6 +372,9 @@ foreign lib {
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.
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.
Parameters
----------
@@ -411,7 +416,7 @@ foreign lib {
See Also
--------
ma_context_get_devices()
ma_context_enumerate_devices()
*/
context_get_devices :: proc(pContext: ^context_type, ppPlaybackDeviceInfos: ^[^]device_info, pPlaybackDeviceCount: ^u32, ppCaptureDeviceInfos: ^[^]device_info, pCaptureDeviceCount: ^u32) -> result ---
@@ -550,7 +555,7 @@ foreign lib {
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
The frequency at which data is delivered to and from a device depends on the size of its 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
@@ -624,7 +629,7 @@ foreign lib {
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.
`ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its 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
@@ -664,7 +669,7 @@ foreign lib {
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 linear resampler applies a low-pass filter as part of its 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)`.
@@ -741,6 +746,9 @@ foreign lib {
pulse.pStreamNameCapture
PulseAudio only. Sets the stream name for capture.
pulse.channelMap
PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF.
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
@@ -914,7 +922,7 @@ foreign lib {
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
You only need to use this function if you want to configure the context differently to its 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.
+10 -3
View File
@@ -427,6 +427,7 @@ device_config :: struct {
pulse: struct {
pStreamNamePlayback: cstring,
pStreamNameCapture: cstring,
channelMap: i32,
},
coreaudio: struct {
allowNominalSampleRateChange: b32, /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
@@ -443,6 +444,7 @@ device_config :: struct {
allowedCapturePolicy: aaudio_allowed_capture_policy,
noAutoStartAfterReroute: b32,
enableCompatibilityWorkarounds: b32,
allowSetBufferCapacity: b32,
},
}
@@ -514,7 +516,7 @@ and on output returns detailed information about the device in `ma_device_info`.
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
This is a little bit more complicated than initialization of the context due to its 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
@@ -535,10 +537,10 @@ asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should
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.
backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its 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
If the backend requires absolute flexibility with its 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
@@ -575,6 +577,9 @@ context_config :: struct {
threadStackSize: c.size_t,
pUserData: rawptr,
allocationCallbacks: allocation_callbacks,
dsound: struct {
hWnd: handle, /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
},
alsa: struct {
useVerboseDeviceEnumeration: b32,
},
@@ -649,6 +654,7 @@ context_type :: struct {
} when SUPPORT_WASAPI else struct {}),
dsound: (struct {
hWnd: handle, /* Can be null. */
hDSoundDLL: handle,
DirectSoundCreate: proc "system" (),
DirectSoundEnumerateA: proc "system" (),
@@ -1195,6 +1201,7 @@ device :: struct {
aaudio: (struct {
/*AAudioStream**/ pStreamPlayback: rawptr,
/*AAudioStream**/ pStreamCapture: rawptr,
rerouteLock: mutex,
usage: aaudio_usage,
contentType: aaudio_content_type,
inputPreset: aaudio_input_preset,
+58 -34
View File
@@ -295,7 +295,7 @@ 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
implement its 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
@@ -400,7 +400,7 @@ the be started and/or stopped at a specific time. This can be done with the foll
```
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
engine. The current global 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:
@@ -432,11 +432,11 @@ Sounds and sound groups are nodes in the engine's node graph and can be plugged
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
A sound can have its 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,
a sound will never have its 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
@@ -485,21 +485,12 @@ link the relevant frameworks but should compile cleanly out of the box with Xcod
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:
notarization process. To fix this there are two options. The first is to compile with
`-DMA_NO_RUNTIME_LINKING` which in turn 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:
```
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
@@ -557,7 +548,7 @@ To run locally, you'll need to use emrun:
2.7. Build Options
------------------
`#define` these options before including miniaudio.h.
`#define` these options before including miniaudio.c, or pass them as compiler flags:
+----------------------------------+--------------------------------------------------------------------+
| Option | Description |
@@ -588,6 +579,8 @@ To run locally, you'll need to use emrun:
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_WEBAUDIO | Disables the Web Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_CUSTOM | Disables support for custom backends. |
+----------------------------------+--------------------------------------------------------------------+
| MA_NO_NULL | Disables the null backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
@@ -632,6 +625,9 @@ To run locally, you'll need to use emrun:
| MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the Web Audio backend. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable custom backends. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
| | enable the null backend. |
+----------------------------------+--------------------------------------------------------------------+
@@ -695,11 +691,30 @@ To run locally, you'll need to use emrun:
| | You may need to enable this if your target platform does not allow |
| | runtime linking via `dlopen()`. |
+----------------------------------+--------------------------------------------------------------------+
| MA_USE_STDINT | (Pass this in a compiler flag. Do not #define this before |
| | miniaudio.c) Forces the use of stdint.h for sized types. |
+----------------------------------+--------------------------------------------------------------------+
| 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_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the |
| | WASAPI backend to use the UWP code path instead of the regular |
| | desktop path. This is normally auto-detected and should rarely be |
| | needed to be used explicitly, but can be useful for debugging. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal |
| | miniaudio-managed thread is created. This will be the first thing |
| | to be executed by the thread entry point. |
+----------------------------------+--------------------------------------------------------------------+
| MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an |
| | internal miniaudio-managed thread upon exit. This will be the last |
| | thing to be executed before the thread's entry point exits. |
+----------------------------------+--------------------------------------------------------------------+
| MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed |
| | threads. |
+----------------------------------+--------------------------------------------------------------------+
| MA_API | Controls how public APIs should be decorated. Default is `extern`. |
+----------------------------------+--------------------------------------------------------------------+
@@ -1311,7 +1326,7 @@ only works for sounds that were initialized with `ma_sound_init_from_file()` and
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
If you would instead rather leave the sound unattached by default, you can specify the
`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
graph.
@@ -1688,6 +1703,7 @@ combination of the following flags:
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING
```
When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
@@ -1708,6 +1724,14 @@ can instead stream audio data which you can do by specifying the
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.
The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop
when it reaches the end by default. It's recommended you use this flag when you want to have a
looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch.
This is because the resource manager needs to pre-fill the initial buffer at initialization time,
and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource
manager will assume the sound is not looping and will stop filling the buffer when it reaches the
end, therefore resulting in a discontinuous buffer.
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
@@ -1722,7 +1746,7 @@ actual file paths. When `ma_resource_manager_data_source_init()` is called (with
`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
caller to ensure the pointer stays valid for its 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`
@@ -2033,7 +2057,7 @@ In the above graph, it starts with two data sources whose outputs are attached t
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
a echo/delay. The outputs of 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
@@ -2074,7 +2098,7 @@ data from the 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
data from its 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
@@ -2320,7 +2344,7 @@ You can start and stop a node with the following:
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
read from any of its 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:
@@ -2413,29 +2437,29 @@ audio thread is finished so that control is not handed back to the caller thereb
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
the node. In order for a node to process data for one of its output buses, it needs to read from
each of its 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
nodes, followed by each of the attachments to each of its 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
includes the cost of recursively processing its inputs. This is the biggest compromise made with
the approach taken by miniaudio for its 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,
data sources. If the node was to terminate processing mid way through processing its 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
@@ -2806,7 +2830,7 @@ 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:
`ma_standard_channel_map` enum as its first parameter, which can be one of the following:
+-----------------------------------+-----------------------------------------------------------+
| Name | Description |
@@ -2892,7 +2916,7 @@ like the following:
ma_resample_algorithm_linear);
ma_resampler resampler;
ma_result result = ma_resampler_init(&config, &resampler);
ma_result result = ma_resampler_init(&config, NULL, &resampler);
if (result != MA_SUCCESS) {
// An error occurred...
}
@@ -3134,7 +3158,7 @@ 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);
ma_result result = ma_biquad_init(&config, NULL, &biquad);
if (result != MA_SUCCESS) {
// Error.
}
+8 -4
View File
@@ -18,7 +18,8 @@ sound_flag :: enum c.int {
ASYNC = 2, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
WAIT_INIT = 3, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
UNKNOWN_LENGTH = 4, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
LOOPING = 5, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */
/* ma_sound specific flags. */
NO_DEFAULT_ATTACHMENT = 12, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
NO_PITCH = 13, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
@@ -51,7 +52,7 @@ engine_node_config :: struct {
/* Base node object for both ma_sound and ma_sound_group. */
engine_node :: struct {
baseNode: node_base, /* Must be the first member for compatiblity with the ma_node API. */
baseNode: node_base, /* Must be the first member for compatibility with the ma_node API. */
pEngine: ^engine, /* A pointer to the engine. Set based on the value from the config. */
sampleRate: u32, /* 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. */
volumeSmoothTimeInPCMFrames: u32,
@@ -113,7 +114,6 @@ sound_config :: struct {
rangeEndInPCMFrames: u64,
loopPointBegInPCMFrames: u64,
loopPointEndInPCMFrames: u64,
isLooping: b32,
endCallback: sound_end_proc, /* 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_*(). */
pEndCallbackUserData: rawptr,
@@ -121,6 +121,8 @@ sound_config :: struct {
initNotifications: resource_manager_pipeline_notifications,
pDoneFence: ^fence, /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
isLooping: b32, /* Deprecated. Use the MA_SOUND_FLAG_LOOPING in `flags` instead. */
}
sound :: struct {
@@ -226,6 +228,7 @@ foreign lib {
sound_is_looping :: proc(pSound: ^sound) -> b32 ---
sound_at_end :: proc(pSound: ^sound) -> b32 ---
sound_seek_to_pcm_frame :: proc(pSound: ^sound, frameIndex: u64) -> result --- /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
sound_seek_to_second :: proc(pSound: ^sound, seekPointInSeconds: f32) -> result --- /* Abstraction to ma_sound_seek_to_pcm_frame() */
sound_get_data_format :: proc(pSound: ^sound, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result ---
sound_get_cursor_in_pcm_frames :: proc(pSound: ^sound, pCursor: ^u64) -> result ---
sound_get_length_in_pcm_frames :: proc(pSound: ^sound, pLength: ^u64) -> result ---
@@ -323,6 +326,7 @@ engine_config :: struct {
gainSmoothTimeInMilliseconds: u32, /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
defaultVolumeSmoothTimeInPCMFrames: u32, /* 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). */
preMixStackSizeInBytes: u32, /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */
allocationCallbacks: allocation_callbacks,
noAutoStart: b32, /* 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(). */
@@ -344,7 +348,7 @@ engine :: struct {
allocationCallbacks: allocation_callbacks,
ownsResourceManager: b8,
ownsDevice: b8,
inlinedSoundLock: spinlock, /* For synchronizing access so the inlined sound list. */
inlinedSoundLock: spinlock, /* For synchronizing access to the inlined sound list. */
pInlinedSoundHead: ^sound_inlined, /* The first inlined sound. Inlined sounds are tracked in a linked list. */
inlinedSoundCount: u32, /*atomic*/ /* The total number of allocated inlined sound objects. Used for debugging. */
gainSmoothTimeInFrames: u32, /* The number of frames to interpolate the gain of spatialized sounds across. */
Binary file not shown.
+23 -9
View File
@@ -19,6 +19,13 @@ MAX_NODE_LOCAL_BUS_COUNT :: 2
/* Use this when the bus count is determined by the node instance rather than the vtable. */
NODE_BUS_COUNT_UNKNOWN :: 255
/* For some internal memory management of ma_node_graph. */
stack :: struct {
offset: uint,
sizeInBytes: uint,
_data: [1]byte,
}
node :: struct {}
/* Node flags. */
@@ -53,7 +60,7 @@ node_vtable :: struct {
onProcess: proc "c" (pNode: ^node, ppFramesIn: ^[^]f32, pFrameCountIn: ^u32, ppFramesOut: ^[^]f32, pFrameCountOut: ^u32),
/*
A callback for retrieving the number of a input frames that are required to output the
A callback for retrieving the number of 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
@@ -134,8 +141,12 @@ node_input_bus :: struct {
node_base :: struct {
/* These variables are set once at startup. */
pNodeGraph: ^node_graph, /* The graph this node belongs to. */
pNodeGraph: ^node_graph, /* The graph this node belongs to. */
vtable: ^node_vtable,
inputBusCount: u32,
outputBusCount: u32,
pInputBuses: [^]node_input_bus `fmt:"v,inputBusCount"`,
pOutputBuses: [^]node_output_bus `fmt:"v,outputBusCount"`,
pCachedData: [^]f32, /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
cachedDataCapInFramesPerBus: u16, /* The capacity of the input data cache in frames, per bus. */
@@ -148,10 +159,6 @@ node_base :: struct {
state: node_state, /*atomic*/ /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
stateTimes: [2]u64, /*atomic*/ /* 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. */
localTime: u64, /*atomic*/ /* 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()`. */
inputBusCount: u32,
outputBusCount: u32,
pInputBuses: [^]node_input_bus,
pOutputBuses: [^]node_output_bus,
/* Memory management. */
_inputBuses: [MAX_NODE_LOCAL_BUS_COUNT]node_input_bus,
@@ -189,18 +196,25 @@ foreign lib {
}
node_graph_config :: struct {
channels: u32,
nodeCacheCapInFrames: u16,
channels: u32,
processingSizeInFrames: u32, /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */
preMixStackSizeInBytes: uint, /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */
}
node_graph :: struct {
/* Immutable. */
base: node_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. */
endpoint: node_base, /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */
nodeCacheCapInFrames: u16,
pProcessingCache: [^]f32, /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */
processingCacheFramesRemaining: u32,
processingSizeInFrames: u32,
/* Read and written by multiple threads. */
isReading: b32, /*atomic*/
/* Modified only by the audio thread. */
pPreMixStack: ^stack,
}
@(default_calling_convention="c", link_prefix="ma_")
+2 -1
View File
@@ -16,6 +16,7 @@ resource_manager_data_source_flag :: enum c.int {
ASYNC = 2, /* When set, the resource manager will load the data source asynchronously. */
WAIT_INIT = 3, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
UNKNOWN_LENGTH = 4, /* 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. */
LOOPING = 5, /* When set, configures the data source to loop by default. */
}
resource_manager_data_source_flags :: bit_set[resource_manager_data_source_flag; u32]
@@ -79,8 +80,8 @@ resource_manager_data_source_config :: struct {
rangeEndInPCMFrames: u64,
loopPointBegInPCMFrames: u64,
loopPointEndInPCMFrames: u64,
isLooping: b32,
flags: u32,
isLooping: b32, /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */
}
resource_manager_data_supply_type :: enum c.int {
+1334 -487
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -62,6 +62,11 @@ when !NO_THREADING {
Signals the specified auto-reset event.
*/
event_signal :: proc(pEvent: ^event) -> result ---
semaphore_init :: proc(initialValue: i32, pSemaphore: ^semaphore) -> result ---
semaphore_uninit :: proc(pSemaphore: ^semaphore) ---
semaphore_wait :: proc(pSemaphore: ^semaphore) -> result ---
semaphore_release :: proc(pSemaphore: ^semaphore) -> result ---
} /* NO_THREADING */
}
+3 -1
View File
@@ -7,7 +7,7 @@ foreign import lib { LIB }
@(default_calling_convention="c", link_prefix="ma_")
foreign lib {
/*
Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate.
*/
calculate_buffer_size_in_milliseconds_from_frames :: proc(bufferSizeInFrames: u32, sampleRate: u32) -> u32 ---
@@ -163,6 +163,8 @@ foreign lib {
data_source_read_pcm_frames :: proc(pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
data_source_seek_pcm_frames :: proc(pDataSource: ^data_source, frameCount: u64, pFramesSeeked: ^u64) -> result --- /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */
data_source_seek_to_pcm_frame :: proc(pDataSource: ^data_source, frameIndex: u64) -> result ---
data_source_seek_seconds :: proc(pDataSource: ^data_source, secondCount: f32, pSecondsSeeked: ^f32) -> result --- /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */
data_source_seek_to_seconds :: proc(pDataSource: ^data_source, seekPointInSeconds: f32) -> result --- /* Abstraction to ma_data_source_seek_to_pcm_frame() */
data_source_get_data_format :: proc(pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^data_source, pCursor: ^u64) -> result ---
data_source_get_length_in_pcm_frames :: proc(pDataSource: ^data_source, pLength: ^u64) -> result --- /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
+2 -2
View File
@@ -831,8 +831,8 @@ fmaxf :: proc "contextless" (x, y: f32) -> f32 {
return x
}
if math.signbit(x) != math.signbit(y) {
return y if math.signbit(x) else x
if math.sign_bit(x) != math.sign_bit(y) {
return y if math.sign_bit(x) else x
}
return y if x < y else x
+14 -11
View File
@@ -375,17 +375,20 @@ foreign lib {
//------------------------------------------------------------------------------------
// Functions Declaration - Matrix operations
//------------------------------------------------------------------------------------
MatrixMode :: proc(mode: c.int) --- // Choose the current matrix to be transformed
PushMatrix :: proc() --- // Push the current matrix to stack
PopMatrix :: proc() --- // Pop lattest inserted matrix from stack
LoadIdentity :: proc() --- // Reset current matrix to identity matrix
Translatef :: proc(x, y, z: f32) --- // Multiply the current matrix by a translation matrix
Rotatef :: proc(angleDeg: f32, x, y, z: f32) --- // Multiply the current matrix by a rotation matrix
Scalef :: proc(x, y, z: f32) --- // Multiply the current matrix by a scaling matrix
MultMatrixf :: proc(matf: [^]f32) --- // Multiply the current matrix by another matrix
Frustum :: proc(left, right, bottom, top, znear, zfar: f64) ---
Ortho :: proc(left, right, bottom, top, znear, zfar: f64) ---
Viewport :: proc(x, y, width, height: c.int) --- // Set the viewport area
MatrixMode :: proc(mode: c.int) --- // Choose the current matrix to be transformed
PushMatrix :: proc() --- // Push the current matrix to stack
PopMatrix :: proc() --- // Pop lattest inserted matrix from stack
LoadIdentity :: proc() --- // Reset current matrix to identity matrix
Translatef :: proc(x, y, z: f32) --- // Multiply the current matrix by a translation matrix
Rotatef :: proc(angleDeg: f32, x, y, z: f32) --- // Multiply the current matrix by a rotation matrix
Scalef :: proc(x, y, z: f32) --- // Multiply the current matrix by a scaling matrix
MultMatrixf :: proc(matf: [^]f32) --- // Multiply the current matrix by another matrix
Frustum :: proc(left, right, bottom, top, znear, zfar: f64) ---
Ortho :: proc(left, right, bottom, top, znear, zfar: f64) ---
Viewport :: proc(x, y, width, height: c.int) --- // Set the viewport area
SetClipPlanes :: proc(near, far: f64) --- // Set clip planes distances
GetCullDistanceNear :: proc() -> f64 --- // Get cull plane distance near
GetCullDistanceFar :: proc() -> f64 --- // Get cull plane distance far
//------------------------------------------------------------------------------------
// Functions Declaration - Vertex level operations
+29 -15
View File
@@ -46,7 +46,7 @@ GPUIndexElementSize :: enum c.int {
GPUTextureFormat :: enum c.int {
INVALID,
/* Unsigned Normalized Float Color Formats */
/* Unsigned Normalized Float Color Formats */
A8_UNORM,
R8_UNORM,
R8G8_UNORM,
@@ -59,34 +59,41 @@ GPUTextureFormat :: enum c.int {
B5G5R5A1_UNORM,
B4G4R4A4_UNORM,
B8G8R8A8_UNORM,
/* Compressed Unsigned Normalized Float Color Formats */
/* Compressed Unsigned Normalized Float Color Formats */
BC1_RGBA_UNORM,
BC2_RGBA_UNORM,
BC3_RGBA_UNORM,
BC4_R_UNORM,
BC5_RG_UNORM,
BC7_RGBA_UNORM,
/* Compressed Signed Float Color Formats */
/* Compressed Signed Float Color Formats */
BC6H_RGB_FLOAT,
/* Compressed Unsigned Float Color Formats */
/* Compressed Unsigned Float Color Formats */
BC6H_RGB_UFLOAT,
/* Signed Normalized Float Color Formats */
/* Signed Normalized Float Color Formats */
R8_SNORM,
R8G8_SNORM,
R8G8B8A8_SNORM,
R16_SNORM,
R16G16_SNORM,
R16G16B16A16_SNORM,
/* Signed Float Color Formats */
/* Signed Float Color Formats */
R16_FLOAT,
R16G16_FLOAT,
R16G16B16A16_FLOAT,
R32_FLOAT,
R32G32_FLOAT,
R32G32B32A32_FLOAT,
/* Unsigned Float Color Formats */
/* Unsigned Float Color Formats */
R11G11B10_UFLOAT,
/* Unsigned Integer Color Formats */
/* Unsigned Integer Color Formats */
R8_UINT,
R8G8_UINT,
R8G8B8A8_UINT,
@@ -96,7 +103,8 @@ GPUTextureFormat :: enum c.int {
R32_UINT,
R32G32_UINT,
R32G32B32A32_UINT,
/* Signed Integer Color Formats */
/* Signed Integer Color Formats */
R8_INT,
R8G8_INT,
R8G8B8A8_INT,
@@ -106,21 +114,25 @@ GPUTextureFormat :: enum c.int {
R32_INT,
R32G32_INT,
R32G32B32A32_INT,
/* SRGB Unsigned Normalized Color Formats */
/* SRGB Unsigned Normalized Color Formats */
R8G8B8A8_UNORM_SRGB,
B8G8R8A8_UNORM_SRGB,
/* Compressed SRGB Unsigned Normalized Color Formats */
/* Compressed SRGB Unsigned Normalized Color Formats */
BC1_RGBA_UNORM_SRGB,
BC2_RGBA_UNORM_SRGB,
BC3_RGBA_UNORM_SRGB,
BC7_RGBA_UNORM_SRGB,
/* Depth Formats */
/* Depth Formats */
D16_UNORM,
D24_UNORM,
D32_FLOAT,
D24_UNORM_S8_UINT,
D32_FLOAT_S8_UINT,
/* Compressed ASTC Normalized Float Color Formats*/
/* Compressed ASTC Normalized Float Color Formats*/
ASTC_4x4_UNORM,
ASTC_5x4_UNORM,
ASTC_5x5_UNORM,
@@ -135,7 +147,8 @@ GPUTextureFormat :: enum c.int {
ASTC_10x10_UNORM,
ASTC_12x10_UNORM,
ASTC_12x12_UNORM,
/* Compressed SRGB ASTC Normalized Float Color Formats*/
/* Compressed SRGB ASTC Normalized Float Color Formats*/
ASTC_4x4_UNORM_SRGB,
ASTC_5x4_UNORM_SRGB,
ASTC_5x5_UNORM_SRGB,
@@ -150,7 +163,8 @@ GPUTextureFormat :: enum c.int {
ASTC_10x10_UNORM_SRGB,
ASTC_12x10_UNORM_SRGB,
ASTC_12x12_UNORM_SRGB,
/* Compressed ASTC Signed Float Color Formats*/
/* Compressed ASTC Signed Float Color Formats*/
ASTC_4x4_FLOAT,
ASTC_5x4_FLOAT,
ASTC_5x5_FLOAT,
+12 -4
View File
@@ -1,8 +1,8 @@
package sdl3
Mutex :: struct {}
RWLock :: struct {}
Mutex :: struct {}
RWLock :: struct {}
Semaphore :: struct {}
@(default_calling_convention="c", link_prefix="SDL_", require_results)
foreign lib {
@@ -19,4 +19,12 @@ foreign lib {
TryLockRWLockForWriting :: proc(rwlock: ^RWLock) -> bool ---
UnlockRWLock :: proc(rwlock: ^RWLock) ---
DestroyRWLock :: proc(rwlock: ^RWLock) ---
}
CreateSemaphore :: proc(initial_value: Uint32) -> ^Semaphore ---
DestroySemaphore :: proc(sem: ^Semaphore) ---
GetSemaphoreValue :: proc(sem: ^Semaphore) -> Uint32 ---
SignalSemaphore :: proc(sem: ^Semaphore) ---
TryWaitSemaphore :: proc(sem: ^Semaphore) -> bool ---
WaitSemaphore :: proc(sem: ^Semaphore) ---
WaitSemaphoreTimeout :: proc(sem: ^Semaphore, timeout_ms: Sint32) ---
}
+2 -2
View File
@@ -49,7 +49,7 @@ PROP_THREAD_CREATE_STACKSIZE_NUMBER :: "SDL.thread.create.stacksize"
BeginThreadFunction :: proc "c" () -> FunctionPointer {
when ODIN_OS == .Windows {
foreign {
_beginthreadx :: proc "c" (
_beginthreadex :: proc "c" (
security: rawptr,
stack_size: c.uint,
start_address: proc "c" (rawptr),
@@ -58,7 +58,7 @@ BeginThreadFunction :: proc "c" () -> FunctionPointer {
thraddr: ^c.uint,
) -> uintptr ---
}
return FunctionPointer(_beginthreadx)
return FunctionPointer(_beginthreadex)
} else {
return nil
}
+2 -2
View File
@@ -336,7 +336,7 @@ foreign lib {
GetNaturalDisplayOrientation :: proc(displayID: DisplayID) -> DisplayOrientation ---
GetCurrentDisplayOrientation :: proc(displayID: DisplayID) -> DisplayOrientation ---
GetDisplayContentScale :: proc(displayID: DisplayID) -> f32 ---
GetFullscreenDisplayModes :: proc(displayID: DisplayID, count: c.int) -> [^]^DisplayMode ---
GetFullscreenDisplayModes :: proc(displayID: DisplayID, count: ^c.int) -> [^]^DisplayMode ---
GetClosestFullscreenDisplayMode :: proc(displayID: DisplayID, w, h: c.int, refresh_rate: f32, include_high_density_modes: bool, closest: ^DisplayMode) -> bool ---
GetDesktopDisplayMode :: proc(displayID: DisplayID) -> ^DisplayMode ---
GetCurrentDisplayMode :: proc(displayID: DisplayID) -> ^DisplayMode ---
@@ -397,7 +397,7 @@ foreign lib {
GetWindowKeyboardGrab :: proc(window: ^Window) -> bool ---
GetWindowMouseGrab :: proc(window: ^Window) -> bool ---
GetGrabbedWindow :: proc() -> ^Window ---
SetWindowMouseRect :: proc(window: ^Window, #by_ptr rect: Rect) -> bool ---
SetWindowMouseRect :: proc(window: ^Window, rect: ^Rect) -> bool ---
GetWindowMouseRect :: proc(window: ^Window) -> ^Rect ---
SetWindowOpacity :: proc(window: ^Window, opacity: f32) -> bool ---
GetWindowOpacity :: proc(window: ^Window) -> f32 ---
+169
View File
@@ -0,0 +1,169 @@
The FreeType Project LICENSE
----------------------------
2006-Jan-27
Copyright 1996-2002, 2006 by
David Turner, Robert Wilhelm, and Werner Lemberg
Introduction
============
The FreeType Project is distributed in several archive packages;
some of them may contain, in addition to the FreeType font engine,
various tools and contributions which rely on, or relate to, the
FreeType Project.
This license applies to all files found in such packages, and
which do not fall under their own explicit license. The license
affects thus the FreeType font engine, the test programs,
documentation and makefiles, at the very least.
This license was inspired by the BSD, Artistic, and IJG
(Independent JPEG Group) licenses, which all encourage inclusion
and use of free software in commercial and freeware products
alike. As a consequence, its main points are that:
o We don't promise that this software works. However, we will be
interested in any kind of bug reports. (`as is' distribution)
o You can use this software for whatever you want, in parts or
full form, without having to pay us. (`royalty-free' usage)
o You may not pretend that you wrote this software. If you use
it, or only parts of it, in a program, you must acknowledge
somewhere in your documentation that you have used the
FreeType code. (`credits')
We specifically permit and encourage the inclusion of this
software, with or without modifications, in commercial products.
We disclaim all warranties covering The FreeType Project and
assume no liability related to The FreeType Project.
Finally, many people asked us for a preferred form for a
credit/disclaimer to use in compliance with this license. We thus
encourage you to use the following text:
"""
Portions of this software are copyright © <year> The FreeType
Project (www.freetype.org). All rights reserved.
"""
Please replace <year> with the value from the FreeType version you
actually use.
Legal Terms
===========
0. Definitions
--------------
Throughout this license, the terms `package', `FreeType Project',
and `FreeType archive' refer to the set of files originally
distributed by the authors (David Turner, Robert Wilhelm, and
Werner Lemberg) as the `FreeType Project', be they named as alpha,
beta or final release.
`You' refers to the licensee, or person using the project, where
`using' is a generic term including compiling the project's source
code as well as linking it to form a `program' or `executable'.
This program is referred to as `a program using the FreeType
engine'.
This license applies to all files distributed in the original
FreeType Project, including all source code, binaries and
documentation, unless otherwise stated in the file in its
original, unmodified form as distributed in the original archive.
If you are unsure whether or not a particular file is covered by
this license, you must contact us to verify this.
The FreeType Project is copyright (C) 1996-2000 by David Turner,
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
specified below.
1. No Warranty
--------------
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
USE, OF THE FREETYPE PROJECT.
2. Redistribution
-----------------
This license grants a worldwide, royalty-free, perpetual and
irrevocable right and license to use, execute, perform, compile,
display, copy, create derivative works of, distribute and
sublicense the FreeType Project (in both source and object code
forms) and derivative works thereof for any purpose; and to
authorize others to exercise some or all of the rights granted
herein, subject to the following conditions:
o Redistribution of source code must retain this license file
(`FTL.TXT') unaltered; any additions, deletions or changes to
the original files must be clearly indicated in accompanying
documentation. The copyright notices of the unaltered,
original files must be preserved in all copies of source
files.
o Redistribution in binary form must provide a disclaimer that
states that the software is based in part of the work of the
FreeType Team, in the distribution documentation. We also
encourage you to put an URL to the FreeType web page in your
documentation, though this isn't mandatory.
These conditions apply to any software derived from or based on
the FreeType Project, not just the unmodified files. If you use
our work, you must acknowledge us. However, no fee need be paid
to us.
3. Advertising
--------------
Neither the FreeType authors and contributors nor you shall use
the name of the other for commercial, advertising, or promotional
purposes without specific prior written permission.
We suggest, but do not require, that you use one or more of the
following phrases to refer to this software in your documentation
or advertising materials: `FreeType Project', `FreeType Engine',
`FreeType library', or `FreeType Distribution'.
As you have not signed this license, you are not required to
accept it. However, as the FreeType Project is copyrighted
material, only this license, or another one contracted with the
authors, grants you the right to use, distribute, and modify it.
Therefore, by using, distributing, or modifying the FreeType
Project, you indicate that you understand and accept all the terms
of this license.
4. Contacts
-----------
There are two mailing lists related to FreeType:
o freetype@nongnu.org
Discusses general use and applications of FreeType, as well as
future and wanted additions to the library and distribution.
If you are looking for support, start in this list if you
haven't found anything to help you in the documentation.
o freetype-devel@nongnu.org
Discusses bugs, as well as engine internals, design issues,
specific licenses, porting, etc.
Our home page can be found at
https://www.freetype.org
--- end of FTL.TXT ---
+42
View File
@@ -0,0 +1,42 @@
HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable.
Copyright © 2010-2022 Google, Inc.
Copyright © 2015-2020 Ebrahim Byagowi
Copyright © 2019,2020 Facebook, Inc.
Copyright © 2012,2015 Mozilla Foundation
Copyright © 2011 Codethink Limited
Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)
Copyright © 2009 Keith Stribley
Copyright © 2011 Martin Hosken and SIL International
Copyright © 2007 Chris Wilson
Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod
Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc.
Copyright © 1998-2005 David Turner and Werner Lemberg
Copyright © 2016 Igalia S.L.
Copyright © 2022 Matthias Clasen
Copyright © 2018,2021 Khaled Hosny
Copyright © 2018,2019,2020 Adobe, Inc
Copyright © 2013-2015 Alexei Podtelezhnikov
For full copyright notices consult the individual files in the package.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in
all copies of this software.
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2025 Samuel Ugochukwu <sammycageagle@gmail.com>
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.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2025 Samuel Ugochukwu <sammycageagle@gmail.com>
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.
+17
View File
@@ -0,0 +1,17 @@
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+181
View File
@@ -0,0 +1,181 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 2001-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* \file SDL_textengine.h
*
* Definitions for implementations of the TTF_TextEngine interface.
*/
#ifndef SDL_TTF_TEXTENGINE_H_
#define SDL_TTF_TEXTENGINE_H_
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* A font atlas draw command.
*
* \since This enum is available since SDL_ttf 3.0.0.
*/
typedef enum TTF_DrawCommand
{
TTF_DRAW_COMMAND_NOOP,
TTF_DRAW_COMMAND_FILL,
TTF_DRAW_COMMAND_COPY
} TTF_DrawCommand;
/**
* A filled rectangle draw operation.
*
* \since This struct is available since SDL_ttf 3.0.0.
*
* \sa TTF_DrawOperation
*/
typedef struct TTF_FillOperation
{
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_FILL */
SDL_Rect rect; /**< The rectangle to fill, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
} TTF_FillOperation;
/**
* A texture copy draw operation.
*
* \since This struct is available since SDL_ttf 3.0.0.
*
* \sa TTF_DrawOperation
*/
typedef struct TTF_CopyOperation
{
TTF_DrawCommand cmd; /**< TTF_DRAW_COMMAND_COPY */
int text_offset; /**< The offset in the text corresponding to this glyph.
There may be multiple glyphs with the same text offset
and the next text offset might be several Unicode codepoints
later. In this case the glyphs and codepoints are grouped
together and the group bounding box is the union of the dst
rectangles for the corresponding glyphs. */
TTF_Font *glyph_font; /**< The font containing the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
Uint32 glyph_index; /**< The glyph index of the glyph to be drawn, can be passed to TTF_GetGlyphImageForIndex() */
SDL_Rect src; /**< The area within the glyph to be drawn */
SDL_Rect dst; /**< The drawing coordinates of the glyph, in pixels. The x coordinate is relative to the left side of the text area, going right, and the y coordinate is relative to the top side of the text area, going down. */
void *reserved;
} TTF_CopyOperation;
/**
* A text engine draw operation.
*
* \since This struct is available since SDL_ttf 3.0.0.
*/
typedef union TTF_DrawOperation
{
TTF_DrawCommand cmd;
TTF_FillOperation fill;
TTF_CopyOperation copy;
} TTF_DrawOperation;
/* Private data in TTF_Text, to assist in text measurement and layout */
typedef struct TTF_TextLayout TTF_TextLayout;
/* Private data in TTF_Text, available to implementations */
struct TTF_TextData
{
TTF_Font *font; /**< The font used by this text, read-only. */
SDL_FColor color; /**< The color of the text, read-only. */
bool needs_layout_update; /**< True if the layout needs to be updated */
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
int w; /**< The width of this text, in pixels, read-only. */
int h; /**< The height of this text, in pixels, read-only. */
int num_ops; /**< The number of drawing operations to render this text, read-only. */
TTF_DrawOperation *ops; /**< The drawing operations used to render this text, read-only. */
int num_clusters; /**< The number of substrings representing clusters of glyphs in the string, read-only */
TTF_SubString *clusters; /**< Substrings representing clusters of glyphs in the string, read-only */
SDL_PropertiesID props; /**< Custom properties associated with this text, read-only. This field is created as-needed using TTF_GetTextProperties() and the properties may be then set and read normally */
bool needs_engine_update; /**< True if the engine text needs to be updated */
TTF_TextEngine *engine; /**< The engine used to render this text, read-only. */
void *engine_text; /**< The implementation-specific representation of this text */
};
/**
* A text engine interface.
*
* This structure should be initialized using SDL_INIT_INTERFACE()
*
* \since This struct is available since SDL_ttf 3.0.0.
*
* \sa SDL_INIT_INTERFACE
*/
struct TTF_TextEngine
{
Uint32 version; /**< The version of this interface */
void *userdata; /**< User data pointer passed to callbacks */
/* Create a text representation from draw instructions.
*
* All fields of `text` except `internal->engine_text` will already be filled out.
*
* This function should set the `internal->engine_text` field to a non-NULL value.
*
* \param userdata the userdata pointer in this interface.
* \param text the text object being created.
*/
bool (SDLCALL *CreateText)(void *userdata, TTF_Text *text);
/**
* Destroy a text representation.
*/
void (SDLCALL *DestroyText)(void *userdata, TTF_Text *text);
};
/* Check the size of TTF_TextEngine
*
* If this assert fails, either the compiler is padding to an unexpected size,
* or the interface has been updated and this should be updated to match and
* the code using this interface should be updated to handle the old version.
*/
SDL_COMPILE_TIME_ASSERT(TTF_TextEngine_SIZE,
(sizeof(void *) == 4 && sizeof(TTF_TextEngine) == 16) ||
(sizeof(void *) == 8 && sizeof(TTF_TextEngine) == 32));
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_TTF_TEXTENGINE_H_ */
+2833
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
package sdl3_ttf
import "core:c"
import SDL "vendor:sdl3"
DrawCommand :: enum c.int {
NOOP,
FILL,
COPY,
}
FillOperation :: struct {
cmd: DrawCommand,
rect: SDL.Rect,
}
CopyOperation :: struct {
cmd: DrawCommand,
text_offset: c.int,
glyph_font: ^Font,
glyph_index: u32,
src: SDL.Rect,
dst: SDL.Rect,
reserved: rawptr,
}
DrawOperation :: struct #raw_union {
cmd: DrawCommand,
fill: FillOperation,
copy: CopyOperation,
}
TextLayout :: struct {}
TextData :: struct {
font: ^Font,
color: SDL.FColor,
needs_layout_update: bool,
layout: ^TextLayout,
x, y: c.int,
w, h: c.int,
num_ops: c.int,
ops: [^]DrawOperation `fmt:"v,num_ops"`,
num_clusters: c.int,
clusters: [^]SubString `fmt:"v,num_clusters"`,
props: SDL.PropertiesID,
needs_engine_update: bool,
engine: ^TextEngine,
engine_text: rawptr,
}
TextEngine :: struct {
version: u32,
userdata: rawptr,
CreateText: proc "c" (userdata: rawptr, text: ^Text) -> bool,
DestroyText: proc "c" (userdata: rawptr, Textext: ^Text),
}
#assert(
(size_of(TextEngine) == 16 && size_of(rawptr) == 4) ||
(size_of(TextEngine) == 32 && size_of(rawptr) == 8),
)
+279
View File
@@ -0,0 +1,279 @@
package sdl3_ttf
import "core:c"
import SDL "vendor:sdl3"
when ODIN_OS == .Windows {
foreign import lib "SDL3_ttf.lib"
} else {
foreign import lib "system:SDL3_ttf"
}
PROP_FONT_CREATE_FILENAME_STRING :: "SDL_ttf.font.create.filename"
PROP_FONT_CREATE_IOSTREAM_POINTER :: "SDL_ttf.font.create.iostream"
PROP_FONT_CREATE_IOSTREAM_OFFSET_NUMBER :: "SDL_ttf.font.create.iostream.offset"
PROP_FONT_CREATE_IOSTREAM_AUTOCLOSE_BOOLEAN :: "SDL_ttf.font.create.iostream.autoclose"
PROP_FONT_CREATE_SIZE_FLOAT :: "SDL_ttf.font.create.size"
PROP_FONT_CREATE_FACE_NUMBER :: "SDL_ttf.font.create.face"
PROP_FONT_CREATE_HORIZONTAL_DPI_NUMBER :: "SDL_ttf.font.create.hdpi"
PROP_FONT_CREATE_VERTICAL_DPI_NUMBER :: "SDL_ttf.font.create.vdpi"
PROP_FONT_CREATE_EXISTING_FONT :: "SDL_ttf.font.create.existing_font"
FONT_WEIGHT_THIN :: 100 /**< Thin (100) named font weight value */
FONT_WEIGHT_EXTRA_LIGHT :: 200 /**< ExtraLight (200) named font weight value */
FONT_WEIGHT_LIGHT :: 300 /**< Light (300) named font weight value */
FONT_WEIGHT_NORMAL :: 400 /**< Normal (400) named font weight value */
FONT_WEIGHT_MEDIUM :: 500 /**< Medium (500) named font weight value */
FONT_WEIGHT_SEMI_BOLD :: 600 /**< SemiBold (600) named font weight value */
FONT_WEIGHT_BOLD :: 700 /**< Bold (700) named font weight value */
FONT_WEIGHT_EXTRA_BOLD :: 800 /**< ExtraBold (800) named font weight value */
FONT_WEIGHT_BLACK :: 900 /**< Black (900) named font weight value */
FONT_WEIGHT_EXTRA_BLACK :: 950 /**< ExtraBlack (950) named font weight value */
PROP_RENDERER_TEXT_ENGINE_RENDERER :: "SDL_ttf.renderer_text_engine.create.renderer"
PROP_RENDERER_TEXT_ENGINE_ATLAS_TEXTURE_SIZE :: "SDL_ttf.renderer_text_engine.create.atlas_texture_size"
PROP_GPU_TEXT_ENGINE_DEVICE :: "SDL_ttf.gpu_text_engine.create.device"
PROP_GPU_TEXT_ENGINE_ATLAS_TEXTURE_SIZE :: "SDL_ttf.gpu_text_engine.create.atlas_texture_size"
MAJOR_VERSION :: 3
MINOR_VERSION :: 2
PATCHLEVEL :: 2
Font :: struct {}
Text :: struct {
text: [^]u8,
num_lines: c.int,
refcount: c.int,
internal: ^TextData,
}
FontStyle :: enum u32 {
NORMAL,
BOLD,
ITALIC,
UNDERLINE,
STRIKETHROUGH,
}
FontStyleFlags :: distinct bit_set[FontStyle; u32]
// NOTE: This is called TTF_HintingFlags but its not a bit_set so
// the "flags" doesnt really make sense, its just the hinting.
Hinting :: enum c.int {
INVALID = -1,
NORMAL,
LIGHT,
MONO,
NONE,
LIGHT_SUBPIXEL,
}
HorizontalAlignment :: enum c.int {
INVALID = -1,
LEFT,
CENTER,
RIGHT,
}
Direction :: enum c.int {
INVALID,
LTR = 4,
RTL,
TTB,
BTT,
}
ImageType :: enum c.int {
INVALID,
ALPHA,
COLOR,
SDF,
}
GPUAtlasDrawSequence :: struct {
atlas_texture: ^SDL.GPUTexture,
xy, uv: [^]SDL.FPoint `fmt:"v,num_vertices"`,
num_vertices: c.int,
indices: [^]c.int `fmt:"v,num_indices"`,
num_indices: c.int,
image_type: ImageType,
next: ^GPUAtlasDrawSequence,
}
GPUTextEngineWinding :: enum c.int {
INVALID = -1,
CLOCKWISE = 0,
COUNTER_CLOCKWISE = +1,
}
SubStringFlags :: bit_field u32 {
direction: u8 | 8,
text_start: bool | 1,
line_start: bool | 1,
line_end: bool | 1,
text_end: bool | 1,
}
SubString :: struct {
flags: SubStringFlags,
offset, length: c.int,
line_index, cluster_index: c.int,
rect: SDL.Rect,
}
@(default_calling_convention="c", link_prefix="TTF_", require_results)
foreign lib {
Version :: proc() -> c.int ---
GetFreeTypeVersion :: proc(major, minor, patch: ^c.int) ---
GetHarfBuzzVersion :: proc(major, minor, patch: ^c.int) ---
Init :: proc() -> bool ---
OpenFont :: proc(file: cstring, ptsize: f32) -> ^Font ---
OpenFontIO :: proc(src: ^SDL.IOStream, closeio: bool, ptsize: f32) -> ^Font ---
OpenFontWithProperties :: proc(props: SDL.PropertiesID) -> ^Font ---
CopyFont :: proc(existing_font: ^Font) -> ^Font ---
GetFontProperties :: proc(font: ^Font) -> SDL.PropertiesID ---
GetFontGeneration :: proc(font: ^Font) -> u32 ---
AddFallbackFont :: proc(font: ^Font, fallback: ^Font) -> bool ---
RemoveFallbackFont :: proc(font: ^Font, fallback: ^Font) ---
ClearFallbackFonts :: proc(font: ^Font) ---
SetFontSize :: proc(font: ^Font, ptsize: f32) -> bool ---
SetFontSizeDPI :: proc(font: ^Font, ptsize: f32, hdpi: c.int, vdpi: c.int) -> bool ---
GetFontSize :: proc(font: ^Font) -> f32 ---
GetFontDPI :: proc(font: ^Font, hdpi: ^c.int, vdpi: ^c.int) -> bool ---
SetFontStyle :: proc(font: ^Font, style: FontStyleFlags) ---
GetFontStyle :: proc(#by_ptr font: Font) -> FontStyleFlags ---
SetFontOutline :: proc(font: ^Font, outline: c.int) -> bool ---
GetFontOutline :: proc(#by_ptr font: Font) -> c.int ---
SetFontHinting :: proc(font: ^Font, hinting: Hinting) ---
GetFontHinting :: proc(#by_ptr font: Font) -> Hinting ---
GetNumFontFaces :: proc(font: ^Font) -> c.int ---
SetFontSDF :: proc(font: ^Font, enabled: bool) -> bool ---
GetFontSDF :: proc(#by_ptr font: Font) -> bool ---
GetFontWeight :: proc(#by_ptr font: Font) -> c.int ---
SetFontWrapAlignment :: proc(font: ^Font, align: HorizontalAlignment) ---
GetFontWrapAlignment :: proc(#by_ptr font: Font) -> HorizontalAlignment ---
GetFontHeight :: proc(#by_ptr font: Font) -> c.int ---
GetFontAscent :: proc(#by_ptr font: Font) -> c.int ---
GetFontDescent :: proc(#by_ptr font: Font) -> c.int ---
SetFontLineSkip :: proc(font: ^Font, lineskip: c.int) ---
GetFontLineSkip :: proc(#by_ptr font: Font) -> c.int ---
SetFontKerning :: proc(font: ^Font, enabled: bool) ---
GetFontKerning :: proc(#by_ptr font: Font) -> bool ---
FontIsFixedWidth :: proc(#by_ptr font: Font) -> bool ---
FontIsScalable :: proc(#by_ptr font: Font) -> bool ---
GetFontFamilyName :: proc(#by_ptr font: Font) -> cstring ---
GetFontStyleName :: proc(#by_ptr font: Font) -> cstring ---
SetFontDirection :: proc(font: ^Font, direction: Direction) -> bool ---
GetFontDirection :: proc(#by_ptr font: Font) -> Direction ---
StringToTag :: proc(string: cstring) -> u32 ---
TagToString :: proc(tag: u32, string: [^]c.char, size: c.size_t) ---
SetFontScript :: proc(font: ^Font, script: u32) -> bool ---
GetFontScript :: proc(font: ^Font) -> u32 ---
SetFontLanguage :: proc(font: ^Font, language_bcp47: cstring) -> bool ---
GetGlyphScript :: proc(ch: u32) -> u32 ---
FontHasGlyph :: proc(font: ^Font, ch: u32) -> bool ---
GetGlyphImage :: proc(font: ^Font, ch: u32, image_type: ^ImageType) -> ^SDL.Surface ---
GetGlyphImageForIndex :: proc(font: ^Font, glyph_index: u32, image_type: ^ImageType) -> ^SDL.Surface ---
GetGlyphMetrics :: proc(font: ^Font, ch: u32, minx, maxx, miny, maxy, advance: ^c.int) -> bool ---
GetGlyphKerning :: proc(font: ^Font, previous_ch: u32, ch: u32, kerning: ^c.int) -> bool ---
GetStringSize :: proc(font: ^Font, text: cstring, length: c.size_t, w, h: ^c.int) -> bool ---
GetStringSizeWrapped :: proc(font: ^Font, text: cstring, length: c.size_t, wrap_width: c.int, w, h: ^c.int) -> bool ---
MeasureString :: proc(font: ^Font, text: cstring, length: c.size_t, max_width: c.int, measured_width: ^c.int, measured_length: ^c.size_t) -> bool ---
RenderText_Solid :: proc(font: ^Font, text: cstring, length: c.size_t, fg: SDL.Color) -> ^SDL.Surface ---
RenderText_Solid_Wrapped :: proc(font: ^Font, text: cstring, length: c.size_t, fg: SDL.Color, wrap_Length: c.int) -> ^SDL.Surface ---
RenderGylph_Solid :: proc(font: ^Font, ch: u32, fg: SDL.Color) -> ^SDL.Surface ---
RenderText_Shaded :: proc(font: ^Font, text: cstring, length: c.size_t, fg, bg: SDL.Color) -> ^SDL.Surface ---
RenderText_Shaded_Wrapped :: proc(font: ^Font, text: cstring, length: c.size_t, fg, bg: SDL.Color, wrap_width: c.int) -> ^SDL.Surface ---
RenderGlyph_Shaded :: proc(font: ^Font, ch: u32, fg, bg: SDL.Color) -> ^SDL.Surface ---
RenderText_Blended :: proc(font: ^Font, text: cstring, length: c.size_t, fg: SDL.Color) -> ^SDL.Surface ---
RnederText_Blended_Wrapped :: proc(font: ^Font, text: cstring, length: c.size_t, fg: SDL.Color, wrap_width: c.int) -> ^SDL.Surface ---
RenderGlyph_Blended :: proc(font: ^Font, ch: u32, fg: SDL.Color) -> ^SDL.Surface ---
RenderText_LCD :: proc(font: ^Font, text: cstring, length: c.size_t, fg, bg: SDL.Color) -> ^SDL.Surface ---
RenderText_LCD_Wrapped :: proc(font: ^Font, text: cstring, length: c.size_t, fg, bg: SDL.Color, wrap_width: c.int) -> ^SDL.Surface ---
RenderGlyph_LCD :: proc(font: ^Font, ch: u32, fg, bg: SDL.Color) -> ^SDL.Surface ---
CreateSurfaceTextEngine :: proc() -> ^TextEngine ---
DrawSurfaceText :: proc(text: ^Text, x, y: c.int, surface: ^SDL.Surface) -> bool ---
DestroySurfaceTextEngine :: proc(engine: ^TextEngine) ---
CreateRendererTextEngine :: proc(renderer: ^SDL.Renderer) -> ^TextEngine ---
CreateRendererTextEngineWithProperties :: proc(props: SDL.PropertiesID) -> ^TextEngine ---
DrawRendererText :: proc(text: ^Text, x, y: f32) -> bool ---
DestroyRendererTextEngine :: proc(engine: ^TextEngine) ---
CreateGPUTextEngine :: proc(device: ^SDL.GPUDevice) -> ^TextEngine ---
CreateGPUTextEngineWithProperties :: proc(props: SDL.PropertiesID) -> ^TextEngine ---
GetGPUTextDrawData :: proc(text: ^Text) -> ^GPUAtlasDrawSequence ---
DestroyGPUTextEngine :: proc(engine: ^TextEngine) ---
SetGPUTextEngineWinding :: proc(engine: ^TextEngine, winding: GPUTextEngineWinding) ---
GetGPUTextEngineWinding :: proc(#by_ptr engine: TextEngine) -> GPUTextEngineWinding ---
CreateText :: proc(engine: ^TextEngine, font: ^Font, text: cstring, length: c.size_t) -> ^Text ---
GetTextProperties :: proc(text: ^Text) -> SDL.PropertiesID ---
SetTextEngine :: proc(text: ^Text, engine: ^TextEngine) -> bool ---
GetTextEngine :: proc(text: ^Text) -> ^TextEngine ---
SetTextFont :: proc(text: ^Text, font: ^Font) -> bool ---
GetTextFont :: proc(text: ^Text) -> ^Font ---
SetTextDirection :: proc(text: ^Text, direction: Direction) -> bool ---
GetTextDirection :: proc(text: ^Text) -> Direction ---
SetTextScript :: proc(text: ^Text, script: u32) -> bool ---
GetTextScript :: proc(text: ^Text) -> u32 ---
SetTextColor :: proc(text: ^Text, r, g, b, a: u8) -> bool ---
SetTextColorFloat :: proc(text: ^Text, r, g, b, a: f32) -> bool ---
GetTextColor :: proc(text: ^Text, r, g, b, a: ^u8) -> bool ---
GetTextColorFloat :: proc(text: ^Text, r, g, b, a: ^f32) -> bool ---
SetTextPosition :: proc(text: ^Text, x, y: c.int) -> bool ---
GetTextPosition :: proc(text: ^Text, x, y: ^c.int) -> bool ---
SetTextWrapWidth :: proc(text: ^Text, wrap_width: c.int) -> bool ---
GetTextWrapWidth :: proc(text: ^Text, wrap_width: ^c.int) -> bool ---
SetTextWrapWhitespaceVisible :: proc(text: ^Text, visible: bool) -> bool ---
TextWrapWhitespaceVisible :: proc(text: ^Text) -> bool ---
SetTextString :: proc(text: ^Text, string: cstring, length: c.size_t) -> bool ---
InsertTextString :: proc(text: ^Text, offset: c.int, string: cstring, length: c.size_t) -> bool ---
AppendTextString :: proc(text: ^Text, string: cstring, length: c.size_t) -> bool ---
DeleteTextString :: proc(text: ^Text, offset, length: c.int) -> bool ---
GetTextSize :: proc(text: ^Text, w, h: ^c.int) -> bool ---
GetTextSubString :: proc(text: ^Text, offset: c.int, substring: ^SubString) -> bool ---
GetTextSubStringForLine :: proc(text: ^Text, line: c.int, substring: ^SubString) -> bool ---
GetTextSubStringsForRange :: proc(text: ^Text, offset, length: c.int, count: ^c.int) -> [^]^SubString ---
GetTextSubStringForPoint :: proc(text: ^Text, x, y: c.int, substring: ^SubString) -> bool ---
GetPreviousTextSubString :: proc(text: ^Text, #by_ptr substring: SubString, previous: ^SubString) -> bool ---
GetNextTextSubString :: proc(text: ^Text, #by_ptr substring: SubString, next: ^SubString) -> bool ---
UpdateText :: proc(text: ^Text) -> bool ---
DestroyText :: proc(text: ^Text) ---
CloseFont :: proc(font: ^Font) ---
Quit :: proc() ---
WasInit :: proc() -> c.int ---
}
+3 -3
View File
@@ -61,10 +61,10 @@ foreign webgl {
BufferData :: proc(target: Enum, size: int, data: rawptr, usage: Enum) ---
BufferSubData :: proc(target: Enum, offset: uintptr, size: int, data: rawptr) ---
Clear :: proc(bits: Enum) ---
Clear :: proc(bits: u32) ---
ClearColor :: proc(r, g, b, a: f32) ---
ClearDepth :: proc(x: Enum) ---
ClearStencil :: proc(x: Enum) ---
ClearDepth :: proc(x: f32) ---
ClearStencil :: proc(x: i32) ---
ColorMask :: proc(r, g, b, a: bool) ---
CompileShader :: proc(shader: Shader) ---
+2 -2
View File
@@ -340,8 +340,8 @@ class WebGPUInterface {
return STATUS_SUCCESS;
}
genericAdapterInfo(infoPtr) {
genericGetAdapterInfo(infoPtr) {
this.assert(infoPtr != 0);
const off = this.struct(infoPtr);
+233
View File
@@ -0,0 +1,233 @@
#+build windows
/* NOTES:
1. Definition of terms:
LFE: Low Frequency Effect -- always omnidirectional.
LPF: Low Pass Filter, divided into two classifications:
Direct -- Applied to the direct signal path,
used for obstruction/occlusion effects.
Reverb -- Applied to the reverb signal path,
used for occlusion effects only.
2. Volume level is expressed as a linear amplitude scaler:
1.0f represents no attenuation applied to the original signal,
0.5f denotes an attenuation of 6dB, and 0.0f results in silence.
Amplification (volume > 1.0f) is also allowed, and is not clamped.
LPF values range from 1.0f representing all frequencies pass through,
to 0.0f which results in silence as all frequencies are filtered out.
3. X3DAudio uses a left-handed Cartesian coordinate system with values
on the x-axis increasing from left to right, on the y-axis from
bottom to top, and on the z-axis from near to far.
Azimuths are measured clockwise from a given reference direction.
Distance measurement is with respect to user-defined world units.
Applications may provide coordinates using any system of measure
as all non-normalized calculations are scale invariant, with such
operations natively occurring in user-defined world unit space.
Metric constants are supplied only as a convenience.
Distance is calculated using the Euclidean norm formula.
4. Only real values are permissible with functions using 32-bit
float parameters -- NAN and infinite values are not accepted.
All computation occurs in 32-bit precision mode. */
package windows_xaudio2
import "core:math"
foreign import xa2 "system:xaudio2.lib"
//--------------<D-E-F-I-N-I-T-I-O-N-S>-------------------------------------//
// speaker geometry configuration flags, specifies assignment of channels to speaker positions, defined as per WAVEFORMATEXTENSIBLE.dwChannelMask
SPEAKER_FLAGS :: distinct bit_set[SPEAKER_FLAG; u32]
SPEAKER_FLAG :: enum u32 {
FRONT_LEFT = 0,
FRONT_RIGHT = 1,
FRONT_CENTER = 2,
LOW_FREQUENCY = 3,
BACK_LEFT = 4,
BACK_RIGHT = 5,
FRONT_LEFT_OF_CENTER = 6,
FRONT_RIGHT_OF_CENTER = 7,
BACK_CENTER = 8,
SIDE_LEFT = 9,
SIDE_RIGHT = 10,
TOP_CENTER = 11,
TOP_FRONT_LEFT = 12,
TOP_FRONT_CENTER = 13,
TOP_FRONT_RIGHT = 14,
TOP_BACK_LEFT = 15,
TOP_BACK_CENTER = 16,
TOP_BACK_RIGHT = 17,
//RESERVED = 0x7FFC0000, // bit mask locations reserved for future use
ALL = 31, // used to specify that any possible permutation of speaker configurations
}
// standard speaker geometry configurations, used with Initialize
SPEAKER_MONO :: SPEAKER_FLAGS{.FRONT_CENTER}
SPEAKER_STEREO :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT}
SPEAKER_2POINT1 :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .LOW_FREQUENCY}
SPEAKER_SURROUND :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .BACK_CENTER}
SPEAKER_QUAD :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .BACK_LEFT, .BACK_RIGHT}
SPEAKER_4POINT1 :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT}
SPEAKER_5POINT1 :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT}
SPEAKER_7POINT1 :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT, .FRONT_LEFT_OF_CENTER, .FRONT_RIGHT_OF_CENTER}
SPEAKER_5POINT1_SURROUND :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .SIDE_LEFT, .SIDE_RIGHT}
SPEAKER_7POINT1_SURROUND :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT, .SIDE_LEFT, .SIDE_RIGHT}
// size of instance handle in bytes
HANDLE_BYTESIZE :: 20
// speed of sound in meters per second for dry air at approximately 20C, used with Initialize
SPEED_OF_SOUND :: 343.5
// calculation control flags, used with Calculate
CALCULATE_FLAGS :: distinct bit_set[CALCULATE_FLAG; u32]
CALCULATE_FLAG :: enum u32 {
MATRIX = 0, // enable matrix coefficient table calculation
DELAY = 1, // enable delay time array calculation (stereo final mix only)
LPF_DIRECT = 2, // enable LPF direct-path coefficient calculation
LPF_REVERB = 3, // enable LPF reverb-path coefficient calculation
REVERB = 4, // enable reverb send level calculation
DOPPLER = 5, // enable doppler shift factor calculation
EMITTER_ANGLE = 6, // enable emitter-to-listener interior angle calculation
ZEROCENTER = 16, // do not position to front center speaker, signal positioned to remaining speakers instead, front center destination channel will be zero in returned matrix coefficient table, valid only for matrix calculations with final mix formats that have a front center channel
REDIRECT_TO_LFE = 17, // apply equal mix of all source channels to LFE destination channel, valid only for matrix calculations with sources that have no LFE channel and final mix formats that have an LFE channel
}
//--------------<D-A-T-A---T-Y-P-E-S>---------------------------------------//
VECTOR :: [3]f32 // float 3D vector
// instance handle of precalculated constants
HANDLE :: distinct [HANDLE_BYTESIZE]byte
// Distance curve point:
// Defines a DSP setting at a given normalized distance.
DISTANCE_CURVE_POINT :: struct #packed {
Distance: f32, // normalized distance, must be within [0.0f, 1.0f]
DSPSetting: f32, // DSP setting
}
// Distance curve:
// A piecewise curve made up of linear segments used to define DSP behaviour with respect to normalized distance.
//
// Note that curve point distances are normalized within [0.0f, 1.0f].
// EMITTER.CurveDistanceScaler must be used to scale the normalized distances to user-defined world units.
// For distances beyond CurveDistanceScaler * 1.0f, pPoints[PointCount-1].DSPSetting is used as the DSP setting.
//
// All distance curve spans must be such that:
// pPoints[k-1].DSPSetting + ((pPoints[k].DSPSetting-pPoints[k-1].DSPSetting) / (pPoints[k].Distance-pPoints[k-1].Distance)) * (pPoints[k].Distance-pPoints[k-1].Distance) != NAN or infinite values
// For all points in the distance curve where 1 <= k < PointCount.
DISTANCE_CURVE :: struct #packed {
pPoints: [^]DISTANCE_CURVE_POINT `fmt:"v,PointCount"`, // distance curve point array, must have at least PointCount elements with no duplicates and be sorted in ascending order with respect to Distance
PointCount: u32, // number of distance curve points, must be >= 2 as all distance curves must have at least two endpoints, defining DSP settings at 0.0f and 1.0f normalized distance
}
Default_LinearCurvePoints := [2]DISTANCE_CURVE_POINT{{0.0, 1.0}, {1.0, 0.0}}
Default_LinearCurve := DISTANCE_CURVE{&Default_LinearCurvePoints[0], 2}
CONE :: struct #packed {
InnerAngle: f32, // inner cone angle in radians, must be within [0.0f, TAU]
OuterAngle: f32, // outer cone angle in radians, must be within [InnerAngle, TAU]
InnerVolume: f32, // volume level scaler on/within inner cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
OuterVolume: f32, // volume level scaler on/beyond outer cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
InnerLPF: f32, // LPF (both direct and reverb paths) coefficient subtrahend on/within inner cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
OuterLPF: f32, // LPF (both direct and reverb paths) coefficient subtrahend on/beyond outer cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
InnerReverb: f32, // reverb send level scaler on/within inner cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
OuterReverb: f32, // reverb send level scaler on/beyond outer cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
}
Default_DirectionalCone := CONE{math.PI / 2, math.PI, 1.0, 0.708, 0.0, 0.25, 0.708, 1.0}
// Listener:
// Defines a point of 3D audio reception.
//
// The cone is directed by the listener's front orientation.
LISTENER :: struct #packed {
OrientFront: VECTOR, // orientation of front direction, used only for matrix and delay calculations or listeners with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used
OrientTop: VECTOR, // orientation of top direction, used only for matrix and delay calculations, must be orthonormal with OrientFront when used
Position: VECTOR, // position in user-defined world units, does not affect Velocity
Velocity: VECTOR, // velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position
pCone: ^CONE, // sound cone, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality
}
// Emitter:
// Defines a 3D audio source, divided into two classifications:
//
// Single-point -- For use with single-channel sounds.
// Positioned at the emitter base, i.e. the channel radius and azimuth are ignored if the number of channels == 1.
//
// May be omnidirectional or directional using a cone.
// The cone originates from the emitter base position, and is directed by the emitter's front orientation.
//
// Multi-point -- For use with multi-channel sounds.
// Each non-LFE channel is positioned using an azimuth along the channel radius with respect to the front orientation vector in the plane orthogonal to the top orientation vector.
// An azimuth of TAU specifies a channel is an LFE. Such channels are positioned at the emitter base and are calculated with respect to pLFECurve only, never pVolumeCurve.
//
// Multi-point emitters are always omnidirectional, i.e. the cone is ignored if the number of channels > 1.
//
// Note that many properties are shared among all channel points, locking certain behaviour with respect to the emitter base position.
// For example, doppler shift is always calculated with respect to the emitter base position and so is constant for all its channel points.
// Distance curve calculations are also with respect to the emitter base position, with the curves being calculated independently of each other.
// For instance, volume and LFE calculations do not affect one another.
EMITTER :: struct #packed {
pCone: ^CONE, // sound cone, used only with single-channel emitters for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality
OrientFront: VECTOR, // orientation of front direction, used only for emitter angle calculations or with multi-channel emitters for matrix calculations or single-channel emitters with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used
OrientTop: VECTOR, // orientation of top direction, used only with multi-channel emitters for matrix calculations, must be orthonormal with OrientFront when used
Position: VECTOR, // position in user-defined world units, does not affect Velocity
Velocity: VECTOR, // velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position
InnerRadius: f32, // inner radius, must be within [0.0f, max(f32)]
InnerRadiusAngle: f32, // inner radius angle, must be within [0.0f, PI/4.0)
ChannelCount: u32, // number of sound channels, must be > 0
ChannelRadius: f32, // channel radius, used only with multi-channel emitters for matrix calculations, must be >= 0.0f when used
pChannelAzimuths: [^]f32 `fmt:"v,ChannelCount"`, // channel azimuth array, used only with multi-channel emitters for matrix calculations, contains positions of each channel expressed in radians along the channel radius with respect to the front orientation vector in the plane orthogonal to the top orientation vector, or TAU to specify an LFE channel, must have at least ChannelCount elements, all within [0.0f, TAU] when used
pVolumeCurve: ^DISTANCE_CURVE, // volume level distance curve, used only for matrix calculations, NULL specifies a default curve that conforms to the inverse square law, calculated in user-defined world units with distances <= CurveDistanceScaler clamped to no attenuation
pLFECurve: ^DISTANCE_CURVE, // LFE level distance curve, used only for matrix calculations, NULL specifies a default curve that conforms to the inverse square law, calculated in user-defined world units with distances <= CurveDistanceScaler clamped to no attenuation
pLPFDirectCurve: ^DISTANCE_CURVE, // LPF direct-path coefficient distance curve, used only for LPF direct-path calculations, NULL specifies the default curve: [0.0f,1.0f], [1.0f,0.75f]
pLPFReverbCurve: ^DISTANCE_CURVE, // LPF reverb-path coefficient distance curve, used only for LPF reverb-path calculations, NULL specifies the default curve: [0.0f,0.75f], [1.0f,0.75f]
pReverbCurve: ^DISTANCE_CURVE, // reverb send level distance curve, used only for reverb calculations, NULL specifies the default curve: [0.0f,1.0f], [1.0f,0.0f]
CurveDistanceScaler: f32, // curve distance scaler, used to scale normalized distance curves to user-defined world units and/or exaggerate their effect, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, must be within [min(f32), max(f32)] when used
DopplerScaler: f32, // doppler shift scaler, used to exaggerate doppler shift effect, used only for doppler calculations, must be within [0.0f, max(f32)] when used
}
// DSP settings:
// Receives results from a call to Calculate to be sent to the low-level audio rendering API for 3D signal processing.
//
// The user is responsible for allocating the matrix coefficient table, delay time array, and initializing the channel counts when used.
DSP_SETTINGS :: struct #packed {
pMatrixCoefficients: [^]f32, // [inout] matrix coefficient table, receives an array representing the volume level used to send from source channel S to destination channel D, stored as pMatrixCoefficients[SrcChannelCount * D + S], must have at least SrcChannelCount*DstChannelCount elements
pDelayTimes: [^]f32, // [inout] delay time array, receives delays for each destination channel in milliseconds, must have at least DstChannelCount elements (stereo final mix only)
SrcChannelCount: u32, // [in] number of source channels, must equal number of channels in respective emitter
DstChannelCount: u32, // [in] number of destination channels, must equal number of channels of the final mix
LPFDirectCoefficient: f32, // [out] LPF direct-path coefficient
LPFReverbCoefficient: f32, // [out] LPF reverb-path coefficient
ReverbLevel: f32, // [out] reverb send level
DopplerFactor: f32, // [out] doppler shift factor, scales resampler ratio for doppler shift effect, where the effective frequency = DopplerFactor * original frequency
EmitterToListenerAngle: f32, // [out] emitter-to-listener interior angle, expressed in radians with respect to the emitter's front orientation
EmitterToListenerDistance: f32, // [out] distance in user-defined world units from the emitter base to listener position, always calculated
EmitterVelocityComponent: f32, // [out] component of emitter velocity vector projected onto emitter->listener vector in user-defined world units/second, calculated only for doppler
ListenerVelocityComponent: f32, // [out] component of listener velocity vector projected onto emitter->listener vector in user-defined world units/second, calculated only for doppler
}
//--------------<F-U-N-C-T-I-O-N-S>-----------------------------------------//
@(default_calling_convention="cdecl", link_prefix="X3DAudio")
foreign xa2 {
// initializes instance handle
Initialize :: proc(SpeakerChannelMask: SPEAKER_FLAGS, SpeedOfSound: f32, Instance: HANDLE) -> HRESULT ---
// calculates DSP settings with respect to 3D parameters
Calculate :: proc(Instance: HANDLE, #by_ptr pListener: LISTENER, #by_ptr pEmitter: EMITTER, Flags: CALCULATE_FLAGS, pDSPSettings: ^DSP_SETTINGS) ---
}
+377
View File
@@ -0,0 +1,377 @@
#+build windows
/* NOTES:
1. Definition of terms:
DSP: Digital Signal Processing.
CBR: Constant BitRate -- DSP that consumes a constant number of
input samples to produce an output sample.
For example, a 22kHz to 44kHz resampler is CBR DSP.
Even though the number of input to output samples differ,
the ratio between input to output rate remains constant.
All user-defined XAPOs are assumed to be CBR as
XAudio2 only allows CBR DSP to be added to an effect chain.
XAPO: Cross-platform Audio Processing Object --
a thin wrapper that manages DSP code, allowing it
to be easily plugged into an XAudio2 effect chain.
Frame: A block of samples, one per channel,
to be played simultaneously.
E.g. a mono stream has one sample per frame.
In-Place: Processing such that the input buffer equals the
output buffer (i.e. input data modified directly).
This form of processing is generally more efficient
than using separate memory for input and output.
However, an XAPO may not perform format conversion
when processing in-place.
2. XAPO member variables are divided into three classifications:
Immutable: Set once via IXAPO.Initialize and remain
constant during the lifespan of the XAPO.
Locked: May change before the XAPO is locked via
IXAPO.LockForProcess but remain constant
until IXAPO.UnlockForProcess is called.
Dynamic: May change from one processing pass to the next,
usually via IXAPOParameters.SetParameters.
XAPOs should assign reasonable defaults to their dynamic
variables during IXAPO.Initialize/LockForProcess so
that calling IXAPOParameters.SetParameters is not
required before processing begins.
When implementing an XAPO, determine the type of each variable and
initialize them in the appropriate method. Immutable variables are
generally preferable over locked which are preferable over dynamic.
That is, one should strive to minimize XAPO state changes for
best performance, maintainability, and ease of use.
3. To minimize glitches, the realtime audio processing thread must
not block. XAPO methods called by the realtime thread are commented
as non-blocking and therefore should not use blocking synchronization,
allocate memory, access the disk, etc. The XAPO interfaces were
designed to allow an effect implementer to move such operations
into other methods called on an application controlled thread.
4. Extending functionality is accomplished through the addition of new
COM interfaces. For example, if a new member is added to a parameter
structure, a new interface using the new structure should be added,
leaving the original interface unchanged.
This ensures consistent communication between future versions of
XAudio2 and various versions of XAPOs that may exist in an application.
5. All audio data is interleaved in XAudio2.
The default audio format for an effect chain is WAVE_FORMAT_IEEE_FLOAT.
6. User-defined XAPOs should assume all input and output buffers are
16-byte aligned.
7. See XAPOBase.odin for an XAPO base class which provides a default
implementation for most of the interface methods defined below. */
package windows_xaudio2
import win "core:sys/windows"
//--------------<D-E-F-I-N-I-T-I-O-N-S>-------------------------------------//
// XAPO error codes
FORMAT_UNSUPPORTED := win.MAKE_HRESULT(win.SEVERITY.ERROR, 0x897, 0x01) // requested audio format unsupported
// supported number of channels (samples per frame) range
XAPO_MIN_CHANNELS :: 1
XAPO_MAX_CHANNELS :: 64
// supported framerate range
XAPO_MIN_FRAMERATE :: 1000
XAPO_MAX_FRAMERATE :: 200000
// unicode string length, including terminator, used with XAPO_REGISTRATION_PROPERTIES
XAPO_REGISTRATION_STRING_LENGTH :: 256
// XAPO property flags, used with XAPO_REGISTRATION_PROPERTIES.Flags:
XAPO_FLAGS :: distinct bit_set[XAPO_FLAG; u32]
XAPO_FLAG :: enum u32 {
// Number of channels of input and output buffers must match, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat.
CHANNELS_MUST_MATCH = 0,
// Framerate of input and output buffers must match, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat.
FRAMERATE_MUST_MATCH = 1,
// Bit depth of input and output buffers must match, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat.
// Container size of input and output buffers must also match if XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat is WAVEFORMATEXTENSIBLE.
BITSPERSAMPLE_MUST_MATCH = 2,
// Number of input and output buffers must match, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.
// Also, XAPO_REGISTRATION_PROPERTIES.MinInputBufferCount must equal XAPO_REGISTRATION_PROPERTIES.MinOutputBufferCount and XAPO_REGISTRATION_PROPERTIES.MaxInputBufferCount must equal XAPO_REGISTRATION_PROPERTIES.MaxOutputBufferCount when used.
BUFFERCOUNT_MUST_MATCH = 3,
// XAPO must be run in-place. Use this flag only if your DSP implementation cannot process separate input and output buffers.
// If set, the following flags must also be set:
// XAPO_FLAG_CHANNELS_MUST_MATCH
// XAPO_FLAG_FRAMERATE_MUST_MATCH
// XAPO_FLAG_BITSPERSAMPLE_MUST_MATCH
// XAPO_FLAG_BUFFERCOUNT_MUST_MATCH
// XAPO_FLAG_INPLACE_SUPPORTED
// Multiple input and output buffers may be used with in-place XAPOs, though the input buffer count must equal the output buffer count.
// When multiple input/output buffers are used, the XAPO may assume input buffer [N] equals output buffer [N] for in-place processing.
INPLACE_REQUIRED = 5,
// XAPO may be run in-place. If the XAPO is used in a chain such that the requirements for XAPO_FLAG_INPLACE_REQUIRED are met, XAudio2 will ensure the XAPO is run in-place.
// If not met, XAudio2 will still run the XAPO albeit with separate input and output buffers.
// For example, consider an effect which may be ran in stereo->5.1 mode or mono->mono mode. When set to stereo->5.1, it will be run with separate input and output buffers as format conversion is not permitted in-place.
// However, if configured to run mono->mono, the same XAPO can be run in-place. Thus the same implementation may be conveniently reused for various input/output configurations, while taking advantage of in-place processing when possible.
INPLACE_SUPPORTED = 4,
}
//--------------<D-A-T-A---T-Y-P-E-S>---------------------------------------//
// XAPO registration properties, describes general XAPO characteristics, used with IXAPO.GetRegistrationProperties
XAPO_REGISTRATION_PROPERTIES :: struct #packed {
clsid: win.CLSID, // COM class ID, used with CoCreate
FriendlyName: [XAPO_REGISTRATION_STRING_LENGTH]u16, // friendly name unicode string
CopyrightInfo: [XAPO_REGISTRATION_STRING_LENGTH]u16, // copyright information unicode string
MajorVersion: u32, // major version
MinorVersion: u32, // minor version
Flags: XAPO_FLAGS, // XAPO property flags, describes supported input/output configuration
MinInputBufferCount: u32, // minimum number of input buffers required for processing, can be 0
MaxInputBufferCount: u32, // maximum number of input buffers supported for processing, must be >= MinInputBufferCount
MinOutputBufferCount: u32, // minimum number of output buffers required for processing, can be 0, must match MinInputBufferCount when XAPO_FLAG_BUFFERCOUNT_MUST_MATCH used
MaxOutputBufferCount: u32, // maximum number of output buffers supported for processing, must be >= MinOutputBufferCount, must match MaxInputBufferCount when XAPO_FLAG_BUFFERCOUNT_MUST_MATCH used
}
// LockForProcess buffer parameters:
// Defines buffer parameters that remain constant while an XAPO is locked.
// Used with IXAPO::LockForProcess.
// For CBR XAPOs, MaxFrameCount is the only number of frames
// IXAPO::Process would have to handle for the respective buffer.
XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS :: struct #packed {
pFormat: ^WAVEFORMATEX, // buffer audio format
MaxFrameCount: u32, // maximum number of frames in respective buffer that IXAPO::Process would have to handle, irrespective of dynamic variable settings, can be 0
}
// Buffer flags:
// Describes assumed content of the respective buffer.
// Used with XAPO_PROCESS_BUFFER_PARAMETERS.BufferFlags.
// This meta-data can be used by an XAPO to implement optimizations that require knowledge of a buffer's content.
// For example, XAPOs that always produce silent output from silent input can check the flag on the input buffer to determine if any signal processing is necessary.
// If silent, the XAPO may simply set the flag on the output buffer to silent and return, optimizing out the work of processing silent data: XAPOs that generate silence for any reason may set the buffer's flag accordingly rather than writing out silent frames to the buffer itself.
// The flags represent what should be assumed is in the respective buffer. The flags may not reflect what is actually stored in memory.
XAPO_BUFFER_FLAGS :: enum i32 {
XAPO_BUFFER_SILENT, // silent data should be assumed, respective memory may be uninitialized
XAPO_BUFFER_VALID, // arbitrary data should be assumed (may or may not be silent frames), respective memory initialized
}
// Process buffer parameters:
// Defines buffer parameters that may change from one
// processing pass to the next. Used with IXAPO::Process.
//
// Note the byte size of the respective buffer must be at least:
// XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount * XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat->nBlockAlign
//
// Although the audio format and maximum size of the respective
// buffer is locked (defined by XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS),
// the actual memory address of the buffer given is permitted to change
// from one processing pass to the next.
//
// For CBR XAPOs, ValidFrameCount is constant while locked and equals
// the respective XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount.
XAPO_PROCESS_BUFFER_PARAMETERS :: struct #packed {
pBuffer: rawptr, // audio data buffer, must be non-NULL
BufferFlags: XAPO_BUFFER_FLAGS, // describes assumed content of pBuffer, does not affect ValidFrameCount
ValidFrameCount: u32, // number of frames of valid data, must be within respective [0, XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount], always XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount for CBR/user-defined XAPOs, does not affect BufferFlags
}
XAPOFree :: win.CoTaskMemFree
IXAPO_UUID_STRING :: "A410B984-9839-4819-A0BE-2856AE6B3ADB"
IXAPO_UUID := &win.IID{0xA410B984, 0x9839, 0x4819, {0xA0, 0xBE, 0x28, 0x56, 0xAE, 0x6B, 0x3A, 0xDB}}
IXAPO :: struct #raw_union {
#subtype iunknown: IUnknown,
using ixapo_vtable: ^IXAPO_VTable,
}
IXAPO_VTable :: struct {
using iunknown_vtable: IUnknown_VTable,
// DESCRIPTION:
// Allocates a copy of the registration properties of the XAPO.
// PARAMETERS:
// ppRegistrationProperties - [out] receives pointer to copy of registration properties, use XAPOFree to free structure, left untouched on failure
// RETURN VALUE:
// COM error code
GetRegistrationProperties: proc "system" (this: ^IXAPO, ppRegistrationProperties: ^^XAPO_REGISTRATION_PROPERTIES) -> HRESULT,
// DESCRIPTION:
// Queries if an input/output configuration is supported.
// REMARKS:
// This method allows XAPOs to express dependency of input format with respect to output format.
// If the input/output format pair configuration is unsupported, this method also determines the nearest input format supported.
// Nearest meaning closest bit depth, framerate, and channel count, in that order of importance.
// The behaviour of this method should remain constant after the XAPO has been initialized.
// PARAMETERS:
// pOutputFormat - [in] output format known to be supported
// pRequestedInputFormat - [in] input format to examine
// ppSupportedInputFormat - [out] receives pointer to nearest input format supported if not NULL and input/output configuration unsupported, use XAPOFree to free structure, left untouched on any failure except XAPO_E_FORMAT_UNSUPPORTED
// RETURN VALUE:
// COM error code, including:
// S_OK - input/output configuration supported, ppSupportedInputFormat left untouched
// FORMAT_UNSUPPORTED - input/output configuration unsupported, ppSupportedInputFormat receives pointer to nearest input format supported if not NULL
// E_INVALIDARG - either audio format invalid, ppSupportedInputFormat left untouched
IsInputFormatSupported: proc "system" (this: ^IXAPO, pOutputFormat: ^WAVEFORMATEX, pRequestedInputFormat: ^WAVEFORMATEX, ppSupportedInputFormat: ^^WAVEFORMATEX) -> HRESULT,
// DESCRIPTION:
// Queries if an input/output configuration is supported.
// REMARKS:
// This method allows XAPOs to express dependency of output format with respect to input format.
// If the input/output format pair configuration is unsupported, this method also determines the nearest output format supported.
// Nearest meaning closest bit depth, framerate, and channel count, in that order of importance.
// The behaviour of this method should remain constant after the XAPO has been initialized.
// PARAMETERS:
// pInputFormat - [in] input format known to be supported
// pRequestedOutputFormat - [in] output format to examine
// ppSupportedOutputFormat - [out] receives pointer to nearest output format supported if not NULL and input/output configuration unsupported, use XAPOFree to free structure, left untouched on any failure except XAPO_E_FORMAT_UNSUPPORTED
// RETURN VALUE:
// COM error code, including:
// S_OK - input/output configuration supported, ppSupportedOutputFormat left untouched
// FORMAT_UNSUPPORTED - input/output configuration unsupported, ppSupportedOutputFormat receives pointer to nearest output format supported if not NULL
// E_INVALIDARG - either audio format invalid, ppSupportedOutputFormat left untouched
IsOutputFormatSupported: proc "system" (this: ^IXAPO, pInputFormat: ^WAVEFORMATEX, pRequestedOutputFormat: ^WAVEFORMATEX, ppSupportedOutputFormat: ^^WAVEFORMATEX) -> HRESULT,
// DESCRIPTION:
// Performs any effect-specific initialization if required.
// REMARKS:
// The contents of pData are defined by the XAPO.
// Immutable variables (constant during the lifespan of the XAPO) should be set once via this method.
// Once initialized, an XAPO cannot be initialized again.
// An XAPO should be initialized before passing it to XAudio2 as part of an effect chain. XAudio2 will not call this method; it exists for future content-driven initialization.
// PARAMETERS:
// pData - [in] effect-specific initialization parameters, may be NULL if DataByteSize == 0
// DataByteSize - [in] size of pData in bytes, may be 0 if pData is NULL
// RETURN VALUE:
// COM error code
Initialize: proc "system" (this: ^IXAPO, pData: rawptr, DataByteSize: u32) -> HRESULT,
// DESCRIPTION:
// Resets variables dependent on frame history.
// REMARKS:
// All other variables remain unchanged, including variables set by IXAPOParameters.SetParameters.
// For example, an effect with delay should zero out its delay line during this method, but should not reallocate anything as the
// XAPO remains locked with a constant input/output configuration. XAudio2 calls this method only if the XAPO is locked.
// This method should not block as it is called from the realtime thread.
// PARAMETERS:
// void
// RETURN VALUE:
// void
Reset: proc "system" (this: ^IXAPO),
// DESCRIPTION:
// Locks the XAPO to a specific input/output configuration,
// allowing it to do any final initialization before Process
// is called on the realtime thread.
// REMARKS:
// Once locked, the input/output configuration and any other locked variables remain constant until UnlockForProcess is called.
// XAPOs should assert the input/output configuration is supported and that any required effect-specific initialization is complete.
// IsInputFormatSupported, IsOutputFormatSupported, and Initialize should be called as necessary before this method is called.
// All internal memory buffers required for Process should be allocated by the time this method returns successfully as Process is non-blocking and should not allocate memory.
// Once locked, an XAPO cannot be locked again until UnLockForProcess is called.
// PARAMETERS:
// InputLockedParameterCount - [in] number of input buffers, must be within [XAPO_REGISTRATION_PROPERTIES.MinInputBufferCount, XAPO_REGISTRATION_PROPERTIES.MaxInputBufferCount]
// pInputLockedParameters - [in] array of input locked buffer parameter structures, may be NULL if InputLockedParameterCount == 0, otherwise must have InputLockedParameterCount elements
// OutputLockedParameterCount - [in] number of output buffers, must be within [XAPO_REGISTRATION_PROPERTIES.MinOutputBufferCount, XAPO_REGISTRATION_PROPERTIES.MaxOutputBufferCount], must match InputLockedParameterCount when XAPO_FLAG_BUFFERCOUNT_MUST_MATCH used
// pOutputLockedParameters - [in] array of output locked buffer parameter structures, may be NULL if OutputLockedParameterCount == 0, otherwise must have OutputLockedParameterCount elements
// RETURN VALUE:
// COM error code
LockForProcess: proc "system" (this: ^IXAPO, InputLockedParameterCount: u32, pInputLockedParameters: [^]XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS, OutputLockedParameterCount: u32, pOutputLockedParameters: [^]XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS) -> HRESULT,
// DESCRIPTION:
// Opposite of LockForProcess. Variables allocated during LockForProcess should be deallocated by this method.
// REMARKS:
// Unlocking an XAPO allows an XAPO instance to be reused with different input/output configurations.
// PARAMETERS:
// void
// RETURN VALUE:
// void
UnlockForProcess: proc "system" (this: ^IXAPO),
// DESCRIPTION:
// Runs the XAPO's DSP code on the given input/output buffers.
// REMARKS:
// In addition to writing to the output buffers as appropriate, an XAPO must set the BufferFlags and ValidFrameCount members of all elements in pOutputProcessParameters accordingly.
// ppInputProcessParameters will not necessarily be the same as ppOutputProcessParameters for in-place processing, rather the pBuffer members of each will point to the same memory.
// Multiple input/output buffers may be used with in-place XAPOs, though the input buffer count must equal the output buffer count.
// When multiple input/output buffers are used with in-place XAPOs, the XAPO may assume input buffer [N] equals output buffer [N].
// When IsEnabled is FALSE, the XAPO should process thru. Thru processing means an XAPO should not apply its normal processing to the given input/output buffers during Process.
// It should instead pass data from input to output with as little modification possible. Effects that perform format conversion should continue to do so.
// The effect must ensure transitions between normal and thru processing do not introduce discontinuities into the signal.
// XAudio2 calls this method only if the XAPO is locked. This method should not block as it is called from the realtime thread.
// PARAMETERS:
// InputProcessParameterCount - [in] number of input buffers, matches respective InputLockedParameterCount parameter given to LockForProcess
// pInputProcessParameters - [in] array of input process buffer parameter structures, may be NULL if InputProcessParameterCount == 0, otherwise must have InputProcessParameterCount elements
// OutputProcessParameterCount - [in] number of output buffers, matches respective OutputLockedParameterCount parameter given to LockForProcess
// pOutputProcessParameters - [in/out] array of output process buffer parameter structures, may be NULL if OutputProcessParameterCount == 0, otherwise must have OutputProcessParameterCount elements
// IsEnabled - [in] TRUE to process normally, FALSE to process thru
// RETURN VALUE:
// void
Process: proc "system" (this: ^IXAPO, InputProcessParameterCount: u32, pInputProcessParameters: [^]XAPO_PROCESS_BUFFER_PARAMETERS, OutputProcessParameterCount: u32, pOutputProcessParameters: [^]XAPO_PROCESS_BUFFER_PARAMETERS, IsEnabled: b32),
// DESCRIPTION:
// Returns the number of input frames required to generate the requested number of output frames.
// REMARKS:
// XAudio2 may call this method to determine how many input frames an XAPO requires.
// This is constant for locked CBR XAPOs; this method need only be called once while an XAPO is locked.
// XAudio2 calls this method only if the XAPO is locked. This method should not block as it is called from the realtime thread.
// PARAMETERS:
// OutputFrameCount - [in] requested number of output frames, must be within respective [0, XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount], always XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount for CBR/user-defined XAPOs
// RETURN VALUE:
// number of input frames required
CalcInputFrames: proc "system" (this: ^IXAPO, OutputFrameCount: u32) -> u32,
// DESCRIPTION:
// Returns the number of output frames generated for the requested number of input frames.
// REMARKS:
// XAudio2 may call this method to determine how many output frames an XAPO will generate. This is constant for locked CBR XAPOs; this method need only be called once while an XAPO is locked.
// XAudio2 calls this method only if the XAPO is locked. This method should not block as it is called from the realtime thread.
// PARAMETERS:
// InputFrameCount - [in] requested number of input frames, must be within respective [0, XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount], always XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.MaxFrameCount for CBR/user-defined XAPOs
// RETURN VALUE:
// number of output frames generated
CalcOutputFrames: proc "system" (this: ^IXAPO, InputFrameCount: u32) -> u32,
}
// IXAPOParameters:
// Optional XAPO COM interface that allows an XAPO to use effect-specific parameters.
IXAPOParameters_UUID_STRING :: "26D95C66-80F2-499A-AD54-5AE7F01C6D98"
IXAPOParameters_UUID := &win.IID{0x26D95C66, 0x80F2, 0x499A, {0xAD, 0x54, 0x5A, 0xE7, 0xF0, 0x1C, 0x6D, 0x98}}
IXAPOParameters :: struct #raw_union {
#subtype iunknown: IUnknown,
using ixapoparameters_vtable: ^IXAPOParameters_VTable,
}
IXAPOParameters_VTable :: struct {
using iunknown_vtable: IUnknown_VTable,
// DESCRIPTION:
// Sets effect-specific parameters.
// REMARKS:
// This method may only be called on the realtime thread; no synchronization between it and IXAPO.Process is necessary.
// This method should not block as it is called from the realtime thread.
// PARAMETERS:
// pParameters - [in] effect-specific parameter block, must be != NULL
// ParameterByteSize - [in] size of pParameters in bytes, must be > 0
// RETURN VALUE:
// void
SetParameters: proc "system" (this: ^IXAPOParameters, pParameters: rawptr, ParameterByteSize: u32),
// DESCRIPTION:
// Gets effect-specific parameters.
// REMARKS:
// Unlike SetParameters, XAudio2 does not call this method on the realtime thread. Thus, the XAPO must protect variables shared with SetParameters/Process using appropriate synchronization.
// PARAMETERS:
// pParameters - [out] receives effect-specific parameter block, must be != NULL
// ParameterByteSize - [in] size of pParameters in bytes, must be > 0
// RETURN VALUE:
// void
GetParameters: proc "system" (this: ^IXAPOParameters, pParameters: rawptr, ParameterByteSize: u32),
}
+138
View File
@@ -0,0 +1,138 @@
#+build windows
package windows_xaudio2
import win "core:sys/windows"
foreign import xa2 "system:xaudio2.lib"
//--------------<D-E-F-I-N-I-T-I-O-N-S>-------------------------------------//
FXEQ_UUID_STRING :: "F5E01117-D6C4-485A-A3F5-695196F3DBFA"
FXEQ_UUID := &win.CLSID{0xF5E01117, 0xD6C4, 0x485A, {0xA3, 0xF5, 0x69, 0x51, 0x96, 0xF3, 0xDB, 0xFA}}
FXMasteringLimiter_UUID_STRING :: "C4137916-2BE1-46FD-8599-441536F49856"
FXMasteringLimiter_UUID := &win.CLSID{0xC4137916, 0x2BE1, 0x46FD, {0x85, 0x99, 0x44, 0x15, 0x36, 0xF4, 0x98, 0x56}}
FXReverb_UUID_STRING :: "7D9ACA56-CB68-4807-B632-B137352E8596"
FXReverb_UUID := &win.CLSID{0x7D9ACA56, 0xCB68, 0x4807, {0xB6, 0x32, 0xB1, 0x37, 0x35, 0x2E, 0x85, 0x96}}
FXEcho_UUID_STRING :: "5039D740-F736-449A-84D3-A56202557B87"
FXEcho_UUID := &win.CLSID{0x5039D740, 0xF736, 0x449A, {0x84, 0xD3, 0xA5, 0x62, 0x02, 0x55, 0x7B, 0x87}}
// EQ parameter bounds (inclusive), used with FXEQ:
FXEQ_MIN_FRAMERATE :: 22000
FXEQ_MAX_FRAMERATE :: 48000
FXEQ_MIN_FREQUENCY_CENTER :: 20.0
FXEQ_MAX_FREQUENCY_CENTER :: 20000.0
FXEQ_DEFAULT_FREQUENCY_CENTER_0 :: 100.0 // band 0
FXEQ_DEFAULT_FREQUENCY_CENTER_1 :: 800.0 // band 1
FXEQ_DEFAULT_FREQUENCY_CENTER_2 :: 2000.0 // band 2
FXEQ_DEFAULT_FREQUENCY_CENTER_3 :: 10000.0 // band 3
FXEQ_MIN_GAIN :: 0.126 // -18dB
FXEQ_MAX_GAIN :: 7.94 // +18dB
FXEQ_DEFAULT_GAIN :: 1.0 // 0dB change, all bands
FXEQ_MIN_BANDWIDTH :: 0.1
FXEQ_MAX_BANDWIDTH :: 2.0
FXEQ_DEFAULT_BANDWIDTH :: 1.0 // all bands
// Mastering limiter parameter bounds (inclusive), used with FXMasteringLimiter:
FXMASTERINGLIMITER_MIN_RELEASE :: 1
FXMASTERINGLIMITER_MAX_RELEASE :: 20
FXMASTERINGLIMITER_DEFAULT_RELEASE :: 6
FXMASTERINGLIMITER_MIN_LOUDNESS :: 1
FXMASTERINGLIMITER_MAX_LOUDNESS :: 1800
FXMASTERINGLIMITER_DEFAULT_LOUDNESS :: 1000
// Reverb parameter bounds (inclusive), used with FXReverb:
FXREVERB_MIN_DIFFUSION :: 0.0
FXREVERB_MAX_DIFFUSION :: 1.0
FXREVERB_DEFAULT_DIFFUSION :: 0.9
FXREVERB_MIN_ROOMSIZE :: 0.0001
FXREVERB_MAX_ROOMSIZE :: 1.0
FXREVERB_DEFAULT_ROOMSIZE :: 0.6
// Loudness defaults used with FXLoudness:
FXLOUDNESS_DEFAULT_MOMENTARY_MS :: 400
FXLOUDNESS_DEFAULT_SHORTTERM_MS :: 3000
// Echo initialization data/parameter bounds (inclusive), used with FXEcho:
FXECHO_MIN_WETDRYMIX :: 0.0
FXECHO_MAX_WETDRYMIX :: 1.0
FXECHO_DEFAULT_WETDRYMIX :: 0.5
FXECHO_MIN_FEEDBACK :: 0.0
FXECHO_MAX_FEEDBACK :: 1.0
FXECHO_DEFAULT_FEEDBACK :: 0.5
FXECHO_MIN_DELAY :: 1.0
FXECHO_MAX_DELAY :: 2000.0
FXECHO_DEFAULT_DELAY :: 500.0
//--------------<D-A-T-A---T-Y-P-E-S>---------------------------------------//
// EQ parameters (4 bands), used with IXAPOParameters.SetParameters:
// The EQ supports only f32 audio foramts.
// The framerate must be within [22000, 48000] Hz.
FXEQ_PARAMETERS :: struct #packed {
FrequencyCenter0: f32, // center frequency in Hz, band 0
Gain0: f32, // boost/cut
Bandwidth0: f32, // bandwidth, region of EQ is center frequency +/- bandwidth/2
FrequencyCenter1: f32, // band 1
Gain1: f32,
Bandwidth1: f32,
FrequencyCenter2: f32, // band 2
Gain2: f32,
Bandwidth2: f32,
FrequencyCenter3: f32, // band 3
Gain3: f32,
Bandwidth3: f32,
}
// Mastering limiter parameters, used with IXAPOParameters.SetParameters:
// The mastering limiter supports only f32 audio formats.
FXMASTERINGLIMITER_PARAMETERS :: struct #packed {
Release: u32, // release time (tuning factor with no specific units)
Loudness: u32, // loudness target (threshold)
}
// Reverb parameters, used with IXAPOParameters.SetParameters:
// The reverb supports only f32 audio formats with the following channel configurations:
// Input: Mono Output: Mono
// Input: Stereo Output: Stereo
FXREVERB_PARAMETERS :: struct #packed {
Diffusion: f32, // diffusion
RoomSize: f32, // room size
}
// Echo initialization data, used with CreateFX:
// Use of this structure is optional, the default MaxDelay is FXECHO_DEFAULT_DELAY.
FXECHO_INITDATA :: struct #packed {
MaxDelay: f32, // maximum delay (all channels) in milliseconds, must be within [FXECHO_MIN_DELAY, FXECHO_MAX_DELAY]
}
// Echo parameters, used with IXAPOParameters.SetParameters:
// The echo supports only f32 audio formats.
FXECHO_PARAMETERS :: struct #packed {
WetDryMix: f32, // ratio of wet (processed) signal to dry (original) signal
Feedback: f32, // amount of output fed back into input
Delay: f32, // delay (all channels) in milliseconds, must be within [FXECHO_MIN_DELAY, FXECHO_PARAMETERS.MaxDelay]
}
//--------------<F-U-N-C-T-I-O-N-S>-----------------------------------------//
@(default_calling_convention="cdecl")
foreign xa2 {
// creates instance of requested XAPO, use Release to free instance
// pInitData - [in] effect-specific initialization parameters, may be NULL if InitDataByteSize == 0
// InitDataByteSize - [in] size of pInitData in bytes, may be 0 if pInitData is NULL
CreateFX :: proc(clsid: win.REFCLSID, pEffect: ^^IUnknown, pInitDat: rawptr = nil, InitDataByteSize: u32 = 0) -> HRESULT ---
}
+839
View File
@@ -0,0 +1,839 @@
#+build windows
/*
Bindings for Windows XAudio2:
https://learn.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-introduction
Compiling for Windows 10 RS5 (1809) and later
*/
package windows_xaudio2
import win "core:sys/windows"
import "core:math"
HRESULT :: win.HRESULT
IUnknown :: win.IUnknown
IUnknown_VTable :: win.IUnknown_VTable
WAVEFORMATEX :: win.WAVEFORMATEX
/**************************************************************************
*
* XAudio2 constants, flags and error codes.
*
**************************************************************************/
// Numeric boundary values
MAX_BUFFER_BYTES :: 0x80000000 // Maximum bytes allowed in a source buffer
MAX_QUEUED_BUFFERS :: 64 // Maximum buffers allowed in a voice queue
MAX_BUFFERS_SYSTEM :: 2 // Maximum buffers allowed for system threads (Xbox 360 only)
MAX_AUDIO_CHANNELS :: 64 // Maximum channels in an audio stream
MIN_SAMPLE_RATE :: 1000 // Minimum audio sample rate supported
MAX_SAMPLE_RATE :: 200000 // Maximum audio sample rate supported
MAX_VOLUME_LEVEL :: 16777216.0 // Maximum acceptable volume level (2^24)
MIN_FREQ_RATIO :: (1.0 / 1024.0) // Minimum SetFrequencyRatio argument
MAX_FREQ_RATIO :: 1024.0 // Maximum MaxFrequencyRatio argument
DEFAULT_FREQ_RATIO :: 2.0 // Default MaxFrequencyRatio argument
MAX_FILTER_ONEOVERQ :: 1.5 // Maximum FILTER_PARAMETERS.OneOverQ
MAX_FILTER_FREQUENCY :: 1.0 // Maximum FILTER_PARAMETERS.Frequency
MAX_LOOP_COUNT :: 254 // Maximum non-infinite BUFFER.LoopCount
MAX_INSTANCES :: 8 // Maximum simultaneous XAudio2 objects on Xbox 360
// For XMA voices on Xbox 360 there is an additional restriction on the MaxFrequencyRatio argument and the voice's sample rate: the product of these numbers cannot exceed 600000 for one-channel voices or 300000 for voices with more than one channel.
MAX_RATIO_TIMES_RATE_XMA_MONO :: 600000
MAX_RATIO_TIMES_RATE_XMA_MULTICHANNEL :: 300000
// Numeric values with special meanings
COMMIT_NOW :: 0 // Used as an OperationSet argument
COMMIT_ALL :: 0 // Used in IXAudio2.CommitChanges
INVALID_OPSET :: 0xffffffff // Not allowed for OperationSet arguments
NO_LOOP_REGION :: 0 // Used in BUFFER.LoopCount
LOOP_INFINITE :: 255 // Used in BUFFER.LoopCount
DEFAULT_CHANNELS :: 0 // Used in CreateMasteringVoice
DEFAULT_SAMPLERATE :: 0 // Used in CreateMasteringVoice
// Flags
FLAGS :: distinct bit_set[FLAG; u32]
FLAG :: enum u32 {
DEBUG_ENGINE = 0, // Used in Create
VOICE_NOPITCH = 1, // Used in IXAudio2.CreateSourceVoice
VOICE_NOSRC = 2, // Used in IXAudio2.CreateSourceVoice
VOICE_USEFILTER = 3, // Used in IXAudio2.CreateSource/SubmixVoice
PLAY_TAILS = 5, // Used in IXAudio2SourceVoice.Stop
END_OF_STREAM = 6, // Used in BUFFER.Flags
SEND_USEFILTER = 7, // Used in SEND_DESCRIPTOR.Flags
VOICE_NOSAMPLESPLAYED = 8, // Used in IXAudio2SourceVoice.GetState
STOP_ENGINE_WHEN_IDLE = 13, // Used in Create to force the engine to Stop when no source voices are Started, and Start when a voice is Started
QUANTUM_1024 = 15, // Used in Create to specify nondefault processing quantum of 21.33 ms (1024 samples at 48KHz)
NO_VIRTUAL_AUDIO_CLIENT = 16, // Used in CreateMasteringVoice to create a virtual audio client
}
// Default parameters for the built-in filter
DEFAULT_FILTER_TYPE :: FILTER_TYPE.LowPassFilter
DEFAULT_FILTER_FREQUENCY :: MAX_FILTER_FREQUENCY
DEFAULT_FILTER_ONEOVERQ :: 1.0
// Internal XAudio2 constants
// The audio frame quantum can be calculated by reducing the fraction:
// SamplesPerAudioFrame / SamplesPerSecond
QUANTUM_NUMERATOR :: 1 // On Windows, XAudio2 processes audio
QUANTUM_DENOMINATOR :: 100 // in 10ms chunks (= 1/100 seconds)
QUANTUM_MS :: (1000.0 * QUANTUM_NUMERATOR / QUANTUM_DENOMINATOR)
// XAudio2 error codes
INVALID_CALL :: HRESULT(-2003435519) // 0x88960001 An API call or one of its arguments was illegal
XMA_DECODER_ERROR :: HRESULT(-2003435518) // 0x88960002 The XMA hardware suffered an unrecoverable error
XAPO_CREATION_FAILED :: HRESULT(-2003435517) // 0x88960003 XAudio2 failed to initialize an XAPO effect
DEVICE_INVALIDATED :: HRESULT(-2003435516) // 0x88960004 An audio device became unusable (unplugged, etc)
/**************************************************************************
*
* XAudio2 structures and enumerations.
*
**************************************************************************/
// Used in Create, specifies which CPU(s) to use.
PROCESSOR_FLAGS :: distinct bit_set[PROCESOR_FLAG; u32]
PROCESOR_FLAG :: enum u32 {
Processor1 = 0,
Processor2 = 1,
Processor3 = 2,
Processor4 = 3,
Processor5 = 4,
Processor6 = 5,
Processor7 = 6,
Processor8 = 7,
Processor9 = 8,
Processor10 = 9,
Processor11 = 10,
Processor12 = 11,
Processor13 = 12,
Processor14 = 13,
Processor15 = 14,
Processor16 = 15,
Processor17 = 16,
Processor18 = 17,
Processor19 = 18,
Processor20 = 19,
Processor21 = 20,
Processor22 = 21,
Processor23 = 22,
Processor24 = 23,
Processor25 = 24,
Processor26 = 25,
Processor27 = 26,
Processor28 = 27,
Processor29 = 28,
Processor30 = 29,
Processor31 = 30,
Processor32 = 31,
}
USE_DEFAULT_PROCESSOR :: PROCESSOR_FLAGS{}
// Returned by IXAudio2Voice.GetVoiceDetails
VOICE_DETAILS :: struct #packed {
CreatingFlags: FLAGS,
ActiveFlags: FLAGS,
InputChannels: u32,
InputSampleRate: u32,
}
// Used in VOICE_SENDS below
SEND_DESCRIPTOR :: struct #packed {
Flags: FLAGS, // Either 0 or SEND_USEFILTER.
pOutputVoice: ^IXAudio2Voice, // This send's destination voice.
}
// Used in the voice creation functions and in IXAudio2Voice.SetOutputVoices
VOICE_SENDS :: struct #packed {
SendCount: u32, // Number of sends from this voice.
pSends: [^]SEND_DESCRIPTOR, // Array of SendCount send descriptors.
}
// Used in EFFECT_CHAIN below
EFFECT_DESCRIPTOR :: struct #packed {
pEffect: ^IUnknown, // Pointer to the effect object's IUnknown interface.
InitialState: b32, // TRUE if the effect should begin in the enabled state.
OutputChannels: u32, // How many output channels the effect should produce.
}
// Used in the voice creation functions and in IXAudio2Voice.SetEffectChain
EFFECT_CHAIN :: struct #packed {
EffectCount: u32, // Number of effects in this voice's effect chain.
pEffectDescriptors: [^]EFFECT_DESCRIPTOR, // Array of effect descriptors.
}
// Used in FILTER_PARAMETERS below
FILTER_TYPE :: enum i32 {
LowPassFilter, // Attenuates frequencies above the cutoff frequency (state-variable filter).
BandPassFilter, // Attenuates frequencies outside a given range (state-variable filter).
HighPassFilter, // Attenuates frequencies below the cutoff frequency (state-variable filter).
NotchFilter, // Attenuates frequencies inside a given range (state-variable filter).
LowPassOnePoleFilter, // Attenuates frequencies above the cutoff frequency (one-pole filter, FILTER_PARAMETERS.OneOverQ has no effect)
HighPassOnePoleFilter, // Attenuates frequencies below the cutoff frequency (one-pole filter, FILTER_PARAMETERS.OneOverQ has no effect)
}
// Used in IXAudio2Voice.Set/GetFilterParameters and Set/GetOutputFilterParameters
FILTER_PARAMETERS :: struct #packed {
Type: FILTER_TYPE, // Filter type.
Frequency: f32, // Filter coefficient. Must be >= 0 and <= MAX_FILTER_FREQUENCY. See CutoffFrequencyToRadians() for state-variable filter types and CutoffFrequencyToOnePoleCoefficient() for one-pole filter types.
OneOverQ: f32, // Reciprocal of the filter's quality factor Q; must be > 0 and <= MAX_FILTER_ONEOVERQ. Has no effect for one-pole filters.
}
// Used in IXAudio2SourceVoice.SubmitSourceBuffer
BUFFER :: struct #packed {
Flags: FLAGS, // Either 0 or END_OF_STREAM.
AudioBytes: u32, // Size of the audio data buffer in bytes.
pAudioData: [^]byte, // Pointer to the audio data buffer.
PlayBegin: u32, // First sample in this buffer to be played.
PlayLength: u32, // Length of the region to be played in samples, or 0 to play the whole buffer.
LoopBegin: u32, // First sample of the region to be looped.
LoopLength: u32, // Length of the desired loop region in samples, or 0 to loop the entire buffer.
LoopCount: u32, // Number of times to repeat the loop region, or LOOP_INFINITE to loop forever.
pContext: rawptr, // Context value to be passed back in callbacks.
}
// Used in IXAudio2SourceVoice.SubmitSourceBuffer when submitting XWMA data.
// NOTE: If an XWMA sound is submitted in more than one buffer, each buffer's pDecodedPacketCumulativeBytes[PacketCount-1] value must be subtracted from all the entries in the next buffer's pDecodedPacketCumulativeBytes array.
// And whether a sound is submitted in more than one buffer or not, the final buffer of the sound should use the END_OF_STREAM flag, or else the client must call IXAudio2SourceVoice.Discontinuity after submitting it.
BUFFER_WMA :: struct #packed {
pDecodedPacketCumulativeBytes: [^]u32, // Decoded packet's cumulative size array. Each element is the number of bytes accumulated when the corresponding XWMA packet is decoded in order. The array must have PacketCount elements.
PacketCount: u32, // Number of XWMA packets submitted. Must be >= 1 and divide evenly into BUFFER.AudioBytes.
}
// Returned by IXAudio2SourceVoice.GetState
VOICE_STATE :: struct #packed {
pCurrentBufferContext: rawptr, // The pContext value provided in the BUFFER that is currently being processed, or NULL if there are no buffers in the queue.
BuffersQueued: u32, // Number of buffers currently queued on the voice (including the one that is being processed).
SamplesPlayed: u64, // Total number of samples produced by the voice since it began processing the current audio stream. If VOICE_NOSAMPLESPLAYED is specified in the call to IXAudio2SourceVoice.GetState, this member will not be calculated, saving CPU.
}
// Returned by IXAudio2.GetPerformanceData
PERFORMANCE_DATA :: struct #packed {
// CPU usage information
AudioCyclesSinceLastQuery: u64, // CPU cycles spent on audio processing since the last call to StartEngine or GetPerformanceData.
TotalCyclesSinceLastQuery: u64, // Total CPU cycles elapsed since the last call (only counts the CPU XAudio2 is running on).
MinimumCyclesPerQuantum: u32, // Fewest CPU cycles spent processing any one audio quantum since the last call.
MaximumCyclesPerQuantum: u32, // Most CPU cycles spent processing any one audio quantum since the last call.
// Memory usage information
MemoryUsageInBytes: u32, // Total heap space currently in use.
// Audio latency and glitching information
CurrentLatencyInSamples: u32, // Minimum delay from when a sample is read from a source buffer to when it reaches the speakers.
GlitchesSinceEngineStarted: u32, // Audio dropouts since the engine was started.
// Data about XAudio2's current workload
ActiveSourceVoiceCount: u32, // Source voices currently playing.
TotalSourceVoiceCount: u32, // Source voices currently existing.
ActiveSubmixVoiceCount: u32, // Submix voices currently playing/existing.
ActiveResamplerCount: u32, // Resample xAPOs currently active.
ActiveMatrixMixCount: u32, // MatrixMix xAPOs currently active.
// Usage of the hardware XMA decoder (Xbox 360 only)
ActiveXmaSourceVoices: u32, // Number of source voices decoding XMA data.
ActiveXmaStreams: u32, // A voice can use more than one XMA stream.
}
// Used in IXAudio2.SetDebugConfiguration
DEBUG_CONFIGURATION :: struct #packed {
TraceMask: DEBUG_CONFIG_FLAGS, // Bitmap of enabled debug message types.
BreakMask: DEBUG_CONFIG_FLAGS, // Message types that will break into the debugger.
LogThreadID: b32, // Whether to log the thread ID with each message.
LogFileline: b32, // Whether to log the source file and line number.
LogFunctionName: b32, // Whether to log the function name.
LogTiming: b32, // Whether to log message timestamps.
}
// Values for the TraceMask and BreakMask bitmaps. Only ERRORS and WARNINGS are valid in BreakMask.
// WARNINGS implies ERRORS, DETAIL implies INFO, and FUNC_CALLS implies API_CALLS.
// By default, TraceMask is ERRORS and WARNINGS and all the other settings are zero.
DEBUG_CONFIG_FLAGS :: distinct bit_set[DEBUG_CONFIG_FLAG; u32]
DEBUG_CONFIG_FLAG :: enum u32 {
ERRORS = 0, // For handled errors with serious effects.
WARNINGS = 1, // For handled errors that may be recoverable.
INFO = 2, // Informational chit-chat (e.g. state changes).
DETAIL = 3, // More detailed chit-chat.
API_CALLS = 4, // Public API function entries and exits.
FUNC_CALLS = 5, // Internal function entries and exits.
TIMING = 6, // Delays detected and other timing data.
LOCKS = 7, // Usage of critical sections and mutexes.
MEMORY = 8, // Memory heap usage information.
STREAMING = 12, // Audio streaming information.
}
/**************************************************************************
*
* IXAudio2: Top-level XAudio2 COM interface.
*
**************************************************************************/
IXAudio2_UUID_STRING :: "2B02E3CF-2E0B-4ec3-BE45-1B2A3FE7210D"
IXAudio2_UUID := &win.IID{0x2B02E3CF, 0x2E0B, 0x4ec3, {0xBE, 0x45, 0x1B, 0x2A, 0x3F, 0xE7, 0x21, 0x0D}}
IXAudio2 :: struct #raw_union {
#subtype iunknown: IUnknown,
using ixaudio2_vtable: ^IXAudio2_VTable,
}
IXAudio2_VTable :: struct {
using iunknown_vtable: IUnknown_VTable,
// NAME: IXAudio2.RegisterForCallbacks
// DESCRIPTION: Adds a new client to receive XAudio2's engine callbacks.
// ARGUMENTS:
// pCallback - Callback interface to be called during each processing pass.
RegisterForCallbacks: proc "system" (this: ^IXAudio2, pCallback: ^IXAudio2EngineCallback) -> HRESULT,
// NAME: IXAudio2.UnregisterForCallbacks
// DESCRIPTION: Removes an existing receiver of XAudio2 engine callbacks.
// ARGUMENTS:
// pCallback - Previously registered callback interface to be removed.
UnregisterForCallbacks: proc "system" (this: ^IXAudio2, pCallback: ^IXAudio2EngineCallback),
// NAME: IXAudio2.CreateSourceVoice
// DESCRIPTION: Creates and configures a source voice.
// ARGUMENTS:
// ppSourceVoice - Returns the new object's IXAudio2SourceVoice interface.
// pSourceFormat - Format of the audio that will be fed to the voice.
// Flags - VOICE flags specifying the source voice's behavior.
// MaxFrequencyRatio - Maximum SetFrequencyRatio argument to be allowed.
// pCallback - Optional pointer to a client-provided callback interface.
// pSendList - Optional list of voices this voice should send audio to.
// pEffectChain - Optional list of effects to apply to the audio data.
CreateSourceVoice: proc "system" (this: ^IXAudio2, ppSourceVoice: ^^IXAudio2SourceVoice, pSourceFormat: ^WAVEFORMATEX, Flags: FLAGS = {}, MaxFrequencyRatio: f32 = DEFAULT_FREQ_RATIO, pCallback: ^IXAudio2VoiceCallback = nil, pSendList: [^]VOICE_SENDS = nil, pEffectChain: [^]EFFECT_CHAIN = nil) -> HRESULT,
// NAME: IXAudio2.CreateSubmixVoice
// DESCRIPTION: Creates and configures a submix voice.
// ARGUMENTS:
// ppSubmixVoice - Returns the new object's IXAudio2SubmixVoice interface.
// InputChannels - Number of channels in this voice's input audio data.
// InputSampleRate - Sample rate of this voice's input audio data.
// Flags - VOICE flags specifying the submix voice's behavior.
// ProcessingStage - Arbitrary number that determines the processing order.
// pSendList - Optional list of voices this voice should send audio to.
// pEffectChain - Optional list of effects to apply to the audio data.
CreateSubmixVoice: proc "system" (this: ^IXAudio2, ppSubmixVoice: ^^IXAudio2SubmixVoice, InputChannels: u32, InputSampleRate: u32, Flags: FLAGS = {}, ProcessingStage: u32 = 0, pSendList: [^]VOICE_SENDS = nil, pEffectChain: [^]EFFECT_CHAIN = nil) -> HRESULT,
// NAME: IXAudio2.CreateMasteringVoice
// DESCRIPTION: Creates and configures a mastering voice.
// ARGUMENTS:
// ppMasteringVoice - Returns the new object's IXAudio2MasteringVoice interface.
// InputChannels - Number of channels in this voice's input audio data.
// InputSampleRate - Sample rate of this voice's input audio data.
// Flags - VOICE flags specifying the mastering voice's behavior.
// szDeviceId - Identifier of the device to receive the output audio.
// pEffectChain - Optional list of effects to apply to the audio data.
// StreamCategory - The audio stream category to use for this mastering voice
CreateMasteringVoice: proc "system" (this: ^IXAudio2, ppMasteringVoice: ^^IXAudio2MasteringVoice, InputChannels: u32 = DEFAULT_CHANNELS, InputSampleRate: u32 = DEFAULT_SAMPLERATE, Flags: FLAGS = {}, szDeviceId: win.LPCWSTR = nil, pEffectChain: [^]EFFECT_CHAIN = nil, StreamCategory: AUDIO_STREAM_CATEGORY = .GameEffects) -> HRESULT,
// NAME: IXAudio2.:StartEngine
// DESCRIPTION: Creates and starts the audio processing thread.
StartEngine: proc "system" (this: ^IXAudio2) -> HRESULT,
// NAME: IXAudio2.StopEngine
// DESCRIPTION: Stops and destroys the audio processing thread.
StopEngine: proc "system" (this: ^IXAudio2),
// NAME: IXAudio2.CommitChanges
// DESCRIPTION: Atomically applies a set of operations previously tagged
// with a given identifier.
// ARGUMENTS:
// OperationSet - Identifier of the set of operations to be applied.
CommitChanges: proc "system" (this: ^IXAudio2, OperationSet: u32) -> HRESULT,
// NAME: IXAudio2.GetPerformanceData
// DESCRIPTION: Returns current resource usage details: memory, CPU, etc.
// ARGUMENTS:
// pPerfData - Returns the performance data structure.
GetPerformanceData: proc "system" (this: ^IXAudio2, pPerfData: ^PERFORMANCE_DATA),
// NAME: IXAudio2.SetDebugConfiguration
// DESCRIPTION: Configures XAudio2's debug output (in debug builds only).
// ARGUMENTS:
// pDebugConfiguration - Structure describing the debug output behavior.
// pReserved - Optional parameter; must be NULL.
SetDebugConfiguration: proc "system" (this: ^IXAudio2, pDebugConfiguration: ^DEBUG_CONFIGURATION, pReserved: rawptr = nil),
}
// This interface extends IXAudio2 with additional functionality.
// Use IXAudio2.QueryInterface to obtain a pointer to this interface.
IXAudio2Extension_UUID_STRING :: "84ac29bb-d619-44d2-b197-e4acf7df3ed6"
IXAudio2Extension_UUID := &win.IID{0x84ac29bb, 0xd619, 0x44d2, {0xb1, 0x97, 0xe4, 0xac, 0xf7, 0xdf, 0x3e, 0xd6}}
IXAudio2Extension :: struct #raw_union {
#subtype iunknown: IUnknown,
using ixaudio2extension_vtable: ^IXAudio2Extension_VTable,
}
IXAudio2Extension_VTable :: struct {
using iunknown_vtable: IUnknown_VTable,
// NAME: IXAudio2Extension.GetProcessingQuantum
// DESCRIPTION: Returns the processing quantum
// quantumMilliseconds = (1000.0f * quantumNumerator / quantumDenominator)
// ARGUMENTS:
// quantumNumerator - Quantum numerator
// quantumDenominator - Quantum denominator
GetProcessingQuantum: proc "system" (this: ^IXAudio2Extension, quantumNumerator: ^u32, quantumDenominator: ^u32),
// NAME: IXAudio2Extension.GetProcessor
// DESCRIPTION: Returns the number of the processor used by XAudio2
// ARGUMENTS:
// processor - Non-zero Processor number
GetProcessor: proc "system" (this: ^IXAudio2Extension, processor: ^PROCESSOR_FLAGS),
}
/**************************************************************************
*
* IXAudio2Voice: Base voice management interface.
*
**************************************************************************/
IXAudio2Voice :: struct {
using ixaudio2voice_vtable: ^IXAudio2Voice_VTable,
}
IXAudio2Voice_VTable :: struct {
// NAME: IXAudio2Voice.GetVoiceDetails
// DESCRIPTION: Returns the basic characteristics of this voice.
// ARGUMENTS:
// pVoiceDetails - Returns the voice's details.
GetVoiceDetails: proc "system" (this: ^IXAudio2Voice, pVoiceDetails: ^VOICE_DETAILS),
// NAME: IXAudio2Voice.SetOutputVoices
// DESCRIPTION: Replaces the set of submix/mastering voices that receive
// this voice's output.
// ARGUMENTS:
// pSendList - Optional list of voices this voice should send audio to.
SetOutputVoices: proc "system" (this: ^IXAudio2Voice, pSendList: [^]VOICE_SENDS) -> HRESULT,
// NAME: IXAudio2Voice.SetEffectChain
// DESCRIPTION: Replaces this voice's current effect chain with a new one.
// ARGUMENTS:
// pEffectChain - Structure describing the new effect chain to be used.
SetEffectChain: proc "system" (this: ^IXAudio2Voice, pEffectChain: ^EFFECT_CHAIN) -> HRESULT,
// NAME: IXAudio2Voice.EnableEffect
// DESCRIPTION: Enables an effect in this voice's effect chain.
// ARGUMENTS:
// EffectIndex - Index of an effect within this voice's effect chain.
// OperationSet - Used to identify this call as part of a deferred batch.
EnableEffect: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.DisableEffect
// DESCRIPTION: Disables an effect in this voice's effect chain.
// ARGUMENTS:
// EffectIndex - Index of an effect within this voice's effect chain.
// OperationSet - Used to identify this call as part of a deferred batch.
DisableEffect: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetEffectState
// DESCRIPTION: Returns the running state of an effect.
// ARGUMENTS:
// EffectIndex - Index of an effect within this voice's effect chain.
// pEnabled - Returns the enabled/disabled state of the given effect.
GetEffectState: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pEnabled: ^b32),
// NAME: IXAudio2Voice.SetEffectParameters
// DESCRIPTION: Sets effect-specific parameters.
// REMARKS: Unlike IXAPOParameters.SetParameters, this method may be called from any thread. XAudio2 implements appropriate synchronization to copy the parameters to the realtime audio processing thread.
// ARGUMENTS:
// EffectIndex - Index of an effect within this voice's effect chain.
// pParameters - Pointer to an effect-specific parameters block.
// ParametersByteSize - Size of the pParameters array in bytes.
// OperationSet - Used to identify this call as part of a deferred batch.
SetEffectParameters: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pParameters: rawptr, ParametersByteSize: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetEffectParameters
// DESCRIPTION: Obtains the current effect-specific parameters.
// ARGUMENTS:
// EffectIndex - Index of an effect within this voice's effect chain.
// pParameters - Returns the current values of the effect-specific parameters.
// ParametersByteSize - Size of the pParameters array in bytes.
GetEffectParameters: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pParameters: rawptr, ParametersByteSize: u32) -> HRESULT,
// NAME: IXAudio2Voice.SetFilterParameters
// DESCRIPTION: Sets this voice's filter parameters.
// ARGUMENTS:
// pParameters - Pointer to the filter's parameter structure.
// OperationSet - Used to identify this call as part of a deferred batch.
SetFilterParameters: proc "system" (this: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetFilterParameters
// DESCRIPTION: Returns this voice's current filter parameters.
// ARGUMENTS:
// pParameters - Returns the filter parameters.
GetFilterParameters: proc "system" (this: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS),
// NAME: IXAudio2Voice.SetOutputFilterParameters
// DESCRIPTION: Sets the filter parameters on one of this voice's sends.
// ARGUMENTS:
// pDestinationVoice - Destination voice of the send whose filter parameters will be set.
// pParameters - Pointer to the filter's parameter structure.
// OperationSet - Used to identify this call as part of a deferred batch.
SetOutputFilterParameters: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetOutputFilterParameters
// DESCRIPTION: Returns the filter parameters from one of this voice's sends.
// ARGUMENTS:
// pDestinationVoice - Destination voice of the send whose filter parameters will be read.
// pParameters - Returns the filter parameters.
GetOutputFilterParameters: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS),
// NAME: IXAudio2Voice.SetVolume
// DESCRIPTION: Sets this voice's overall volume level.
// ARGUMENTS:
// Volume - New overall volume level to be used, as an amplitude factor.
// OperationSet - Used to identify this call as part of a deferred batch.
SetVolume: proc "system" (this: ^IXAudio2Voice, Volume: f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetVolume
// DESCRIPTION: Obtains this voice's current overall volume level.
// ARGUMENTS:
// pVolume: Returns the voice's current overall volume level.
GetVolume: proc "system" (this: ^IXAudio2Voice, pVolume: ^f32),
// NAME: IXAudio2Voice.SetChannelVolumes
// DESCRIPTION: Sets this voice's per-channel volume levels.
// ARGUMENTS:
// Channels - Used to confirm the voice's channel count.
// pVolumes - Array of per-channel volume levels to be used.
// OperationSet - Used to identify this call as part of a deferred batch.
SetChannelVolumes: proc "system" (this: ^IXAudio2Voice, Channels: u32, pVolumes: [^]f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetChannelVolumes
// DESCRIPTION: Returns this voice's current per-channel volume levels.
// ARGUMENTS:
// Channels - Used to confirm the voice's channel count.
// pVolumes - Returns an array of the current per-channel volume levels.
GetChannelVolumes: proc "system" (this: ^IXAudio2Voice, Channels: u32, pVolumes: [^]f32),
// NAME: IXAudio2Voice.SetOutputMatrix
// DESCRIPTION: Sets the volume levels used to mix from each channel of this voice's output audio to each channel of a given destination voice's input audio.
// ARGUMENTS:
// pDestinationVoice - The destination voice whose mix matrix to change.
// SourceChannels - Used to confirm this voice's output channel count (the number of channels produced by the last effect in the chain).
// DestinationChannels - Confirms the destination voice's input channels.
// pLevelMatrix - Array of [SourceChannels * DestinationChannels] send levels. The level used to send from source channel S to destination channel D should be in pLevelMatrix[S + SourceChannels * D].
// OperationSet - Used to identify this call as part of a deferred batch.
SetOutputMatrix: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, SourceChannels: u32, DestinationChannels: u32, pLevelMatrix: [^]f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2Voice.GetOutputMatrix
// DESCRIPTION: Obtains the volume levels used to send each channel of this voice's output audio to each channel of a given destination voice's input audio.
// ARGUMENTS:
// pDestinationVoice - The destination voice whose mix matrix to obtain.
// SourceChannels - Used to confirm this voice's output channel count (the number of channels produced by the last effect in the chain).
// DestinationChannels - Confirms the destination voice's input channels.
// pLevelMatrix - Array of send levels, as above.
GetOutputMatrix: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, SourceChannels: u32, DestinationChannels: u32, pLevelMatrix: [^]f32),
// NAME: IXAudio2Voice.DestroyVoice
// DESCRIPTION: Destroys this voice, stopping it if necessary and removing it from the XAudio2 graph.
DestroyVoice: proc "system" (this: ^IXAudio2Voice),
}
/**************************************************************************
*
* IXAudio2SourceVoice: Source voice management interface.
*
**************************************************************************/
IXAudio2SourceVoice :: struct #raw_union {
#subtype ixaudio2voice: IXAudio2Voice,
using ixaudio2sourcevoice_vtable: ^IXAudio2SourceVoice_VTable,
}
IXAudio2SourceVoice_VTable :: struct {
using ixaudio2voice_vtable: IXAudio2Voice_VTable,
// NAME: IXAudio2SourceVoice.Start
// DESCRIPTION: Makes this voice start consuming and processing audio.
// ARGUMENTS:
// Flags - Flags controlling how the voice should be started.
// OperationSet - Used to identify this call as part of a deferred batch.
Start: proc "system" (this: ^IXAudio2SourceVoice, Flags: FLAGS = {}, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2SourceVoice.Stop
// DESCRIPTION: Makes this voice stop consuming audio.
// ARGUMENTS:
// Flags - Flags controlling how the voice should be stopped.
// OperationSet - Used to identify this call as part of a deferred batch.
Stop: proc "system" (this: ^IXAudio2SourceVoice, Flags: FLAGS = {}, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2SourceVoice.SubmitSourceBuffer
// DESCRIPTION: Adds a new audio buffer to this voice's input queue.
// ARGUMENTS:
// pBuffer - Pointer to the buffer structure to be queued.
// pBufferWMA - Additional structure used only when submitting XWMA data.
SubmitSourceBuffer: proc "system" (this: ^IXAudio2SourceVoice, pBuffer: ^BUFFER, pBufferWMA: ^BUFFER_WMA = nil) -> HRESULT,
// NAME: IXAudio2SourceVoice.FlushSourceBuffers
// DESCRIPTION: Removes all pending audio buffers from this voice's queue.
FlushSourceBuffers: proc "system" (this: ^IXAudio2SourceVoice) -> HRESULT,
// NAME: IXAudio2SourceVoice.Discontinuity
// DESCRIPTION: Notifies the voice of an intentional break in the stream of audio buffers (e.g. the end of a sound), to prevent XAudio2 from interpreting an empty buffer queue as a glitch.
Discontinuity: proc "system" (this: ^IXAudio2SourceVoice) -> HRESULT,
// NAME: IXAudio2SourceVoice.ExitLoop
// DESCRIPTION: Breaks out of the current loop when its end is reached.
// ARGUMENTS:
// OperationSet - Used to identify this call as part of a deferred batch.
ExitLoop: proc "system" (this: ^IXAudio2SourceVoice, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2SourceVoice.GetState
// DESCRIPTION: Returns the number of buffers currently queued on this voice, the pContext value associated with the currently processing buffer (if any), and other voice state information.
// ARGUMENTS:
// pVoiceState - Returns the state information.
// Flags - Flags controlling what voice state is returned.
GetState: proc "system" (this: ^IXAudio2SourceVoice, pVoiceState: ^VOICE_STATE, Flags: FLAGS = {}),
// NAME: IXAudio2SourceVoice.SetFrequencyRatio
// DESCRIPTION: Sets this voice's frequency adjustment, i.e. its pitch.
// ARGUMENTS:
// Ratio - Frequency change, expressed as source frequency / target frequency.
// OperationSet - Used to identify this call as part of a deferred batch.
SetFrequencyRatio: proc "system" (this: ^IXAudio2SourceVoice, Ratio: f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
// NAME: IXAudio2SourceVoice.GetFrequencyRatio
// DESCRIPTION: Returns this voice's current frequency adjustment ratio.
// ARGUMENTS:
// pRatio - Returns the frequency adjustment.
GetFrequencyRatio: proc "system" (this: ^IXAudio2SourceVoice, pRatio: ^f32),
// NAME: IXAudio2SourceVoice.SetSourceSampleRate
// DESCRIPTION: Reconfigures this voice to treat its source data as being at a different sample rate than the original one specified in CreateSourceVoice's pSourceFormat argument.
// ARGUMENTS:
// UINT32 - The intended sample rate of further submitted source data.
SetSourceSampleRate: proc "system" (this: ^IXAudio2SourceVoice, NewSourceSampleRate: u32) -> HRESULT,
}
/**************************************************************************
*
* IXAudio2SubmixVoice: Submixing voice management interface.
*
**************************************************************************/
IXAudio2SubmixVoice :: struct #raw_union {
#subtype ixaudio2voice: IXAudio2Voice,
using ixaudio2submixvoice_vtable: ^IXAudio2SubmixVoice_VTable,
}
IXAudio2SubmixVoice_VTable :: struct {
using ixaudio2voice_vtable: IXAudio2Voice_VTable,
// There are currently no methods specific to submix voices.
}
/**************************************************************************
*
* IXAudio2MasteringVoice: Mastering voice management interface.
*
**************************************************************************/
IXAudio2MasteringVoice :: struct #raw_union {
#subtype ixaudio2voice: IXAudio2Voice,
using ixaudio2masteringvoice_vtable: ^IXAudio2MasteringVoice_VTable,
}
IXAudio2MasteringVoice_VTable :: struct {
using ixaudio2voice_vtable: IXAudio2Voice_VTable,
// NAME: IXAudio2MasteringVoice.GetChannelMask
// DESCRIPTION: Returns the channel mask for this voice
// ARGUMENTS:
// pChannelMask - returns the channel mask for this voice. This corresponds to the dwChannelMask member of WAVEFORMATEXTENSIBLE.
GetChannelMask: proc "system" (this: ^IXAudio2MasteringVoice, pChannelmask: ^win.DWORD) -> HRESULT,
}
/**************************************************************************
*
* IXAudio2EngineCallback: Client notification interface for engine events.
*
* REMARKS: Contains methods to notify the client when certain events happen in the XAudio2 engine. This interface should be implemented by the client.
* XAudio2 will call these methods via the interface pointer provided by the client when it calls IXAudio2.RegisterForCallbacks.
*
**************************************************************************/
IXAudio2EngineCallback :: struct {
using ixaudio2enginecallback_vtable: ^IXAudio2EngineCallback_VTable,
}
IXAudio2EngineCallback_VTable :: struct {
// Called by XAudio2 just before an audio processing pass begins.
OnProcessingPassStart: proc "system" (this: ^IXAudio2EngineCallback),
// Called just after an audio processing pass ends.
OnProcessingPassEnd: proc "system" (this: ^IXAudio2EngineCallback),
// Called in the event of a critical system error which requires XAudio2 to be closed down and restarted. The error code is given in Error.
OnCriticalError: proc "system" (this: ^IXAudio2EngineCallback, Error: HRESULT),
}
/**************************************************************************
*
* IXAudio2VoiceCallback: Client notification interface for voice events.
*
* REMARKS: Contains methods to notify the client when certain events happen in an XAudio2 voice. This interface should be implemented by the client.
* XAudio2 will call these methods via an interface pointer provided by the client in the IXAudio2.CreateSourceVoice call.
*
**************************************************************************/
IXAudio2VoiceCallback :: struct {
using ixaudio2voicecallback_vtable: ^IXAudio2VoiceCallback_VTable,
}
IXAudio2VoiceCallback_VTable :: struct {
// Called just before this voice's processing pass begins.
OnVoiceProcessingPassStart: proc "system" (this: ^IXAudio2VoiceCallback, BytesRequired: u32),
// Called just after this voice's processing pass ends.
OnVoiceProcessingPassEnd: proc "system" (this: ^IXAudio2VoiceCallback),
// Called when this voice has just finished playing a buffer stream (as marked with the END_OF_STREAM flag on the last buffer).
OnStreamEnd: proc "system" (this: ^IXAudio2VoiceCallback),
// Called when this voice is about to start processing a new buffer.
OnBufferStart: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
// Called when this voice has just finished processing a buffer.
// The buffer can now be reused or destroyed.
OnBufferEnd: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
// Called when this voice has just reached the end position of a loop.
OnLoopEnd: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
// Called in the event of a critical error during voice processing, such as a failing xAPO or an error from the hardware XMA decoder.
// The voice may have to be destroyed and re-created to recover from the error.
// The callback arguments report which buffer was being processed when the error occurred, and its HRESULT code.
OnVoiceError: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr, Error: HRESULT),
}
/**************************************************************************
*
* XAudio2Create: Top-level function that creates an XAudio2 instance.
*
* ARGUMENTS:
*
* Flags - Flags specifying the XAudio2 object's behavior.
*
* XAudio2Processor - A PROCESSOR_FLAGS value that specifies the hardware threads (Xbox) or processors (Windows) that XAudio2 will use.
* Note that XAudio2 supports concurrent processing on multiple threads, using any combination of PROCESSOR_FLAGS flags.
* The values are platform-specific; platform-independent code can use USE_DEFAULT_PROCESSOR to use the default on each platform.
*
**************************************************************************/
Create :: proc "stdcall" (ppXAudio2: ^^IXAudio2, Flags: FLAGS = {}, XAudio2Processor: PROCESSOR_FLAGS = {.Processor1}) -> HRESULT {
CreateWithVersionInfoFunc :: #type proc "c" (a0: ^^IXAudio2, a1: FLAGS, a2: PROCESSOR_FLAGS, a3: win.DWORD) -> HRESULT
CreateInfoFunc :: #type proc "c" (a0: ^^IXAudio2, a1: FLAGS, a2: PROCESSOR_FLAGS) -> HRESULT
dll_Instance: win.HMODULE
create_with_version_info: CreateWithVersionInfoFunc
create_info: CreateInfoFunc
if dll_Instance == nil {
dll_Instance = win.LoadLibraryExW(win.L("xaudio2_9.dll"), nil, {.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS})
if dll_Instance == nil {
return HRESULT(win.GetLastError())
}
create_with_version_info = cast(CreateWithVersionInfoFunc)win.GetProcAddress(dll_Instance, "XAudio2CreateWithVersionInfo")
if create_with_version_info == nil {
create_info = cast(CreateInfoFunc)win.GetProcAddress(dll_Instance, "XAudio2Create")
if create_info == nil {
return HRESULT(win.GetLastError())
}
}
}
if create_with_version_info != nil {
return create_with_version_info(ppXAudio2, Flags, XAudio2Processor, 0x0A000010)
}
return create_info(ppXAudio2, Flags, XAudio2Processor)
}
/**************************************************************************
*
* Utility functions used to convert from pitch in semitones and volume in decibels to the frequency and amplitude ratio units used by XAudio2.
*
**************************************************************************/
// Calculate the argument to SetVolume from a decibel value
DecibelsToAmplitudeRatio :: proc "contextless" (Decibels: f32) -> f32 {
return math.pow_f32(10.0, Decibels / 20.0)
}
// Recover a volume in decibels from an amplitude factor
AmplitudeRatioToDecibels :: proc "contextless" (Volume: f32) -> f32 {
if Volume == 0 {
return min(f32)
}
return 20.0 * math.log10_f32(Volume)
}
// Calculate the argument to SetFrequencyRatio from a semitone value
SemitonesToFrequencyRatio :: proc "contextless" (Semitones: f32) -> f32 {
// FrequencyRatio = 2 ^ Octaves
// = 2 ^ (Semitones / 12)
return math.pow_f32(2.0, Semitones / 12.0)
}
// Recover a pitch in semitones from a frequency ratio
FrequencyRatioToSemitones :: proc "contextless" (FrequencyRatio: f32) -> f32 {
// Semitones = 12 * log2(FrequencyRatio)
// = 12 * log2(10) * log10(FrequencyRatio)
return 12.0 * math.log2_f32(FrequencyRatio)
}
// Convert from filter cutoff frequencies expressed in Hertz to the radian frequency values used in FILTER_PARAMETERS.Frequency, state-variable filter types only.
// Use CutoffFrequencyToOnePoleCoefficient() for one-pole filter types.
// Note that the highest CutoffFrequency supported is SampleRate/6.
// Higher values of CutoffFrequency will return MAX_FILTER_FREQUENCY.
CutoffFrequencyToRadians :: proc "contextless" (CutoffFrequency: f32, SampleRate: u32) -> f32 {
if u32(CutoffFrequency * 6.0) >= SampleRate {
return MAX_FILTER_FREQUENCY
}
return 2.0 * math.sin_f32(math.PI * CutoffFrequency / f32(SampleRate))
}
// Convert from radian frequencies back to absolute frequencies in Hertz
RadiansToCutoffFrequency :: proc "contextless" (Radians: f32, SampleRate: f32) -> f32 {
return SampleRate * math.asin_f32(Radians / 2.0) / math.PI
}
// Convert from filter cutoff frequencies expressed in Hertz to the filter coefficients used with FILTER_PARAMETERS.Frequency,
// LowPassOnePoleFilter and HighPassOnePoleFilter filter types only.
// Use CutoffFrequencyToRadians() for state-variable filter types.
CutoffFrequencyToOnePoleCoefficient :: proc "contextless" (CutoffFrequency: f32, SampleRate: u32) -> f32 {
if u32(CutoffFrequency) >= SampleRate {
return MAX_FILTER_FREQUENCY
}
return 1.0 - math.pow_f32(1.0 - 2.0 * CutoffFrequency / f32(SampleRate), 2.0)
}
//-------------------------------------------------------------------------
// Description: Audio stream categories
//
// Other - All other streams (default)
// ForegroundOnlyMedia - (deprecated for Win10) Music, Streaming audio
// BackgroundCapableMedia - (deprecated for Win10) Video with audio
// Communications - VOIP, chat, phone call
// Alerts - Alarm, Ring tones
// SoundEffects - Sound effects, clicks, dings
// GameEffects - Game sound effects
// GameMedia - Background audio for games
// GameChat - In game player chat
// Speech - Speech recognition
// Media - Music, Streaming audio
// Movie - Video with audio
// FarFieldSpeech - Capture of far field speech
// UniformSpeech - Uniform, device agnostic speech processing
// VoiceTyping - Dictation, typing by voice
//
AUDIO_STREAM_CATEGORY :: enum i32 {
Other = 0,
//ForegroundOnlyMedia = 1,
//BackgroundCapableMedia = 2,
Communications = 3,
Alerts = 4,
SoundEffects = 5,
GameEffects = 6,
GameMedia = 7,
GameChat = 8,
Speech = 9,
Movie = 10,
Media = 11,
FarFieldSpeech = 12,
UniformSpeech = 13,
VoiceTyping = 14,
}
+282
View File
@@ -0,0 +1,282 @@
#+build windows
package windows_xaudio2
import "core:math"
foreign import xa2 "system:xaudio2.lib"
/**************************************************************************
*
* Effect creation functions.
*
* On Xbox the application can link with the debug library to use the debug
* functionality.
*
**************************************************************************/
@(default_calling_convention="system")
foreign xa2 {
CreateAudioVolumeMeter :: proc(ppApo: ^^IUnknown) -> HRESULT ---
CreateAudioReverb :: proc(ppApo: ^^IUnknown) -> HRESULT ---
}
/**************************************************************************
*
* Volume meter parameters.
* The volume meter supports f32 audio formats and must be used in-place.
*
**************************************************************************/
// VOLUMEMETER_LEVELS: Receives results from GetEffectParameters().
// The user is responsible for allocating pPeakLevels, pRMSLevels, and initializing ChannelCount accordingly.
// The volume meter does not support SetEffectParameters().
VOLUMEMETER_LEVELS :: struct #packed {
pPeakLevels: [^]f32 `fmt:"v,ChannelCount"`, // Peak levels table: receives maximum absolute level for each channel over a processing pass, may be NULL if pRMSLevls != NULL, otherwise must have at least ChannelCount elements.
pRMSLevels: [^]f32 `fmt:"v,ChannelCount"`, // Root mean square levels table: receives RMS level for each channel over a processing pass, may be NULL if pPeakLevels != NULL, otherwise must have at least ChannelCount elements.
ChannelCount: u32, // Number of channels being processed by the volume meter APO
}
/**************************************************************************
*
* Reverb parameters.
* The reverb supports only f32 audio with the following channel configurations:
* Input: Mono Output: Mono
* Input: Mono Output: 5.1
* Input: Stereo Output: Stereo
* Input: Stereo Output: 5.1
* The framerate must be within [20000, 48000] Hz.
*
* When using mono input, delay filters associated with the right channel are not executed.
* In this case, parameters such as PositionRight and PositionMatrixRight have no effect.
* This also means the reverb uses less CPU when hosted in a mono submix.
*
**************************************************************************/
REVERB_MIN_FRAMERATE :: 20000
REVERB_MAX_FRAMERATE :: 48000
// REVERB_PARAMETERS: Native parameter set for the reverb effect
REVERB_PARAMETERS :: struct #packed {
// ratio of wet (processed) signal to dry (original) signal
WetDryMix: f32, // [0, 100] (percentage)
// Delay times
ReflectionsDelay: u32, // [0, 300] in ms
ReverbDelay: byte, // [0, 85] in ms
RearDelay: byte, // 7.1: [0, 20] in ms, all other: [0, 5] in ms
SideDelay: byte, // 7.1: [0, 5] in ms, all other: not used, but still validated
// Indexed parameters
PositionLeft: byte, // [0, 30] no units
PositionRight: byte, // [0, 30] no units, ignored when configured to mono
PositionMatrixLeft: byte, // [0, 30] no units
PositionMatrixRight: byte, // [0, 30] no units, ignored when configured to mono
EarlyDiffusion: byte, // [0, 15] no units
LateDiffusion: byte, // [0, 15] no units
LowEQGain: byte, // [0, 12] no units
LowEQCutoff: byte, // [0, 9] no units
HighEQGain: byte, // [0, 8] no units
HighEQCutoff: byte, // [0, 14] no units
// Direct parameters
RoomFilterFreq: f32, // [20, 20000] in Hz
RoomFilterMain: f32, // [-100, 0] in dB
RoomFilterHF: f32, // [-100, 0] in dB
ReflectionsGain: f32, // [-100, 20] in dB
ReverbGain: f32, // [-100, 20] in dB
DecayTime: f32, // [0.1, inf] in seconds
Density: f32, // [0, 100] (percentage)
RoomSize: f32, // [1, 100] in feet
// component control
DisableLateField: b32, // TRUE to disable late field reflections
}
// Maximum, minimum and default values for the parameters above
REVERB_MIN_WET_DRY_MIX :: 0.0
REVERB_MIN_REFLECTIONS_DELAY :: 0
REVERB_MIN_REVERB_DELAY :: 0
REVERB_MIN_REAR_DELAY :: 0
REVERB_MIN_7POINT1_SIDE_DELAY :: 0
REVERB_MIN_7POINT1_REAR_DELAY :: 0
REVERB_MIN_POSITION :: 0
REVERB_MIN_DIFFUSION :: 0
REVERB_MIN_LOW_EQ_GAIN :: 0
REVERB_MIN_LOW_EQ_CUTOFF :: 0
REVERB_MIN_HIGH_EQ_GAIN :: 0
REVERB_MIN_HIGH_EQ_CUTOFF :: 0
REVERB_MIN_ROOM_FILTER_FREQ :: 20.0
REVERB_MIN_ROOM_FILTER_MAIN :: -100.0
REVERB_MIN_ROOM_FILTER_HF :: -100.0
REVERB_MIN_REFLECTIONS_GAIN :: -100.0
REVERB_MIN_REVERB_GAIN :: -100.0
REVERB_MIN_DECAY_TIME :: 0.1
REVERB_MIN_DENSITY :: 0.0
REVERB_MIN_ROOM_SIZE :: 0.0
REVERB_MAX_WET_DRY_MIX :: 100.0
REVERB_MAX_REFLECTIONS_DELAY :: 300
REVERB_MAX_REVERB_DELAY :: 85
REVERB_MAX_REAR_DELAY :: 5
REVERB_MAX_7POINT1_SIDE_DELAY :: 5
REVERB_MAX_7POINT1_REAR_DELAY :: 20
REVERB_MAX_POSITION :: 30
REVERB_MAX_DIFFUSION :: 15
REVERB_MAX_LOW_EQ_GAIN :: 12
REVERB_MAX_LOW_EQ_CUTOFF :: 9
REVERB_MAX_HIGH_EQ_GAIN :: 8
REVERB_MAX_HIGH_EQ_CUTOFF :: 14
REVERB_MAX_ROOM_FILTER_FREQ :: 20000.0
REVERB_MAX_ROOM_FILTER_MAIN :: 0.0
REVERB_MAX_ROOM_FILTER_HF :: 0.0
REVERB_MAX_REFLECTIONS_GAIN :: 20.0
REVERB_MAX_REVERB_GAIN :: 20.0
REVERB_MAX_DENSITY :: 100.0
REVERB_MAX_ROOM_SIZE :: 100.0
REVERB_DEFAULT_WET_DRY_MIX :: 100.0
REVERB_DEFAULT_REFLECTIONS_DELAY :: 5
REVERB_DEFAULT_REVERB_DELAY :: 5
REVERB_DEFAULT_REAR_DELAY :: 5
REVERB_DEFAULT_7POINT1_SIDE_DELAY :: 5
REVERB_DEFAULT_7POINT1_REAR_DELAY :: 20
REVERB_DEFAULT_POSITION :: 6
REVERB_DEFAULT_POSITION_MATRIX :: 27
REVERB_DEFAULT_EARLY_DIFFUSION :: 8
REVERB_DEFAULT_LATE_DIFFUSION :: 8
REVERB_DEFAULT_LOW_EQ_GAIN :: 8
REVERB_DEFAULT_LOW_EQ_CUTOFF :: 4
REVERB_DEFAULT_HIGH_EQ_GAIN :: 8
REVERB_DEFAULT_HIGH_EQ_CUTOFF :: 4
REVERB_DEFAULT_ROOM_FILTER_FREQ :: 5000.0
REVERB_DEFAULT_ROOM_FILTER_MAIN :: 0.0
REVERB_DEFAULT_ROOM_FILTER_HF :: 0.0
REVERB_DEFAULT_REFLECTIONS_GAIN :: 0.0
REVERB_DEFAULT_REVERB_GAIN :: 0.0
REVERB_DEFAULT_DECAY_TIME :: 1.0
REVERB_DEFAULT_DENSITY :: 100.0
REVERB_DEFAULT_ROOM_SIZE :: 100.0
REVERB_DEFAULT_DISABLE_LATE_FIELD: b32 : false
// REVERB_I3DL2_PARAMETERS: Parameter set compliant with the I3DL2 standard
REVERB_I3DL2_PARAMETERS :: struct #packed {
// ratio of wet (processed) signal to dry (original) signal
WetDryMix: f32, // [0, 100] (percentage)
// Standard I3DL2 parameters
Room: i32, // [-10000, 0] in mB (hundredths of decibels)
RoomHF: i32, // [-10000, 0] in mB (hundredths of decibels)
RoomRolloffFactor: f32, // [0.0, 10.0]
DecayTime: f32, // [0.1, 20.0] in seconds
DecayHFRatio: f32, // [0.1, 2.0]
Reflections: i32, // [-10000, 1000] in mB (hundredths of decibels)
ReflectionsDelay: f32, // [0.0, 0.3] in seconds
Reverb: i32, // [-10000, 2000] in mB (hundredths of decibels)
ReverbDelay: f32, // [0.0, 0.1] in seconds
Diffusion: f32, // [0.0, 100.0] (percentage)
Density: f32, // [0.0, 100.0] (percentage)
HFReference: f32, // [20.0, 20000.0] in Hz
}
/**************************************************************************
*
* Standard I3DL2 reverb presets (100% wet).
*
**************************************************************************/
I3DL2_PRESET_DEFAULT := REVERB_I3DL2_PARAMETERS{100.0,-10000, 0,0.0, 1.00,0.50,-10000,0.020,-10000,0.040,100.0,100.0,5000.0}
I3DL2_PRESET_GENERIC := REVERB_I3DL2_PARAMETERS{100.0, -1000, -100,0.0, 1.49,0.83, -2602,0.007, 200,0.011,100.0,100.0,5000.0}
I3DL2_PRESET_PADDEDCELL := REVERB_I3DL2_PARAMETERS{100.0, -1000,-6000,0.0, 0.17,0.10, -1204,0.001, 207,0.002,100.0,100.0,5000.0}
I3DL2_PRESET_ROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -454,0.0, 0.40,0.83, -1646,0.002, 53,0.003,100.0,100.0,5000.0}
I3DL2_PRESET_BATHROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1200,0.0, 1.49,0.54, -370,0.007, 1030,0.011,100.0, 60.0,5000.0}
I3DL2_PRESET_LIVINGROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000,-6000,0.0, 0.50,0.10, -1376,0.003, -1104,0.004,100.0,100.0,5000.0}
I3DL2_PRESET_STONEROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -300,0.0, 2.31,0.64, -711,0.012, 83,0.017,100.0,100.0,5000.0}
I3DL2_PRESET_AUDITORIUM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -476,0.0, 4.32,0.59, -789,0.020, -289,0.030,100.0,100.0,5000.0}
I3DL2_PRESET_CONCERTHALL := REVERB_I3DL2_PARAMETERS{100.0, -1000, -500,0.0, 3.92,0.70, -1230,0.020, -2,0.029,100.0,100.0,5000.0}
I3DL2_PRESET_CAVE := REVERB_I3DL2_PARAMETERS{100.0, -1000, 0,0.0, 2.91,1.30, -602,0.015, -302,0.022,100.0,100.0,5000.0}
I3DL2_PRESET_ARENA := REVERB_I3DL2_PARAMETERS{100.0, -1000, -698,0.0, 7.24,0.33, -1166,0.020, 16,0.030,100.0,100.0,5000.0}
I3DL2_PRESET_HANGAR := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0,10.05,0.23, -602,0.020, 198,0.030,100.0,100.0,5000.0}
I3DL2_PRESET_CARPETEDHALLWAY := REVERB_I3DL2_PARAMETERS{100.0, -1000,-4000,0.0, 0.30,0.10, -1831,0.002, -1630,0.030,100.0,100.0,5000.0}
I3DL2_PRESET_HALLWAY := REVERB_I3DL2_PARAMETERS{100.0, -1000, -300,0.0, 1.49,0.59, -1219,0.007, 441,0.011,100.0,100.0,5000.0}
I3DL2_PRESET_STONECORRIDOR := REVERB_I3DL2_PARAMETERS{100.0, -1000, -237,0.0, 2.70,0.79, -1214,0.013, 395,0.020,100.0,100.0,5000.0}
I3DL2_PRESET_ALLEY := REVERB_I3DL2_PARAMETERS{100.0, -1000, -270,0.0, 1.49,0.86, -1204,0.007, -4,0.011,100.0,100.0,5000.0}
I3DL2_PRESET_FOREST := REVERB_I3DL2_PARAMETERS{100.0, -1000,-3300,0.0, 1.49,0.54, -2560,0.162, -613,0.088, 79.0,100.0,5000.0}
I3DL2_PRESET_CITY := REVERB_I3DL2_PARAMETERS{100.0, -1000, -800,0.0, 1.49,0.67, -2273,0.007, -2217,0.011, 50.0,100.0,5000.0}
I3DL2_PRESET_MOUNTAINS := REVERB_I3DL2_PARAMETERS{100.0, -1000,-2500,0.0, 1.49,0.21, -2780,0.300, -2014,0.100, 27.0,100.0,5000.0}
I3DL2_PRESET_QUARRY := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0, 1.49,0.83,-10000,0.061, 500,0.025,100.0,100.0,5000.0}
I3DL2_PRESET_PLAIN := REVERB_I3DL2_PARAMETERS{100.0, -1000,-2000,0.0, 1.49,0.50, -2466,0.179, -2514,0.100, 21.0,100.0,5000.0}
I3DL2_PRESET_PARKINGLOT := REVERB_I3DL2_PARAMETERS{100.0, -1000, 0,0.0, 1.65,1.50, -1363,0.008, -1153,0.012,100.0,100.0,5000.0}
I3DL2_PRESET_SEWERPIPE := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0, 2.81,0.14, 429,0.014, 648,0.021, 80.0, 60.0,5000.0}
I3DL2_PRESET_UNDERWATER := REVERB_I3DL2_PARAMETERS{100.0, -1000,-4000,0.0, 1.49,0.10, -449,0.007, 1700,0.011,100.0,100.0,5000.0}
I3DL2_PRESET_SMALLROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.10,0.83, -400,0.005, 500,0.010,100.0,100.0,5000.0}
I3DL2_PRESET_MEDIUMROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.30,0.83, -1000,0.010, -200,0.020,100.0,100.0,5000.0}
I3DL2_PRESET_LARGEROOM := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.50,0.83, -1600,0.020, -1000,0.040,100.0,100.0,5000.0}
I3DL2_PRESET_MEDIUMHALL := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.80,0.70, -1300,0.015, -800,0.030,100.0,100.0,5000.0}
I3DL2_PRESET_LARGEHALL := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.80,0.70, -2000,0.030, -1400,0.060,100.0,100.0,5000.0}
I3DL2_PRESET_PLATE := REVERB_I3DL2_PARAMETERS{100.0, -1000, -200,0.0, 1.30,0.90, 0,0.002, 0,0.010,100.0, 75.0,5000.0}
// ReverbConvertI3DL2ToNative: Utility function to map from I3DL2 to native parameters
ReverbConvertI3DL2ToNative :: proc "contextless" (pI3DL2: ^REVERB_I3DL2_PARAMETERS, pNative: ^REVERB_PARAMETERS, sevenDotOneReverb: b32 = true) {
reflectionsDelay: f32
reverbDelay: f32
// RoomRolloffFactor is ignored
// These parameters have no equivalent in I3DL2
if sevenDotOneReverb {
pNative.RearDelay = REVERB_DEFAULT_7POINT1_REAR_DELAY // 20
} else {
pNative.RearDelay = REVERB_DEFAULT_REAR_DELAY // 5
}
pNative.SideDelay = REVERB_DEFAULT_7POINT1_SIDE_DELAY // 5
pNative.PositionLeft = REVERB_DEFAULT_POSITION // 6
pNative.PositionRight = REVERB_DEFAULT_POSITION // 6
pNative.PositionMatrixLeft = REVERB_DEFAULT_POSITION_MATRIX // 27
pNative.PositionMatrixRight = REVERB_DEFAULT_POSITION_MATRIX // 27
pNative.RoomSize = REVERB_DEFAULT_ROOM_SIZE // 100
pNative.LowEQCutoff = 4
pNative.HighEQCutoff = 6
// The rest of the I3DL2 parameters map to the native property set
pNative.RoomFilterMain = f32(pI3DL2.Room) / 100.0
pNative.RoomFilterHF = f32(pI3DL2.RoomHF) / 100.0
if pI3DL2.DecayHFRatio >= 1.0 {
index := i32(-4.0 * math.log10_f32(pI3DL2.DecayHFRatio))
if index < -8 { index = -8 }
pNative.LowEQGain = byte((index < 0) ? index + 8 : 8)
pNative.HighEQGain = 8
pNative.DecayTime = pI3DL2.DecayTime * pI3DL2.DecayHFRatio
} else {
index := i32(4.0 * math.log10_f32(pI3DL2.DecayHFRatio))
if index < -8 { index = -8 }
pNative.LowEQGain = 8
pNative.HighEQGain = byte((index < 0) ? index + 8 : 8)
pNative.DecayTime = pI3DL2.DecayTime
}
reflectionsDelay = pI3DL2.ReflectionsDelay * 1000.0
if reflectionsDelay >= REVERB_MAX_REFLECTIONS_DELAY { // 300
reflectionsDelay = f32(REVERB_MAX_REFLECTIONS_DELAY - 1)
} else if reflectionsDelay <= 1 {
reflectionsDelay = 1
}
pNative.ReflectionsDelay = u32(reflectionsDelay)
reverbDelay = pI3DL2.ReverbDelay * 1000.0
if reverbDelay >= REVERB_MAX_REVERB_DELAY { // 85
reverbDelay = f32(REVERB_MAX_REVERB_DELAY - 1)
}
pNative.ReverbDelay = byte(reverbDelay)
pNative.ReflectionsGain = f32(pI3DL2.Reflections) / 100.0
pNative.ReverbGain = f32(pI3DL2.Reverb) / 100.0
pNative.EarlyDiffusion = byte(15.0 * pI3DL2.Diffusion / 100.0)
pNative.LateDiffusion = pNative.EarlyDiffusion
pNative.Density = pI3DL2.Density
pNative.RoomFilterFreq = pI3DL2.HFReference
pNative.WetDryMix = pI3DL2.WetDryMix
pNative.DisableLateField = false
}
+1 -1
View File
@@ -234,7 +234,7 @@ foreign xlib {
display: ^Display,
window: Window,
attr_mask: WindowAttributeMask,
attr: XWindowAttributes,
attr: ^XWindowAttributes,
) ---
SetWindowBackground :: proc(
display: ^Display,