#[repr(C)]pub struct Param {
pub current: f32,
pub target: f32,
pub step: f32,
}Expand description
A single smoothed parameter.
Provides sample-accurate parameter automation with linear ramping.
Parameters smoothly transition from current to target over a
specified number of samples, preventing audio clicks and zipper noise.
§Real-Time Safety
- ✅ No allocation
- ✅ No locks
- ✅ Bounded execution time
- ✅ Safe to use in audio thread
§Example
use aether_core::param::Param;
let mut gain = Param::new(0.5);
// Schedule ramp to 1.0 over 480 samples (10ms @ 48kHz)
gain.set_target(1.0, 480);
// Tick through samples
for _ in 0..480 {
let value = gain.current;
// Use value for processing...
gain.tick();
}
// Close enough to 1.0 (floating point precision)
assert!((gain.current - 1.0).abs() < 0.0001);§Performance
- Fast path when not ramping (step == 0.0)
- SIMD-friendly linear interpolation
- Automatic overshoot clamping
§See Also
ParamBlock- Collection of parameters for a nodeParam::fill_buffer- Efficient buffer filling
Fields§
§current: f32§target: f32§step: f32Per-sample increment. Set by set_target.
Implementations§
Source§impl Param
impl Param
Sourcepub fn new(value: f32) -> Self
pub fn new(value: f32) -> Self
Creates a new parameter with the given initial value.
The parameter starts at the specified value with no ramping (current == target, step == 0.0).
§Arguments
value- Initial parameter value
§Example
use aether_core::param::Param;
let gain = Param::new(0.75);
assert_eq!(gain.current, 0.75);
assert_eq!(gain.target, 0.75);
assert_eq!(gain.step, 0.0);Sourcepub fn set_target(&mut self, target: f32, ramp_samples: u32)
pub fn set_target(&mut self, target: f32, ramp_samples: u32)
Schedule a ramp to target over ramp_samples samples.
Sets up linear interpolation from current value to target value.
Call from the control thread before pushing an UpdateParam command.
§Arguments
target- Target value to ramp towardsramp_samples- Number of samples for the ramp (0 = instant)
§Example
use aether_core::param::Param;
let mut cutoff = Param::new(1000.0);
// Ramp to 5000 Hz over 960 samples (20ms @ 48kHz)
cutoff.set_target(5000.0, 960);
// After 480 samples, we're halfway
for _ in 0..480 {
cutoff.tick();
}
assert!((cutoff.current - 3000.0).abs() < 1.0);
// After 960 samples total, we've reached the target
for _ in 0..480 {
cutoff.tick();
}
assert!((cutoff.current - 5000.0).abs() < 0.01);§Instant Changes
use aether_core::param::Param;
let mut gain = Param::new(0.5);
// Instant change (0 samples)
gain.set_target(1.0, 0);
assert_eq!(gain.current, 1.0);
assert_eq!(gain.step, 0.0);Sourcepub fn tick(&mut self)
pub fn tick(&mut self)
Advance by one sample. Call once per sample in the RT loop.
Updates current by adding step. When the target is reached,
automatically stops ramping by setting step to 0.0.
§Example
use aether_core::param::Param;
let mut gain = Param::new(0.0);
gain.set_target(1.0, 100);
// Tick through 100 samples
for i in 0..100 {
gain.tick();
}
// Reached target value
assert!((gain.current - 1.0).abs() < 0.0001);§Performance
This function is highly optimized for the audio thread:
- Inlined for zero call overhead
- Branch-free when not ramping
- Automatic overshoot clamping
Sourcepub fn fill_buffer(&mut self, out: &mut [f32])
pub fn fill_buffer(&mut self, out: &mut [f32])
Advance by a full buffer, returning per-sample values into out.
Efficiently fills a buffer with parameter values, advancing the ramp for each sample. Uses a fast path when the parameter is stable (not ramping).
§Arguments
out- Output buffer to fill with parameter values
§Example
use aether_core::param::Param;
use aether_core::BUFFER_SIZE;
let mut cutoff = Param::new(1000.0);
cutoff.set_target(2000.0, BUFFER_SIZE as u32);
let mut buffer = [0.0f32; BUFFER_SIZE];
cutoff.fill_buffer(&mut buffer);
// First sample is near 1000, last sample is near 2000
assert!((buffer[0] - 1000.0).abs() < 50.0);
assert!((buffer[BUFFER_SIZE-1] - 2000.0).abs() < 50.0);§Performance
This function has two paths:
- Fast path (step == 0.0): Fills buffer with single value (SIMD-friendly)
- Ramp path (step != 0.0): Advances sample-by-sample
The fast path is taken 90%+ of the time in typical usage.
§Use Case
Use this when you need per-sample parameter values for modulation:
use aether_core::param::Param;
use aether_core::BUFFER_SIZE;
let mut gain = Param::new(0.5);
let mut gain_buffer = [0.0f32; BUFFER_SIZE];
let input = [1.0f32; BUFFER_SIZE];
let mut output = [0.0f32; BUFFER_SIZE];
// Fill gain buffer
gain.fill_buffer(&mut gain_buffer);
// Apply per-sample gain
for i in 0..BUFFER_SIZE {
output[i] = input[i] * gain_buffer[i];
}Trait Implementations§
Auto Trait Implementations§
impl Freeze for Param
impl RefUnwindSafe for Param
impl Send for Param
impl Sync for Param
impl Unpin for Param
impl UnsafeUnpin for Param
impl UnwindSafe for Param
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more