From 09c1af11d82ae29fb0f192422187cd7f8a1e3267 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:02:08 +0200 Subject: [PATCH 1/8] workspace per example --- example/audio/moon.work | 4 ++++ example/font/moon.work | 4 ++++ example/image/moon.work | 4 ++++ example/input/moon.work | 4 ++++ example/moon.work | 3 --- example/random/moon.work | 4 ++++ example/scores/moon.work | 4 ++++ example/shapes/moon.work | 4 ++++ 8 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 example/audio/moon.work create mode 100644 example/font/moon.work create mode 100644 example/image/moon.work create mode 100644 example/input/moon.work delete mode 100644 example/moon.work create mode 100644 example/random/moon.work create mode 100644 example/scores/moon.work create mode 100644 example/shapes/moon.work diff --git a/example/audio/moon.work b/example/audio/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/audio/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/font/moon.work b/example/font/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/font/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/image/moon.work b/example/image/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/image/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/input/moon.work b/example/input/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/input/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/moon.work b/example/moon.work deleted file mode 100644 index 431a1f5..0000000 --- a/example/moon.work +++ /dev/null @@ -1,3 +0,0 @@ -members = [ - "..", -] diff --git a/example/random/moon.work b/example/random/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/random/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/scores/moon.work b/example/scores/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/scores/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] diff --git a/example/shapes/moon.work b/example/shapes/moon.work new file mode 100644 index 0000000..5edbb9a --- /dev/null +++ b/example/shapes/moon.work @@ -0,0 +1,4 @@ +members = [ + ".", + "../..", +] From 6c36da7fdd09659a118d750d7e0328a276c9c11c Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:04:25 +0200 Subject: [PATCH 2/8] flatten Node with traits --- example/audio/main.mbt | 3 +- src/audio/node.mbt | 441 ++++++++++++++++++++++------------------- 2 files changed, 235 insertions(+), 209 deletions(-) diff --git a/example/audio/main.mbt b/example/audio/main.mbt index f010c7b..4f083cc 100644 --- a/example/audio/main.mbt +++ b/example/audio/main.mbt @@ -7,7 +7,8 @@ fn main { /// boot is only called once, after all the memory is initialized. pub fn boot() -> Unit { @audio.out.add_file(b"pickupCoin") |> ignore - @audio.out.add_gain(0.5).next().add_sine(@audio.A4, 1) |> ignore + let gain = @audio.out.add_gain(0.5) + gain.add_sine(@audio.A4, 1) |> ignore } ///| diff --git a/src/audio/node.mbt b/src/audio/node.mbt index 6430c46..0d07066 100644 --- a/src/audio/node.mbt +++ b/src/audio/node.mbt @@ -1,451 +1,476 @@ ///| -#valtype -struct Node { - id : UInt -} derive(Eq, Compare, Hash) - -///| -pub trait Marker { - fn next(Self) -> Node +pub trait Node { + fn id(Self) -> UInt + /// Add sine wave oscillator source (`∿`). + fn add_sine(Self, freq : Hz, phase : Float) -> Sine = _ + /// Add square wave oscillator source (`⎍`). + fn add_square(Self, freq : Hz, phase : Float) -> Square = _ + /// Add sawtooth wave oscillator source (`╱│`). + fn add_sawtooth(Self, freq : Hz, phase : Float) -> Sawtooth = _ + /// Add triangle wave oscillator source (`╱╲`). + fn add_triangle(Self, freq : Hz, phase : Float) -> Triangle = _ + /// Add white noise source (amplitude on each tick is random). + fn add_noise(Self, seed : Int) -> Noise = _ + /// Add always stopped source. + fn add_empty(Self) -> Empty = _ + /// Add silent source producing zeros. + fn add_zero(Self) -> Zero = _ + /// Add source playing audio from a file. + fn add_file(Self, path : @firefly.Utf8View) -> File = _ + /// Add node simply mixing all inputs. + fn add_mix(Self) -> Mix = _ + /// Add mixer node that stops if any of the sources stops. + fn add_all_for_one(Self) -> AllForOne = _ + /// Add gain control node. + fn add_gain(Self, level : Float) -> Gain = _ + /// Add a loop node that resets the input if it stops. + fn add_loop(Self) -> Loop = _ + /// Add a node that plays the inputs one after the other, in the order as they added. + fn add_concat(Self) -> Concat = _ + /// Add node panning the audio to the left (0.), right (1.), or something in between. + fn add_pan(Self, level : Float) -> Pan = _ + /// Add node that can be muted using modulation. + fn add_mute(Self) -> Mute = _ + /// Add node that can be paused using modulation. + fn add_pause(Self) -> Pause = _ + /// Add node tracking the elapsed playback time. + fn add_track_position(Self) -> TrackPosition = _ + /// Add lowpass filter node. + fn add_low_pass(Self, freq : Hz, q : Float) -> LowPass = _ + /// Add highpass filter node. + fn add_high_pass(Self, freq : Hz, q : Float) -> HighPass = _ + /// Add node converting stereo to mono by taking the left channel. + fn add_take_left(Self) -> TakeLeft = _ + /// Add node converting stereo to mono by taking the right channel. + fn add_take_right(Self) -> TakeRight = _ + /// Add node swapping left and right channels of the stereo input. + fn add_swap(Self) -> Swap = _ + /// Add node clamping the input amplitude. Can be used for hard distortion. + fn add_clip(Self, low : Float, high : Float) -> Clip = _ +} + +///| +struct Out {} + +///| +pub impl Node for Out with fn id(_self) -> UInt { + return 0 } ///| /// The output audio node. Mixes all inputs and plays them on the device's speaker. -pub let out : Node = Node::{ id: 0 } +pub let out : Out = Out::{ } ///| /// A marker for a specific node type. See `Node::add_sine`. -pub struct Sine(Node) +pub struct Sine { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Sine with fn next(self : Sine) -> Node { - self.0 +pub impl Node for Sine with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_mix`. -struct Mix(Node) +struct Mix { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Mix with fn next(self : Mix) -> Node { - self.0 +pub impl Node for Mix with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_all_for_one`. -struct AllForOne(Node) +struct AllForOne { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for AllForOne with fn next(self : AllForOne) -> Node { - self.0 +pub impl Node for AllForOne with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_gain`. -struct Gain(Node) +struct Gain { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Gain with fn next(self : Gain) -> Node { - self.0 +pub impl Node for Gain with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_loop`. -struct Loop(Node) +struct Loop { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Loop with fn next(self : Loop) -> Node { - self.0 +pub impl Node for Loop with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_concat`. -struct Concat(Node) +struct Concat { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Concat with fn next(self : Concat) -> Node { - self.0 +pub impl Node for Concat with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_pan`. -struct Pan(Node) +struct Pan { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Pan with fn next(self : Pan) -> Node { - self.0 +pub impl Node for Pan with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_mute`. -struct Mute(Node) +struct Mute { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Mute with fn next(self : Mute) -> Node { - self.0 +pub impl Node for Mute with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_pause`. -struct Pause(Node) +struct Pause { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Pause with fn next(self : Pause) -> Node { - self.0 +pub impl Node for Pause with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_track_position`. -struct TrackPosition(Node) +struct TrackPosition { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for TrackPosition with fn next(self : TrackPosition) -> Node { - self.0 +pub impl Node for TrackPosition with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_low_pass`. -struct LowPass(Node) +struct LowPass { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for LowPass with fn next(self : LowPass) -> Node { - self.0 +pub impl Node for LowPass with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_high_pass`. -struct HighPass(Node) +struct HighPass { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for HighPass with fn next(self : HighPass) -> Node { - self.0 +pub impl Node for HighPass with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_take_left`. -struct TakeLeft(Node) +struct TakeLeft { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for TakeLeft with fn next(self : TakeLeft) -> Node { - self.0 +pub impl Node for TakeLeft with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_take_right`. -struct TakeRight(Node) +struct TakeRight { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for TakeRight with fn next(self : TakeRight) -> Node { - self.0 +pub impl Node for TakeRight with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_swap`. -struct Swap(Node) +struct Swap { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Swap with fn next(self : Swap) -> Node { - self.0 +pub impl Node for Swap with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_clip`. -struct Clip(Node) +struct Clip { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Clip with fn next(self : Clip) -> Node { - self.0 +pub impl Node for Clip with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_square`. -struct Square(Node) +struct Square { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Square with fn next(self : Square) -> Node { - self.0 +pub impl Node for Square with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_sawtooth`. -struct Sawtooth(Node) +struct Sawtooth { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Sawtooth with fn next(self : Sawtooth) -> Node { - self.0 +pub impl Node for Sawtooth with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_triangle`. -struct Triangle(Node) +struct Triangle { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Triangle with fn next(self : Triangle) -> Node { - self.0 +pub impl Node for Triangle with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_noise`. -struct Noise(Node) +struct Noise { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Noise with fn next(self : Noise) -> Node { - self.0 +pub impl Node for Noise with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_empty`. -struct Empty(Node) +struct Empty { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Empty with fn next(self : Empty) -> Node { - self.0 +pub impl Node for Empty with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_zero`. -struct Zero(Node) +struct Zero { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for Zero with fn next(self : Zero) -> Node { - self.0 +pub impl Node for Zero with fn id(self) -> UInt { + return self.id } ///| /// A marker for a specific node type. See `Node::add_file`. -struct File(Node) +struct File { + id : UInt +} ///| -/// Get the next node for applying more effects. -#inline -pub impl Marker for File with fn next(self : File) -> Node { - self.0 +pub impl Node for File with fn id(self) -> UInt { + return self.id } ///| -/// Add sine wave oscillator source (`∿`). -pub fn Node::add_sine(self : Node, freq : Hz, phase : Float) -> Sine { - Sine(Node::{ id: @ffi.audio_add_sine(self.id, freq, phase) }) +impl Node with fn add_sine(self, freq : Hz, phase : Float) -> Sine { + Sine::{ id: @ffi.audio_add_sine(self.id(), freq, phase) } } ///| -/// Add square wave oscillator source (`⎍`). -pub fn Node::add_square(self : Node, freq : Hz, phase : Float) -> Square { - Square(Node::{ id: @ffi.audio_add_square(self.id, freq, phase) }) +impl Node with fn add_square(self, freq : Hz, phase : Float) -> Square { + Square::{ id: @ffi.audio_add_square(self.id(), freq, phase) } } ///| -/// Add sawtooth wave oscillator source (`╱│`). -pub fn Node::add_sawtooth(self : Node, freq : Hz, phase : Float) -> Sawtooth { - Sawtooth(Node::{ id: @ffi.audio_add_sawtooth(self.id, freq, phase) }) +impl Node with fn add_sawtooth(self, freq : Hz, phase : Float) -> Sawtooth { + Sawtooth::{ id: @ffi.audio_add_sawtooth(self.id(), freq, phase) } } ///| -/// Add triangle wave oscillator source (`╱╲`). -pub fn Node::add_triangle(self : Node, freq : Hz, phase : Float) -> Triangle { - Triangle(Node::{ id: @ffi.audio_add_triangle(self.id, freq, phase) }) +impl Node with fn add_triangle(self, freq : Hz, phase : Float) -> Triangle { + Triangle::{ id: @ffi.audio_add_triangle(self.id(), freq, phase) } } ///| -/// Add white noise source (amplitude on each tick is random). -pub fn Node::add_noise(self : Node, seed : Int) -> Noise { - Noise(Node::{ id: @ffi.audio_add_noise(self.id, seed) }) +impl Node with fn add_noise(self, seed : Int) -> Noise { + Noise::{ id: @ffi.audio_add_noise(self.id(), seed) } } ///| -/// Add always stopped source. -pub fn Node::add_empty(self : Node) -> Empty { - Empty(Node::{ id: @ffi.audio_add_empty(self.id) }) +impl Node with fn add_empty(self) -> Empty { + Empty::{ id: @ffi.audio_add_empty(self.id()) } } ///| -/// Add silent source producing zeros. -pub fn Node::add_zero(self : Node) -> Zero { - Zero(Node::{ id: @ffi.audio_add_zero(self.id) }) +impl Node with fn add_zero(self) -> Zero { + Zero::{ id: @ffi.audio_add_zero(self.id()) } } ///| -/// Add source playing audio from a file. -pub fn Node::add_file(self : Node, path : @firefly.Utf8View) -> File { - let file = File(Node::{ +impl Node with fn add_file(self, path : @firefly.Utf8View) -> File { + let file = File::{ id: @ffi.audio_add_file( - self.id, + self.id(), @memory.bytesview_addr(path), @memory.bytesview_size(path), ), - }) + } @memory.keep(path) file } ///| -/// Add node simply mixing all inputs. -pub fn Node::add_mix(self : Node) -> Mix { - Mix(Node::{ id: @ffi.audio_add_mix(self.id) }) +impl Node with fn add_mix(self) -> Mix { + Mix::{ id: @ffi.audio_add_mix(self.id()) } } ///| -/// Add mixer node that stops if any of the sources stops. -pub fn Node::add_all_for_one(self : Node) -> AllForOne { - AllForOne(Node::{ id: @ffi.audio_add_all_for_one(self.id) }) +impl Node with fn add_all_for_one(self) -> AllForOne { + AllForOne::{ id: @ffi.audio_add_all_for_one(self.id()) } } ///| -/// Add gain control node. -pub fn Node::add_gain(self : Node, level : Float) -> Gain { - Gain(Node::{ id: @ffi.audio_add_gain(self.id, level) }) +impl Node with fn add_gain(self, level : Float) -> Gain { + Gain::{ id: @ffi.audio_add_gain(self.id(), level) } } ///| -/// Add a loop node that resets the input if it stops. -pub fn Node::add_loop(self : Node) -> Loop { - Loop(Node::{ id: @ffi.audio_add_loop(self.id) }) +impl Node with fn add_loop(self) -> Loop { + Loop::{ id: @ffi.audio_add_loop(self.id()) } } ///| -/// Add a node that plays the inputs one after the other, in the order as they added. -pub fn Node::add_concat(self : Node) -> Concat { - Concat(Node::{ id: @ffi.audio_add_concat(self.id) }) +impl Node with fn add_concat(self) -> Concat { + Concat::{ id: @ffi.audio_add_concat(self.id()) } } ///| -/// Add node panning the audio to the left (0.), right (1.), or something in between. -pub fn Node::add_pan(self : Node, level : Float) -> Pan { - Pan(Node::{ id: @ffi.audio_add_pan(self.id, level) }) +impl Node with fn add_pan(self, level : Float) -> Pan { + Pan::{ id: @ffi.audio_add_pan(self.id(), level) } } ///| -/// Add node that can be muted using modulation. -pub fn Node::add_mute(self : Node) -> Mute { - Mute(Node::{ id: @ffi.audio_add_mute(self.id) }) +impl Node with fn add_mute(self) -> Mute { + Mute::{ id: @ffi.audio_add_mute(self.id()) } } ///| -/// Add node that can be paused using modulation. -pub fn Node::add_pause(self : Node) -> Pause { - Pause(Node::{ id: @ffi.audio_add_pause(self.id) }) +impl Node with fn add_pause(self) -> Pause { + Pause::{ id: @ffi.audio_add_pause(self.id()) } } ///| -/// Add node tracking the elapsed playback time. -pub fn Node::add_track_position(self : Node) -> TrackPosition { - TrackPosition(Node::{ id: @ffi.audio_add_track_position(self.id) }) +impl Node with fn add_track_position(self) -> TrackPosition { + TrackPosition::{ id: @ffi.audio_add_track_position(self.id()) } } ///| -/// Add lowpass filter node. -pub fn Node::add_low_pass(self : Node, freq : Hz, q : Float) -> LowPass { - LowPass(Node::{ id: @ffi.audio_add_low_pass(self.id, freq, q) }) +impl Node with fn add_low_pass(self, freq : Hz, q : Float) -> LowPass { + LowPass::{ id: @ffi.audio_add_low_pass(self.id(), freq, q) } } ///| -/// Add highpass filter node. -pub fn Node::add_high_pass(self : Node, freq : Hz, q : Float) -> HighPass { - HighPass(Node::{ id: @ffi.audio_add_high_pass(self.id, freq, q) }) +impl Node with fn add_high_pass(self, freq : Hz, q : Float) -> HighPass { + HighPass::{ id: @ffi.audio_add_high_pass(self.id(), freq, q) } } ///| -/// Add node converting stereo to mono by taking the left channel. -pub fn Node::add_take_left(self : Node) -> TakeLeft { - TakeLeft(Node::{ id: @ffi.audio_add_take_left(self.id) }) +impl Node with fn add_take_left(self) -> TakeLeft { + TakeLeft::{ id: @ffi.audio_add_take_left(self.id()) } } ///| -/// Add node converting stereo to mono by taking the right channel. -pub fn Node::add_take_right(self : Node) -> TakeRight { - TakeRight(Node::{ id: @ffi.audio_add_take_right(self.id) }) +impl Node with fn add_take_right(self) -> TakeRight { + TakeRight::{ id: @ffi.audio_add_take_right(self.id()) } } ///| -/// Add node swapping left and right channels of the stereo input. -pub fn Node::add_swap(self : Node) -> Swap { - Swap(Node::{ id: @ffi.audio_add_swap(self.id) }) +impl Node with fn add_swap(self) -> Swap { + Swap::{ id: @ffi.audio_add_swap(self.id()) } } ///| -/// Add node clamping the input amplitude. Can be used for hard distortion. -pub fn Node::add_clip(self : Node, low : Float, high : Float) -> Clip { - Clip(Node::{ id: @ffi.audio_add_clip(self.id, low, high) }) +impl Node with fn add_clip(self, low : Float, high : Float) -> Clip { + Clip::{ id: @ffi.audio_add_clip(self.id(), low, high) } } ///| /// Modulate oscillation frequency. pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate oscillation frequency. pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate oscillation frequency. pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate oscillation frequency. pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate the gain level. pub fn[Mod : Modulator] Gain::modulate(self : Gain, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate the pan value (from 0. to 1.: 0. is only left, 1. is only right). pub fn[Mod : Modulator] Pan::modulate(self : Pan, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| @@ -453,7 +478,7 @@ pub fn[Mod : Modulator] Pan::modulate(self : Pan, mod : Mod) -> Unit { /// /// Below 0.5 is muted, above is unmuted. pub fn[Mod : Modulator] Mute::modulate(self : Mute, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| @@ -461,19 +486,19 @@ pub fn[Mod : Modulator] Mute::modulate(self : Mute, mod : Mod) -> Unit { /// /// Below 0.5 is paused, above is playing. pub fn[Mod : Modulator] Pause::modulate(self : Pause, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate the cut-off frequency. pub fn[Mod : Modulator] LowPass::modulate(self : LowPass, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate the cut-off frequency. pub fn[Mod : Modulator] HighPass::modulate(self : HighPass, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| @@ -481,17 +506,17 @@ pub fn[Mod : Modulator] HighPass::modulate(self : HighPass, mod : Mod) -> Unit { /// /// In other words, the difference between low and high cut points will stay the same. pub fn[Mod : Modulator] Clip::modulate_both(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.0.id, 0) + mod.modulate(self.id(), 0) } ///| /// Modulate the low cut amplitude. pub fn[Mod : Modulator] Clip::modulate_low(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.0.id, 1) + mod.modulate(self.id(), 1) } ///| /// Modulate the high cut amplitude. pub fn[Mod : Modulator] Clip::modulate_high(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.0.id, 2) + mod.modulate(self.id(), 2) } From 5ff60264b2345a87601c5766a42df6e25a7235a3 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:05:47 +0200 Subject: [PATCH 3/8] decouple nodes --- src/audio/node.mbt | 349 -------------------------------------------- src/audio/nodes.mbt | 348 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+), 349 deletions(-) create mode 100644 src/audio/nodes.mbt diff --git a/src/audio/node.mbt b/src/audio/node.mbt index 0d07066..1eb3808 100644 --- a/src/audio/node.mbt +++ b/src/audio/node.mbt @@ -49,271 +49,6 @@ pub trait Node { fn add_clip(Self, low : Float, high : Float) -> Clip = _ } -///| -struct Out {} - -///| -pub impl Node for Out with fn id(_self) -> UInt { - return 0 -} - -///| -/// The output audio node. Mixes all inputs and plays them on the device's speaker. -pub let out : Out = Out::{ } - -///| -/// A marker for a specific node type. See `Node::add_sine`. -pub struct Sine { - id : UInt -} - -///| -pub impl Node for Sine with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_mix`. -struct Mix { - id : UInt -} - -///| -pub impl Node for Mix with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_all_for_one`. -struct AllForOne { - id : UInt -} - -///| -pub impl Node for AllForOne with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_gain`. -struct Gain { - id : UInt -} - -///| -pub impl Node for Gain with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_loop`. -struct Loop { - id : UInt -} - -///| -pub impl Node for Loop with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_concat`. -struct Concat { - id : UInt -} - -///| -pub impl Node for Concat with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_pan`. -struct Pan { - id : UInt -} - -///| -pub impl Node for Pan with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_mute`. -struct Mute { - id : UInt -} - -///| -pub impl Node for Mute with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_pause`. -struct Pause { - id : UInt -} - -///| -pub impl Node for Pause with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_track_position`. -struct TrackPosition { - id : UInt -} - -///| -pub impl Node for TrackPosition with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_low_pass`. -struct LowPass { - id : UInt -} - -///| -pub impl Node for LowPass with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_high_pass`. -struct HighPass { - id : UInt -} - -///| -pub impl Node for HighPass with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_take_left`. -struct TakeLeft { - id : UInt -} - -///| -pub impl Node for TakeLeft with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_take_right`. -struct TakeRight { - id : UInt -} - -///| -pub impl Node for TakeRight with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_swap`. -struct Swap { - id : UInt -} - -///| -pub impl Node for Swap with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_clip`. -struct Clip { - id : UInt -} - -///| -pub impl Node for Clip with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_square`. -struct Square { - id : UInt -} - -///| -pub impl Node for Square with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_sawtooth`. -struct Sawtooth { - id : UInt -} - -///| -pub impl Node for Sawtooth with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_triangle`. -struct Triangle { - id : UInt -} - -///| -pub impl Node for Triangle with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_noise`. -struct Noise { - id : UInt -} - -///| -pub impl Node for Noise with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_empty`. -struct Empty { - id : UInt -} - -///| -pub impl Node for Empty with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_zero`. -struct Zero { - id : UInt -} - -///| -pub impl Node for Zero with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_file`. -struct File { - id : UInt -} - -///| -pub impl Node for File with fn id(self) -> UInt { - return self.id -} - ///| impl Node with fn add_sine(self, freq : Hz, phase : Float) -> Sine { Sine::{ id: @ffi.audio_add_sine(self.id(), freq, phase) } @@ -436,87 +171,3 @@ impl Node with fn add_swap(self) -> Swap { impl Node with fn add_clip(self, low : Float, high : Float) -> Clip { Clip::{ id: @ffi.audio_add_clip(self.id(), low, high) } } - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the gain level. -pub fn[Mod : Modulator] Gain::modulate(self : Gain, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the pan value (from 0. to 1.: 0. is only left, 1. is only right). -pub fn[Mod : Modulator] Pan::modulate(self : Pan, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the muted state. -/// -/// Below 0.5 is muted, above is unmuted. -pub fn[Mod : Modulator] Mute::modulate(self : Mute, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the paused state. -/// -/// Below 0.5 is paused, above is playing. -pub fn[Mod : Modulator] Pause::modulate(self : Pause, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the cut-off frequency. -pub fn[Mod : Modulator] LowPass::modulate(self : LowPass, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the cut-off frequency. -pub fn[Mod : Modulator] HighPass::modulate(self : HighPass, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// 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[Mod : Modulator] Clip::modulate_both(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate the low cut amplitude. -pub fn[Mod : Modulator] Clip::modulate_low(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.id(), 1) -} - -///| -/// Modulate the high cut amplitude. -pub fn[Mod : Modulator] Clip::modulate_high(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.id(), 2) -} diff --git a/src/audio/nodes.mbt b/src/audio/nodes.mbt new file mode 100644 index 0000000..b6f4eb0 --- /dev/null +++ b/src/audio/nodes.mbt @@ -0,0 +1,348 @@ +///| +struct Out {} + +///| +pub impl Node for Out with fn id(_self) -> UInt { + return 0 +} + +///| +/// The output audio node. Mixes all inputs and plays them on the device's speaker. +pub let out : Out = Out::{ } + +///| +/// A marker for a specific node type. See `Node::add_sine`. +pub struct Sine { + id : UInt +} + +///| +pub impl Node for Sine with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_mix`. +struct Mix { + id : UInt +} + +///| +pub impl Node for Mix with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_all_for_one`. +struct AllForOne { + id : UInt +} + +///| +pub impl Node for AllForOne with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_gain`. +struct Gain { + id : UInt +} + +///| +pub impl Node for Gain with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_loop`. +struct Loop { + id : UInt +} + +///| +pub impl Node for Loop with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_concat`. +struct Concat { + id : UInt +} + +///| +pub impl Node for Concat with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_pan`. +struct Pan { + id : UInt +} + +///| +pub impl Node for Pan with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_mute`. +struct Mute { + id : UInt +} + +///| +pub impl Node for Mute with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_pause`. +struct Pause { + id : UInt +} + +///| +pub impl Node for Pause with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_track_position`. +struct TrackPosition { + id : UInt +} + +///| +pub impl Node for TrackPosition with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_low_pass`. +struct LowPass { + id : UInt +} + +///| +pub impl Node for LowPass with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_high_pass`. +struct HighPass { + id : UInt +} + +///| +pub impl Node for HighPass with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_take_left`. +struct TakeLeft { + id : UInt +} + +///| +pub impl Node for TakeLeft with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_take_right`. +struct TakeRight { + id : UInt +} + +///| +pub impl Node for TakeRight with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_swap`. +struct Swap { + id : UInt +} + +///| +pub impl Node for Swap with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_clip`. +struct Clip { + id : UInt +} + +///| +pub impl Node for Clip with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_square`. +struct Square { + id : UInt +} + +///| +pub impl Node for Square with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_sawtooth`. +struct Sawtooth { + id : UInt +} + +///| +pub impl Node for Sawtooth with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_triangle`. +struct Triangle { + id : UInt +} + +///| +pub impl Node for Triangle with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_noise`. +struct Noise { + id : UInt +} + +///| +pub impl Node for Noise with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_empty`. +struct Empty { + id : UInt +} + +///| +pub impl Node for Empty with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_zero`. +struct Zero { + id : UInt +} + +///| +pub impl Node for Zero with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_file`. +struct File { + id : UInt +} + +///| +pub impl Node for File with fn id(self) -> UInt { + return self.id +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the gain level. +pub fn[Mod : Modulator] Gain::modulate(self : Gain, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the pan value (from 0. to 1.: 0. is only left, 1. is only right). +pub fn[Mod : Modulator] Pan::modulate(self : Pan, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the muted state. +/// +/// Below 0.5 is muted, above is unmuted. +pub fn[Mod : Modulator] Mute::modulate(self : Mute, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the paused state. +/// +/// Below 0.5 is paused, above is playing. +pub fn[Mod : Modulator] Pause::modulate(self : Pause, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the cut-off frequency. +pub fn[Mod : Modulator] LowPass::modulate(self : LowPass, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the cut-off frequency. +pub fn[Mod : Modulator] HighPass::modulate(self : HighPass, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// 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[Mod : Modulator] Clip::modulate_both(self : Clip, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate the low cut amplitude. +pub fn[Mod : Modulator] Clip::modulate_low(self : Clip, mod : Mod) -> Unit { + mod.modulate(self.id(), 1) +} + +///| +/// Modulate the high cut amplitude. +pub fn[Mod : Modulator] Clip::modulate_high(self : Clip, mod : Mod) -> Unit { + mod.modulate(self.id(), 2) +} From f8140597fadfe157eecc63e7581806e69a242991 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:18:56 +0200 Subject: [PATCH 4/8] add reset, rest_all, clear --- src/audio/node.mbt | 36 ++++++++++++++++++++++++++++++++++++ src/internal/ffi/audio.mbt | 6 +++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/audio/node.mbt b/src/audio/node.mbt index 1eb3808..4679ad8 100644 --- a/src/audio/node.mbt +++ b/src/audio/node.mbt @@ -1,3 +1,10 @@ +///| +pub trait SourceNode { + fn id(Self) -> UInt + /// Reset the node state to how it was when it was just added. + fn reset(Self) -> Unit = _ +} + ///| pub trait Node { fn id(Self) -> UInt @@ -47,6 +54,15 @@ pub trait Node { fn add_swap(Self) -> Swap = _ /// Add node clamping the input amplitude. Can be used for hard distortion. fn add_clip(Self, low : Float, high : Float) -> Clip = _ + /// Reset the node state to how it was when it was just added. + fn reset(Self) -> Unit = _ + /// Reset the node and all child nodes to the state to how it was when they were just added. + fn reset_all(Self) -> Unit = _ + /// Remove all child nodes. + /// + /// After it is called, you should make sure to discard all references to the + /// old child nodes. + fn clear(Self) -> Unit = _ } ///| @@ -171,3 +187,23 @@ impl Node with fn add_swap(self) -> Swap { impl Node with fn add_clip(self, low : Float, high : Float) -> Clip { Clip::{ id: @ffi.audio_add_clip(self.id(), low, high) } } + +///| +impl SourceNode with fn reset(self) { + @ffi.audio_reset(self.id()) +} + +///| +impl Node with fn reset(self) { + @ffi.audio_reset(self.id()) +} + +///| +impl Node with fn reset_all(self) { + @ffi.audio_reset_all(self.id()) +} + +///| +impl Node with fn clear(self) { + @ffi.audio_clear(self.id()) +} diff --git a/src/internal/ffi/audio.mbt b/src/internal/ffi/audio.mbt index 99f199c..e390baf 100644 --- a/src/internal/ffi/audio.mbt +++ b/src/internal/ffi/audio.mbt @@ -100,18 +100,18 @@ pub fn audio_add_clip(parent_id : UInt, low : Float, high : Float) -> UInt = "au ///| /// Reset the node state to how it was when it was just added. -pub fn audio_reset(node_id : UInt) -> Unit = "audio" "reset" +pub fn audio_reset(node_id : UInt) = "audio" "reset" ///| /// Reset the node and all child nodes to the state to how it was when they were just added. -pub fn audio_reset_all(node_id : UInt) -> Unit = "audio" "reset_all" +pub fn audio_reset_all(node_id : UInt) = "audio" "reset_all" ///| /// Remove all child nodes. /// /// After it is called, you should make sure to discard all references to the /// old child nodes. -pub fn audio_clear_node(node_id : UInt) -> Unit = "audio" "clear_node" +pub fn audio_clear(node_id : UInt) = "audio" "clear_node" ///| /// Linear (ramp up or down) envelope. From ec6d01a8b6d8a45b92205b3557541dae72e30f46 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:20:02 +0200 Subject: [PATCH 5/8] move node docs --- src/audio/node.mbt | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/audio/node.mbt b/src/audio/node.mbt index 4679ad8..e7eef22 100644 --- a/src/audio/node.mbt +++ b/src/audio/node.mbt @@ -1,106 +1,84 @@ ///| pub trait SourceNode { fn id(Self) -> UInt - /// Reset the node state to how it was when it was just added. fn reset(Self) -> Unit = _ } ///| pub trait Node { fn id(Self) -> UInt - /// Add sine wave oscillator source (`∿`). fn add_sine(Self, freq : Hz, phase : Float) -> Sine = _ - /// Add square wave oscillator source (`⎍`). fn add_square(Self, freq : Hz, phase : Float) -> Square = _ - /// Add sawtooth wave oscillator source (`╱│`). fn add_sawtooth(Self, freq : Hz, phase : Float) -> Sawtooth = _ - /// Add triangle wave oscillator source (`╱╲`). fn add_triangle(Self, freq : Hz, phase : Float) -> Triangle = _ - /// Add white noise source (amplitude on each tick is random). fn add_noise(Self, seed : Int) -> Noise = _ - /// Add always stopped source. fn add_empty(Self) -> Empty = _ - /// Add silent source producing zeros. fn add_zero(Self) -> Zero = _ - /// Add source playing audio from a file. fn add_file(Self, path : @firefly.Utf8View) -> File = _ - /// Add node simply mixing all inputs. fn add_mix(Self) -> Mix = _ - /// Add mixer node that stops if any of the sources stops. fn add_all_for_one(Self) -> AllForOne = _ - /// Add gain control node. fn add_gain(Self, level : Float) -> Gain = _ - /// Add a loop node that resets the input if it stops. fn add_loop(Self) -> Loop = _ - /// Add a node that plays the inputs one after the other, in the order as they added. fn add_concat(Self) -> Concat = _ - /// Add node panning the audio to the left (0.), right (1.), or something in between. fn add_pan(Self, level : Float) -> Pan = _ - /// Add node that can be muted using modulation. fn add_mute(Self) -> Mute = _ - /// Add node that can be paused using modulation. fn add_pause(Self) -> Pause = _ - /// Add node tracking the elapsed playback time. fn add_track_position(Self) -> TrackPosition = _ - /// Add lowpass filter node. fn add_low_pass(Self, freq : Hz, q : Float) -> LowPass = _ - /// Add highpass filter node. fn add_high_pass(Self, freq : Hz, q : Float) -> HighPass = _ - /// Add node converting stereo to mono by taking the left channel. fn add_take_left(Self) -> TakeLeft = _ - /// Add node converting stereo to mono by taking the right channel. fn add_take_right(Self) -> TakeRight = _ - /// Add node swapping left and right channels of the stereo input. fn add_swap(Self) -> Swap = _ - /// Add node clamping the input amplitude. Can be used for hard distortion. fn add_clip(Self, low : Float, high : Float) -> Clip = _ - /// Reset the node state to how it was when it was just added. fn reset(Self) -> Unit = _ - /// Reset the node and all child nodes to the state to how it was when they were just added. fn reset_all(Self) -> Unit = _ - /// Remove all child nodes. - /// - /// After it is called, you should make sure to discard all references to the - /// old child nodes. fn clear(Self) -> Unit = _ } ///| +/// Add sine wave oscillator source (`∿`). impl Node with fn add_sine(self, freq : Hz, phase : Float) -> Sine { Sine::{ id: @ffi.audio_add_sine(self.id(), freq, phase) } } ///| +/// Add square wave oscillator source (`⎍`). impl Node with fn add_square(self, freq : Hz, phase : Float) -> Square { Square::{ id: @ffi.audio_add_square(self.id(), freq, phase) } } ///| +/// Add sawtooth wave oscillator source (`╱│`). impl Node with fn add_sawtooth(self, freq : Hz, phase : Float) -> Sawtooth { Sawtooth::{ id: @ffi.audio_add_sawtooth(self.id(), freq, phase) } } ///| +/// Add triangle wave oscillator source (`╱╲`). impl Node with fn add_triangle(self, freq : Hz, phase : Float) -> Triangle { Triangle::{ id: @ffi.audio_add_triangle(self.id(), freq, phase) } } ///| +/// Add white noise source (amplitude on each tick is random). impl Node with fn add_noise(self, seed : Int) -> Noise { Noise::{ id: @ffi.audio_add_noise(self.id(), seed) } } ///| +/// Add always stopped source. impl Node with fn add_empty(self) -> Empty { Empty::{ id: @ffi.audio_add_empty(self.id()) } } ///| +/// Add silent source producing zeros. impl Node with fn add_zero(self) -> Zero { Zero::{ id: @ffi.audio_add_zero(self.id()) } } ///| +/// Add source playing audio from a file. impl Node with fn add_file(self, path : @firefly.Utf8View) -> File { let file = File::{ id: @ffi.audio_add_file( @@ -114,96 +92,118 @@ impl Node with fn add_file(self, path : @firefly.Utf8View) -> File { } ///| +/// Add node simply mixing all inputs. impl Node with fn add_mix(self) -> Mix { Mix::{ id: @ffi.audio_add_mix(self.id()) } } ///| +/// Add mixer node that stops if any of the sources stops. impl Node with fn add_all_for_one(self) -> AllForOne { AllForOne::{ id: @ffi.audio_add_all_for_one(self.id()) } } ///| +/// Add gain control node. impl Node with fn add_gain(self, level : Float) -> Gain { Gain::{ id: @ffi.audio_add_gain(self.id(), level) } } ///| +/// Add a loop node that resets the input if it stops. impl Node with fn add_loop(self) -> Loop { Loop::{ id: @ffi.audio_add_loop(self.id()) } } ///| +/// Add a node that plays the inputs one after the other, in the order as they added. impl Node with fn add_concat(self) -> Concat { Concat::{ id: @ffi.audio_add_concat(self.id()) } } ///| +/// Add node panning the audio to the left (0.), right (1.), or something in between. impl Node with fn add_pan(self, level : Float) -> Pan { Pan::{ id: @ffi.audio_add_pan(self.id(), level) } } ///| +/// Add node that can be muted using modulation. impl Node with fn add_mute(self) -> Mute { Mute::{ id: @ffi.audio_add_mute(self.id()) } } ///| +/// Add node that can be paused using modulation. impl Node with fn add_pause(self) -> Pause { Pause::{ id: @ffi.audio_add_pause(self.id()) } } ///| +/// Add node tracking the elapsed playback time. impl Node with fn add_track_position(self) -> TrackPosition { TrackPosition::{ id: @ffi.audio_add_track_position(self.id()) } } ///| +/// Add lowpass filter node. impl Node with fn add_low_pass(self, freq : Hz, q : Float) -> LowPass { LowPass::{ id: @ffi.audio_add_low_pass(self.id(), freq, q) } } ///| +/// Add highpass filter node. impl Node with fn add_high_pass(self, freq : Hz, q : Float) -> HighPass { HighPass::{ id: @ffi.audio_add_high_pass(self.id(), freq, q) } } ///| +/// Add node converting stereo to mono by taking the left channel. impl Node with fn add_take_left(self) -> TakeLeft { TakeLeft::{ id: @ffi.audio_add_take_left(self.id()) } } ///| +/// Add node converting stereo to mono by taking the right channel. impl Node with fn add_take_right(self) -> TakeRight { TakeRight::{ id: @ffi.audio_add_take_right(self.id()) } } ///| +/// Add node swapping left and right channels of the stereo input. impl Node with fn add_swap(self) -> Swap { Swap::{ id: @ffi.audio_add_swap(self.id()) } } ///| +/// Add node clamping the input amplitude. Can be used for hard distortion. impl Node with fn add_clip(self, low : Float, high : Float) -> Clip { Clip::{ id: @ffi.audio_add_clip(self.id(), low, high) } } ///| +/// Reset the node state to how it was when it was just added. impl SourceNode with fn reset(self) { @ffi.audio_reset(self.id()) } ///| +/// Reset the node state to how it was when it was just added. impl Node with fn reset(self) { @ffi.audio_reset(self.id()) } ///| +/// Reset the node and all child nodes to the state to how it was when they were just added. impl Node with fn reset_all(self) { @ffi.audio_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. impl Node with fn clear(self) { @ffi.audio_clear(self.id()) } From 36662cf60cd90d7fb4329dcdd8fbd61d9199775f Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:21:58 +0200 Subject: [PATCH 6/8] decouple source nodes --- src/audio/nodes.mbt | 112 ------------------------------------- src/audio/source_nodes.mbt | 111 ++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 112 deletions(-) create mode 100644 src/audio/source_nodes.mbt diff --git a/src/audio/nodes.mbt b/src/audio/nodes.mbt index b6f4eb0..887dc1a 100644 --- a/src/audio/nodes.mbt +++ b/src/audio/nodes.mbt @@ -10,17 +10,6 @@ pub impl Node for Out with fn id(_self) -> UInt { /// The output audio node. Mixes all inputs and plays them on the device's speaker. pub let out : Out = Out::{ } -///| -/// A marker for a specific node type. See `Node::add_sine`. -pub struct Sine { - id : UInt -} - -///| -pub impl Node for Sine with fn id(self) -> UInt { - return self.id -} - ///| /// A marker for a specific node type. See `Node::add_mix`. struct Mix { @@ -186,107 +175,6 @@ pub impl Node for Clip with fn id(self) -> UInt { return self.id } -///| -/// A marker for a specific node type. See `Node::add_square`. -struct Square { - id : UInt -} - -///| -pub impl Node for Square with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_sawtooth`. -struct Sawtooth { - id : UInt -} - -///| -pub impl Node for Sawtooth with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_triangle`. -struct Triangle { - id : UInt -} - -///| -pub impl Node for Triangle with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_noise`. -struct Noise { - id : UInt -} - -///| -pub impl Node for Noise with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_empty`. -struct Empty { - id : UInt -} - -///| -pub impl Node for Empty with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_zero`. -struct Zero { - id : UInt -} - -///| -pub impl Node for Zero with fn id(self) -> UInt { - return self.id -} - -///| -/// A marker for a specific node type. See `Node::add_file`. -struct File { - id : UInt -} - -///| -pub impl Node for File with fn id(self) -> UInt { - return self.id -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - -///| -/// Modulate oscillation frequency. -pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) -} - ///| /// Modulate the gain level. pub fn[Mod : Modulator] Gain::modulate(self : Gain, mod : Mod) -> Unit { diff --git a/src/audio/source_nodes.mbt b/src/audio/source_nodes.mbt new file mode 100644 index 0000000..a33f894 --- /dev/null +++ b/src/audio/source_nodes.mbt @@ -0,0 +1,111 @@ +///| +/// A marker for a specific node type. See `Node::add_sine`. +pub struct Sine { + id : UInt +} + +///| +pub impl SourceNode for Sine with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_square`. +struct Square { + id : UInt +} + +///| +pub impl SourceNode for Square with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_sawtooth`. +struct Sawtooth { + id : UInt +} + +///| +pub impl SourceNode for Sawtooth with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_triangle`. +struct Triangle { + id : UInt +} + +///| +pub impl SourceNode for Triangle with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_noise`. +struct Noise { + id : UInt +} + +///| +pub impl SourceNode for Noise with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_empty`. +struct Empty { + id : UInt +} + +///| +pub impl SourceNode for Empty with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_zero`. +struct Zero { + id : UInt +} + +///| +pub impl SourceNode for Zero with fn id(self) -> UInt { + return self.id +} + +///| +/// A marker for a specific node type. See `Node::add_file`. +struct File { + id : UInt +} + +///| +pub impl SourceNode for File with fn id(self) -> UInt { + return self.id +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} + +///| +/// Modulate oscillation frequency. +pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { + mod.modulate(self.id(), 0) +} From 8c8ce46650574768845e3a55630ff88eaaec272f Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:24:15 +0200 Subject: [PATCH 7/8] fix naming --- src/internal/ffi/audio.mbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/internal/ffi/audio.mbt b/src/internal/ffi/audio.mbt index e390baf..bf0260b 100644 --- a/src/internal/ffi/audio.mbt +++ b/src/internal/ffi/audio.mbt @@ -100,18 +100,18 @@ pub fn audio_add_clip(parent_id : UInt, low : Float, high : Float) -> UInt = "au ///| /// Reset the node state to how it was when it was just added. -pub fn audio_reset(node_id : UInt) = "audio" "reset" +pub fn audio_reset(node_id : UInt) -> Unit = "audio" "reset" ///| /// Reset the node and all child nodes to the state to how it was when they were just added. -pub fn audio_reset_all(node_id : UInt) = "audio" "reset_all" +pub fn audio_reset_all(node_id : UInt) -> Unit = "audio" "reset_all" ///| /// Remove all child nodes. /// /// After it is called, you should make sure to discard all references to the /// old child nodes. -pub fn audio_clear(node_id : UInt) = "audio" "clear_node" +pub fn audio_clear(node_id : UInt) -> Unit = "audio" "clear" ///| /// Linear (ramp up or down) envelope. From e44b9ec23eb787db44ba2dac1f698230d0008652 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 20:25:08 +0200 Subject: [PATCH 8/8] fix REUSE --- REUSE.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/REUSE.toml b/REUSE.toml index 5a07fdd..d292269 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,6 +10,6 @@ SPDX-FileCopyrightText = "2025 Kalle Fagerberg" SPDX-License-Identifier = "MIT" [[annotations]] -path = ["**/moon.mod", "example/moon.work", "**/moon.pkg.json", "**/moon.pkg"] +path = ["**/moon.mod", "**/moon.work", "**/moon.pkg.json", "**/moon.pkg"] SPDX-FileCopyrightText = "2025 Kalle Fagerberg" SPDX-License-Identifier = "CC0-1.0"