use sim_lib_audio_graph_core::{BlockEvent, PrepareConfig, ProcessBlock, Processor};
use crate::common::{input_sample, output_channels};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SmoothValue {
current: f32,
target: f32,
step: f32,
remaining: u32,
}
impl SmoothValue {
pub fn new(value: f32) -> Self {
Self {
current: value,
target: value,
step: 0.0,
remaining: 0,
}
}
pub fn current(&self) -> f32 {
self.current
}
pub fn target(&self) -> f32 {
self.target
}
pub fn set_target(&mut self, target: f32, samples: u32) {
self.target = target;
if samples == 0 {
self.current = target;
self.step = 0.0;
self.remaining = 0;
return;
}
self.remaining = samples;
self.step = (target - self.current) / samples as f32;
}
pub fn next_sample(&mut self) -> f32 {
if self.remaining > 0 {
self.current += self.step;
self.remaining -= 1;
if self.remaining == 0 {
self.current = self.target;
}
}
self.current
}
pub fn reset(&mut self, value: f32) {
*self = Self::new(value);
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SmoothedGain {
gain: SmoothValue,
ramp_ms: f32,
param_id: u32,
sample_rate_hz: f32,
}
impl SmoothedGain {
pub fn new(initial_gain: f32, ramp_ms: f32) -> Self {
Self {
gain: SmoothValue::new(initial_gain),
ramp_ms,
param_id: 0,
sample_rate_hz: 48_000.0,
}
}
pub fn with_param(mut self, param_id: u32) -> Self {
self.param_id = param_id;
self
}
fn ramp_samples(&self) -> u32 {
((self.sample_rate_hz * self.ramp_ms.max(0.0)) / 1000.0).round() as u32
}
}
impl Processor for SmoothedGain {
fn prepare(&mut self, cfg: PrepareConfig) {
self.sample_rate_hz = cfg.sample_rate_hz as f32;
}
fn reset(&mut self) {
let value = self.gain.target();
self.gain.reset(value);
}
fn process(&mut self, block: &mut ProcessBlock<'_>) {
let frames = block.frames as usize;
let channels = output_channels(block);
for frame in 0..frames {
for event in block.in_events {
if let BlockEvent::ParamSet {
offset,
param,
value,
} = *event
&& offset as usize == frame
&& param == self.param_id
{
self.gain.set_target(value as f32, self.ramp_samples());
}
}
let gain = self.gain.next_sample();
for channel in 0..channels {
block.out_audio[channel][frame] = input_sample(block, channel, frame) * gain;
}
}
}
}