From e31293cc107b0c0c02dc0245efcaf1d4584332ed Mon Sep 17 00:00:00 2001
From: JasonLG1979 <jasonlevigray3@gmail.com>
Date: Tue, 4 Jul 2023 04:02:39 -0500
Subject: [PATCH] Better Filters

All Windows calculated with pyfda (Python Filter Design Analysis Tool)
https://github.com/chipmuenk/pyfda
Window = Kaiser
beta = 8.6 (Similar to a Blackman Window)
fc = 22.5kHz
-86dB by 23kHz

This also gets rid of Linear Interpolation which leaves only Low and High both being Windowed Sinc.
---
 playback/src/config.rs              |  156 +---
 playback/src/filter_coefficients.rs | 1187 +++++++++++++++++++++++++++
 playback/src/lib.rs                 |    1 +
 playback/src/resampler.rs           |  132 +--
 src/main.rs                         |    4 +-
 5 files changed, 1234 insertions(+), 246 deletions(-)
 create mode 100644 playback/src/filter_coefficients.rs

diff --git a/playback/src/config.rs b/playback/src/config.rs
index 4efae2b4..85449eb4 100644
--- a/playback/src/config.rs
+++ b/playback/src/config.rs
@@ -1,7 +1,14 @@
 use std::{mem, str::FromStr, time::Duration};
 
 pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer};
-use crate::{convert::i24, RESAMPLER_INPUT_SIZE, SAMPLE_RATE};
+
+use crate::{
+    convert::i24,
+    filter_coefficients::{
+        HZ48000_HIGH, HZ48000_LOW, HZ88200_HIGH, HZ88200_LOW, HZ96000_HIGH, HZ96000_LOW,
+    },
+    RESAMPLER_INPUT_SIZE, SAMPLE_RATE,
+};
 
 // Reciprocals allow us to multiply instead of divide during interpolation.
 const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
@@ -26,21 +33,9 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
 const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize =
     (RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
 
-pub const NUM_FIR_FILTER_TAPS: usize = 5;
-
-// Blackman Window coefficients
-const BLACKMAN_A0: f64 = 0.42;
-const BLACKMAN_A1: f64 = 0.5;
-const BLACKMAN_A2: f64 = 0.08;
-
-// Constants for calculations
-const TWO_TIMES_PI: f64 = 2.0 * std::f64::consts::PI;
-const FOUR_TIMES_PI: f64 = 4.0 * std::f64::consts::PI;
-
 #[derive(Clone, Copy, Debug, Default)]
 pub enum InterpolationQuality {
     Low,
-    Medium,
     #[default]
     High,
 }
@@ -53,7 +48,6 @@ impl FromStr for InterpolationQuality {
 
         match s.to_lowercase().as_ref() {
             "low" => Ok(Low),
-            "medium" => Ok(Medium),
             "high" => Ok(High),
             _ => Err(()),
         }
@@ -66,52 +60,24 @@ impl std::fmt::Display for InterpolationQuality {
 
         match self {
             Low => write!(f, "Low"),
-            Medium => write!(f, "Medium"),
             High => write!(f, "High"),
         }
     }
 }
 
 impl InterpolationQuality {
-    pub fn get_interpolation_coefficients(&self, resample_factor_reciprocal: f64) -> Vec<f64> {
-        let interpolation_coefficients_length = self.get_interpolation_coefficients_length();
-
-        let mut coefficients = Vec::with_capacity(interpolation_coefficients_length);
-
-        if interpolation_coefficients_length == 0 {
-            warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec<f64>");
-            warn!("Linear Interpolation does not use coefficients");
-
-            return coefficients;
-        }
-
-        let last_index = interpolation_coefficients_length as f64 - 1.0;
-
-        let sinc_center = last_index * 0.5;
-
+    pub fn get_interpolation_coefficients(
+        &self,
+        mut coefficients: Vec<f64>,
+        resample_factor_reciprocal: f64,
+    ) -> Vec<f64> {
         let mut coefficient_sum = 0.0;
 
-        coefficients.extend((0..interpolation_coefficients_length).map(
-            |interpolation_coefficient_index| {
-                let index_float = interpolation_coefficient_index as f64;
-                let sample_index_fractional = (index_float * resample_factor_reciprocal).fract();
+        for (index, coefficient) in coefficients.iter_mut().enumerate() {
+            *coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract());
 
-                let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional);
-
-                let fir_filter = Self::fir_filter(
-                    index_float,
-                    last_index,
-                    sinc_center,
-                    resample_factor_reciprocal,
-                );
-
-                let coefficient = sample_index_fractional_sinc_weight * fir_filter;
-
-                coefficient_sum += coefficient;
-
-                coefficient
-            },
-        ));
+            coefficient_sum += *coefficient;
+        }
 
         coefficients
             .iter_mut()
@@ -120,53 +86,6 @@ impl InterpolationQuality {
         coefficients
     }
 
-    pub fn get_fir_filter_coefficients(&self, resample_factor_reciprocal: f64) -> Vec<f64> {
-        let mut coefficients = Vec::with_capacity(NUM_FIR_FILTER_TAPS);
-
-        if self.get_interpolation_coefficients_length() != 0 {
-            warn!("InterpolationQuality::Medium/High::get_fir_filter_coefficients always returns an empty Vec<f64>");
-            warn!("The FIR Filter coefficients are a part of the Windowed Sinc Interpolation coefficients");
-
-            return coefficients;
-        }
-
-        let last_index = NUM_FIR_FILTER_TAPS as f64 - 1.0;
-
-        let sinc_center = last_index * 0.5;
-
-        let mut coefficient_sum = 0.0;
-
-        coefficients.extend(
-            (0..NUM_FIR_FILTER_TAPS).map(|fir_filter_coefficient_index| {
-                let coefficient = Self::fir_filter(
-                    fir_filter_coefficient_index as f64,
-                    last_index,
-                    sinc_center,
-                    resample_factor_reciprocal,
-                );
-
-                coefficient_sum += coefficient;
-
-                coefficient
-            }),
-        );
-
-        coefficients
-            .iter_mut()
-            .for_each(|coefficient| *coefficient /= coefficient_sum);
-
-        coefficients
-    }
-
-    pub fn get_interpolation_coefficients_length(&self) -> usize {
-        use InterpolationQuality::*;
-        match self {
-            Low => 0,
-            Medium => 129,
-            High => 257,
-        }
-    }
-
     fn sinc(x: f64) -> f64 {
         if x.abs() < f64::EPSILON {
             1.0
@@ -175,35 +94,6 @@ impl InterpolationQuality {
             pi_x.sin() / pi_x
         }
     }
-
-    fn blackman(index: f64, last_index: f64) -> f64 {
-        // Calculate the Blackman window function for the given center offset
-        // w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)),
-        // where n is the center offset, N is the window size,
-        // and A0, A1, A2 are precalculated coefficients
-        let two_pi_n = TWO_TIMES_PI * index;
-        let four_pi_n = FOUR_TIMES_PI * index;
-
-        BLACKMAN_A0 - BLACKMAN_A1 * (two_pi_n / last_index).cos()
-            + BLACKMAN_A2 * (four_pi_n / last_index).cos()
-    }
-
-    fn fir_filter(
-        index: f64,
-        last_index: f64,
-        sinc_center: f64,
-        resample_factor_reciprocal: f64,
-    ) -> f64 {
-        // The resample_factor_reciprocal also happens to be our
-        // anti-alias cutoff. In this case it represents the minimum
-        // output bandwidth needed to fully represent the input.
-        let adjusted_sinc_center_offset = (index - sinc_center) * resample_factor_reciprocal;
-
-        let sinc_value = Self::sinc(adjusted_sinc_center_offset);
-        let blackman_window_value = Self::blackman(index, last_index);
-
-        sinc_value * blackman_window_value
-    }
 }
 
 #[derive(Clone, Copy, Debug, Default)]
@@ -262,10 +152,12 @@ impl std::fmt::Display for SampleRate {
     }
 }
 
-#[derive(Clone, Copy, Debug, Default)]
+#[derive(Debug, Default)]
 pub struct ResampleSpec {
     pub resample_factor_reciprocal: f64,
     pub interpolation_output_size: usize,
+    pub high_coefficients: Vec<f64>,
+    pub low_coefficients: Vec<f64>,
 }
 
 impl SampleRate {
@@ -328,19 +220,27 @@ impl SampleRate {
                 ResampleSpec {
                     resample_factor_reciprocal: 1.0,
                     interpolation_output_size: RESAMPLER_INPUT_SIZE,
+                    high_coefficients: vec![],
+                    low_coefficients: vec![],
                 }
             }
             Hz48000 => ResampleSpec {
                 resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL,
                 interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE,
+                high_coefficients: HZ48000_HIGH.to_vec(),
+                low_coefficients: HZ48000_LOW.to_vec(),
             },
             Hz88200 => ResampleSpec {
                 resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL,
                 interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE,
+                high_coefficients: HZ88200_HIGH.to_vec(),
+                low_coefficients: HZ88200_LOW.to_vec(),
             },
             Hz96000 => ResampleSpec {
                 resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL,
                 interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE,
+                high_coefficients: HZ96000_HIGH.to_vec(),
+                low_coefficients: HZ96000_LOW.to_vec(),
             },
         }
     }
