use crate::{graph::DspProcessor, prelude::*};
use super::{biquad_coefficients::BiquadCoefficients, filter_type::BiquadFilterType};
pub struct BiquadProcessor {
filter_type: BiquadFilterType,
sample_rate: usize,
coefficients: BiquadCoefficients,
last_parameters: Parameters,
delays: Vec<[f64; 4]>,
}
#[derive(PartialEq)]
struct Parameters {
frequency: f64,
q: f64,
shelf_gain: f64,
}
fn calculate_coefficients(
filter_type: BiquadFilterType,
parameters: &Parameters,
sample_rate: usize,
) -> BiquadCoefficients {
match filter_type {
BiquadFilterType::HighPass => {
BiquadCoefficients::high_pass(parameters.frequency, sample_rate as f64, parameters.q)
}
BiquadFilterType::LowPass => {
BiquadCoefficients::low_pass(parameters.frequency, sample_rate as f64, parameters.q)
}
BiquadFilterType::BandPass => {
BiquadCoefficients::band_pass(parameters.frequency, sample_rate as f64, parameters.q)
}
BiquadFilterType::Notch => {
BiquadCoefficients::notch(parameters.frequency, sample_rate as f64, parameters.q)
}
BiquadFilterType::HighShelf => BiquadCoefficients::high_shelf(
parameters.frequency,
sample_rate as f64,
Level::from_linear(parameters.shelf_gain),
),
BiquadFilterType::LowShelf => BiquadCoefficients::low_shelf(
parameters.frequency,
sample_rate as f64,
Level::from_linear(parameters.shelf_gain),
),
}
}
impl BiquadProcessor {
pub fn new(sample_rate: usize, channel_count: usize, filter_type: BiquadFilterType) -> Self {
let parameters = Parameters {
frequency: 1_000.0,
q: 1.0 / 2.0_f64.sqrt(),
shelf_gain: 1.0,
};
Self {
sample_rate,
filter_type,
coefficients: calculate_coefficients(filter_type, ¶meters, sample_rate),
last_parameters: parameters,
delays: (0..channel_count).map(|_| [0.0, 0.0, 0.0, 0.0]).collect(),
}
}
}
impl DspProcessor for BiquadProcessor {
fn process_audio(&mut self, context: &mut crate::ProcessContext) {
let frequency = context
.parameters
.get_parameter_values("frequency", context.output_buffer.frame_count());
let q = context
.parameters
.get_parameter_values("q", context.output_buffer.frame_count());
let shelf_gain = context
.parameters
.get_parameter_values("shelf-gain", context.output_buffer.frame_count());
let gain = context
.parameters
.get_parameter_values("gain", context.output_buffer.frame_count());
let frame_count = context.output_buffer.frame_count();
let channel_count = context.output_buffer.channel_count();
for frame in 0..frame_count {
let parameters = Parameters {
frequency: frequency[frame] as f64,
q: q[frame] as f64,
shelf_gain: shelf_gain[frame] as f64,
};
if parameters != self.last_parameters {
self.coefficients =
calculate_coefficients(self.filter_type, ¶meters, self.sample_rate);
self.last_parameters = parameters;
}
for channel in 0..channel_count {
let location = SampleLocation::new(channel, frame);
let input_sample = context.input_buffer.get_sample(location) as f64;
let x1 = self.delays[channel][0];
let x2 = self.delays[channel][1];
let y1 = self.delays[channel][2];
let y2 = self.delays[channel][3];
let out = self.coefficients.b0() * input_sample
+ self.coefficients.b1() * x1
+ self.coefficients.b2() * x2
- self.coefficients.a1() * y1
- self.coefficients.a2() * y2;
self.delays[channel][1] = x1;
self.delays[channel][0] = input_sample;
self.delays[channel][3] = y1;
self.delays[channel][2] = out;
for delay in self.delays[channel].iter_mut() {
*delay = denormal(*delay);
}
context.output_buffer.set_sample(location, out as f32);
}
}
context.output_buffer.apply_gain(gain);
}
}
fn denormal(sample: f64) -> f64 {
let denormal_threshold = 1e-8;
if -denormal_threshold <= sample && sample <= denormal_threshold {
return 0.0;
}
sample
}