simper_filter/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3use core::f64::consts::{PI, TAU};
4use num_complex::Complex64;
5use num_traits::Float;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum SvfError {
10    #[error("the sample rate must be set first")]
11    NoSampleRate,
12    #[error("the frequency is higher than nyqist")]
13    FrequencyOverNyqist,
14    #[error("the frequency is 0 or lower than 0")]
15    FrequencyTooLow,
16    #[error("q is lower than zero")]
17    NegativeQ,
18    #[error("fatal number conversion error")]
19    Fatal,
20}
21
22/// All available filter types for the State Variable Filter
23#[derive(Default, Clone, PartialEq, Eq)]
24pub enum FilterType {
25    #[default]
26    Lowpass,
27    Highpass,
28    Bandpass,
29    Notch,
30    Peak,
31    Allpass,
32    Bell,
33    Lowshelf,
34    Highshelf,
35}
36
37/// A State Variable Filter that is copied from Andrew Simper (Cytomic)
38/// https://cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf
39#[derive(Default)]
40pub struct Svf<F: Float> {
41    coefficients: SvfCoefficients<F>,
42    ic1eq: F,
43    ic2eq: F,
44}
45
46/// The filter coefficients that hold old necessary data to reproduce the filter
47#[derive(Default, Clone, PartialEq, Eq)]
48pub struct SvfCoefficients<F: Float> {
49    filter_type: FilterType,
50    sample_rate: F,
51    cutoff: F,
52    gain: F,
53    q: F,
54    a1: F,
55    a2: F,
56    a3: F,
57    m0: F,
58    m1: F,
59    m2: F,
60}
61
62impl<F: Float + Default> Svf<F> {
63    /// sets the filter to an inital response
64    /// use `Svf::default` otherwise
65    /// Parameters:
66    /// - filter_type: choose one of the filter types, like peak, lowpass or highpass
67    /// - sample_rate: the sample_rate of the audio buffer that the filter should be applied on
68    /// - frequency: the frequency in Hz where the cutoff of the filter should be
69    /// - q: the steepness of the filter
70    /// - gain: the gain boost or decrease of the filter
71    pub fn new(
72        filter_type: FilterType,
73        sample_rate: F,
74        cutoff: F,
75        q: F,
76        gain: F,
77    ) -> Result<Self, SvfError> {
78        let mut svf = Self::default();
79        svf.set(filter_type, sample_rate, cutoff, q, gain)?;
80        Ok(svf)
81    }
82
83    /// process helper function that calls `Svf::tick` on each sample
84    #[inline]
85    pub fn process(&mut self, input: &[F], output: &mut [F]) {
86        output
87            .iter_mut()
88            .zip(input)
89            .for_each(|(out_sample, in_sample)| {
90                *out_sample = self.tick(*in_sample);
91            });
92    }
93
94    /// Reset state of filter.
95    /// Can be used when the audio callback is restarted.
96    #[inline]
97    pub fn reset(&mut self) {
98        self.ic1eq = F::zero();
99        self.ic2eq = F::zero();
100    }
101
102    /// The set function is setting the current parameters of the filter.
103    /// Don't call from a different thread than tick.
104    /// Parameters:
105    /// - filter_type: choose one of the filter types, like peak, lowpass or highpass
106    /// - sample_rate: the sample_rate of the audio buffer that the filter should be applied on
107    /// - frequency: the frequency in Hz where the cutoff of the filter should be
108    /// - q: the steepness of the filter
109    /// - gain: the gain boost or decrease of the filter
110    #[inline]
111    pub fn set(
112        &mut self,
113        filter_type: FilterType,
114        sample_rate: F,
115        cutoff: F,
116        q: F,
117        gain: F,
118    ) -> Result<(), SvfError> {
119        self.coefficients
120            .set(filter_type, sample_rate, cutoff, q, gain)
121    }
122
123    /// Set new filter parameters from coefficients struct
124    pub fn set_coeffs(&mut self, coefficients: SvfCoefficients<F>) {
125        self.coefficients = coefficients;
126    }
127
128    /// The process that is applying the filter on a sample by sample basis
129    /// `process` is a utility function to call tick on a full frame
130    #[inline]
131    pub fn tick(&mut self, input: F) -> F {
132        let v0 = input;
133        let v3 = v0 - self.ic2eq;
134        let v1 = self.coefficients.a1 * self.ic1eq + self.coefficients.a2 * v3;
135        let v2 = self.ic2eq + self.coefficients.a2 * self.ic1eq + self.coefficients.a3 * v3;
136        let two = F::one() + F::one();
137        self.ic1eq = two * v1 - self.ic1eq;
138        self.ic2eq = two * v2 - self.ic2eq;
139
140        self.coefficients.m0 * v0 + self.coefficients.m1 * v1 + self.coefficients.m2 * v2
141    }
142
143    /// get a reference to the coefficients
144    /// can be used to clone it to the UI thread, to plot the response
145    pub fn coefficients(&self) -> &SvfCoefficients<F> {
146        &self.coefficients
147    }
148
149    /// utility method to get the response
150    /// should not be called from another thread than the set methods
151    pub fn get_response(&self, frequency: f64) -> Result<Complex64, SvfError> {
152        SvfResponse::response(&self.coefficients, frequency)
153    }
154}
155
156impl<F: Float> SvfCoefficients<F> {
157    /// The set function is setting the current parameters of the filter.
158    /// Don't call from a different thread than tick.
159    /// Parameters:
160    /// - filter_type: choose one of the filter types, like peak, lowpass or highpass
161    /// - sample_rate: the sample_rate of the audio buffer that the filter should be applied on
162    /// - frequency: the frequency in Hz where the cutoff of the filter should be
163    /// - q: the steepness of the filter
164    /// - gain: the gain boost or decrease of the filter
165    pub fn set(
166        &mut self,
167        filter_type: FilterType,
168        sample_rate: F,
169        cutoff_frequency: F,
170        q: F,
171        gain: F,
172    ) -> Result<(), SvfError> {
173        if q < F::zero() {
174            return Err(SvfError::NegativeQ);
175        }
176        if cutoff_frequency > sample_rate / F::from(2.0).unwrap() {
177            return Err(SvfError::FrequencyOverNyqist);
178        }
179        self.filter_type = filter_type;
180        self.sample_rate = sample_rate;
181        self.cutoff = cutoff_frequency;
182        self.q = q;
183        self.gain = gain;
184
185        match self.filter_type {
186            FilterType::Lowpass => {
187                let g =
188                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
189                let k = F::one() / self.q;
190                self.a1 = F::one() / (F::one() + g * (g + k));
191                self.a2 = g * self.a1;
192                self.a3 = g * self.a2;
193                self.m0 = F::zero();
194                self.m1 = F::zero();
195                self.m2 = F::one();
196            }
197            FilterType::Highpass => {
198                let g =
199                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
200                let k = F::one() / self.q;
201                self.a1 = F::one() / (F::one() + g * (g + k));
202                self.a2 = g * self.a1;
203                self.a3 = g * self.a2;
204                self.m0 = F::one();
205                self.m1 = -k;
206                self.m2 = -F::one();
207            }
208            FilterType::Bandpass => {
209                let g =
210                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
211                let k = F::one() / self.q;
212                self.a1 = F::one() / (F::one() + g * (g + k));
213                self.a2 = g * self.a1;
214                self.a3 = g * self.a2;
215                self.m0 = F::zero();
216                self.m1 = F::one();
217                self.m2 = F::zero();
218            }
219            FilterType::Notch => {
220                let g =
221                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
222                let k = F::one() / self.q;
223                self.a1 = F::one() / (F::one() + g * (g + k));
224                self.a2 = g * self.a1;
225                self.a3 = g * self.a2;
226                self.m0 = F::one();
227                self.m1 = -k;
228                self.m2 = F::zero();
229            }
230            FilterType::Peak => {
231                let g =
232                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
233                let k = F::one() / self.q;
234                self.a1 = F::one() / (F::one() + g * (g + k));
235                self.a2 = g * self.a1;
236                self.a3 = g * self.a2;
237                self.m0 = F::one();
238                self.m1 = -k;
239                self.m2 = F::from(-2).ok_or(SvfError::Fatal)?;
240            }
241            FilterType::Allpass => {
242                let g =
243                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
244                let k = F::one() / self.q;
245                self.a1 = F::one() / (F::one() + g * (g + k));
246                self.a2 = g * self.a1;
247                self.a3 = g * self.a2;
248                self.m0 = F::one();
249                self.m1 = F::from(-2).ok_or(SvfError::Fatal)? * k;
250                self.m2 = F::zero();
251            }
252            FilterType::Bell => {
253                let a = F::powf(
254                    F::from(10).ok_or(SvfError::Fatal)?,
255                    self.gain / F::from(40).unwrap(),
256                );
257                let g =
258                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate);
259                let k = F::one() / (self.q * a);
260                self.a1 = F::one() / (F::one() + g * (g + k));
261                self.a2 = g * self.a1;
262                self.a3 = g * self.a2;
263                self.m0 = F::one();
264                self.m1 = k * (a * a - F::one());
265                self.m2 = F::zero();
266            }
267            FilterType::Lowshelf => {
268                let a = F::powf(
269                    F::from(10).ok_or(SvfError::Fatal)?,
270                    self.gain / F::from(40).unwrap(),
271                );
272                let g =
273                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate)
274                        / F::sqrt(a);
275                let k = F::one() / self.q;
276                self.a1 = F::one() / (F::one() + g * (g + k));
277                self.a2 = g * self.a1;
278                self.a3 = g * self.a2;
279                self.m0 = F::one();
280                self.m1 = k * (a - F::one());
281                self.m2 = a * a - F::one();
282            }
283            FilterType::Highshelf => {
284                let a = F::powf(
285                    F::from(10).ok_or(SvfError::Fatal)?,
286                    self.gain / F::from(40).unwrap(),
287                );
288                let g =
289                    F::tan(F::from(PI).ok_or(SvfError::Fatal)? * self.cutoff / self.sample_rate)
290                        * F::sqrt(a);
291                let k = F::one() / self.q;
292                self.a1 = F::one() / (F::one() + g * (g + k));
293                self.a2 = g * self.a1;
294                self.a3 = g * self.a2;
295                self.m0 = a * a;
296                self.m1 = k * (F::one() - a) * a;
297                self.m2 = F::one() - a * a;
298            }
299        }
300        Ok(())
301    }
302}
303
304/// Coefficients can be copied in the UI Thread, so that the response can be shown
305pub struct SvfResponse;
306impl SvfResponse {
307    /// Retrieves the filter response of the current settings for a specific frequency
308    /// Can be used to plot the filter magnitue and phase
309    pub fn response<F: Float>(
310        coeffs: &SvfCoefficients<F>,
311        frequency: f64,
312    ) -> Result<Complex64, SvfError> {
313        if frequency <= 0.0 {
314            return Err(SvfError::FrequencyTooLow);
315        }
316        let nyquist = coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)? / 2.0;
317        if frequency > nyquist {
318            return Err(SvfError::FrequencyOverNyqist);
319        }
320
321        match coeffs.filter_type {
322            FilterType::Lowpass => SvfResponse::lowpass_response(coeffs, frequency),
323            FilterType::Highpass => SvfResponse::highpass_response(coeffs, frequency),
324            FilterType::Bandpass => SvfResponse::bandpass_response(coeffs, frequency),
325            FilterType::Notch => SvfResponse::notch_response(coeffs, frequency),
326            FilterType::Peak => SvfResponse::peak_response(coeffs, frequency),
327            FilterType::Allpass => SvfResponse::allpass_response(coeffs, frequency),
328            FilterType::Bell => SvfResponse::bell_response(coeffs, frequency),
329            FilterType::Lowshelf => SvfResponse::lowshelf_response(coeffs, frequency),
330            FilterType::Highshelf => SvfResponse::highshelf_response(coeffs, frequency),
331        }
332    }
333
334    fn lowpass_response<F: Float>(
335        coeffs: &SvfCoefficients<F>,
336        frequency: f64,
337    ) -> Result<Complex64, SvfError> {
338        let g = f64::tan(
339            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
340                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
341        );
342        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
343        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
344        let z = Complex64::from_polar(1.0, f);
345        let response = (g * g * (1.0 + z) * (1.0 + z))
346            / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
347        Ok(response)
348    }
349
350    fn highpass_response<F: Float>(
351        coeffs: &SvfCoefficients<F>,
352        frequency: f64,
353    ) -> Result<Complex64, SvfError> {
354        let g = f64::tan(
355            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
356                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
357        );
358        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
359        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
360        let z = Complex64::from_polar(1.0, f);
361        let response = ((z - 1.0) * (z - 1.0))
362            / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
363        Ok(response)
364    }
365
366    fn bandpass_response<F: Float>(
367        coeffs: &SvfCoefficients<F>,
368        frequency: f64,
369    ) -> Result<Complex64, SvfError> {
370        let g = f64::tan(
371            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
372                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
373        );
374        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
375        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
376        let z = Complex64::from_polar(1.0, f);
377        let response = (g * (z * z - 1.0))
378            / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
379        Ok(response)
380    }
381
382    fn notch_response<F: Float>(
383        coeffs: &SvfCoefficients<F>,
384        frequency: f64,
385    ) -> Result<Complex64, SvfError> {
386        let g = f64::tan(
387            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
388                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
389        );
390        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
391        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
392        let z = Complex64::from_polar(1.0, f);
393        let response = ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z))
394            / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
395        Ok(response)
396    }
397
398    fn peak_response<F: Float>(
399        coeffs: &SvfCoefficients<F>,
400        frequency: f64,
401    ) -> Result<Complex64, SvfError> {
402        let g = f64::tan(
403            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
404                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
405        );
406        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
407        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
408        let z = Complex64::from_polar(1.0, f);
409        // Note: this is the negation of the transfer function reported in the derivation.
410        let response = -((1.0 + g + (g - 1.0) * z) * (-1.0 + g + z + g * z))
411            / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
412        Ok(response)
413    }
414
415    fn allpass_response<F: Float>(
416        coeffs: &SvfCoefficients<F>,
417        frequency: f64,
418    ) -> Result<Complex64, SvfError> {
419        let g = f64::tan(
420            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
421                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
422        );
423        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
424        let f = frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?;
425        let z = Complex64::from_polar(1.0, f);
426        let response =
427            ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * (k - k * z * z))
428                / ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z) + g * k * (z * z - 1.0));
429        Ok(response)
430    }
431
432    fn bell_response<F: Float>(
433        coeffs: &SvfCoefficients<F>,
434        frequency: f64,
435    ) -> Result<Complex64, SvfError> {
436        let a = f64::sqrt(coeffs.gain.to_f64().ok_or(SvfError::Fatal)?);
437        let g = f64::tan(
438            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
439                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
440        );
441        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
442        let z = Complex64::from_polar(
443            1.0,
444            frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
445        );
446        let response = (g * k * (z * z - 1.0)
447            + a * (g * (1.0 + z) * ((a * a - 1.0) * k / a * (z - 1.0))
448                + ((z - 1.0) * (z - 1.0) + g * g * (1.0 + z) * (1.0 + z))))
449            / (g * k * (z * z - 1.0) + a * ((z - 1.0) * (z - 1.0) + g * g * (z + 1.0) * (z + 1.0)));
450        Ok(response)
451    }
452
453    fn lowshelf_response<F: Float>(
454        coeffs: &SvfCoefficients<F>,
455        frequency: f64,
456    ) -> Result<Complex64, SvfError> {
457        let a = f64::sqrt(coeffs.gain.to_f64().ok_or(SvfError::Fatal)?);
458        let g = f64::tan(
459            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
460                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
461        );
462        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
463        let z = Complex64::from_polar(
464            1.0,
465            frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
466        );
467        let sqrt_a = f64::sqrt(a);
468        let response = (a * (z - 1.0) * (z - 1.0)
469            + g * g * a * a * (z + 1.0) * (z + 1.0)
470            + sqrt_a * g * a * k * (z * z - 1.0))
471            / (a * (z - 1.0) * (z - 1.0)
472                + g * g * (1.0 + z) * (1.0 + z)
473                + sqrt_a * g * k * (z * z - 1.0));
474        Ok(response)
475    }
476
477    fn highshelf_response<F: Float>(
478        coeffs: &SvfCoefficients<F>,
479        frequency: f64,
480    ) -> Result<Complex64, SvfError> {
481        let a = f64::sqrt(coeffs.gain.to_f64().ok_or(SvfError::Fatal)?);
482        let g = f64::tan(
483            PI * coeffs.cutoff.to_f64().ok_or(SvfError::Fatal)?
484                / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
485        );
486        let k = 1.0 / coeffs.q.to_f64().ok_or(SvfError::Fatal)?;
487        let z = Complex64::from_polar(
488            1.0,
489            frequency * TAU / coeffs.sample_rate.to_f64().ok_or(SvfError::Fatal)?,
490        );
491        let sqrt_a = f64::sqrt(a);
492        let response = (sqrt_a
493            * g
494            * (1.0 + z)
495            * (-(a - 1.0) * a * k * (z - 1.0) + sqrt_a * g * (1.0 - a * a) * (1.0 + z))
496            + a * a
497                * ((z - 1.0) * (z - 1.0)
498                    + a * g * g * (1.0 + z) * (1.0 + z)
499                    + sqrt_a * g * k * (z * z - 1.0)))
500            / ((z - 1.0) * (z - 1.0)
501                + a * g * g * (1.0 + z) * (1.0 + z)
502                + sqrt_a * g * k * (z * z - 1.0));
503        Ok(response)
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use super::*;
510
511    fn generate_sine<F: Float>(sample_rate: f64, frequency: f64, num_samples: usize) -> Vec<F> {
512        (0..num_samples)
513            .map(|i| F::from((2.0 * PI * frequency * i as f64 / sample_rate).sin()).unwrap())
514            .collect()
515    }
516
517    fn rms_db<F: Float>(data: &[F]) -> F {
518        let sum_of_squares = data.iter().fold(F::zero(), |acc, &x| acc + x * x);
519        let mean_of_squares = sum_of_squares / F::from(data.len()).unwrap();
520        let rms = mean_of_squares.sqrt();
521        let twenty = F::from(20.0).unwrap();
522        twenty * rms.log10()
523    }
524
525    struct Point {
526        freq: f64,
527        expected_gain_db: f64,
528    }
529
530    fn test_fiter(mut filter: Svf<f64>, points: &[Point]) {
531        let sample_rate = filter.coefficients.sample_rate;
532        let num_samples = sample_rate as usize;
533        let mut output = vec![0.0; num_samples];
534
535        for point in points {
536            let sine = generate_sine::<f64>(sample_rate, point.freq, num_samples);
537            filter.reset();
538            filter.process(&sine, &mut output);
539            assert_eq!(rms_db(&output).round(), point.expected_gain_db);
540        }
541    }
542
543    #[test]
544    fn test_lowpass() {
545        test_fiter(
546            Svf::new(FilterType::Lowpass, 44100.0, 1000.0, 0.71, 0.0).unwrap(),
547            &[
548                Point {
549                    freq: 20.0,
550                    expected_gain_db: -3.0,
551                },
552                Point {
553                    freq: 500.0,
554                    expected_gain_db: -3.0,
555                },
556                Point {
557                    freq: 1000.0,
558                    expected_gain_db: -6.0,
559                },
560                Point {
561                    freq: 5000.0,
562                    expected_gain_db: -32.0,
563                },
564                Point {
565                    freq: 10000.0,
566                    expected_gain_db: -46.0,
567                },
568                Point {
569                    freq: 20000.0,
570                    expected_gain_db: -79.0,
571                },
572            ],
573        );
574    }
575
576    #[test]
577    fn test_highpass() {
578        test_fiter(
579            Svf::new(FilterType::Highpass, 44100.0, 1000.0, 0.71, 0.0).unwrap(),
580            &[
581                Point {
582                    freq: 20.0,
583                    expected_gain_db: -70.0,
584                },
585                Point {
586                    freq: 500.0,
587                    expected_gain_db: -15.0,
588                },
589                Point {
590                    freq: 1000.0,
591                    expected_gain_db: -6.0,
592                },
593                Point {
594                    freq: 5000.0,
595                    expected_gain_db: -3.0,
596                },
597                Point {
598                    freq: 10000.0,
599                    expected_gain_db: -3.0,
600                },
601                Point {
602                    freq: 20000.0,
603                    expected_gain_db: -3.0,
604                },
605            ],
606        );
607    }
608
609    #[test]
610    fn test_bandpass() {
611        test_fiter(
612            Svf::new(FilterType::Bandpass, 44100.0, 1000.0, 0.71, 0.0).unwrap(),
613            &[
614                Point {
615                    freq: 20.0,
616                    expected_gain_db: -37.0,
617                },
618                Point {
619                    freq: 500.0,
620                    expected_gain_db: -9.0,
621                },
622                Point {
623                    freq: 1000.0,
624                    expected_gain_db: -6.0,
625                },
626                Point {
627                    freq: 5000.0,
628                    expected_gain_db: -17.0,
629                },
630                Point {
631                    freq: 10000.0,
632                    expected_gain_db: -25.0,
633                },
634                Point {
635                    freq: 20000.0,
636                    expected_gain_db: -43.0,
637                },
638            ],
639        );
640    }
641
642    #[test]
643    fn test_notch() {
644        test_fiter(
645            Svf::new(FilterType::Notch, 44100.0, 1000.0, 0.71, 0.0).unwrap(),
646            &[
647                Point {
648                    freq: 20.0,
649                    expected_gain_db: -3.0,
650                },
651                Point {
652                    freq: 500.0,
653                    expected_gain_db: -6.0,
654                },
655                Point {
656                    freq: 1000.0,
657                    expected_gain_db: -42.0,
658                },
659                Point {
660                    freq: 5000.0,
661                    expected_gain_db: -3.0,
662                },
663                Point {
664                    freq: 10000.0,
665                    expected_gain_db: -3.0,
666                },
667                Point {
668                    freq: 20000.0,
669                    expected_gain_db: -3.0,
670                },
671            ],
672        );
673    }
674
675    #[test]
676    fn test_peak() {
677        test_fiter(
678            Svf::new(FilterType::Peak, 44100.0, 1000.0, 0.71, 0.0).unwrap(),
679            &[
680                Point {
681                    freq: 20.0,
682                    expected_gain_db: -3.0,
683                },
684                Point {
685                    freq: 500.0,
686                    expected_gain_db: -1.0,
687                },
688                Point {
689                    freq: 1000.0,
690                    expected_gain_db: 0.0,
691                },
692                Point {
693                    freq: 5000.0,
694                    expected_gain_db: -3.0,
695                },
696                Point {
697                    freq: 10000.0,
698                    expected_gain_db: -3.0,
699                },
700                Point {
701                    freq: 20000.0,
702                    expected_gain_db: -3.0,
703                },
704            ],
705        );
706    }
707
708    #[test]
709    fn test_bell() {
710        test_fiter(
711            Svf::new(FilterType::Bell, 44100.0, 1000.0, 0.71, 3.0).unwrap(),
712            &[
713                Point {
714                    freq: 20.0,
715                    expected_gain_db: -3.0,
716                },
717                Point {
718                    freq: 500.0,
719                    expected_gain_db: -2.0,
720                },
721                Point {
722                    freq: 1000.0,
723                    expected_gain_db: 0.0,
724                },
725                Point {
726                    freq: 5000.0,
727                    expected_gain_db: -3.0,
728                },
729                Point {
730                    freq: 10000.0,
731                    expected_gain_db: -3.0,
732                },
733                Point {
734                    freq: 20000.0,
735                    expected_gain_db: -3.0,
736                },
737            ],
738        );
739
740        test_fiter(
741            Svf::new(FilterType::Bell, 44100.0, 1000.0, 0.71, -3.0).unwrap(),
742            &[
743                Point {
744                    freq: 20.0,
745                    expected_gain_db: -3.0,
746                },
747                Point {
748                    freq: 500.0,
749                    expected_gain_db: -4.0,
750                },
751                Point {
752                    freq: 1000.0,
753                    expected_gain_db: -6.0,
754                },
755                Point {
756                    freq: 5000.0,
757                    expected_gain_db: -3.0,
758                },
759                Point {
760                    freq: 10000.0,
761                    expected_gain_db: -3.0,
762                },
763                Point {
764                    freq: 20000.0,
765                    expected_gain_db: -3.0,
766                },
767            ],
768        );
769    }
770
771    #[test]
772    fn test_lowshelf() {
773        test_fiter(
774            Svf::new(FilterType::Lowshelf, 44100.0, 1000.0, 0.71, -6.0).unwrap(),
775            &[
776                Point {
777                    freq: 20.0,
778                    expected_gain_db: -9.0,
779                },
780                Point {
781                    freq: 500.0,
782                    expected_gain_db: -9.0,
783                },
784                Point {
785                    freq: 1000.0,
786                    expected_gain_db: -6.0,
787                },
788                Point {
789                    freq: 5000.0,
790                    expected_gain_db: -3.0,
791                },
792                Point {
793                    freq: 10000.0,
794                    expected_gain_db: -3.0,
795                },
796                Point {
797                    freq: 20000.0,
798                    expected_gain_db: -3.0,
799                },
800            ],
801        );
802
803        test_fiter(
804            Svf::new(FilterType::Lowshelf, 44100.0, 1000.0, 0.71, 6.0).unwrap(),
805            &[
806                Point {
807                    freq: 20.0,
808                    expected_gain_db: 3.0,
809                },
810                Point {
811                    freq: 500.0,
812                    expected_gain_db: 3.0,
813                },
814                Point {
815                    freq: 1000.0,
816                    expected_gain_db: 0.0,
817                },
818                Point {
819                    freq: 5000.0,
820                    expected_gain_db: -3.0,
821                },
822                Point {
823                    freq: 10000.0,
824                    expected_gain_db: -3.0,
825                },
826                Point {
827                    freq: 20000.0,
828                    expected_gain_db: -3.0,
829                },
830            ],
831        );
832    }
833
834    #[test]
835    fn test_highshelf() {
836        test_fiter(
837            Svf::new(FilterType::Highshelf, 44100.0, 1000.0, 0.71, -6.0).unwrap(),
838            &[
839                Point {
840                    freq: 20.0,
841                    expected_gain_db: -3.0,
842                },
843                Point {
844                    freq: 500.0,
845                    expected_gain_db: -3.0,
846                },
847                Point {
848                    freq: 1000.0,
849                    expected_gain_db: -6.0,
850                },
851                Point {
852                    freq: 5000.0,
853                    expected_gain_db: -9.0,
854                },
855                Point {
856                    freq: 10000.0,
857                    expected_gain_db: -9.0,
858                },
859                Point {
860                    freq: 20000.0,
861                    expected_gain_db: -9.0,
862                },
863            ],
864        );
865
866        test_fiter(
867            Svf::new(FilterType::Highshelf, 44100.0, 1000.0, 0.71, 6.0).unwrap(),
868            &[
869                Point {
870                    freq: 20.0,
871                    expected_gain_db: -3.0,
872                },
873                Point {
874                    freq: 500.0,
875                    expected_gain_db: -3.0,
876                },
877                Point {
878                    freq: 1000.0,
879                    expected_gain_db: 0.0,
880                },
881                Point {
882                    freq: 5000.0,
883                    expected_gain_db: 3.0,
884                },
885                Point {
886                    freq: 10000.0,
887                    expected_gain_db: 3.0,
888                },
889                Point {
890                    freq: 20000.0,
891                    expected_gain_db: 3.0,
892                },
893            ],
894        );
895    }
896}