Skip to main content

tremolo/
tremolo.rs

1//! Example: Tremolo node built with #[aether_node]
2//!
3//! Run with: cargo run --example tremolo -p aether-ndk
4
5use aether_ndk::prelude::*;
6
7/// Amplitude modulation (tremolo) using a sine LFO.
8#[aether_node]
9pub struct Tremolo {
10    #[param(name = "Rate",  min = 0.1,  max = 20.0, default = 4.0)]
11    rate: f32,
12    #[param(name = "Depth", min = 0.0,  max = 1.0,  default = 0.5)]
13    depth: f32,
14    // Internal state — not a param
15    phase: f32,
16}
17
18impl DspProcess for Tremolo {
19    fn process(
20        &mut self,
21        inputs: &NodeInputs,
22        output: &mut NodeOutput,
23        params: &mut ParamBlock,
24        sample_rate: f32,
25    ) {
26        let input = inputs.get(0);
27        for (i, out) in output.iter_mut().enumerate() {
28            let rate  = params.get(0).current;
29            let depth = params.get(1).current;
30            // LFO: 1.0 at phase=0, dips to (1-depth) at phase=0.5
31            let lfo = 1.0 - depth * 0.5 * (1.0 - (self.phase * std::f32::consts::TAU).cos());
32            *out = input[i] * lfo;
33            self.phase = (self.phase + rate / sample_rate).fract();
34            params.tick_all();
35        }
36    }
37}
38
39fn main() {
40    // Demonstrate that the macro generated Default, param_defs, and type_name.
41    let t = Tremolo::default();
42    println!("Node type:  {}", Tremolo::type_name());
43    println!("Param count: {}", Tremolo::PARAM_COUNT);
44    for def in Tremolo::param_defs() {
45        println!("  {} [{:.1}–{:.1}] default={:.2}", def.name, def.min, def.max, def.default);
46    }
47
48    // Wrap it for the engine
49    let _boxed: Box<dyn aether_ndk::DspNode> = into_node(t);
50    println!("\nNode wrapped and ready for the graph ✓");
51}