use nih_plug::params::enums::Enum; use serde::{Deserialize, Serialize}; // Rust port of https://www.musicdsp.org/en/latest/Filters/24-moog-vcf.html // Ardura #[derive(Enum, PartialEq, Serialize, Deserialize, Clone)] pub enum ResponseType { Lowpass, Bandpass, Highpass, } pub struct VCFilter { // Parameters center_freq: f32, resonance: f32, shape: ResponseType, // Internal f: f32, k: f32, p: f32, r: f32, olds: [f32; 4], y: [f32; 4], sample_rate: f32, } impl VCFilter { pub fn new() -> Self { VCFilter { center_freq: 1000.0, resonance: 0.0, shape: ResponseType::Lowpass, f: 0.0, k: 0.0, p: 0.0, r: 0.0, olds: [0.0; 4], y: [0.0; 4], sample_rate: 44100.0, } } pub fn update( &mut self, center_freq: f32, resonance: f32, shape: ResponseType, sample_rate: f32, ) { let mut recalculate = false; if self.center_freq != center_freq { self.center_freq = center_freq; recalculate = true; } if self.resonance != resonance { self.resonance = resonance; recalculate = true; } if self.shape != shape { self.shape = shape; } if self.sample_rate != sample_rate { self.sample_rate = sample_rate; recalculate = true; } if recalculate { self.f = 2.0 * self.center_freq / self.sample_rate; self.k = 3.6 * self.f - 1.6 * self.f * self.f - 1.0; self.p = (self.k + 1.0) * 0.5; //let scale = (1.0 - self.p).exp() * 1.386249; let scale = (1.0 - self.p).exp() * 0.9; self.r = (1.01 - self.resonance) * scale; } } pub fn process(&mut self, input: f32) -> f32 { let x = input - self.r * self.y[3]; self.y[0] = x * self.p + self.olds[0] * self.p - self.k * self.y[0]; self.y[1] = self.y[0] * self.p + self.olds[1] * self.p - self.k * self.y[1]; self.y[2] = self.y[1] * self.p + self.olds[2] * self.p - self.k * self.y[2]; self.y[3] = self.y[2] * self.p + self.olds[3] * self.p - self.k * self.y[3]; self.y[3] = self.y[3] - (self.y[3].powf(3.0)) / 6.0; self.olds[0] = x; self.olds[1] = self.y[0]; self.olds[2] = self.y[1]; self.olds[3] = self.y[2]; match self.shape { ResponseType::Lowpass => self.y[3], ResponseType::Highpass => input - self.y[3], ResponseType::Bandpass => self.y[3] - (input - self.y[3]), } } }