From 4eeca1c17aa10358e9580c4c8cdb5031ecfcb99f Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 21:35:27 +0200 Subject: [PATCH 1/2] new modulation API --- src/audio/modulators.mbt | 22 +++++------ src/audio/nodes.mbt | 81 +++++++++++++++++++++++++++++--------- src/audio/source_nodes.mbt | 36 +++++++++++++---- 3 files changed, 102 insertions(+), 37 deletions(-) diff --git a/src/audio/modulators.mbt b/src/audio/modulators.mbt index 0a763f9..8685293 100644 --- a/src/audio/modulators.mbt +++ b/src/audio/modulators.mbt @@ -1,6 +1,6 @@ ///| pub trait Modulator { - fn modulate(Self, node_id : UInt, param : UInt) -> Unit + fn modulate(Self, node_id : UInt, param : UInt, low : Float, high : Float) -> Unit } ///| @@ -12,8 +12,6 @@ pub trait Modulator { /// and the value between `start_at` and `end_at` changes linearly from `start` to `end`. #valtype pub(all) struct LinearModulator { - start : Float - end : Float start_at : Time end_at : Time } @@ -24,12 +22,14 @@ pub impl Modulator for LinearModulator with fn modulate( self : LinearModulator, node_id : UInt, param : UInt, + low : Float, + high : Float, ) -> Unit { @ffi.audio_mod_linear( node_id, param, - self.start, - self.end, + low, + high, self.start_at.0, self.end_at.0, ) @@ -44,8 +44,6 @@ pub impl Modulator for LinearModulator with fn modulate( /// Equivalent to [`LinearModulator`] with `start_at` being equal to `end_at`. #valtype pub(all) struct HoldModulator { - before : Float - after : Float time : Time } @@ -55,8 +53,10 @@ pub impl Modulator for HoldModulator with fn modulate( self : HoldModulator, node_id : UInt, param : UInt, + low : Float, + high : Float, ) -> Unit { - @ffi.audio_mod_hold(node_id, param, self.before, self.after, self.time.0) + @ffi.audio_mod_hold(node_id, param, low, high, self.time.0) } ///| @@ -68,8 +68,6 @@ pub impl Modulator for HoldModulator with fn modulate( #valtype pub(all) struct SineModulator { freq : Hz - low : Float - high : Float } ///| @@ -78,6 +76,8 @@ pub impl Modulator for SineModulator with fn modulate( self : SineModulator, node_id : UInt, param : UInt, + low : Float, + high : Float, ) -> Unit { - @ffi.audio_mod_sine(node_id, param, self.freq, self.low, self.high) + @ffi.audio_mod_sine(node_id, param, self.freq, low, high) } diff --git a/src/audio/nodes.mbt b/src/audio/nodes.mbt index 887dc1a..084edd3 100644 --- a/src/audio/nodes.mbt +++ b/src/audio/nodes.mbt @@ -177,60 +177,105 @@ pub impl Node for Clip with fn id(self) -> UInt { ///| /// Modulate the gain level. -pub fn[Mod : Modulator] Gain::modulate(self : Gain, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] Gain::modulate( + self : Gain, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// 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) +pub fn[Mod : Modulator] Pan::modulate( + self : Pan, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// 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) +pub fn[Mod : Modulator] Mute::modulate( + self : Mute, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// 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) +pub fn[Mod : Modulator] Pause::modulate( + self : Pause, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate the cut-off frequency. -pub fn[Mod : Modulator] LowPass::modulate(self : LowPass, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] LowPass::modulate( + self : LowPass, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate the cut-off frequency. -pub fn[Mod : Modulator] HighPass::modulate(self : HighPass, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] HighPass::modulate( + self : HighPass, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// 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) +pub fn[Mod : Modulator] Clip::modulate_both( + self : Clip, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate the low cut amplitude. -pub fn[Mod : Modulator] Clip::modulate_low(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.id(), 1) +pub fn[Mod : Modulator] Clip::modulate_low( + self : Clip, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 1, low, high) } ///| /// Modulate the high cut amplitude. -pub fn[Mod : Modulator] Clip::modulate_high(self : Clip, mod : Mod) -> Unit { - mod.modulate(self.id(), 2) +pub fn[Mod : Modulator] Clip::modulate_high( + self : Clip, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 2, low, high) } diff --git a/src/audio/source_nodes.mbt b/src/audio/source_nodes.mbt index a33f894..368c943 100644 --- a/src/audio/source_nodes.mbt +++ b/src/audio/source_nodes.mbt @@ -88,24 +88,44 @@ pub impl SourceNode for File with fn id(self) -> UInt { ///| /// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sine::modulate(self : Sine, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] Sine::modulate( + self : Sine, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate oscillation frequency. -pub fn[Mod : Modulator] Square::modulate(self : Square, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] Square::modulate( + self : Square, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate oscillation frequency. -pub fn[Mod : Modulator] Sawtooth::modulate(self : Sawtooth, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] Sawtooth::modulate( + self : Sawtooth, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } ///| /// Modulate oscillation frequency. -pub fn[Mod : Modulator] Triangle::modulate(self : Triangle, mod : Mod) -> Unit { - mod.modulate(self.id(), 0) +pub fn[Mod : Modulator] Triangle::modulate( + self : Triangle, + low : Float, + high : Float, + mod : Mod, +) -> Unit { + mod.modulate(self.id(), 0, low, high) } From 44c58d45418123c052148c9baf369668b1d99956 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 22 Jun 2026 21:50:46 +0200 Subject: [PATCH 2/2] more modulators --- src/audio/modulators.mbt | 90 ++++++++++++++++++++++++++++++++++++++ src/internal/ffi/audio.mbt | 31 +++++++++++++ 2 files changed, 121 insertions(+) diff --git a/src/audio/modulators.mbt b/src/audio/modulators.mbt index 8685293..ba39d4b 100644 --- a/src/audio/modulators.mbt +++ b/src/audio/modulators.mbt @@ -59,6 +59,54 @@ pub impl Modulator for HoldModulator with fn modulate( @ffi.audio_mod_hold(node_id, param, low, high, self.time.0) } +///| +/// ADSR envelope. +/// +/// It looks like this: `🭋🭍🬹🬿` +/// +/// 1. Until `attack`, the value goes from 0 to 1; +/// 2. Until `decay`, it goes from 1 to `sustain_level`; +/// 3. Until `sustain`, it holds `sustain_level`; +/// 4. Until `release`, it goes from `sustain_level` to 0; +/// 5. After `release`, it holds 0. +/// +/// Most commonly used with `Gain`. +#valtype +pub(all) struct AdsrModulator { + /// When the value reaches 1. + attack : Time + /// When the value reaches `sustain_level`. + decay : Time + /// Until when the value holds `sustain_level`. + sustain : Time + /// The value generated from `decay` until `sustain`. + sustain_level : Float + /// When the value drops to 0. + release : Time +} + +///| +#inline +pub impl Modulator for AdsrModulator with fn modulate( + self : AdsrModulator, + node_id : UInt, + param : UInt, + low : Float, + high : Float, +) -> Unit { + @ffi.audio_mod_adsr( + node_id, + param, + low, + high, + self.attack.0, + self.decay.0, + self.sustain.0, + self.sustain_level, + self.release.0, + ) +} + ///| /// Sine wave low-frequency oscillator. /// @@ -81,3 +129,45 @@ pub impl Modulator for SineModulator with fn modulate( ) -> Unit { @ffi.audio_mod_sine(node_id, param, self.freq, low, high) } + +///| +/// Square wave low-frequency oscillator. +/// +/// It looks like this: `🭿🭾🭿🭾🭿🭾🭿🭾`. +#valtype +pub(all) struct SquareModulator { + period : Time +} + +///| +#inline +pub impl Modulator for SquareModulator with fn modulate( + self : SquareModulator, + node_id : UInt, + param : UInt, + low : Float, + high : Float, +) -> Unit { + @ffi.audio_mod_square(node_id, param, low, high, self.period.0) +} + +///| +/// Sawtooth wave low-frequency oscillator. +/// +/// It looks like this: `╱│╱│╱│╱│`. +#valtype +pub(all) struct SawtoothModulator { + period : Time +} + +///| +#inline +pub impl Modulator for SawtoothModulator with fn modulate( + self : SawtoothModulator, + node_id : UInt, + param : UInt, + low : Float, + high : Float, +) -> Unit { + @ffi.audio_mod_sawtooth(node_id, param, low, high, self.period.0) +} diff --git a/src/internal/ffi/audio.mbt b/src/internal/ffi/audio.mbt index bf0260b..2993aa6 100644 --- a/src/internal/ffi/audio.mbt +++ b/src/internal/ffi/audio.mbt @@ -144,6 +144,19 @@ pub fn audio_mod_hold( time : UInt, ) = "audio" "mod_hold" +///| +pub fn audio_mod_adsr( + node_id : UInt, + param : UInt, + low : Float, + high : Float, + attack : UInt, + decay : UInt, + sustain : UInt, + sustain_level : Float, + release : UInt, +) = "audio" "mod_adsr" + ///| /// Sine wave low-frequency oscillator. /// @@ -157,3 +170,21 @@ pub fn audio_mod_sine( low : Float, high : Float, ) = "audio" "mod_sine" + +///| +pub fn audio_mod_square( + node_id : UInt, + param : UInt, + low : Float, + high : Float, + period : UInt, +) = "audio" "mod_square" + +///| +pub fn audio_mod_sawtooth( + node_id : UInt, + param : UInt, + low : Float, + high : Float, + period : UInt, +) = "audio" "mod_sawtooth"