Skip to content

Commit e31293c

Browse files
committed
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.
1 parent ac68d24 commit e31293c

File tree

5 files changed

+1233
-245
lines changed

5 files changed

+1233
-245
lines changed

playback/src/config.rs

Lines changed: 27 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
use std::{mem, str::FromStr, time::Duration};
22

33
pub use crate::dither::{mk_ditherer, DithererBuilder, TriangularDitherer};
4-
use crate::{convert::i24, RESAMPLER_INPUT_SIZE, SAMPLE_RATE};
4+
5+
use crate::{
6+
convert::i24,
7+
filter_coefficients::{
8+
HZ48000_HIGH, HZ48000_LOW, HZ88200_HIGH, HZ88200_LOW, HZ96000_HIGH, HZ96000_LOW,
9+
},
10+
RESAMPLER_INPUT_SIZE, SAMPLE_RATE,
11+
};
512

613
// Reciprocals allow us to multiply instead of divide during interpolation.
714
const HZ48000_RESAMPLE_FACTOR_RECIPROCAL: f64 = SAMPLE_RATE as f64 / 48_000.0;
@@ -26,21 +33,9 @@ const HZ88200_INTERPOLATION_OUTPUT_SIZE: usize =
2633
const HZ96000_INTERPOLATION_OUTPUT_SIZE: usize =
2734
(RESAMPLER_INPUT_SIZE as f64 * (1.0 / HZ96000_RESAMPLE_FACTOR_RECIPROCAL)) as usize;
2835

29-
pub const NUM_FIR_FILTER_TAPS: usize = 5;
30-
31-
// Blackman Window coefficients
32-
const BLACKMAN_A0: f64 = 0.42;
33-
const BLACKMAN_A1: f64 = 0.5;
34-
const BLACKMAN_A2: f64 = 0.08;
35-
36-
// Constants for calculations
37-
const TWO_TIMES_PI: f64 = 2.0 * std::f64::consts::PI;
38-
const FOUR_TIMES_PI: f64 = 4.0 * std::f64::consts::PI;
39-
4036
#[derive(Clone, Copy, Debug, Default)]
4137
pub enum InterpolationQuality {
4238
Low,
43-
Medium,
4439
#[default]
4540
High,
4641
}
@@ -53,7 +48,6 @@ impl FromStr for InterpolationQuality {
5348

5449
match s.to_lowercase().as_ref() {
5550
"low" => Ok(Low),
56-
"medium" => Ok(Medium),
5751
"high" => Ok(High),
5852
_ => Err(()),
5953
}
@@ -66,107 +60,32 @@ impl std::fmt::Display for InterpolationQuality {
6660

6761
match self {
6862
Low => write!(f, "Low"),
69-
Medium => write!(f, "Medium"),
7063
High => write!(f, "High"),
7164
}
7265
}
7366
}
7467

