use super::*;
pub struct ModFiltInput<T: DspFormatBase> {
pub signal: T::Sample,
pub env: T::Scalar,
pub vel: T::Scalar,
pub kbd: T::Note,
}
#[derive(Clone, Default)]
pub struct ModFiltParams<T: DspFormatBase> {
pub env_mod: T::Scalar,
pub vel_mod: T::Scalar,
pub kbd_tracking: T::Scalar,
pub cutoff: T::Note,
pub resonance: T::Scalar,
pub low_mix: T::Scalar,
pub band_mix: T::Scalar,
pub high_mix: T::Scalar,
}
impl<T: DspFloat> From<&ModFiltParams<i16>> for ModFiltParams<T> {
fn from(value: &ModFiltParams<i16>) -> Self {
Self {
env_mod: value.env_mod.to_num(),
vel_mod: value.vel_mod.to_num(),
kbd_tracking: value.kbd_tracking.to_num(),
cutoff: value.cutoff.to_num(),
resonance: value.resonance.to_num(),
low_mix: value.low_mix.to_num(),
band_mix: value.band_mix.to_num(),
high_mix: value.high_mix.to_num(),
}
}
}
impl<T: DspFormatBase> ModFiltParams<T> {
pub fn to_filt_params(&self, input: &ModFiltInput<T>) -> FiltParams<T> {
let mut cutoff = self.cutoff;
let kbd = input.kbd.scale(self.kbd_tracking);
let vel = T::note_from_scalar(input.vel.scale(self.vel_mod));
let env = T::note_from_scalar(input.env.scale(self.env_mod));
cutoff = cutoff.dsp_saturating_add(kbd).dsp_saturating_add(vel).dsp_saturating_add(env);
FiltParams {
cutoff,
resonance: self.resonance,
}
}
}
#[derive(Clone, Default)]
pub struct ModFilt<T: DspFormat> {
filter: Filt<T>,
mixer: Mixer<T, 3>,
}
impl<T: DspFormat> Device<T> for ModFilt<T> {
type Input = ModFiltInput<T>;
type Params = ModFiltParams<T>;
type Output = T::Sample;
fn next(
&mut self,
context: &T::Context,
input: ModFiltInput<T>,
params: ModFiltParams<T>,
) -> T::Sample {
let filt_out = self.filter.next(context, input.signal, params.to_filt_params(&input));
self.mixer.next(
context,
[filt_out.low, filt_out.band, filt_out.high],
[params.low_mix, params.band_mix, params.high_mix],
)
}
}