firefly_rust/audio/
nodes.rs

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