use std::thread;
use std::time::Duration;
use tunes::prelude::*;
use tunes::synthesis::spatial::SoundCone;
fn main() -> anyhow::Result<()> {
println!("Advanced Spatial Audio Demo");
println!("===========================\n");
let engine = AudioEngine::new()?;
use tunes::synthesis::spatial::{AttenuationModel, SpatialParams};
let mut params = SpatialParams::default();
params.attenuation_model = AttenuationModel::Linear;
params.ref_distance = 1.0;
params.max_distance = 20.0; engine.set_spatial_params(params)?;
let mut comp = Composition::new(Tempo::new(120.0));
comp.instrument("tone", &Instrument::synth_lead())
.notes(&[C4], 0.5)
.repeat(100);
let mixer = comp.into_mixer();
println!("Demo 1: Elevation Panning");
println!("--------------------------");
println!("Sound moving from below to above the listener...\n");
let sound_id = engine.play_mixer_realtime(&mixer)?;
engine.set_listener_position(0.0, 1.7, 0.0)?;
for i in 0..=20 {
let y = -3.0 + (i as f32 * 6.0 / 20.0); engine.set_sound_position(sound_id, 0.0, y, 5.0)?;
if i == 0 {
println!(" Below listener (y = -3.0)");
} else if i == 10 {
println!(" At ear level (y = 0.0)");
} else if i == 20 {
println!(" Above listener (y = +3.0)");
}
thread::sleep(Duration::from_millis(200));
}
engine.stop(sound_id)?;
thread::sleep(Duration::from_millis(500));
println!("\nDemo 2: Directional Sound Cone");
println!("--------------------------------");
println!("Walking smoothly around a directional speaker...\n");
let sound_id = engine.play_mixer_realtime(&mixer)?;
let cone = SoundCone::narrow().with_direction(0.0, 0.0, 1.0);
engine.set_sound_position(sound_id, 0.0, 1.7, 0.0)?;
engine.set_sound_cone(sound_id, Some(cone))?;
for i in 0..=72 {
let angle = (i as f32 * 5.0).to_radians(); let x = angle.sin() * 5.0;
let z = angle.cos() * 5.0;
engine.set_listener_position(x, 1.7, z)?;
engine.set_listener_forward(-x, 0.0, -z)?;
let degrees = i * 5;
if degrees % 90 == 0 {
let position = match degrees {
0 => "Front (loud)",
90 => "90° Right (quieter)",
180 => "Behind (very quiet)",
270 => "270° Left (quieter)",
_ => "Back to front",
};
println!(" {} - {}°", position, degrees);
}
thread::sleep(Duration::from_millis(50));
}
engine.stop(sound_id)?;
thread::sleep(Duration::from_millis(500));
engine.set_listener_position(0.0, 1.7, 0.0)?;
engine.set_listener_forward(0.0, 0.0, 1.0)?;
println!("\nDemo 3: Occlusion");
println!("-----------------");
println!("Sound becoming occluded/unoccluded (volume reduction)...\n");
let sound_id = engine.play_mixer_realtime(&mixer)?;
engine.set_sound_cone(sound_id, None)?;
engine.set_sound_position(sound_id, 5.0, 1.7, 0.0)?;
println!(" 0% occluded - Clear path (100% volume)");
engine.set_sound_occlusion(sound_id, 0.0)?;
thread::sleep(Duration::from_millis(1500));
println!(" 30% occluded - Thin wall (70% volume)");
engine.set_sound_occlusion(sound_id, 0.3)?;
thread::sleep(Duration::from_millis(1500));
println!(" 60% occluded - Thick wall (40% volume)");
engine.set_sound_occlusion(sound_id, 0.6)?;
thread::sleep(Duration::from_millis(1500));
println!(" 90% occluded - Multiple walls (10% volume)");
engine.set_sound_occlusion(sound_id, 0.9)?;
thread::sleep(Duration::from_millis(1500));
println!(" 100% occluded - Completely blocked (0% volume / SILENT)");
engine.set_sound_occlusion(sound_id, 1.0)?;
thread::sleep(Duration::from_millis(1500));
println!(" Back to clear (100% volume)");
engine.set_sound_occlusion(sound_id, 0.0)?;
thread::sleep(Duration::from_millis(1000));
engine.stop(sound_id)?;
thread::sleep(Duration::from_millis(500));
println!("\nDemo 4: Combined Features");
println!("-------------------------");
println!("Helicopter flying overhead with directional rotor sound...\n");
let sound_id = engine.play_mixer_realtime(&mixer)?;
let rotor_cone = SoundCone::wide().with_direction(0.0, -1.0, 0.0);
engine.set_sound_cone(sound_id, Some(rotor_cone))?;
for i in 0..=30 {
let t = i as f32 / 30.0;
let x = -10.0 + (t * 20.0);
let y = 10.0 + ((t * std::f32::consts::PI).sin() * 5.0);
let z = 0.0;
engine.set_sound_position(sound_id, x, y, z)?;
engine.set_sound_velocity(sound_id, 20.0 / 3.0, 0.0, 0.0)?;
let occlusion = if t > 0.4 && t < 0.6 {
0.7 } else {
0.0 };
engine.set_sound_occlusion(sound_id, occlusion)?;
if i == 0 {
println!(" Approaching from the left...");
} else if i == 15 {
println!(" Directly overhead!");
} else if (12..=18).contains(&i) {
if i == 12 {
println!(" Behind buildings (occluded)");
}
} else if i == 30 {
println!(" Departing to the right...");
}
thread::sleep(Duration::from_millis(100));
}
engine.stop(sound_id)?;
println!("\n✓ Demo complete!");
println!("\nNew Features Summary:");
println!("---------------------");
println!("1. Elevation: Sounds above/below are quieter and centered");
println!("2. Sound Cones: Directional sources (speakers, NPCs, vehicles)");
println!("3. Occlusion: Sounds can be blocked by obstacles");
println!("\nThese features make spatial audio more realistic for games!");
println!("\nNote: This demo uses Linear attenuation for better audibility.");
println!(" Default is InverseSquare (more realistic but quieter at distance).");
println!(" Adjust with SpatialParams::attenuation_model.");
Ok(())
}