diff --git a/playback/src/filter_coefficients.rs b/playback/src/filter_coefficients.rs
new file mode 100644
index 00000000..96a2c42b
--- /dev/null
+++ b/playback/src/filter_coefficients.rs
@@ -0,0 +1,1187 @@
+// All Windows calculated with pyfda (Python Filter Design Analysis Tool)
+// https://github.com/chipmuenk/pyfda
+// Window = Kaiser
+// beta = 8.6 (Similar to a Blackman Window)
+// fc = 22.5kHz
+// -86dB by 23kHz
+#[allow(clippy::excessive_precision)]
+pub const HZ48000_HIGH: [f64; 257] = [
+    -1.4287799853519804e-19,
+    -8.529928242661527e-07,
+    2.1395999264221267e-06,
+    -3.880458963785995e-06,
+    6.05893601403254e-06,
+    -8.613527303281799e-06,
+    1.1432294466108613e-05,
+    -1.4350136921309217e-05,
+    1.7149627991629572e-05,
+    -1.956600058898423e-05,
+    2.1296664705444228e-05,
+    -2.201537812656286e-05,
+    2.1390883855947267e-05,
+    -1.9109487313809015e-05,
+    1.490069206906713e-05,
+    -8.564666558168402e-06,
+    -2.4187562985455694e-19,
+    1.0770051522629687e-05,
+    -2.357480377576126e-05,
+    3.80789154370508e-05,
+    -5.37726902353467e-05,
+    6.997172608513909e-05,
+    -8.582740473543781e-05,
+    0.0001003492559243388,
+    -0.00011243964464523357,
+    0.00012094053924787859,
+    -0.0001246913509069703,
+    0.00012259602869291528,
+    -0.00011369679289372431,
+    9.725114087319391e-05,
+    -7.280811540502643e-05,
+    4.027933539700687e-05,
+    -8.761722338284784e-19,
+    -4.7224970562679786e-05,
+    0.0001000942700094669,
+    -0.00015680711547266284,
+    0.00021508702286534775,
+    -0.00027223465216542913,
+    0.00032521058924288055,
+    -0.00037074739096535606,
+    0.00040548854166410335,
+    -0.00042615022958145474,
+    0.0004297001408507999,
+    -0.00041354588242148774,
+    0.00037572428338265103,
+    -0.0003150817831073127,
+    0.00023143548325566652,
+    -0.00012570429408715404,
+    8.736848542712518e-18,
+    0.00014233096258373752,
+    -0.00029672267470682895,
+    0.00045746741021428363,
+    -0.0006178587763843014,
+    0.0007704002699025563,
+    -0.0009070758950716863,
+    0.0010196760363510072,
+    -0.001100168333253743,
+    0.0011411000481453986,
+    -0.0011360155279515375,
+    0.0010798700171927062,
+    -0.0009694194473815131,
+    0.0008035650497546995,
+    -0.0005836318265664195,
+    0.0003135611435773604,
+    -3.649849800074037e-17,
+    -0.0003477271314898191,
+    0.0007177736996011124,
+    -0.0010960583702874734,
+    0.0014666830951029159,
+    -0.0018124745987316703,
+    0.0021156355406508768,
+    -0.0023584853377038536,
+    0.002524264795160409,
+    -0.0025979735902941564,
+    0.0025672055778467244,
+    -0.002422944105830126,
+    0.0021602782649567656,
+    -0.0017790014114850766,
+    0.001284055499878637,
+    -0.0006857887606066072,
+    7.209995999923543e-18,
+    0.000752249860282943,
+    -0.0015450696726810457,
+    0.0023484142653889145,
+    -0.003128995352023552,
+    0.00385140677724417,
+    -0.004479433137231434,
+    0.004977500880593015,
+    -0.005312222191564676,
+    0.005453974732678532,
+    -0.005378455081534177,
+    0.005068140772503758,
+    -0.004513595500789128,
+    0.003714554414731469,
+    -0.0026807315526779464,
+    0.0014322992948101853,
+    -8.297338214945322e-17,
+    -0.001575137531433456,
+    0.003242489907858635,
+    -0.004942936992960944,
+    0.006610347331237228,
+    -0.008173416627546943,
+    0.009557809308226558,
+    -0.010688539065884867,
+    0.011492512118343826,
+    -0.011901147266026046,
+    0.01185298019699458,
+    -0.011296156240663045,
+    0.01019071615665419,
+    -0.00851058366772981,
+    0.006245171256720193,
+    -0.0034005320447053696,
+    1.0063905996254467e-16,
+    0.003915722194550309,
+    -0.008289049174613953,
+    0.013046573943979376,
+    -0.01810068406300195,
+    0.023351692368405477,
+    -0.02869042197225499,
+    0.03400116752010972,
+    -0.03916493952679325,
+    0.044062886688337265,
+    -0.04857978290438118,
+    0.052607461706064784,
+    -0.056048081078209105,
+    0.058817106327538726,
+    -0.06084590754218613,
+    0.06208388100251895,
+    0.9375003025680546,
+    0.06208388100251895,
+    -0.06084590754218613,
+    0.058817106327538726,
+    -0.056048081078209105,
+    0.052607461706064784,
+    -0.04857978290438118,
+    0.044062886688337265,
+    -0.03916493952679325,
+    0.03400116752010972,
+    -0.02869042197225499,
+    0.023351692368405477,
+    -0.01810068406300195,
+    0.013046573943979376,
+    -0.008289049174613953,
+    0.003915722194550309,
+    1.0063905996254467e-16,
+    -0.0034005320447053696,
+    0.006245171256720193,
+    -0.00851058366772981,
+    0.01019071615665419,
+    -0.011296156240663045,
+    0.01185298019699458,
+    -0.011901147266026046,
+    0.011492512118343826,
+    -0.010688539065884867,
+    0.009557809308226558,
+    -0.008173416627546943,
+    0.006610347331237228,
+    -0.004942936992960944,
+    0.003242489907858635,
+    -0.001575137531433456,
+    -8.297338214945322e-17,
+    0.0014322992948101853,
+    -0.0026807315526779464,
+    0.003714554414731469,
+    -0.004513595500789128,
+    0.005068140772503758,
+    -0.005378455081534177,
+    0.005453974732678532,
+    -0.005312222191564676,
+    0.004977500880593015,
+    -0.004479433137231434,
+    0.00385140677724417,
+    -0.003128995352023552,
+    0.0023484142653889145,
+    -0.0015450696726810457,
+    0.000752249860282943,
+    7.209995999923543e-18,
+    -0.0006857887606066072,
+    0.001284055499878637,
+    -0.0017790014114850766,
+    0.0021602782649567656,
+    -0.002422944105830126,
+    0.0025672055778467244,
+    -0.0025979735902941564,
+    0.002524264795160409,
+    -0.0023584853377038536,
+    0.0021156355406508768,
+    -0.0018124745987316703,
+    0.0014666830951029159,
+    -0.0010960583702874734,
+    0.0007177736996011124,
+    -0.0003477271314898191,
+    -3.649849800074037e-17,
+    0.0003135611435773604,
+    -0.0005836318265664195,
+    0.0008035650497546995,
+    -0.0009694194473815131,
+    0.0010798700171927062,
+    -0.0011360155279515375,
+    0.0011411000481453986,
+    -0.001100168333253743,
+    0.0010196760363510072,
+    -0.0009070758950716863,
+    0.0007704002699025563,
+    -0.0006178587763843014,
+    0.00045746741021428363,
+    -0.00029672267470682895,
+    0.00014233096258373752,
+    8.736848542712518e-18,
+    -0.00012570429408715404,
+    0.00023143548325566652,
+    -0.0003150817831073127,
+    0.00037572428338265103,
+    -0.00041354588242148774,
+    0.0004297001408507999,
+    -0.00042615022958145474,
+    0.00040548854166410335,
+    -0.00037074739096535606,
+    0.00032521058924288055,
+    -0.00027223465216542913,
+    0.00021508702286534775,
+    -0.00015680711547266284,
+    0.0001000942700094669,
+    -4.7224970562679786e-05,
+    -8.761722338284784e-19,
+    4.027933539700687e-05,
+    -7.280811540502643e-05,
+    9.725114087319391e-05,
+    -0.00011369679289372431,
+    0.00012259602869291528,
+    -0.0001246913509069703,
+    0.00012094053924787859,
+    -0.00011243964464523357,
+    0.0001003492559243388,
+    -8.582740473543781e-05,
+    6.997172608513909e-05,
+    -5.37726902353467e-05,
+    3.80789154370508e-05,
+    -2.357480377576126e-05,
+    1.0770051522629687e-05,
+    -2.4187562985455694e-19,
+    -8.564666558168402e-06,
+    1.490069206906713e-05,
+    -1.9109487313809015e-05,
+    2.1390883855947267e-05,
+    -2.201537812656286e-05,
+    2.1296664705444228e-05,
+    -1.956600058898423e-05,
+    1.7149627991629572e-05,
+    -1.4350136921309217e-05,
+    1.1432294466108613e-05,
+    -8.613527303281799e-06,
+    6.05893601403254e-06,
+    -3.880458963785995e-06,
+    2.1395999264221267e-06,
+    -8.529928242661527e-07,
+    -1.4287799853519804e-19,
+];
+
+#[allow(clippy::excessive_precision)]
+pub const HZ88200_HIGH: [f64; 257] = [
+    -2.7177973650537016e-06,
+    2.615116391456718e-06,
+    4.371253470079787e-06,
+    -4.527642205236769e-06,
+    -6.343166732321034e-06,
+    7.206853357278462e-06,
+    8.608531809529578e-06,
+    -1.0831207319855358e-05,
+    -1.11168738348255e-05,
+    1.5597004565971875e-05,
+    1.3787248972132889e-05,
+    -2.1716246512168148e-05,
+    -1.6503125021559895e-05,
+    2.9413629227200937e-05,
+    1.9107254724178818e-05,
+    -3.8922617925675015e-05,
+    -2.139666772922098e-05,
+    5.04805630067818e-05,
+    2.3117918532840763e-05,
+    -6.432283649476294e-05,
+    -2.396273660981686e-05,
+    8.067598787603222e-05,
+    2.3564231002785538e-05,
+    -9.974994011030785e-05,
+    -2.149380441843341e-05,
+    0.00012172926954179013,
+    1.7258931076241913e-05,
+    -0.00014676363699943706,
+    -1.030194790401862e-05,
+    0.0001749574609349626,
+    1.8686890713852827e-19,
+    -0.0002063589463295164,
+    1.4333731499066454e-05,
+    0.00024094860458611302,
+    -3.344740787382722e-05,
+    -0.0002786274189628284,
+    5.814642716712084e-05,
+    0.00031920482651865724,
+    -8.928776855240112e-05,
+    -0.0003623867002575848,
+    0.0001277727488249831,
+    0.0004077635233859557,
+    -0.00017453818403045162,
+    -0.00045479895057210033,
+    0.0002305459825657413,
+    0.000502818948057389,
+    -0.00029677123118056025,
+    -0.000551001694669649,
+    0.00037418887145730905,
+    0.0005983684084941591,
+    -0.00046375910077735565,
+    -0.0006437752384124257,
+    0.0005664116676635973,
+    0.0006859063251323009,
+    -0.0006830292658687915,
+    -0.0007232680918226466,
+    0.0008144302637888361,
+    0.000754184768991671,
+    -0.0009613510348663591,
+    -0.0007767950905092509,
+    0.0011244281797910076,
+    0.0007890500159662434,
+    -0.0013041809517326277,
+    -0.000788711236573936,
+    0.0015009942108703117,
+    0.00077335010432126,
+    -0.00171510224350985,
+    -0.0007403464826028671,
+    0.0019465737836333772,
+    0.0006868868445776879,
+    -0.002195298570445892,
+    -0.0006099607339225542,
+    0.002460975764167073,
+    0.0005063544381987515,
+    -0.002743104523907686,
+    -0.00037264038864942025,
+    0.003040977026074337,
+    0.00020516036410315268,
+    -0.0033536741696310896,
+    -1.7263210195899785e-18,
+    0.0036800641761425457,
+    -0.0002470486740043912,
+    -0.004018804248416292,
+    0.0005405410133353366,
+    0.004368345402486541,
+    -0.000885455697808869,
+    -0.004726940534504569,
+    0.0012873008119620815,
+    0.005092655727810584,
+    -0.001752261157027223,
+    -0.005463384747140825,
+    0.0022874024955170562,
+    0.005836866607731414,
+    -0.002900955402436162,
+    -0.006210706048228085,
+    0.0036027121698355103,
+    0.006582396679029297,
+    -0.0044045872407267005,
+    -0.006949346523208571,
+    0.005321419255211435,
+    0.007308905616676035,
+    -0.00637213881767667,
+    -0.007658395288879785,
+    0.007581505281049055,
+    0.00799513870616462,
+    -0.008982757034954967,
+    -0.008316492227824646,
+    0.010621782037025395,
+    0.00861987710070591,
+    -0.012563926080135948,
+    -0.008902811002566763,
+    0.01490560790319659,
+    0.0091629389377553,
+    -0.017795223882502632,
+    -0.009398062991380806,
+    0.021473342249052296,
+    0.009606170460125666,
+    -0.026356732255292163,
+    -0.009785459899039199,
+    0.033234410282523816,
+    0.009934364653757865,
+    -0.04379972993086113,
+    -0.010051573486085123,
+    0.06245943058441552,
+    0.01013604794705137,
+    -0.1053787714300839,
+    -0.010187036204577683,
+    0.31806791600342954,
+    0.5102041545837825,
+    0.31806791600342954,
+    -0.010187036204577683,
+    -0.1053787714300839,
+    0.01013604794705137,
+    0.06245943058441552,
+    -0.010051573486085123,
+    -0.04379972993086113,
+    0.009934364653757865,
+    0.033234410282523816,
+    -0.009785459899039199,
+    -0.026356732255292163,
+    0.009606170460125666,
+    0.021473342249052296,
+    -0.009398062991380806,
+    -0.017795223882502632,
+    0.0091629389377553,
+    0.01490560790319659,
+    -0.008902811002566763,
+    -0.012563926080135948,
+    0.00861987710070591,
+    0.010621782037025395,
+    -0.008316492227824646,
+    -0.008982757034954967,
+    0.00799513870616462,
+    0.007581505281049055,
+    -0.007658395288879785,
+    -0.00637213881767667,
+    0.007308905616676035,
+    0.005321419255211435,
+    -0.006949346523208571,
+    -0.0044045872407267005,
+    0.006582396679029297,
+    0.0036027121698355103,
+    -0.006210706048228085,
+    -0.002900955402436162,
+    0.005836866607731414,
+    0.0022874024955170562,
+    -0.005463384747140825,
+    -0.001752261157027223,
+    0.005092655727810584,
+    0.0012873008119620815,
+    -0.004726940534504569,
+    -0.000885455697808869,
+    0.004368345402486541,
+    0.0005405410133353366,
+    -0.004018804248416292,
+    -0.0002470486740043912,
+    0.0036800641761425457,
+    -1.7263210195899785e-18,
+    -0.0033536741696310896,
+    0.00020516036410315268,
+    0.003040977026074337,
+    -0.00037264038864942025,
+    -0.002743104523907686,
+    0.0005063544381987515,
+    0.002460975764167073,
+    -0.0006099607339225542,
+    -0.002195298570445892,
+    0.0006868868445776879,
+    0.0019465737836333772,
+    -0.0007403464826028671,
+    -0.00171510224350985,
+    0.00077335010432126,
+    0.0015009942108703117,
+    -0.000788711236573936,
+    -0.0013041809517326277,
+    0.0007890500159662434,
+    0.0011244281797910076,
+    -0.0007767950905092509,
+    -0.0009613510348663591,
+    0.000754184768991671,
+    0.0008144302637888361,
+    -0.0007232680918226466,
+    -0.0006830292658687915,
+    0.0006859063251323009,
+    0.0005664116676635973,
+    -0.0006437752384124257,
+    -0.00046375910077735565,
+    0.0005983684084941591,
+    0.00037418887145730905,
+    -0.000551001694669649,
+    -0.00029677123118056025,
+    0.000502818948057389,
+    0.0002305459825657413,
+    -0.00045479895057210033,
+    -0.00017453818403045162,
+    0.0004077635233859557,
+    0.0001277727488249831,
+    -0.0003623867002575848,
+    -8.928776855240112e-05,
+    0.00031920482651865724,
+    5.814642716712084e-05,
+    -0.0002786274189628284,
+    -3.344740787382722e-05,
+    0.00024094860458611302,
+    1.4333731499066454e-05,
+    -0.0002063589463295164,
+    1.8686890713852827e-19,
+    0.0001749574609349626,
+    -1.030194790401862e-05,
+    -0.00014676363699943706,
+    1.7258931076241913e-05,
+    0.00012172926954179013,
+    -2.149380441843341e-05,
+    -9.974994011030785e-05,
+    2.3564231002785538e-05,
+    8.067598787603222e-05,
+    -2.396273660981686e-05,
+    -6.432283649476294e-05,
+    2.3117918532840763e-05,
+    5.04805630067818e-05,
+    -2.139666772922098e-05,
+    -3.8922617925675015e-05,
+    1.9107254724178818e-05,
+    2.9413629227200937e-05,
+    -1.6503125021559895e-05,
+    -2.1716246512168148e-05,
+    1.3787248972132889e-05,
+    1.5597004565971875e-05,
+    -1.11168738348255e-05,
+    -1.0831207319855358e-05,
+    8.608531809529578e-06,
+    7.206853357278462e-06,
+    -6.343166732321034e-06,
+    -4.527642205236769e-06,
+    4.371253470079787e-06,
+    2.615116391456718e-06,
+    -2.7177973650537016e-06,
+];
+
+#[allow(clippy::excessive_precision)]
+pub const HZ96000_HIGH: [f64; 257] = [
+    -7.143923102926616e-20,
+    -4.351257283515671e-06,
+    -1.0907621221693655e-06,
+    6.683906965798109e-06,
+    3.2790831797631692e-06,
+    -9.13620584774856e-06,
+    -6.874774126129371e-06,
+    1.1310163453885296e-05,
+    1.2126657588758059e-05,
+    -1.265575645173113e-05,
+    -1.9166554046744026e-05,
+    1.248152779539428e-05,
+    2.794862730250771e-05,
+    -9.984713045871162e-06,
+    -3.8189337778906546e-05,
+    4.303067618516991e-06,
+    4.931445698739215e-05,
+    5.411099152784563e-06,
+    -6.042042478905683e-05,
+    -1.989624512124905e-05,
+    7.025763351163002e-05,
+    3.967018140695091e-05,
+    -7.72428741451272e-05,
+    -6.490829524996992e-05,
+    7.9507093138109e-05,
+    9.532015440656505e-05,
+    -7.498274957040692e-05,
+    -0.00013003529388340405,
+    6.153246053554203e-05,
+    0.0001675104888314477,
+    -3.711737577180626e-05,
+    -0.00020547154270862667,
+    4.380875381487169e-19,
+    0.0002409023748839593,
+    5.102778188775271e-05,
+    -0.0002700928372585857,
+    -0.00011640463560427953,
+    0.00028875415767545045,
+    0.00019556435946416691,
+    -0.0002922072182944144,
+    -0.0002867246276901985,
+    0.0002756441457161496,
+    0.0003867211644368446,
+    -0.0002344581317859229,
+    -0.0004909090262693411,
+    0.00016463032935658855,
+    0.0005931514994811955,
+    -6.315646659694906e-05,
+    -0.0006859142317553148,
+    -7.151005261521953e-05,
+    0.0007604775938269121,
+    0.0002390268636629881,
+    -0.0008072740136891249,
+    -0.00043677525441936056,
+    0.0008163493865283938,
+    0.0006595508119830226,
+    -0.0007779390126639911,
+    -0.0008993661964713373,
+    0.0006831393454389286,
+    0.00114539774688185,
+    -0.0005246477263799965,
+    -0.00138410277847316,
+    0.00029753389026130304,
+    0.0015995271835870914,
+    -1.8249308204396214e-17,
+    -0.001773813531049159,
+    -0.0003659190459608399,
+    0.0018879086841156133,
+    0.0007937657463383715,
+    -0.0019224576000959562,
+    -0.0012722307423329708,
+    0.0018588571537189837,
+    0.0017849305448029394,
+    -0.0016804313624166863,
+    -0.002310431475418928,
+    0.0013736781639519724,
+    0.0028225487316737353,
+    -0.0009295287890346657,
+    -0.0032909363527835883,
+    0.0003445546173767534,
+    0.003681968512252244,
+    0.0003779460639632543,
+    -0.00395989577856263,
+    -0.0012270471817312566,
+    0.004088242707216325,
+    0.0021835391818022633,
+    -0.004031396395209965,
+    -0.003219566441111553,
+    0.0037563205210019465,
+    0.004298589347141008,
+    -0.003234316955594173,
+    -0.005375681266525964,
+    0.0024427482073774184,
+    0.00639814422962896,
+    -0.0013666295279113297,
+    -0.007306395272526002,
+    4.1486825665423373e-17,
+    0.008035036709816487,
+    0.0016530123829845687,
+    -0.008513975101161425,
+    -0.0035775058023473096,
+    0.008669388760192265,
+    0.005747558403911614,
+    -0.008424248812489233,
+    -0.008126459615440715,
+    0.007697946272157984,
+    0.01066743495767241,
+    -0.006404309173724075,
+    -0.013314855511628901,
+    0.004446782604876781,
+    0.016005897813639956,
+    -0.00170849842523587,
+    -0.018672595414079837,
+    -0.001967340732302961,
+    0.021244201096702293,
+    0.006816838930484501,
+    -0.02364976015970634,
+    -0.013239145641295004,
+    0.02582078137402188,
+    0.021992767160156825,
+    -0.027693884168079493,
+    -0.03472848053143565,
+    0.02921329883114049,
+    0.05579974177257601,
+    -0.03033310130622086,
+    -0.10130968278740993,
+    0.03101907530302571,
+    0.3167001312508388,
+    0.46875167199845,
+    0.3167001312508388,
+    0.03101907530302571,
+    -0.10130968278740993,
+    -0.03033310130622086,
+    0.05579974177257601,
+    0.02921329883114049,
+    -0.03472848053143565,
+    -0.027693884168079493,
+    0.021992767160156825,
+    0.02582078137402188,
+    -0.013239145641295004,
+    -0.02364976015970634,
+    0.006816838930484501,
+    0.021244201096702293,
+    -0.001967340732302961,
+    -0.018672595414079837,
+    -0.00170849842523587,
+    0.016005897813639956,
+    0.004446782604876781,
+    -0.013314855511628901,
+    -0.006404309173724075,
+    0.01066743495767241,
+    0.007697946272157984,
+    -0.008126459615440715,
+    -0.008424248812489233,
+    0.005747558403911614,
+    0.008669388760192265,
+    -0.0035775058023473096,
+    -0.008513975101161425,
+    0.0016530123829845687,
+    0.008035036709816487,
+    4.1486825665423373e-17,
+    -0.007306395272526002,
+    -0.0013666295279113297,
+    0.00639814422962896,
+    0.0024427482073774184,
+    -0.005375681266525964,
+    -0.003234316955594173,
+    0.004298589347141008,
+    0.0037563205210019465,
+    -0.003219566441111553,
+    -0.004031396395209965,
+    0.0021835391818022633,
+    0.004088242707216325,
+    -0.0012270471817312566,
+    -0.00395989577856263,
+    0.0003779460639632543,
+    0.003681968512252244,
+    0.0003445546173767534,
+    -0.0032909363527835883,
+    -0.0009295287890346657,
+    0.0028225487316737353,
+    0.0013736781639519724,
+    -0.002310431475418928,
+    -0.0016804313624166863,
+    0.0017849305448029394,
+    0.0018588571537189837,
+    -0.0012722307423329708,
+    -0.0019224576000959562,
+    0.0007937657463383715,
+    0.0018879086841156133,
+    -0.0003659190459608399,
+    -0.001773813531049159,
+    -1.8249308204396214e-17,
+    0.0015995271835870914,
+    0.00029753389026130304,
+    -0.00138410277847316,
+    -0.0005246477263799965,
+    0.00114539774688185,
+    0.0006831393454389286,
+    -0.0008993661964713373,
+    -0.0007779390126639911,
+    0.0006595508119830226,
+    0.0008163493865283938,
+    -0.00043677525441936056,
+    -0.0008072740136891249,
+    0.0002390268636629881,
+    0.0007604775938269121,
+    -7.151005261521953e-05,
+    -0.0006859142317553148,
+    -6.315646659694906e-05,
+    0.0005931514994811955,
+    0.00016463032935658855,
+    -0.0004909090262693411,
+    -0.0002344581317859229,
+    0.0003867211644368446,
+    0.0002756441457161496,
+    -0.0002867246276901985,
+    -0.0002922072182944144,
+    0.00019556435946416691,
+    0.00028875415767545045,
+    -0.00011640463560427953,
+    -0.0002700928372585857,
+    5.102778188775271e-05,
+    0.0002409023748839593,
+    4.380875381487169e-19,
+    -0.00020547154270862667,
+    -3.711737577180626e-05,
+    0.0001675104888314477,
+    6.153246053554203e-05,
+    -0.00013003529388340405,
+    -7.498274957040692e-05,
+    9.532015440656505e-05,
+    7.9507093138109e-05,
+    -6.490829524996992e-05,
+    -7.72428741451272e-05,
+    3.967018140695091e-05,
+    7.025763351163002e-05,
+    -1.989624512124905e-05,
+    -6.042042478905683e-05,
+    5.411099152784563e-06,
+    4.931445698739215e-05,
+    4.303067618516991e-06,
+    -3.8189337778906546e-05,
+    -9.984713045871162e-06,
+    2.794862730250771e-05,
+    1.248152779539428e-05,
+    -1.9166554046744026e-05,
+    -1.265575645173113e-05,
+    1.2126657588758059e-05,
+    1.1310163453885296e-05,
+    -6.874774126129371e-06,
+    -9.13620584774856e-06,
+    3.2790831797631692e-06,
+    6.683906965798109e-06,
+    -1.0907621221693655e-06,
+    -4.351257283515671e-06,
+    -7.143923102926616e-20,
+];
+
+#[allow(clippy::excessive_precision)]
+pub const HZ48000_LOW: [f64; 129] = [
+    -1.4287804154623814e-19,
+    -2.181517823792544e-06,
+    6.5581470578797384e-06,
+    -1.3749507785319245e-05,
+    2.4253243796448185e-05,
+    -3.8332995273522084e-05,
+    5.589709009118714e-05,
+    -7.637845076415545e-05,
+    9.862862369540776e-05,
+    -0.00012084049392574903,
+    0.00014051485346619586,
+    -0.0001544852936160124,
+    0.00015901371827411808,
+    -0.0001499650577703289,
+    0.00012306455887295043,
+    -7.423453305983806e-05,
+    8.761724975855263e-19,
+    0.00010205526341099535,
+    -0.0002328085860166881,
+    0.00039112756777908054,
+    -0.0005734475676350329,
+    0.0007734400525193427,
+    -0.000981815162903983,
+    0.001186299507498468,
+    -0.0013718244260180216,
+    0.0015209507112593692,
+    -0.0016145432755263242,
+    0.0016326939677845523,
+    -0.0015558734461504878,
+    0.001366274669719045,
+    -0.0010492923645293963,
+    0.000595066029150748,
+    -3.649850898800486e-17,
+    -0.0007318359380147118,
+    0.0015875268203384464,
+    -0.0025444539959421624,
+    0.0035698505829804923,
+    -0.004620849350959662,
+    0.005645080848996707,
+    -0.006581853334149319,
+    0.007363915351356501,
+    -0.007919768248015761,
+    0.008176461349835438,
+    -0.008062769060436804,
+    0.007512618931198147,
+    -0.006468614873048888,
+    0.004885482036021499,
+    -0.002733251011439905,
+    8.297340712720699e-17,
+    0.0033060150358527928,
+    -0.007154990546444822,
+    0.011495082976006515,
+    -0.016252871396153546,
+    0.021334807123689837,
+    -0.026629632648107943,
+    0.032011701411864754,
+    -0.03734508091577928,
+    0.042488277143797916,
+    -0.047299381109978895,
+    0.051641410759341526,
+    -0.05538760532182361,
+    0.05842642570422467,
+    -0.06066602406289918,
+    0.06203796801866447,
+    0.9375005847868952,
+    0.06203796801866447,
+    -0.06066602406289918,
+    0.05842642570422467,
+    -0.05538760532182361,
+    0.051641410759341526,
+    -0.047299381109978895,
+    0.042488277143797916,
+    -0.03734508091577928,
+    0.032011701411864754,
+    -0.026629632648107943,
+    0.021334807123689837,
+    -0.016252871396153546,
+    0.011495082976006515,
+    -0.007154990546444822,
+    0.0033060150358527928,
+    8.297340712720699e-17,
+    -0.002733251011439905,
+    0.004885482036021499,
+    -0.006468614873048888,
+    0.007512618931198147,
+    -0.008062769060436804,
+    0.008176461349835438,
+    -0.007919768248015761,
+    0.007363915351356501,
+    -0.006581853334149319,
+    0.005645080848996707,
+    -0.004620849350959662,
+    0.0035698505829804923,
+    -0.0025444539959421624,
+    0.0015875268203384464,
+    -0.0007318359380147118,
+    -3.649850898800486e-17,
+    0.000595066029150748,
+    -0.0010492923645293963,
+    0.001366274669719045,
+    -0.0015558734461504878,
+    0.0016326939677845523,
+    -0.0016145432755263242,
+    0.0015209507112593692,
+    -0.0013718244260180216,
+    0.001186299507498468,
+    -0.000981815162903983,
+    0.0007734400525193427,
+    -0.0005734475676350329,
+    0.00039112756777908054,
+    -0.0002328085860166881,
+    0.00010205526341099535,
+    8.761724975855263e-19,
+    -7.423453305983806e-05,
+    0.00012306455887295043,
+    -0.0001499650577703289,
+    0.00015901371827411808,
+    -0.0001544852936160124,
+    0.00014051485346619586,
+    -0.00012084049392574903,
+    9.862862369540776e-05,
+    -7.637845076415545e-05,
+    5.589709009118714e-05,
+    -3.8332995273522084e-05,
+    2.4253243796448185e-05,
+    -1.3749507785319245e-05,
+    6.5581470578797384e-06,
+    -2.181517823792544e-06,
+    -1.4287804154623814e-19,
+];
+
+#[allow(clippy::excessive_precision)]
+pub const HZ88200_LOW: [f64; 129] = [
+    5.875800344814529e-06,
+    4.851699044013074e-06,
+    -1.5670438235895964e-05,
+    -9.287225758882044e-06,
+    3.2188588517572685e-05,
+    1.452725442412174e-05,
+    -5.80015310113526e-05,
+    -1.975318175268538e-05,
+    9.615523116437566e-05,
+    2.3552114164532385e-05,
+    -0.00015014174552603567,
+    -2.375916580017386e-05,
+    0.00022383889492568076,
+    1.729437074025395e-05,
+    -0.00032141607023776947,
+    -1.8686789504441635e-19,
+    0.00044720587729591056,
+    -3.351608912018519e-05,
+    -0.0006055434276587542,
+    9.002640046744051e-05,
+    0.0008005773235174634,
+    -0.00017781632159122524,
+    -0.0010360586296973909,
+    0.00030680368023040425,
+    0.0013151162221446207,
+    -0.0004886505249968164,
+    -0.0016400286962743556,
+    0.0007368790790811487,
+    0.0020120043638640903,
+    -0.0010670133701345262,
+    -0.0024309816366330315,
+    0.0014967816965620656,
+    0.002895462190517508,
+    -0.002046435147324143,
+    -0.0034023886674580135,
+    0.002739267989293373,
+    0.003947077286631264,
+    -0.003602474524851333,
+    -0.0045232136384107564,
+    0.0046685585578356776,
+    0.005122917204805467,
+    -0.005977654559924746,
+    -0.005736876918489128,
+    0.007581382917565772,
+    0.006354556507022021,
+    -0.009549372598113655,
+    -0.006964464667634032,
+    0.011980635507338782,
+    0.007554481497059819,
+    -0.015024288868017965,
+    -0.008112229280669358,
+    0.018919636740583255,
+    0.008625472935527727,
+    -0.024080164100849143,
+    -0.009082533288527662,
+    0.031289629556764974,
+    0.009472695101363785,
+    -0.042234282967316725,
+    -0.009786591428611496,
+    0.06131211363458383,
+    0.01001654655736988,
+    -0.10467821599139523,
+    -0.010156861410646928,
+    0.31783087768940993,
+    0.5102013912850153,
+    0.31783087768940993,
+    -0.010156861410646928,
+    -0.10467821599139523,
+    0.01001654655736988,
+    0.06131211363458383,
+    -0.009786591428611496,
+    -0.042234282967316725,
+    0.009472695101363785,
+    0.031289629556764974,
+    -0.009082533288527662,
+    -0.024080164100849143,
+    0.008625472935527727,
+    0.018919636740583255,
+    -0.008112229280669358,
+    -0.015024288868017965,
+    0.007554481497059819,
+    0.011980635507338782,
+    -0.006964464667634032,
+    -0.009549372598113655,
+    0.006354556507022021,
+    0.007581382917565772,
+    -0.005736876918489128,
+    -0.005977654559924746,
+    0.005122917204805467,
+    0.0046685585578356776,
+    -0.0045232136384107564,
+    -0.003602474524851333,
+    0.003947077286631264,
+    0.002739267989293373,
+    -0.0034023886674580135,
+    -0.002046435147324143,
+    0.002895462190517508,
+    0.0014967816965620656,
+    -0.0024309816366330315,
+    -0.0010670133701345262,
+    0.0020120043638640903,
+    0.0007368790790811487,
+    -0.0016400286962743556,
+    -0.0004886505249968164,
+    0.0013151162221446207,
+    0.00030680368023040425,
+    -0.0010360586296973909,
+    -0.00017781632159122524,
+    0.0008005773235174634,
+    9.002640046744051e-05,
+    -0.0006055434276587542,
+    -3.351608912018519e-05,
+    0.00044720587729591056,
+    -1.8686789504441635e-19,
+    -0.00032141607023776947,
+    1.729437074025395e-05,
+    0.00022383889492568076,
+    -2.375916580017386e-05,
+    -0.00015014174552603567,
+    2.3552114164532385e-05,
+    9.615523116437566e-05,
+    -1.975318175268538e-05,
+    -5.80015310113526e-05,
+    1.452725442412174e-05,
+    3.2188588517572685e-05,
+    -9.287225758882044e-06,
+    -1.5670438235895964e-05,
+    4.851699044013074e-06,
+    5.875800344814529e-06,
+];
+
+#[allow(clippy::excessive_precision)]
+pub const HZ96000_LOW: [f64; 129] = [
+    -7.143944801213435e-20,
+    -1.1128313185646072e-05,
+    -3.3433343718178e-06,
+    2.368294142133405e-05,
+    1.3125839456769717e-05,
+    -4.065919588349948e-05,
+    -3.361363034503314e-05,
+    6.0198389114399714e-05,
+    6.974138571798226e-05,
+    -7.816273123033217e-05,
+    -0.000126460783407638,
+    8.758503947913682e-05,
+    0.0002077626777155509,
+    -7.835700358335798e-05,
+    -0.0003154059452699033,
+    3.729708416324331e-05,
+    0.0004474410769117699,
+    5.1274839240568415e-05,
+    -0.0005966722898292008,
+    -0.00020436483462002328,
+    0.0007492498350107482,
+    0.0004384998464839666,
+    -0.0008836155865344651,
+    -0.0007673289520005847,
+    0.000970032155449486,
+    0.0011987515041766601,
+    -0.0009709031498968383,
+    -0.0017317724164334631,
+    0.0008420376771297366,
+    0.0023533499143115976,
+    -0.0005349278077261196,
+    -0.00303553840647302,
+    1.824936363314565e-17,
+    0.003733226210629766,
+    0.0008093189947979181,
+    -0.004382713612447862,
+    -0.0019320007678197645,
+    0.004901261108562161,
+    0.0033946608064293073,
+    -0.00518754915999129,
+    -0.005207105621787521,
+    0.0051227092580119855,
+    0.007358664812266444,
+    -0.004571166160481191,
+    -0.009815768367302802,
+    0.003379862524796346,
+    0.012521152261727227,
+    -0.0013732462347513066,
+    -0.015394948058705999,
+    -0.0016610156480375167,
+    0.018337745467632928,
+    0.006006200853277362,
+    -0.021235526836812676,
+    -0.012095713970371423,
+    0.02396617954234677,
+    0.020705989626446524,
+    -0.02640711788557473,
+    -0.03348753234339166,
+    0.028443411089665006,
+    0.05477521964516673,
+    -0.029975735264560804,
+    -0.10063702926794785,
+    0.030927455828749166,
+    0.3164667874739216,
+    0.4687530957411302,
+    0.3164667874739216,
+    0.030927455828749166,
+    -0.10063702926794785,
+    -0.029975735264560804,
+    0.05477521964516673,
+    0.028443411089665006,
+    -0.03348753234339166,
+    -0.02640711788557473,
+    0.020705989626446524,
+    0.02396617954234677,
+    -0.012095713970371423,
+    -0.021235526836812676,
+    0.006006200853277362,
+    0.018337745467632928,
+    -0.0016610156480375167,
+    -0.015394948058705999,
+    -0.0013732462347513066,
+    0.012521152261727227,
+    0.003379862524796346,
+    -0.009815768367302802,
+    -0.004571166160481191,
+    0.007358664812266444,
+    0.0051227092580119855,
+    -0.005207105621787521,
+    -0.00518754915999129,
+    0.0033946608064293073,
+    0.004901261108562161,
+    -0.0019320007678197645,
+    -0.004382713612447862,
+    0.0008093189947979181,
+    0.003733226210629766,
+    1.824936363314565e-17,
+    -0.00303553840647302,
+    -0.0005349278077261196,
+    0.0023533499143115976,
+    0.0008420376771297366,
+    -0.0017317724164334631,
+    -0.0009709031498968383,
+    0.0011987515041766601,
+    0.000970032155449486,
+    -0.0007673289520005847,
+    -0.0008836155865344651,
+    0.0004384998464839666,
+    0.0007492498350107482,
+    -0.00020436483462002328,
+    -0.0005966722898292008,
+    5.1274839240568415e-05,
+    0.0004474410769117699,
+    3.729708416324331e-05,
+    -0.0003154059452699033,
+    -7.835700358335798e-05,
+    0.0002077626777155509,
+    8.758503947913682e-05,
+    -0.000126460783407638,
+    -7.816273123033217e-05,
+    6.974138571798226e-05,
+    6.0198389114399714e-05,
+    -3.361363034503314e-05,
+    -4.065919588349948e-05,
+    1.3125839456769717e-05,
+    2.368294142133405e-05,
+    -3.3433343718178e-06,
+    -1.1128313185646072e-05,
+    -7.143944801213435e-20,
+];
diff --git a/playback/src/lib.rs b/playback/src/lib.rs
index c7d8297d..5c60791c 100644
--- a/playback/src/lib.rs
+++ b/playback/src/lib.rs
@@ -13,6 +13,7 @@ pub mod config;
 pub mod convert;
 pub mod decoder;
 pub mod dither;
