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}