use four_cc::FourCC;
use strum::VariantNames;
use crate::{
effect::{Effect, EffectTime},
parameter::{
EnumParameter, EnumParameterValue, FloatParameter, ParameterValueUpdate,
SmoothedParameterValue,
},
utils::{
buffer::InterleavedBufferMut,
dsp::filters::biquad::{BiquadFilter, BiquadFilterCoefficients, BiquadFilterType},
smoothing::LinearSmoothedValue,
},
Error, Parameter, ParameterScaling,
};
#[derive(
Default, Clone, Copy, PartialEq, strum::Display, strum::EnumString, strum::VariantNames,
)]
#[allow(unused)]
pub enum FilterEffectType {
#[default]
Lowpass,
Bandpass,
Bandstop,
Highpass,
}
impl From<FilterEffectType> for BiquadFilterType {
fn from(val: FilterEffectType) -> Self {
match val {
FilterEffectType::Lowpass => BiquadFilterType::Lowpass,
FilterEffectType::Bandpass => BiquadFilterType::Bandpass,
FilterEffectType::Bandstop => BiquadFilterType::Notch,
FilterEffectType::Highpass => BiquadFilterType::Highpass,
}
}
}
#[derive(Clone)]
pub struct FilterEffect {
channel_count: usize,
sample_rate: u32,
filters: Vec<BiquadFilter>,
filter_coefficients: BiquadFilterCoefficients,
filter_type: EnumParameterValue<FilterEffectType>,
cutoff: SmoothedParameterValue,
q: SmoothedParameterValue<LinearSmoothedValue>,
}
impl FilterEffect {
pub const EFFECT_NAME: &str = "Filter";
pub const TYPE: EnumParameter = EnumParameter::new(
FourCC(*b"type"),
"Type",
FilterEffectType::VARIANTS,
0, );
pub const CUTOFF: FloatParameter = FloatParameter::new(
FourCC(*b"cuto"),
"Cutoff",
20.0..=20000.0,
20000.0, )
.with_scaling(ParameterScaling::Exponential(2.5))
.with_unit("Hz");
pub const Q: FloatParameter = FloatParameter::new(
FourCC(*b"fltq"),
"Resonance",
0.001..=4.0,
0.707, );
pub fn new() -> Self {
Self {
channel_count: 0,
sample_rate: 0,
filters: vec![],
filter_coefficients: BiquadFilterCoefficients::new(
BiquadFilterType::Lowpass,
44100,
22050.0,
0.707,
0.0,
)
.expect("Failed to create default filter"),
filter_type: EnumParameterValue::from_description(Self::TYPE),
cutoff: SmoothedParameterValue::from_description(Self::CUTOFF),
q: SmoothedParameterValue::from_description(Self::Q),
}
}
pub fn with_parameters(filter_type: FilterEffectType, cutoff: f32, q: f32) -> Self {
let mut filter = Self::new();
filter.filter_type.set_value(filter_type);
filter.cutoff.init_value(cutoff);
filter.q.init_value(q);
filter
.filter_coefficients
.set(filter_type.into(), 44100, cutoff, q, 0.0)
.expect("Invalid filter parameters");
filter
}
}
impl Default for FilterEffect {
fn default() -> Self {
Self::new()
}
}
impl Effect for FilterEffect {
fn name(&self) -> &'static str {
Self::EFFECT_NAME
}
fn weight(&self) -> usize {
2
}
fn parameters(&self) -> Vec<&dyn Parameter> {
vec![
self.filter_type.description(),
self.cutoff.description(),
self.q.description(),
]
}
fn initialize(
&mut self,
sample_rate: u32,
channel_count: usize,
_max_frames: usize,
) -> Result<(), Error> {
self.sample_rate = sample_rate;
self.channel_count = channel_count;
self.filter_coefficients
.set_cutoff(
self.filter_coefficients
.cutoff()
.clamp(20.0, sample_rate as f32 / 2.0),
)
.expect("Failed to set filter parameters");
self.filters.resize_with(channel_count, BiquadFilter::new);
self.cutoff.set_sample_rate(sample_rate);
self.q.set_sample_rate(sample_rate);
Ok(())
}
fn process(&mut self, mut buffer: &mut [f32], _time: &EffectTime) {
if self.cutoff.value_need_ramp() || self.q.value_need_ramp() {
for frame in buffer.frames_mut(self.channel_count) {
let cutoff = self
.cutoff
.next_value()
.clamp(20.0, self.sample_rate as f32 / 2.0);
let q = self.q.next_value();
self.filter_coefficients
.set(
self.filter_type.value().into(),
self.sample_rate,
cutoff,
q,
0.0,
)
.expect("Invalid filter parameters");
for (sample, filter) in frame.zip(self.filters.iter_mut()) {
*sample = filter.process_sample(
&self.filter_coefficients,
*sample as f64, ) as f32;
}
}
} else {
for (filter, channel_iter) in self
.filters
.iter_mut()
.zip(buffer.channels_mut(self.channel_count))
{
filter.process(&self.filter_coefficients, channel_iter);
}
}
}
fn process_tail(&self) -> Option<usize> {
Some(self.sample_rate as usize / 10)
}
fn process_parameter_update(
&mut self,
id: FourCC,
value: &ParameterValueUpdate,
) -> Result<(), Error> {
match id {
_ if id == Self::TYPE.id() => self.filter_type.apply_update(value),
_ if id == Self::CUTOFF.id() => self.cutoff.apply_update(value),
_ if id == Self::Q.id() => self.q.apply_update(value),
_ => {
return Err(Error::ParameterError(format!(
"Unknown parameter: '{id}' for effect '{}'",
self.name()
)))
}
};
let result = match id {
_ if id == Self::TYPE.id() => self
.filter_coefficients
.set_filter_type(self.filter_type.value().into()),
_ => Ok(()),
};
if let Err(err) = result {
log::error!("Failed to apply new filter parameters: {err}");
}
Ok(())
}
}