Skip to main content

firefly_rust/audio/
nodes.rs

1use core::marker::PhantomData;
2
3use super::*;
4
5/// A marker for a node created by [`Node::add_sine`].
6pub struct Sine {}
7/// A marker for a node created by [`Node::add_mix`].
8pub struct Mix {}
9/// A marker for a node created by [`Node::add_all_for_one`].
10pub struct AllForOne {}
11/// A marker for a node created by [`Node::add_gain`].
12pub struct Gain {}
13/// A marker for a node created by [`Node::add_loop`].
14pub struct Loop {}
15/// A marker for a node created by [`Node::add_concat`].
16pub struct Concat {}
17/// A marker for a node created by [`Node::add_pan`].
18pub struct Pan {}
19/// A marker for a node created by [`Node::add_mute`].
20pub struct Mute {}
21/// A marker for a node created by [`Node::add_pause`].
22pub struct Pause {}
23/// A marker for a node created by [`Node::add_track_position`].
24pub struct TrackPosition {}
25/// A marker for a node created by [`Node::add_low_pass`].
26pub struct LowPass {}
27/// A marker for a node created by [`Node::add_high_pass`].
28pub struct HighPass {}
29/// A marker for a node created by [`Node::add_take_left`].
30pub struct TakeLeft {}
31/// A marker for a node created by [`Node::add_take_right`].
32pub struct TakeRight {}
33/// A marker for a node created by [`Node::add_swap`].
34pub struct Swap {}
35/// A marker for a node created by [`Node::add_clip`].
36pub struct Clip {}
37/// A marker for a node created by [`Node::add_square`].
38pub struct Square {}
39/// A marker for a node created by [`Node::add_sawtooth`].
40pub struct Sawtooth {}
41/// A marker for a node created by [`Node::add_triangle`].
42pub struct Triangle {}
43/// A marker for a node created by [`Node::add_noise`].
44pub struct Noise {}
45/// A marker for a node created by [`Node::add_empty`].
46pub struct Empty {}
47/// A marker for a node created by [`Node::add_zero`].
48pub struct Zero {}
49
50/// A marker for a node created by [`Node::add_file`].
51pub struct File {}
52
53/// A marker for nodes that can have sub-nodes.
54trait Parent {}
55impl Parent for Mix {}
56impl Parent for AllForOne {}
57impl Parent for Gain {}
58impl Parent for Loop {}
59impl Parent for Concat {}
60impl Parent for Pan {}
61impl Parent for Mute {}
62impl Parent for Pause {}
63impl Parent for TrackPosition {}
64impl Parent for LowPass {}
65impl Parent for HighPass {}
66impl Parent for TakeLeft {}
67impl Parent for TakeRight {}
68impl Parent for Swap {}
69impl Parent for Clip {}
70
71/// An audio node: a source, a sink, a filter, an effect, etc.
72pub struct Node<F> {
73    id: u32,
74    /// A marker for a specific node type. Used to control which parameters can be modulated.
75    _flavor: PhantomData<F>,
76}
77
78/// The output audio node. Mixes all inputs and plays them on the device's speaker.
79pub const OUT: Node<Mix> = Node::new(0);
80
81impl<F> Node<F> {
82    #[must_use]
83    const fn new(id: u32) -> Self {
84        Self {
85            id,
86            _flavor: PhantomData,
87        }
88    }
89
90    /// Reset the node state to how it was when it was just added.
91    pub fn reset(&self) {
92        unsafe { bindings::reset(self.id) }
93    }
94}
95
96#[expect(clippy::must_use_candidate, private_bounds)]
97impl<F: Parent> Node<F> {
98    /// Add sine wave oscillator source (`∿`).
99    pub fn add_sine(&self, f: Freq, phase: f32) -> Node<Sine> {
100        let id = unsafe { bindings::add_sine(self.id, f.0, phase) };
101        Node::new(id)
102    }
103
104    /// Add square wave oscillator source (`⎍`).
105    pub fn add_square(&self, f: Freq, phase: f32) -> Node<Square> {
106        let id = unsafe { bindings::add_square(self.id, f.0, phase) };
107        Node::new(id)
108    }
109
110    /// Add sawtooth wave oscillator source (`╱│`).
111    pub fn add_sawtooth(&self, f: Freq, phase: f32) -> Node<Sawtooth> {
112        let id = unsafe { bindings::add_sawtooth(self.id, f.0, phase) };
113        Node::new(id)
114    }
115
116    /// Add triangle wave oscillator source (`╱╲`).
117    pub fn add_triangle(&self, f: Freq, phase: f32) -> Node<Triangle> {
118        let id = unsafe { bindings::add_triangle(self.id, f.0, phase) };
119        Node::new(id)
120    }
121
122    /// Add white noise source (amplitude on each tick is random).
123    pub fn add_noise(&self, seed: i32) -> Node<Noise> {
124        let id = unsafe { bindings::add_noise(self.id, seed) };
125        Node::new(id)
126    }
127
128    /// Add always stopped source.
129    pub fn add_empty(&self) -> Node<Empty> {
130        let id = unsafe { bindings::add_empty(self.id) };
131        Node::new(id)
132    }
133
134    /// Add silent source producing zeros.
135    pub fn add_zero(&self) -> Node<Zero> {
136        let id = unsafe { bindings::add_zero(self.id) };
137        Node::new(id)
138    }
139
140    /// Play an audio file from ROM.
141    pub fn add_file(&self, path: &str) -> Node<File> {
142        let ptr = path.as_ptr() as u32;
143        let len = path.len() as u32;
144        let id = unsafe { bindings::add_file(self.id, ptr, len) };
145        Node::new(id)
146    }
147
148    /// Add node simply mixing all inputs.
149    pub fn add_mix(&self) -> Node<Mix> {
150        let id = unsafe { bindings::add_mix(self.id) };
151        Node::new(id)
152    }
153
154    /// Add mixer node that stops if any of the sources stops.
155    pub fn add_all_for_one(&self) -> Node<AllForOne> {
156        let id = unsafe { bindings::add_all_for_one(self.id) };
157        Node::new(id)
158    }
159
160    /// Add gain control node.
161    pub fn add_gain(&self, lvl: f32) -> Node<Gain> {
162        let id = unsafe { bindings::add_gain(self.id, lvl) };
163        Node::new(id)
164    }
165
166    /// Add a loop node that resets the input if it stops.
167    pub fn add_loop(&self) -> Node<Loop> {
168        let id = unsafe { bindings::add_loop(self.id) };
169        Node::new(id)
170    }
171
172    /// Add a node that plays the inputs one after the other, in the order as they added.
173    pub fn add_concat(&self) -> Node<Concat> {
174        let id = unsafe { bindings::add_concat(self.id) };
175        Node::new(id)
176    }
177
178    /// Add node panning the audio to the left (0.), right (1.), or something in between.
179    pub fn add_pan(&self, lvl: f32) -> Node<Pan> {
180        let id = unsafe { bindings::add_pan(self.id, lvl) };
181        Node::new(id)
182    }
183
184    /// Add node that can be muted using modulation.
185    pub fn add_mute(&self) -> Node<Mute> {
186        let id = unsafe { bindings::add_mute(self.id) };
187        Node::new(id)
188    }
189
190    /// Add node that can be paused using modulation.
191    pub fn add_pause(&self) -> Node<Pause> {
192        let id = unsafe { bindings::add_pause(self.id) };
193        Node::new(id)
194    }
195
196    /// Add node tracking the elapsed playback time.
197    pub fn add_track_position(&self) -> Node<TrackPosition> {
198        let id = unsafe { bindings::add_track_position(self.id) };
199        Node::new(id)
200    }
201
202    /// Add lowpass filter node.
203    pub fn add_low_pass(&self, freq: Freq, q: f32) -> Node<LowPass> {
204        let id = unsafe { bindings::add_low_pass(self.id, freq.0, q) };
205        Node::new(id)
206    }
207
208    /// Add highpass filter node.
209    pub fn add_high_pass(&self, freq: Freq, q: f32) -> Node<HighPass> {
210        let id = unsafe { bindings::add_high_pass(self.id, freq.0, q) };
211        Node::new(id)
212    }
213
214    /// Add node converting stereo to mono by taking the left channel.
215    pub fn add_take_left(&self) -> Node<TakeLeft> {
216        let id = unsafe { bindings::add_take_left(self.id) };
217        Node::new(id)
218    }
219
220    /// Add node converting stereo to mono by taking the right channel.
221    pub fn add_take_right(&self) -> Node<TakeRight> {
222        let id = unsafe { bindings::add_take_right(self.id) };
223        Node::new(id)
224    }
225
226    /// Add node swapping left and right channels of the stereo input.
227    pub fn add_swap(&self) -> Node<Swap> {
228        let id = unsafe { bindings::add_swap(self.id) };
229        Node::new(id)
230    }
231
232    /// Add node clamping the input amplitude. Can be used for hard distortion.
233    pub fn add_clip(&self, low: f32, high: f32) -> Node<Clip> {
234        let id = unsafe { bindings::add_clip(self.id, low, high) };
235        Node::new(id)
236    }
237
238    /// Reset the node and all child nodes to the state to how it was when they were just added.
239    pub fn reset_all(&self) {
240        unsafe { bindings::reset_all(self.id) }
241    }
242
243    /// Remove all child nodes.
244    ///
245    /// After it is called, you should make sure to discard all references to the old
246    /// child nodes.
247    pub fn clear(&self) {
248        unsafe { bindings::clear(self.id) }
249    }
250}
251
252impl Node<Sine> {
253    /// Modulate oscillation frequency.
254    pub fn modulate<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
255        m.modulate(self.id, 0, low.0, high.0);
256    }
257
258    /// Set the oscillation frequency.
259    pub fn set(&self, val: Freq) {
260        unsafe { bindings::set_param(self.id, 0, val.0) };
261    }
262}
263
264impl Node<Square> {
265    /// Modulate oscillation frequency.
266    pub fn modulate<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
267        m.modulate(self.id, 0, low.0, high.0);
268    }
269
270    /// Set the oscillation frequency.
271    pub fn set(&self, val: Freq) {
272        unsafe { bindings::set_param(self.id, 0, val.0) };
273    }
274}
275
276impl Node<Sawtooth> {
277    /// Modulate oscillation frequency.
278    pub fn modulate<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
279        m.modulate(self.id, 0, low.0, high.0);
280    }
281
282    /// Set the oscillation frequency.
283    pub fn set(&self, val: Freq) {
284        unsafe { bindings::set_param(self.id, 0, val.0) };
285    }
286}
287
288impl Node<Triangle> {
289    /// Modulate oscillation frequency.
290    pub fn modulate<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
291        m.modulate(self.id, 0, low.0, high.0);
292    }
293
294    /// Set the oscillation frequency.
295    pub fn set(&self, val: Freq) {
296        unsafe { bindings::set_param(self.id, 0, val.0) };
297    }
298}
299
300impl Node<File> {
301    /// Go to the specified timestamp in the file.
302    #[expect(clippy::cast_precision_loss)]
303    pub fn seek(&self, t: Time) {
304        unsafe { bindings::set_param(self.id, 0, t.0 as f32) };
305    }
306}
307
308impl Node<Gain> {
309    /// Modulate the gain level.
310    pub fn modulate<M: Modulator>(&self, low: f32, high: f32, m: M) {
311        m.modulate(self.id, 0, low, high);
312    }
313
314    /// Set the the gain level.
315    pub fn set(&self, val: f32) {
316        unsafe { bindings::set_param(self.id, 0, val) };
317    }
318}
319
320impl Node<Pan> {
321    /// Modulate the pan value (from 0. to 1.: 0. is only left, 1. is only right).
322    pub fn modulate<M: Modulator>(&self, low: f32, high: f32, m: M) {
323        m.modulate(self.id, 0, low, high);
324    }
325
326    /// Set the the pan value (from 0. to 1.: 0. is only left, 1. is only right).
327    pub fn set(&self, val: f32) {
328        unsafe { bindings::set_param(self.id, 0, val) };
329    }
330}
331
332impl Node<Mute> {
333    /// Modulate the muted state.
334    ///
335    /// Below 0.5 is muted, above is unmuted.
336    pub fn modulate<M: Modulator>(&self, low: f32, high: f32, m: M) {
337        m.modulate(self.id, 0, low, high);
338    }
339
340    pub fn mute(&self) {
341        unsafe { bindings::set_param(self.id, 0, 0.) };
342    }
343
344    pub fn unmute(&self) {
345        unsafe { bindings::set_param(self.id, 0, 1.) };
346    }
347}
348
349impl Node<Pause> {
350    /// Modulate the paused state.
351    ///
352    /// Below 0.5 is paused, above is playing.
353    pub fn modulate<M: Modulator>(&self, low: f32, high: f32, m: M) {
354        m.modulate(self.id, 0, low, high);
355    }
356
357    pub fn pause(&self) {
358        unsafe { bindings::set_param(self.id, 0, 0.) };
359    }
360
361    pub fn play(&self) {
362        unsafe { bindings::set_param(self.id, 0, 1.) };
363    }
364}
365
366impl Node<LowPass> {
367    /// Modulate the cut-off frequency.
368    pub fn modulate_freq<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
369        m.modulate(self.id, 0, low.0, high.0);
370    }
371
372    /// Set the the cut-off frequency.
373    pub fn set_freq(&self, val: Freq) {
374        unsafe { bindings::set_param(self.id, 0, val.0) };
375    }
376}
377
378impl Node<HighPass> {
379    /// Modulate the cut-off frequency.
380    pub fn modulate_freq<M: Modulator>(&self, low: Freq, high: Freq, m: M) {
381        m.modulate(self.id, 0, low.0, high.0);
382    }
383
384    /// Set the the cut-off frequency.
385    pub fn set_freq(&self, val: Freq) {
386        unsafe { bindings::set_param(self.id, 0, val.0) };
387    }
388}
389
390impl Node<Clip> {
391    /// Modulate the low cut amplitude and adjust the high amplitude to keep the gap.
392    ///
393    /// In other words, the difference between low and high cut points will stay the same.
394    pub fn modulate_both<M: Modulator>(&self, low: f32, high: f32, m: M) {
395        m.modulate(self.id, 0, low, high);
396    }
397
398    /// Set the the low cut amplitude and adjust the high amplitude to keep the gap.
399    pub fn set_both(&self, val: f32) {
400        unsafe { bindings::set_param(self.id, 0, val) };
401    }
402
403    /// Modulate the low cut amplitude.
404    pub fn modulate_low<M: Modulator>(&self, low: f32, high: f32, m: M) {
405        m.modulate(self.id, 1, low, high);
406    }
407
408    /// Set the the low cut amplitude.
409    pub fn set_low(&self, val: f32) {
410        unsafe { bindings::set_param(self.id, 1, val) };
411    }
412
413    /// Modulate the high cut amplitude.
414    pub fn modulate_high<M: Modulator>(&self, low: f32, high: f32, m: M) {
415        m.modulate(self.id, 2, low, high);
416    }
417
418    /// Set the the high cut amplitude.
419    pub fn set_high(&self, val: f32) {
420        unsafe { bindings::set_param(self.id, 2, val) };
421    }
422}
423
424mod bindings {
425    #[link(wasm_import_module = "audio")]
426    unsafe extern "C" {
427        // generators
428        pub(super) unsafe fn add_sine(parent_id: u32, freq: f32, phase: f32) -> u32;
429        pub(super) unsafe fn add_square(parent_id: u32, freq: f32, phase: f32) -> u32;
430        pub(super) unsafe fn add_sawtooth(parent_id: u32, freq: f32, phase: f32) -> u32;
431        pub(super) unsafe fn add_triangle(parent_id: u32, freq: f32, phase: f32) -> u32;
432        pub(super) unsafe fn add_noise(parent_id: u32, seed: i32) -> u32;
433        pub(super) unsafe fn add_empty(parent_id: u32) -> u32;
434        pub(super) unsafe fn add_zero(parent_id: u32) -> u32;
435        pub(super) unsafe fn add_file(parent: u32, ptr: u32, len: u32) -> u32;
436
437        // nodes
438        pub(super) unsafe fn add_mix(parent_id: u32) -> u32;
439        pub(super) unsafe fn add_all_for_one(parent_id: u32) -> u32;
440        pub(super) unsafe fn add_gain(parent_id: u32, lvl: f32) -> u32;
441        pub(super) unsafe fn add_loop(parent_id: u32) -> u32;
442        pub(super) unsafe fn add_concat(parent_id: u32) -> u32;
443        pub(super) unsafe fn add_pan(parent_id: u32, lvl: f32) -> u32;
444        pub(super) unsafe fn add_mute(parent_id: u32) -> u32;
445        pub(super) unsafe fn add_pause(parent_id: u32) -> u32;
446        pub(super) unsafe fn add_track_position(parent_id: u32) -> u32;
447        pub(super) unsafe fn add_low_pass(parent_id: u32, freq: f32, q: f32) -> u32;
448        pub(super) unsafe fn add_high_pass(parent_id: u32, freq: f32, q: f32) -> u32;
449        pub(super) unsafe fn add_take_left(parent_id: u32) -> u32;
450        pub(super) unsafe fn add_take_right(parent_id: u32) -> u32;
451        pub(super) unsafe fn add_swap(parent_id: u32) -> u32;
452        pub(super) unsafe fn add_clip(parent_id: u32, low: f32, high: f32) -> u32;
453
454        pub(super) unsafe fn reset(node_id: u32);
455        pub(super) unsafe fn reset_all(node_id: u32);
456        pub(super) unsafe fn clear(node_id: u32);
457        pub(super) unsafe fn set_param(node_id: u32, param: u32, val: f32);
458    }
459}