Skip to main content

play_instrument/
play_instrument.rs

1//! Play a *designed instrument* like a mini GarageBand: build an instrument from
2//! a patch (a supersaw lead), play a chord and a melody with velocity, release
3//! notes, and host it in a mixer. Run:
4//!   cargo run -p tono-core --example play_instrument
5
6use tono_core::dsl::Adsr;
7use tono_core::instrument::{Instrument, InstrumentDesign, Note};
8use tono_core::patch::Patch;
9use tono_core::runtime::{AudioSource, Mixer};
10
11/// A supersaw lead with a `pitch` param bound to the oscillator — a designed,
12/// playable instrument.
13fn lead() -> Patch {
14    serde_json::from_str(
15        r#"{ "doc": { "name":"lead", "duration":1.0, "engine":2, "root": { "type":"chain", "stages": [
16                { "type":"super", "wave":"sawtooth", "freq":220, "voices":5, "detune_cents":14 },
17                { "type":"lowpass", "cutoff":2400, "q":0.9 } ] } },
18             "params": [ { "name":"pitch", "paths":["root.stages[0].freq"], "min":20, "max":8000, "default":220 } ] }"#,
19    )
20    .expect("valid patch")
21}
22
23fn peak(b: &[f32]) -> f32 {
24    b.iter().fold(0.0f32, |m, &x| m.max(x.abs()))
25}
26
27fn n(name: &str) -> Note {
28    Note::parse(name).unwrap()
29}
30
31fn main() {
32    let sr = 48_000;
33    let design = InstrumentDesign::new(lead()).with_amp(Adsr {
34        a: 0.01,
35        d: 0.15,
36        s: 0.6,
37        r: 0.25,
38        punch: 0.0,
39    });
40    let mut inst = Instrument::new(design, sr).expect("streamable instrument");
41
42    // --- Play a C-major chord, hold, then release -------------------------
43    inst.note_on(n("C4"), 0.9);
44    inst.note_on(n("E4"), 0.8);
45    inst.note_on(n("G4"), 0.7);
46    println!("chord: {} voices", inst.active_voices());
47
48    let mut block = vec![0.0f32; 512 * 2];
49    let mut p = 0.0f32;
50    for _ in 0..40 {
51        inst.fill(&mut block);
52        p = p.max(peak(&block));
53    }
54    inst.all_notes_off();
55    for _ in 0..60 {
56        inst.fill(&mut block);
57    }
58    println!(
59        "after release: {} voices (chord peak {p:.3})",
60        inst.active_voices()
61    );
62
63    // --- Host it in a mixer, and play a melody by reaching back in --------
64    let mut mixer = Mixer::new(sr);
65    let lead_id = mixer.add(inst);
66    mixer.set_gain(lead_id, 0.8);
67
68    let melody = ["C4", "E4", "G4", "C5", "G4", "E4"];
69    let mut apeak = 0.0f32;
70    for name in melody {
71        let voice = mixer.get_mut::<Instrument>(lead_id).unwrap();
72        voice.note_on(n(name), 0.85);
73        // ~120 ms of audio per note.
74        for _ in 0..11 {
75            mixer.fill(&mut block);
76            apeak = apeak.max(peak(&block));
77        }
78        mixer
79            .get_mut::<Instrument>(lead_id)
80            .unwrap()
81            .note_off(n(name));
82    }
83    println!("melody through the mixer: peak {apeak:.3}");
84}