use super::*;
use rustfft::num_complex::Complex;
#[derive(Clone, Debug)]
pub struct SpectralDelay {
stft: STFT,
sample_rate: f32,
hop_size: usize,
delay_time: f32, feedback: f32, frequency_scale: f32, mix: f32, delay_buffers: Vec<Vec<Complex<f32>>>, write_positions: Vec<usize>, max_delay_frames: usize, enabled: bool,
}
impl SpectralDelay {
pub fn new(fft_size: usize, hop_size: usize, sample_rate: f32) -> Self {
Self::with_params(fft_size, hop_size, sample_rate, 100.0, 0.3, 0.0, 0.5)
}
pub fn with_params(
fft_size: usize,
hop_size: usize,
sample_rate: f32,
delay_time: f32,
feedback: f32,
frequency_scale: f32,
mix: f32,
) -> Self {
let stft = STFT::new(fft_size, hop_size, WindowType::Hann);
let num_bins = fft_size / 2 + 1;
let max_delay_ms = 2000.0;
let frames_per_ms = sample_rate / (hop_size as f32 * 1000.0);
let max_delay_frames = (max_delay_ms * frames_per_ms).ceil() as usize;
let delay_buffers = vec![vec![Complex::new(0.0, 0.0); max_delay_frames]; num_bins];
let write_positions = vec![0; num_bins];
Self {
stft,
sample_rate,
hop_size,
delay_time: delay_time.clamp(0.0, max_delay_ms),
feedback: feedback.clamp(0.0, 1.0),
frequency_scale: frequency_scale.clamp(-1.0, 1.0),
mix: mix.clamp(0.0, 1.0),
delay_buffers,
write_positions,
max_delay_frames,
enabled: true,
}
}
pub fn set_delay_time(&mut self, delay_time: f32) {
let max_delay_ms = 2000.0;
self.delay_time = delay_time.clamp(0.0, max_delay_ms);
}
pub fn delay_time(&self) -> f32 {
self.delay_time
}
pub fn set_feedback(&mut self, feedback: f32) {
self.feedback = feedback.clamp(0.0, 1.0);
}
pub fn feedback(&self) -> f32 {
self.feedback
}
pub fn set_frequency_scale(&mut self, scale: f32) {
self.frequency_scale = scale.clamp(-1.0, 1.0);
}
pub fn frequency_scale(&self) -> f32 {
self.frequency_scale
}
pub fn set_mix(&mut self, mix: f32) {
self.mix = mix.clamp(0.0, 1.0);
}
pub fn mix(&self) -> f32 {
self.mix
}
pub fn process(&mut self, output: &mut [f32], input: &[f32]) {
if !self.enabled {
output.copy_from_slice(input);
return;
}
self.stft.add_input(input);
let sample_rate = self.sample_rate;
let hop_size = self.hop_size;
let delay_time = self.delay_time;
let feedback = self.feedback;
let frequency_scale = self.frequency_scale;
let mix = self.mix;
let max_delay_frames = self.max_delay_frames;
let delay_buffers = &mut self.delay_buffers;
let write_positions = &mut self.write_positions;
self.stft.process(output, |spectrum| {
let num_bins = spectrum.len();
let frames_per_ms = sample_rate / (hop_size as f32 * 1000.0);
for (bin_idx, bin) in spectrum.iter_mut().enumerate() {
if bin_idx >= delay_buffers.len() || bin_idx >= write_positions.len() {
continue;
}
let normalized_freq = bin_idx as f32 / num_bins as f32;
let freq_multiplier = 1.0 + frequency_scale * normalized_freq;
let bin_delay_ms = delay_time * freq_multiplier;
let delay_frames = ((bin_delay_ms * frames_per_ms).round() as usize)
.min(max_delay_frames - 1)
.max(1);
let write_pos = write_positions[bin_idx];
let read_pos = if write_pos >= delay_frames {
write_pos - delay_frames
} else {
max_delay_frames - (delay_frames - write_pos)
};
let delayed_sample = delay_buffers[bin_idx][read_pos];
let dry = *bin;
let wet = delayed_sample;
let mixed = Complex::new(
dry.re * (1.0 - mix) + wet.re * mix,
dry.im * (1.0 - mix) + wet.im * mix,
);
let feedback_signal =
Complex::new(dry.re + wet.re * feedback, dry.im + wet.im * feedback);
delay_buffers[bin_idx][write_pos] = feedback_signal;
write_positions[bin_idx] = (write_pos + 1) % max_delay_frames;
*bin = mixed;
}
});
}
pub fn clear_buffers(&mut self) {
for buffer in &mut self.delay_buffers {
buffer.fill(Complex::new(0.0, 0.0));
}
self.write_positions.fill(0);
}
pub fn reset(&mut self) {
self.stft.reset();
self.clear_buffers();
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spectral_delay_creation() {
let delay = SpectralDelay::new(2048, 512, 44100.0);
assert_eq!(delay.delay_time(), 100.0);
assert_eq!(delay.feedback(), 0.3);
assert_eq!(delay.frequency_scale(), 0.0);
assert_eq!(delay.mix(), 0.5);
assert!(delay.is_enabled());
}
#[test]
fn test_spectral_delay_with_params() {
let delay = SpectralDelay::with_params(2048, 512, 44100.0, 200.0, 0.5, 0.8, 0.7);
assert_eq!(delay.delay_time(), 200.0);
assert_eq!(delay.feedback(), 0.5);
assert_eq!(delay.frequency_scale(), 0.8);
assert_eq!(delay.mix(), 0.7);
}
#[test]
fn test_spectral_delay_set_delay_time() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
delay.set_delay_time(500.0);
assert_eq!(delay.delay_time(), 500.0);
delay.set_delay_time(-10.0);
assert_eq!(delay.delay_time(), 0.0);
delay.set_delay_time(3000.0);
assert_eq!(delay.delay_time(), 2000.0); }
#[test]
fn test_spectral_delay_set_feedback() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
delay.set_feedback(0.8);
assert_eq!(delay.feedback(), 0.8);
delay.set_feedback(-0.1);
assert_eq!(delay.feedback(), 0.0);
delay.set_feedback(1.5);
assert_eq!(delay.feedback(), 1.0);
}
#[test]
fn test_spectral_delay_set_frequency_scale() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
delay.set_frequency_scale(0.5);
assert_eq!(delay.frequency_scale(), 0.5);
delay.set_frequency_scale(-0.5);
assert_eq!(delay.frequency_scale(), -0.5);
delay.set_frequency_scale(-2.0);
assert_eq!(delay.frequency_scale(), -1.0);
delay.set_frequency_scale(2.0);
assert_eq!(delay.frequency_scale(), 1.0);
}
#[test]
fn test_spectral_delay_set_mix() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
delay.set_mix(0.75);
assert_eq!(delay.mix(), 0.75);
delay.set_mix(-0.1);
assert_eq!(delay.mix(), 0.0);
delay.set_mix(1.5);
assert_eq!(delay.mix(), 1.0);
}
#[test]
fn test_spectral_delay_process_silent() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
let input = vec![0.0; 512];
let mut output = vec![0.0; 512];
delay.process(&mut output, &input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy < 1e-6);
}
#[test]
fn test_spectral_delay_process_with_signal() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_delay_time(50.0);
delay.set_feedback(0.3);
delay.set_mix(0.5);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
delay.process(&mut output, &input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
}
#[test]
fn test_spectral_delay_disabled() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
delay.set_enabled(false);
let input = vec![0.5; 512];
let mut output = vec![0.0; 512];
delay.process(&mut output, &input);
for i in 0..512 {
assert_eq!(output[i], input[i]);
}
}
#[test]
fn test_spectral_delay_clear_buffers() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
let input = vec![0.5; 2048];
let mut output = vec![0.0; 2048];
for _ in 0..10 {
delay.process(&mut output, &input);
}
delay.clear_buffers();
let silent_input = vec![0.0; 2048];
delay.process(&mut output, &silent_input);
}
#[test]
fn test_spectral_delay_reset() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
let input = vec![0.5; 512];
let mut output = vec![0.0; 512];
delay.process(&mut output, &input);
delay.reset();
let silent_input = vec![0.0; 512];
delay.process(&mut output, &silent_input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy < 0.1);
}
#[test]
fn test_spectral_delay_enable_disable() {
let mut delay = SpectralDelay::new(512, 128, 44100.0);
assert!(delay.is_enabled());
delay.set_enabled(false);
assert!(!delay.is_enabled());
delay.set_enabled(true);
assert!(delay.is_enabled());
}
#[test]
fn test_spectral_delay_zero_mix() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_mix(0.0);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
for _ in 0..5 {
delay.process(&mut output, &input);
}
let input_energy: f32 = input.iter().map(|x| x * x).sum();
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
assert!(output_energy > input_energy * 0.1); }
#[test]
fn test_spectral_delay_full_mix() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_mix(1.0); delay.set_delay_time(100.0);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
delay.process(&mut output, &input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
}
#[test]
fn test_spectral_delay_frequency_scale_shimmer_up() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_frequency_scale(1.0); delay.set_delay_time(100.0);
delay.set_mix(0.8);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
delay.process(&mut output, &input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
}
#[test]
fn test_spectral_delay_frequency_scale_shimmer_down() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_frequency_scale(-1.0); delay.set_delay_time(100.0);
delay.set_mix(0.8);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 220.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
delay.process(&mut output, &input);
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
}
#[test]
fn test_spectral_delay_with_feedback() {
let mut delay = SpectralDelay::new(2048, 512, 44100.0);
delay.set_delay_time(50.0);
delay.set_feedback(0.7); delay.set_mix(0.5);
let mut input = vec![0.0; 2048];
for i in 0..2048 {
input[i] = (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin() * 0.5;
}
let mut output = vec![0.0; 2048];
for _ in 0..10 {
delay.process(&mut output, &input);
}
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(output_energy > 0.0);
}
}