use crate::{
utils::{FractionalDelayLine, InterpolationMode},
AudioEffect,
};
#[derive(Debug, Clone)]
pub struct TimeStretchConfig {
pub rate: f32,
}
impl Default for TimeStretchConfig {
fn default() -> Self {
Self { rate: 1.0 }
}
}
pub struct TimeStretcher {
delay: FractionalDelayLine,
read_pos: f32,
config: TimeStretchConfig,
}
impl TimeStretcher {
#[must_use]
pub fn new(config: TimeStretchConfig, sample_rate: f32) -> Self {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let delay_size = (sample_rate as usize) * 2;
Self {
delay: FractionalDelayLine::new(delay_size, InterpolationMode::Linear),
read_pos: 0.0,
config,
}
}
pub fn set_rate(&mut self, rate: f32) {
self.config.rate = rate.clamp(0.25, 4.0);
}
}
impl AudioEffect for TimeStretcher {
fn process_sample(&mut self, input: f32) -> f32 {
self.delay.write(input);
let output = self.delay.read(self.read_pos);
self.read_pos += self.config.rate;
if self.read_pos >= 10000.0 {
self.read_pos -= 10000.0;
}
output
}
fn reset(&mut self) {
self.delay.clear();
self.read_pos = 0.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_time_stretcher() {
let config = TimeStretchConfig::default();
let mut stretcher = TimeStretcher::new(config, 48000.0);
let output = stretcher.process_sample(0.5);
assert!(output.is_finite());
}
}