From a554ef339b23d460938a33534fcccb995d495dc9 Mon Sep 17 00:00:00 2001 From: Bigfoot71 Date: Wed, 9 Apr 2025 17:27:45 +0200 Subject: [PATCH] add `audio_sound_positionning` example --- examples/audio/audio_sound_positioning.c | 115 +++++++++++++++++++++ examples/audio/audio_sound_positioning.png | Bin 0 -> 12217 bytes 2 files changed, 115 insertions(+) create mode 100644 examples/audio/audio_sound_positioning.c create mode 100644 examples/audio/audio_sound_positioning.png diff --git a/examples/audio/audio_sound_positioning.c b/examples/audio/audio_sound_positioning.c new file mode 100644 index 000000000..4c78b66e3 --- /dev/null +++ b/examples/audio/audio_sound_positioning.c @@ -0,0 +1,115 @@ +/******************************************************************************************* +* +* raylib [audio] example - Playing spatialized 3D sound +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Le Juez Victor (@Bigfoot71) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Le Juez Victor (@Bigfoot71) +* +********************************************************************************************/ + +#include +#include + +//------------------------------------------------------------------------------------ +// Sound positioning function +//------------------------------------------------------------------------------------ +static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, float maxDist) +{ + // Calculate direction vector and distance between listener and sound source + Vector3 direction = Vector3Subtract(position, listener.position); + float distance = Vector3Length(direction); + + // Apply logarithmic distance attenuation and clamp between 0-1 + float attenuation = 1.0f / (1.0f + (distance / maxDist)); + attenuation = Clamp(attenuation, 0.0f, 1.0f); + + // Calculate normalized vectors for spatial positioning + Vector3 normalizedDirection = Vector3Normalize(direction); + Vector3 forward = Vector3Normalize(Vector3Subtract(listener.target, listener.position)); + Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, listener.up)); + + // Reduce volume for sounds behind the listener + float dotProduct = Vector3DotProduct(forward, normalizedDirection); + if (dotProduct < 0.0f) { + attenuation *= (1.0f + dotProduct * 0.5f); + } + + // Set stereo panning based on sound position relative to listener + float pan = 0.5f + 0.5f * Vector3DotProduct(normalizedDirection, right); + + // Apply final sound properties + SetSoundVolume(sound, attenuation); + SetSoundPan(sound, pan); +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(800, 600, "Quick Spatial Sound"); + InitAudioDevice(); + + SetTargetFPS(60); + DisableCursor(); + + Sound sound = LoadSound("resources/coin.wav"); + + Camera camera = { + .position = (Vector3) { 0, 5, 5 }, + .target = (Vector3) { 0, 0, 0 }, + .up = (Vector3) { 0, 1, 0 }, + .fovy = 60, + }; + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_FREE); + + float th = GetTime(); + + Vector3 spherePos = { + .x = 5.0f * cosf(th), + .y = 0.0f, + .z = 5.0f * sinf(th) + }; + + SetSoundPosition(camera, sound, spherePos, 20.0f); + if (!IsSoundPlaying(sound)) PlaySound(sound); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + { + ClearBackground(BLACK); + + BeginMode3D(camera); + DrawGrid(10, 2); + DrawSphere(spherePos, 0.5f, RED); + EndMode3D(); + } + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadSound(sound); + CloseAudioDevice(); + CloseWindow(); +} diff --git a/examples/audio/audio_sound_positioning.png b/examples/audio/audio_sound_positioning.png new file mode 100644 index 0000000000000000000000000000000000000000..3e7a8ffdd0362afc0840717bc176d30d6f1daa0a GIT binary patch literal 12217 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 F2>|2Ff8GE9 literal 0 HcmV?d00001