Skip to main content

firefly_rust/audio/
modulators.rs

1use super::*;
2
3/// Modulator can be attached to a node to change a node parameter over time.
4///
5/// Modulators include both LFOs (Low-Frequency Oscillator) and envelopes.
6/// The difference is that LFOs keep oscillating between values
7/// while envelopes go from one value to another and then stop.
8///
9/// Internally, modulators only produce values from 0 to 1.
10/// Then, to get the final value of the parameter,
11/// the value from the modulator is projected on the range
12/// between `low` and `high` arguments passed together
13/// with the modulator when attaching a modulator to a node.
14/// For example, [`Node<Sine>::modulate`] accepts the range of modulated values
15/// for the sine wave frequency (which can be used for vibrato effect).
16///
17/// Even if a node has multiple parameters that can be modulated,
18/// currently  single node may have at most one modulator attached.
19pub trait Modulator {
20    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32);
21}
22
23/// Linear (ramp up or down) envelope.
24///
25/// It looks like this: `⎽╱⎺` (or `⎺╲⎽` if `low` is bigger than `high`).
26///
27/// The value before `start_at` is 0, the value after `end_at` is 1,
28/// and the value between `start_at` and `end_at` changes linearly from 0 to 1.
29///
30/// Most often used with [`Gain`] for fade in and fade out effect.
31pub struct LinearModulator {
32    pub start_at: Time,
33    pub end_at: Time,
34}
35
36impl Modulator for LinearModulator {
37    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
38        unsafe {
39            bindings::mod_linear(node_id, param, low, high, self.start_at.0, self.end_at.0);
40        }
41    }
42}
43
44/// Hold envelope.
45///
46/// It looks like this: `⎽│⎺` (or `⎺│⎽` if `low` is bigger than `high`).
47///
48/// The value before `time` is 0 and the value after `time` is 1.
49/// Equivalent to [`LinearModulator`] with `start_at` being equal to `end_at`.
50pub struct HoldModulator {
51    pub time: Time,
52}
53
54impl Modulator for HoldModulator {
55    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
56        unsafe {
57            bindings::mod_hold(node_id, param, low, high, self.time.0);
58        }
59    }
60}
61
62/// ADSR envelope.
63///
64/// It looks like this: `🭋🭍🬹🬿`
65///
66///  1. Until `attack`, the value goes from 0 to 1;
67///  2. Until `decay`, it goes from 1 to `sustain_level`;
68///  3. Until `sustain`, it holds `sustain_level`;
69///  4. Until `release`, it goes from `sustain_level` to 0;
70///  5. After `release`, it holds 0.
71///
72/// Most commonly used with [`Gain`].
73pub struct AdsrModulator {
74    /// When the value reaches 1.
75    pub attack: Time,
76    /// When the value reaches `sustain_level`.
77    pub decay: Time,
78    /// Until when the value holds `sustain_level`.
79    pub sustain: Time,
80    /// The value generated from `decay` until `sustain`.
81    pub sustain_level: f32,
82    /// When the value drops to 0.
83    pub release: Time,
84}
85
86impl Modulator for AdsrModulator {
87    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
88        unsafe {
89            bindings::mod_adsr(
90                node_id,
91                param,
92                low,
93                high,
94                self.attack.0,
95                self.decay.0,
96                self.sustain.0,
97                self.sustain_level,
98                self.release.0,
99            );
100        }
101    }
102}
103
104/// Sine wave low-frequency oscillator.
105///
106/// It looks like this: `∿`.
107///
108/// Most commonly used with [`Sine`] (or another wave generator)
109/// to produce vibrato effect.
110pub struct SineModulator {
111    pub freq: Freq,
112}
113
114impl Modulator for SineModulator {
115    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
116        unsafe {
117            bindings::mod_sine(node_id, param, self.freq.0, low, high);
118        }
119    }
120}
121
122/// Square wave low-frequency oscillator.
123///
124/// It looks like this: `🭿🭾🭿🭾🭿🭾🭿🭾`.
125pub struct SquareModulator {
126    pub period: Time,
127}
128
129impl Modulator for SquareModulator {
130    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
131        unsafe {
132            bindings::mod_square(node_id, param, low, high, self.period.0);
133        }
134    }
135}
136
137/// Sawtooth wave low-frequency oscillator.
138///
139/// It looks like this: `╱│╱│╱│╱│`.
140pub struct SawtoothModulator {
141    pub period: Time,
142}
143
144impl Modulator for SawtoothModulator {
145    fn modulate(self, node_id: u32, param: u32, low: f32, high: f32) {
146        unsafe {
147            bindings::mod_sawtooth(node_id, param, low, high, self.period.0);
148        }
149    }
150}
151
152mod bindings {
153    #[link(wasm_import_module = "audio")]
154    unsafe extern "C" {
155        pub(super) unsafe fn mod_linear(
156            node_id: u32,
157            param: u32,
158            start: f32,
159            end: f32,
160            start_at: u32,
161            end_at: u32,
162        );
163        pub(super) unsafe fn mod_hold(id: u32, param: u32, low: f32, high: f32, time: u32);
164        pub(super) unsafe fn mod_sine(id: u32, param: u32, freq: f32, low: f32, high: f32);
165        pub(super) unsafe fn mod_square(id: u32, param: u32, low: f32, high: f32, period: u32);
166        pub(super) unsafe fn mod_sawtooth(id: u32, param: u32, low: f32, high: f32, period: u32);
167        pub(super) unsafe fn mod_adsr(
168            id: u32,
169            param: u32,
170            low: f32,
171            high: f32,
172            attack: u32,
173            decay: u32,
174            sustain: u32,
175            sustain_level: f32,
176            release: u32,
177        );
178    }
179}