7568
impl InterpolationQuality {
76-
pub fn get_interpolation_coefficients(&self, resample_factor_reciprocal: f64) -> Vec<f64> {
77-
let interpolation_coefficients_length = self.get_interpolation_coefficients_length();
78-
79-
let mut coefficients = Vec::with_capacity(interpolation_coefficients_length);
80-
81-
if interpolation_coefficients_length == 0 {
82-
warn!("InterpolationQuality::Low::get_interpolation_coefficients always returns an empty Vec<f64>");
83-
warn!("Linear Interpolation does not use coefficients");
84-
85-
return coefficients;
86-
}
87-
88-
let last_index = interpolation_coefficients_length as f64 - 1.0;
89-
90-
let sinc_center = last_index * 0.5;
91-
69+
pub fn get_interpolation_coefficients(
70+
&self,
71+
mut coefficients: Vec<f64>,
72+
resample_factor_reciprocal: f64,
73+
) -> Vec<f64> {
9274
let mut coefficient_sum = 0.0;
9375

94-
coefficients.extend((0..interpolation_coefficients_length).map(
95-
|interpolation_coefficient_index| {
96-
let index_float = interpolation_coefficient_index as f64;
97-
let sample_index_fractional = (index_float * resample_factor_reciprocal).fract();
98-
99-
let sample_index_fractional_sinc_weight = Self::sinc(sample_index_fractional);
100-
101-
let fir_filter = Self::fir_filter(
102-
index_float,
103-
last_index,
104-
sinc_center,
105-
resample_factor_reciprocal,
106-
);
107-
108-
let coefficient = sample_index_fractional_sinc_weight * fir_filter;
109-
110-
coefficient_sum += coefficient;
111-
112-
coefficient
113-
},
114-
));
115-
116-
coefficients
117-
.iter_mut()
118-
.for_each(|coefficient| *coefficient /= coefficient_sum);
119-
120-
coefficients
121-
}
122-
123-
pub fn get_fir_filter_coefficients(&self, resample_factor_reciprocal: f64) -> Vec<f64> {
124-
let mut coefficients = Vec::with_capacity(NUM_FIR_FILTER_TAPS);
125-
126-
if self.get_interpolation_coefficients_length() != 0 {
127-
warn!("InterpolationQuality::Medium/High::get_fir_filter_coefficients always returns an empty Vec<f64>");
128-
warn!("The FIR Filter coefficients are a part of the Windowed Sinc Interpolation coefficients");
76+
for (index, coefficient) in coefficients.iter_mut().enumerate() {
77+
*coefficient *= Self::sinc((index as f64 * resample_factor_reciprocal).fract());
12978

130-
return coefficients;
79+
coefficient_sum += *coefficient;
13180
}
13281

133-
let last_index = NUM_FIR_FILTER_TAPS as f64 - 1.0;
134-
135-
let sinc_center = last_index * 0.5;
136-
137-
let mut coefficient_sum = 0.0;
138-
139-
coefficients.extend(
140-
(0..NUM_FIR_FILTER_TAPS).map(|fir_filter_coefficient_index| {
141-
let coefficient = Self::fir_filter(
142-
fir_filter_coefficient_index as f64,
143-
last_index,
144-
sinc_center,
145-
resample_factor_reciprocal,
146-
);
147-
148-
coefficient_sum += coefficient;
149-
150-
coefficient
151-
}),
152-
);
153-
15482
coefficients
15583
.iter_mut()
15684
.for_each(|coefficient| *coefficient /= coefficient_sum);
15785

15886
coefficients
15987
}
16088

161-
pub fn get_interpolation_coefficients_length(&self) -> usize {
162-
use InterpolationQuality::*;
163-
match self {
164-
Low => 0,
165-
Medium => 129,
166-
High => 257,
167-
}
168-
}
169-
17089
fn sinc(x: f64) -> f64 {
17190
if x.abs() < f64::EPSILON {
17291
1.0
@@ -175,35 +94,6 @@ impl InterpolationQuality {
17594
pi_x.sin() / pi_x
17695
}
17796
}
178-
179-
fn blackman(index: f64, last_index: f64) -> f64 {
180-
// Calculate the Blackman window function for the given center offset
181-
// w(n) = A0 - A1*cos(2πn / (N-1)) + A2*cos(4πn / (N-1)),
182-
// where n is the center offset, N is the window size,
183-
// and A0, A1, A2 are precalculated coefficients
184-
let two_pi_n = TWO_TIMES_PI * index;
185-
let four_pi_n = FOUR_TIMES_PI * index;
186-
187-
BLACKMAN_A0 - BLACKMAN_A1 * (two_pi_n / last_index).cos()
188-
+ BLACKMAN_A2 * (four_pi_n / last_index).cos()
189-
}
190-
191-
fn fir_filter(
192-
index: f64,
193-
last_index: f64,
194-
sinc_center: f64,
195-
resample_factor_reciprocal: f64,
196-
) -> f64 {
197-
// The resample_factor_reciprocal also happens to be our
198-
// anti-alias cutoff. In this case it represents the minimum
199-
// output bandwidth needed to fully represent the input.
200-
let adjusted_sinc_center_offset = (index - sinc_center) * resample_factor_reciprocal;
201-
202-
let sinc_value = Self::sinc(adjusted_sinc_center_offset);
203-
let blackman_window_value = Self::blackman(index, last_index);
204-
205-
sinc_value * blackman_window_value
206-
}
20797
}
20898

20999
#[derive(Clone, Copy, Debug, Default)]
@@ -262,10 +152,12 @@ impl std::fmt::Display for SampleRate {
262152
}
263153
}
264154

265-
#[derive(Clone, Copy, Debug, Default)]
155+
#[derive(Debug, Default)]
266156
pub struct ResampleSpec {
267157
pub resample_factor_reciprocal: f64,
268158
pub interpolation_output_size: usize,
159+
pub high_coefficients: Vec<f64>,
160+
pub low_coefficients: Vec<f64>,
269161
}
270162

271163
impl SampleRate {
@@ -328,19 +220,27 @@ impl SampleRate {
328220
ResampleSpec {
329221
resample_factor_reciprocal: 1.0,
330222
interpolation_output_size: RESAMPLER_INPUT_SIZE,
223+
high_coefficients: vec![],
224+
low_coefficients: vec![],
331225
}
332226
}
333227
Hz48000 => ResampleSpec {
334228
resample_factor_reciprocal: HZ48000_RESAMPLE_FACTOR_RECIPROCAL,
335229
interpolation_output_size: HZ48000_INTERPOLATION_OUTPUT_SIZE,
230+
high_coefficients: HZ48000_HIGH.to_vec(),
231+
low_coefficients: HZ48000_LOW.to_vec(),
336232
},
337233
Hz88200 => ResampleSpec {
338234
resample_factor_reciprocal: HZ88200_RESAMPLE_FACTOR_RECIPROCAL,
339235
interpolation_output_size: HZ88200_INTERPOLATION_OUTPUT_SIZE,
236+
high_coefficients: HZ88200_HIGH.to_vec(),
237+
low_coefficients: HZ88200_LOW.to_vec(),
340238
},
341239
Hz96000 => ResampleSpec {
342240
resample_factor_reciprocal: HZ96000_RESAMPLE_FACTOR_RECIPROCAL,
343241
interpolation_output_size: HZ96000_INTERPOLATION_OUTPUT_SIZE,
242+
high_coefficients: HZ96000_HIGH.to_vec(),
243+
low_coefficients: HZ96000_LOW.to_vec(),
344244
},
345245
}
346246
}

0 commit comments

Comments
 (0)