diff --git a/crates/augmented/dsp/dsp-filters/src/coefficients/biquad.rs b/crates/augmented/dsp/dsp-filters/src/coefficients/biquad.rs index 51fcfc6cbc2..33a0a58fd8f 100644 --- a/crates/augmented/dsp/dsp-filters/src/coefficients/biquad.rs +++ b/crates/augmented/dsp/dsp-filters/src/coefficients/biquad.rs @@ -20,7 +20,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -use num::Float; + +use num::{Complex, Float}; +use std::f64::consts::PI; pub struct BiquadCoefficients { pub(crate) a0: Sample, @@ -44,7 +46,41 @@ impl Default for BiquadCoefficients { } } +fn add_mul(c: Complex, v: T, c1: Complex) -> Complex { + Complex::new(c.re + v * c1.re, c.im + v * c1.im) +} + impl BiquadCoefficients { + /// Calculate the filter response to a given frequency. Useful for drawing frequency response + /// charts. + /// + /// Takes in a normalized_frequency between 0 and 1. + pub fn response(&self, normalized_frequency: Sample) -> Complex { + let w: f64 = 2.0 * PI * normalized_frequency.to_f64().unwrap(); + let czn1 = Complex::from_polar(1., -w); + let czn2 = Complex::from_polar(1., -2.0 * w); + let mut ch = Complex::new(1.0, 0.0); + let mut cbot = Complex::new(1.0, 0.0); + + let a0: f64 = self.a0.to_f64().unwrap(); + let b0: f64 = self.b0.to_f64().unwrap(); + let b1: f64 = self.b1.to_f64().unwrap(); + let b2: f64 = self.b2.to_f64().unwrap(); + let a1: f64 = self.a1.to_f64().unwrap(); + let a2: f64 = self.a2.to_f64().unwrap(); + + let mut ct = Complex::new(b0 / a0, 0.0); + let mut cb = Complex::new(1.0, 0.0); + ct = add_mul(ct, b1 / a0, czn1); + ct = add_mul(ct, b2 / a0, czn2); + cb = add_mul(cb, a1 / a0, czn1); + cb = add_mul(cb, a2 / a0, czn2); + ch *= ct; + cbot *= cb; + + ch / cbot + } + pub fn set_coefficients( &mut self, a0: Sample, diff --git a/crates/augmented/dsp/dsp-filters/src/rbj/mod.rs b/crates/augmented/dsp/dsp-filters/src/rbj/mod.rs index bdb4d06c6ac..218ad1b9f21 100644 --- a/crates/augmented/dsp/dsp-filters/src/rbj/mod.rs +++ b/crates/augmented/dsp/dsp-filters/src/rbj/mod.rs @@ -251,8 +251,7 @@ where #[cfg(test)] mod test { use audio_processor_testing_helpers::charts::*; - - use audio_processor_traits::simple_processor::MultiChannel; + use num::complex::ComplexFloat; use super::*; @@ -273,16 +272,21 @@ mod test { ]; for (filter_name, filter_type) in filters { - let mut processor = MultiChannel::new(move || { + let processor = { let mut processor = FilterProcessor::new(filter_type); processor.set_cutoff(880.0); processor - }); - generate_frequency_response_plot( - &format!("{}{}", env!("CARGO_MANIFEST_DIR"), "/src/rbj/mod.rs"), - &format!("{}-880hz-frequency-response", filter_name), - &mut processor, - ); + }; + let filename = format!("{}{}", env!("CARGO_MANIFEST_DIR"), "/src/rbj/mod.rs"); + let plot_name = format!("{}-880hz-frequency-response", filter_name); + let mut data = vec![]; + let mut freq = 0.0; + while freq < 44100.0 { + let response = processor.filter.coefficients.response(freq / 44100.0); + data.push(response.abs() as f32); + freq += 100.0; + } + draw_vec_chart(&filename, &plot_name, data); } } }