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