+pub mod filter_coefficients;
 pub mod mixer;
 pub mod normaliser;
 pub mod player;
diff --git a/playback/src/resampler.rs b/playback/src/resampler.rs
index a35dcefa..25c3a749 100644
--- a/playback/src/resampler.rs
+++ b/playback/src/resampler.rs
@@ -1,6 +1,5 @@
 use std::{
     collections::{vec_deque, VecDeque},
-    marker::Send,
     process::exit,
     sync::atomic::Ordering,
     sync::mpsc,
@@ -8,7 +7,7 @@ use std::{
 };
 
 use crate::{
-    config::{InterpolationQuality, SampleRate, NUM_FIR_FILTER_TAPS},
+    config::{InterpolationQuality, SampleRate},
     player::PLAYER_COUNTER,
     RESAMPLER_INPUT_SIZE, SAMPLE_RATE as SOURCE_SAMPLE_RATE,
 };
@@ -80,16 +79,6 @@ impl ConvolutionFilter {
     }
 }
 
-trait MonoResampler {
-    fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self
-    where
-        Self: Sized;
-
-    fn stop(&mut self);
-    fn get_latency_pcm(&mut self) -> u64;
-    fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>>;
-}
-
 struct MonoSincResampler {
     interpolator: ConvolutionFilter,
     input_buffer: Vec<f64>,
@@ -98,18 +87,22 @@ struct MonoSincResampler {
     interpolation_output_size: usize,
 }
 
-impl MonoResampler for MonoSincResampler {
+impl MonoSincResampler {
     fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self {
         let spec = sample_rate.get_resample_spec();
 
-        let delay_line_latency = (interpolation_quality.get_interpolation_coefficients_length()
-            as f64
-            * spec.resample_factor_reciprocal) as u64;
+        let coefficients = match interpolation_quality {
+            InterpolationQuality::Low => spec.low_coefficients,
+            InterpolationQuality::High => spec.high_coefficients,
+        };
+
+        let delay_line_latency =
+            (coefficients.len() as f64 * spec.resample_factor_reciprocal) as u64;
 
         Self {
             interpolator: ConvolutionFilter::new(
                 interpolation_quality
-                    .get_interpolation_coefficients(spec.resample_factor_reciprocal),
+                    .get_interpolation_coefficients(coefficients, spec.resample_factor_reciprocal),
             ),
 
             input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
@@ -160,82 +153,6 @@ impl MonoResampler for MonoSincResampler {
     }
 }
 
-struct MonoLinearResampler {
-    fir_filter: ConvolutionFilter,
-    input_buffer: Vec<f64>,
-    resample_factor_reciprocal: f64,
-    delay_line_latency: u64,
-    interpolation_output_size: usize,
-}
-
-impl MonoResampler for MonoLinearResampler {
-    fn new(sample_rate: SampleRate, interpolation_quality: InterpolationQuality) -> Self {
-        let spec = sample_rate.get_resample_spec();
-
-        let delay_line_latency =
-            (NUM_FIR_FILTER_TAPS as f64 * spec.resample_factor_reciprocal) as u64;
-
-        Self {
-            fir_filter: ConvolutionFilter::new(
-                interpolation_quality.get_fir_filter_coefficients(spec.resample_factor_reciprocal),
-            ),
-
-            input_buffer: Vec::with_capacity(SOURCE_SAMPLE_RATE as usize),
-            resample_factor_reciprocal: spec.resample_factor_reciprocal,
-            delay_line_latency,
-            interpolation_output_size: spec.interpolation_output_size,
-        }
-    }
-
-    fn get_latency_pcm(&mut self) -> u64 {
-        self.input_buffer.len() as u64 + self.delay_line_latency
-    }
-
-    fn stop(&mut self) {
-        self.fir_filter.clear();
-        self.input_buffer.clear();
-    }
-
-    fn resample(&mut self, samples: &[f64]) -> Option<Vec<f64>> {
-        self.input_buffer.extend_from_slice(samples);
-
-        let num_buffer_chunks = self.input_buffer.len().saturating_div(RESAMPLER_INPUT_SIZE);
-
-        if num_buffer_chunks == 0 {
-            return None;
-        }
-
-        let input_size = num_buffer_chunks * RESAMPLER_INPUT_SIZE;
-        // The size of the output after interpolation.
-        // We have to account for the fact that to do effective linear
-        // interpolation we need an extra sample to be able to throw away later.
-        let output_size = num_buffer_chunks * self.interpolation_output_size + 1;
-
-        let mut output = Vec::with_capacity(output_size);
-
-        output.extend((0..output_size).map(|output_index| {
-            let sample_index = output_index as f64 * self.resample_factor_reciprocal;
-            let sample_index_fractional = sample_index.fract();
-            let sample_index = sample_index as usize;
-            let sample = *self.input_buffer.get(sample_index).unwrap_or(&0.0);
-            let next_sample = *self.input_buffer.get(sample_index + 1).unwrap_or(&0.0);
-            let sample_index_fractional_complementary = 1.0 - sample_index_fractional;
-            sample * sample_index_fractional_complementary + next_sample * sample_index_fractional
-        }));
-
-        // Remove the last garbage sample.
-        output.pop();
-
-        output
-            .iter_mut()
-            .for_each(|sample| *sample = self.fir_filter.convolute(*sample));
-
-        self.input_buffer.drain(..input_size);
-
-        Some(output)
-    }
-}
-
 enum ResampleTask {
     Stop,
     Terminate,
@@ -249,7 +166,7 @@ struct ResampleWorker {
 }
 
 impl ResampleWorker {
-    fn new(mut resampler: impl MonoResampler + Send + 'static, name: String) -> Self {
+    fn new(mut resampler: MonoSincResampler, name: String) -> Self {
         let (task_sender, task_receiver) = mpsc::channel();
         let (result_sender, result_receiver) = mpsc::channel();
 
@@ -383,29 +300,12 @@ impl StereoInterleavedResampler {
                 let left_thread_name = format!("resampler:{player_id}:left");
                 let right_thread_name = format!("resampler:{player_id}:right");
 
-                match interpolation_quality {
-                    InterpolationQuality::Low => {
-                        debug!("Interpolation Type: Linear");
+                let left = MonoSincResampler::new(sample_rate, interpolation_quality);
+                let right = MonoSincResampler::new(sample_rate, interpolation_quality);
 
-                        let left = MonoLinearResampler::new(sample_rate, interpolation_quality);
-                        let right = MonoLinearResampler::new(sample_rate, interpolation_quality);
-
-                        Resampler::Worker {
-                            left_resampler: ResampleWorker::new(left, left_thread_name),
-                            right_resampler: ResampleWorker::new(right, right_thread_name),
-                        }
-                    }
-                    _ => {
-                        debug!("Interpolation Type: Windowed Sinc");
-
-                        let left = MonoSincResampler::new(sample_rate, interpolation_quality);
-                        let right = MonoSincResampler::new(sample_rate, interpolation_quality);
-
-                        Resampler::Worker {
-                            left_resampler: ResampleWorker::new(left, left_thread_name),
-                            right_resampler: ResampleWorker::new(right, right_thread_name),
-                        }
-                    }
+                Resampler::Worker {
+                    left_resampler: ResampleWorker::new(left, left_thread_name),
+                    right_resampler: ResampleWorker::new(right, right_thread_name),
                 }
             }
         };
diff --git a/src/main.rs b/src/main.rs
index f31526cb..231b2451 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -582,7 +582,7 @@ fn get_setup() -> Setup {
     ).optopt(
         "",
         INTERPOLATION_QUALITY,
-        "Interpolation Quality to use if Resampling {Low|Medium|High}. Defaults to High.",
+        "Interpolation Quality to use if Resampling {Low|High}. Defaults to High.",
         "QUALITY"
     ).optopt(
         "",
@@ -817,7 +817,7 @@ fn get_setup() -> Setup {
                     INTERPOLATION_QUALITY,
                     "",
                     interpolation_quality,
-                    "Low, Medium, High",
+                    "Low, High",
                     default_value,
                 );