Skip to main content

runtime_mixer/
runtime_mixer.rs

1//! Headless demo of the tono runtime engine: load a patch, spawn several
2//! independent instances, drive them by handle, and pull mixed audio — both
3//! directly and across the wait-free control/audio `split()`.
4//!
5//! Run: `cargo run -p tono-core --example runtime_mixer`
6
7use tono_core::dsl::SoundDoc;
8use tono_core::runtime::{AudioSource, Engine, Tween};
9
10fn peak(buf: &[f32]) -> f32 {
11    buf.iter().fold(0.0f32, |m, &x| m.max(x.abs()))
12}
13
14fn blip() -> SoundDoc {
15    serde_json::from_str(
16        r#"{ "name":"blip", "duration":0.4, "root":{ "type":"sine", "freq":440 } }"#,
17    )
18    .expect("valid doc")
19}
20
21fn main() {
22    let sr = 48_000;
23
24    // --- Direct (single-threaded) use -------------------------------------
25    let mut engine = Engine::new(sr);
26    let patch = engine.load(&blip());
27
28    // One patch → several independent instances, each controlled by handle.
29    let a = engine.play_looping(patch);
30    let b = engine.play_looping(patch);
31    engine.set_gain(b, 0.5, Tween::ms(20.0, sr));
32    engine.set_pan(a, -0.7, Tween::ms(20.0, sr));
33    engine.set_pan(b, 0.7, Tween::ms(20.0, sr));
34
35    let mut block = vec![0.0f32; 512 * 2];
36    let mut p = 0.0f32;
37    for _ in 0..20 {
38        engine.fill(&mut block);
39        p = p.max(peak(&block));
40    }
41    println!("direct mix: {} instances, peak {p:.3}", engine.active());
42
43    // --- Split (control thread + audio thread) ----------------------------
44    let mut engine = Engine::new(sr);
45    let patch = engine.load(&blip());
46    let (mut control, mut audio) = engine.split(2048);
47    control.play_looping(patch); // Deref → Engine::play_looping
48
49    control.pump(2048); // control thread: keep the ring fed
50    let mut out = vec![0.0f32; 512 * 2];
51    let frames = audio.fill(&mut out); // audio thread: drain in the callback
52    println!("split mix: pulled {frames} frames, peak {:.3}", peak(&out));
53}