use super::*;
pub struct FiltOutput<'a, Smp> {
pub low: &'a [Smp],
pub band: &'a [Smp],
pub high: &'a [Smp],
}
pub struct FiltParams<'a, Smp> {
pub cutoff: &'a [Smp],
pub resonance: &'a [Smp],
}
impl<'a, Smp> FiltParams<'a, Smp> {
pub fn len(&self) -> usize {
core::cmp::min(self.cutoff.len(), self.resonance.len())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Clone)]
pub struct Filt<Smp> {
low: BufferT<Smp>,
band: BufferT<Smp>,
high: BufferT<Smp>,
low_z: Smp,
band_z: Smp,
}
impl<Smp: Float> Filt<Smp> {
pub fn new() -> Self {
Self {
low: [Smp::ZERO; STATIC_BUFFER_SIZE],
band: [Smp::ZERO; STATIC_BUFFER_SIZE],
high: [Smp::ZERO; STATIC_BUFFER_SIZE],
low_z: Smp::ZERO,
band_z: Smp::ZERO,
}
}
fn prewarped_gain(sr: Smp, f: Smp) -> Smp {
let f_c = f.midi_to_freq();
Smp::ftan(Smp::PI() * f_c / sr)
}
pub fn process(
&mut self,
ctx: &Context<Smp>,
input: &[Smp],
params: FiltParams<Smp>,
) -> FiltOutput<Smp> {
let cutoff = params.cutoff;
let resonance = params.resonance;
let numsamples = min_size(&[input.len(), params.len(), STATIC_BUFFER_SIZE]);
for i in 0..numsamples {
let res = Smp::ONE
- if resonance[i] < Smp::RES_MAX {
resonance[i]
} else {
Smp::RES_MAX
};
let gain = Self::prewarped_gain(ctx.sample_rate, cutoff[i]);
let denom = gain * gain + Smp::TWO * res * gain + Smp::ONE;
self.high[i] = (input[i] - (Smp::TWO * res + gain) * self.band_z - self.low_z) / denom;
let band_gain = gain * self.high[i];
self.band[i] = band_gain + self.band_z;
self.band_z = self.band[i] + band_gain;
let low_gain = gain * self.band[i];
self.low[i] = low_gain + self.low_z;
self.low_z = self.low[i] + low_gain;
}
FiltOutput {
low: &self.low[0..numsamples],
band: &self.band[0..numsamples],
high: &self.high[0..numsamples],
}
}
}
impl<Smp: Float> Default for Filt<Smp> {
fn default() -> Self {
Self::new()
}
}
pub struct FiltOutputFxP<'a> {
pub low: &'a [SampleFxP],
pub band: &'a [SampleFxP],
pub high: &'a [SampleFxP],
}
pub struct FiltParamsFxP<'a> {
pub cutoff: &'a [NoteFxP],
pub resonance: &'a [ScalarFxP],
}
impl<'a> FiltParamsFxP<'a> {
pub fn len(&self) -> usize {
core::cmp::min(self.cutoff.len(), self.resonance.len())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct MutFiltParamsFxP<'a> {
pub cutoff: &'a mut [NoteFxP],
pub resonance: &'a mut [ScalarFxP],
}
impl<'a> MutFiltParamsFxP<'a> {
pub fn len(&self) -> usize {
core::cmp::min(self.cutoff.len(), self.resonance.len())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a> From<MutFiltParamsFxP<'a>> for FiltParamsFxP<'a> {
fn from(value: MutFiltParamsFxP<'a>) -> Self {
Self {
cutoff: value.cutoff,
resonance: value.resonance,
}
}
}
#[derive(Clone)]
pub struct FiltFxP {
low: BufferT<SampleFxP>,
band: BufferT<SampleFxP>,
high: BufferT<SampleFxP>,
low_z: fixedmath::I12F20,
band_z: fixedmath::I12F20,
}
impl FiltFxP {
const RES_MAX: ScalarFxP = ScalarFxP::lit("0x0.F000");
pub fn new() -> Self {
Self {
low: [SampleFxP::ZERO; STATIC_BUFFER_SIZE],
band: [SampleFxP::ZERO; STATIC_BUFFER_SIZE],
high: [SampleFxP::ZERO; STATIC_BUFFER_SIZE],
low_z: fixedmath::I12F20::ZERO,
band_z: fixedmath::I12F20::ZERO,
}
}
fn prewarped_gain(c: &ContextFxP, n: NoteFxP) -> fixedmath::U1F15 {
let f_c = fixedmath::U14F2::from_num(fixedmath::midi_note_to_frequency(n));
let omega_d = ScalarFxP::from_num(
f_c.wide_mul(c.sample_rate.frac_2pi4096_sr())
.unwrapped_shr(13),
);
fixedmath::tan_fixed(omega_d)
}
pub fn process(
&mut self,
ctx: &ContextFxP,
input: &[SampleFxP],
params: FiltParamsFxP,
) -> FiltOutputFxP {
let cutoff = params.cutoff;
let resonance = params.resonance;
let numsamples = min_size(&[
input.len(),
cutoff.len(),
resonance.len(),
STATIC_BUFFER_SIZE,
]);
for i in 0..numsamples {
let res = ScalarFxP::MAX - core::cmp::min(resonance[i], Self::RES_MAX);
let gain: fixedmath::U1F15 = Self::prewarped_gain(ctx, cutoff[i]);
let gain2 = fixedmath::U3F29::from_num(gain.wide_mul(gain));
let gain_r = fixedmath::U3F29::from_num(res.wide_mul(gain));
let k = gain2 + gain_r.unwrapped_shl(1);
let (denom_inv, shift) = fixedmath::one_over_one_plus(k);
let gain_plus_2r =
fixedmath::U3F29::from_num(res).unwrapped_shl(1) + fixedmath::U3F29::from_num(gain);
let band_high_feedback: fixedmath::I7F25 = fixedmath::U3F13::from_num(gain_plus_2r)
.wide_mul_signed(SampleFxP::saturating_from_num(self.band_z));
let high_num = SampleFxP::saturating_from_num(
fixedmath::I12F20::from_num(input[i])
- fixedmath::I12F20::from_num(band_high_feedback)
- self.low_z,
);
let high_unshifted: fixedmath::I5F27 = high_num.wide_mul_unsigned(denom_inv);
self.high[i] = SampleFxP::saturating_from_num(high_unshifted.unwrapped_shr(shift));
let band_gain = fixedmath::I12F20::from_num(gain.wide_mul_signed(self.high[i]));
let band = band_gain + self.band_z;
self.band[i] = SampleFxP::saturating_from_num(band_gain + self.band_z);
self.band_z = band + band_gain;
let low_gain = fixedmath::I12F20::from_num(gain.wide_mul_signed(self.band[i]));
let low = low_gain + self.low_z;
self.low[i] = SampleFxP::saturating_from_num(low);
self.low_z = low + low_gain;
}
FiltOutputFxP {
low: &self.low[0..numsamples],
band: &self.band[0..numsamples],
high: &self.high[0..numsamples],
}
}
}
impl Default for FiltFxP {
fn default() -> Self {
Self::new()
}
}