use crate::building_blocks::{Modulator, MonoEffect, SynthParameterLabel, SynthParameterValue};
pub struct Lpf18<const BUFSIZE: usize> {
cutoff: f32,
res: f32,
dist: f32,
ay1: f32,
ay2: f32,
ay11: f32,
ay31: f32,
ax1: f32,
kfcn: f32,
kp: f32,
kp1: f32,
kp1h: f32,
kres: f32,
value: f32,
aout: f32,
lastin: f32,
samplerate: f32,
cutoff_mod: Option<Modulator<BUFSIZE>>,
res_mod: Option<Modulator<BUFSIZE>>,
dist_mod: Option<Modulator<BUFSIZE>>,
}
impl<const BUFSIZE: usize> Lpf18<BUFSIZE> {
pub fn new(freq: f32, res: f32, dist: f32, samplerate: f32) -> Self {
let kfcn = 2.0 * freq * (1.0 / samplerate);
let kp = ((-2.7528 * kfcn + 3.0429) * kfcn + 1.718) * kfcn - 0.9984;
let kp1 = kp + 1.0;
let kp1h = 0.5 * kp1;
let kres = res * (((-2.7079 * kp1 + 10.963) * kp1 - 14.934) * kp1 + 8.4974);
let value = 1.0 + (dist * (1.5 + 2.0 * res * (1.0 - kfcn)));
Lpf18 {
cutoff: freq,
res,
dist,
ay1: 0.0,
ay2: 0.0,
ax1: 0.0,
ay11: 0.0,
ay31: 0.0,
kfcn,
kp,
kp1,
kp1h,
kres,
value,
aout: 0.0,
lastin: 0.0,
samplerate,
cutoff_mod: None,
res_mod: None,
dist_mod: None,
}
}
#[inline(always)]
pub fn process_sample(&mut self, sample: f32) -> f32 {
self.ax1 = self.lastin;
self.ay11 = self.ay1;
self.ay31 = self.ay2;
self.lastin = sample - (self.kres * self.aout).tanh();
self.ay1 = self.kp1h * (self.lastin + self.ax1) - self.kp * self.ay1;
self.ay2 = self.kp1h * (self.ay1 + self.ay11) - self.kp * self.ay2;
self.aout = self.kp1h * (self.ay2 + self.ay31) - self.kp * self.aout;
(self.aout * self.value).tanh()
}
#[inline(always)]
fn update_internals(&mut self, cutoff: f32, res: f32, dist: f32) {
self.kfcn = 2.0 * cutoff * (1.0 / self.samplerate);
self.kp = ((-2.7528 * self.kfcn + 3.0429) * self.kfcn + 1.718) * self.kfcn - 0.9984;
self.kp1 = self.kp + 1.0;
self.kp1h = 0.5 * self.kp1;
self.kres = res * (((-2.7079 * self.kp1 + 10.963) * self.kp1 - 14.934) * self.kp1 + 8.4974);
self.value = 1.0 + (dist * (1.5 + 2.0 * res * (1.0 - self.kfcn)));
}
}
impl<const BUFSIZE: usize> MonoEffect<BUFSIZE> for Lpf18<BUFSIZE> {
fn set_modulator(
&mut self,
par: SynthParameterLabel,
init: f32,
modulator: Modulator<BUFSIZE>,
) {
let mut update = false;
match par {
SynthParameterLabel::LowpassCutoffFrequency => {
self.cutoff = init;
self.cutoff_mod = Some(modulator);
update = true;
}
SynthParameterLabel::LowpassQFactor => {
self.res = init;
self.res_mod = Some(modulator);
update = true;
}
SynthParameterLabel::LowpassFilterDistortion => {
self.dist = init;
self.dist_mod = Some(modulator);
update = true;
}
_ => {}
}
if update {
self.update_internals(self.cutoff, self.res, self.dist);
}
}
fn set_parameter(&mut self, par: SynthParameterLabel, value: &SynthParameterValue) {
let mut update = false;
if let SynthParameterValue::ScalarF32(val) = value {
match par {
SynthParameterLabel::LowpassCutoffFrequency => {
self.cutoff = *val;
update = true;
}
SynthParameterLabel::LowpassQFactor => {
self.res = *val;
update = true;
}
SynthParameterLabel::LowpassFilterDistortion => {
self.dist = *val;
update = true
}
_ => (),
};
if update {
self.update_internals(self.cutoff, self.res, self.dist);
}
}
}
fn finish(&mut self) {} fn is_finished(&self) -> bool {
false
}
fn process_block(
&mut self,
block: [f32; BUFSIZE],
start_sample: usize,
in_buffers: &[Vec<f32>],
) -> [f32; BUFSIZE] {
let mut out_buf: [f32; BUFSIZE] = [0.0; BUFSIZE];
if self.cutoff_mod.is_some() || self.res_mod.is_some() || self.dist_mod.is_some() {
let cutoff_buf = if let Some(m) = self.cutoff_mod.as_mut() {
m.process(self.cutoff, start_sample, in_buffers)
} else {
[self.cutoff; BUFSIZE]
};
let res_buf = if let Some(m) = self.res_mod.as_mut() {
m.process(self.res, start_sample, in_buffers)
} else {
[self.res; BUFSIZE]
};
let dist_buf = if let Some(m) = self.dist_mod.as_mut() {
m.process(self.dist, start_sample, in_buffers)
} else {
[self.dist; BUFSIZE]
};
for i in 0..BUFSIZE {
self.update_internals(cutoff_buf[i], res_buf[i], dist_buf[i]);
self.ax1 = self.lastin;
self.ay11 = self.ay1;
self.ay31 = self.ay2;
self.lastin = block[i] - (self.kres * self.aout).tanh();
self.ay1 = self.kp1h * (self.lastin + self.ax1) - self.kp * self.ay1;
self.ay2 = self.kp1h * (self.ay1 + self.ay11) - self.kp * self.ay2;
self.aout = self.kp1h * (self.ay2 + self.ay31) - self.kp * self.aout;
out_buf[i] = (self.aout * self.value).tanh();
}
} else {
for i in 0..BUFSIZE {
self.ax1 = self.lastin;
self.ay11 = self.ay1;
self.ay31 = self.ay2;
self.lastin = block[i] - (self.kres * self.aout).tanh();
self.ay1 = self.kp1h * (self.lastin + self.ax1) - self.kp * self.ay1;
self.ay2 = self.kp1h * (self.ay1 + self.ay11) - self.kp * self.ay2;
self.aout = self.kp1h * (self.ay2 + self.ay31) - self.kp * self.aout;
out_buf[i] = (self.aout * self.value).tanh();
}
}
out_buf
}
}