use crate::{
utils::{Lfo, LfoWaveform},
AudioEffect,
};
#[derive(Debug, Clone)]
pub struct TremoloConfig {
pub rate: f32,
pub depth: f32,
pub waveform: LfoWaveform,
}
impl Default for TremoloConfig {
fn default() -> Self {
Self {
rate: 5.0,
depth: 0.5,
waveform: LfoWaveform::Sine,
}
}
}
pub struct StereoTremolo {
lfo_l: Lfo,
lfo_r: Lfo,
config: TremoloConfig,
}
impl StereoTremolo {
#[must_use]
pub fn new(config: TremoloConfig, sample_rate: f32, stereo_phase: f32) -> Self {
let lfo_l = Lfo::new(config.rate, sample_rate, config.waveform);
let mut lfo_r = Lfo::new(config.rate, sample_rate, config.waveform);
lfo_r.set_phase(stereo_phase);
Self {
lfo_l,
lfo_r,
config,
}
}
pub fn set_rate(&mut self, rate: f32) {
self.config.rate = rate;
self.lfo_l.set_frequency(rate);
self.lfo_r.set_frequency(rate);
}
pub fn set_depth(&mut self, depth: f32) {
self.config.depth = depth.clamp(0.0, 1.0);
}
fn process_sample_internal(&mut self, input_l: f32, input_r: f32) -> (f32, f32) {
let mod_l = self.lfo_l.next_unipolar();
let mod_r = self.lfo_r.next_unipolar();
let gain_l = 1.0 - self.config.depth + mod_l * self.config.depth;
let gain_r = 1.0 - self.config.depth + mod_r * self.config.depth;
(input_l * gain_l, input_r * gain_r)
}
}
impl AudioEffect for StereoTremolo {
const EFFECT_ID: &'static str = "stereo_tremolo";
fn process_sample(&mut self, input: f32) -> f32 {
let (left, _) = self.process_sample_internal(input, input);
left
}
fn process_sample_stereo(&mut self, left: f32, right: f32) -> (f32, f32) {
self.process_sample_internal(left, right)
}
fn reset(&mut self) {
self.lfo_l.reset();
self.lfo_r.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tremolo() {
let config = TremoloConfig::default();
let mut tremolo = StereoTremolo::new(config, 48000.0, 0.0);
let output = tremolo.process_sample(1.0);
assert!(output.is_finite());
}
}