Skip to content

Commit 9668870

Browse files
committed
fix: scale sinc kernel by bandwidth
1 parent 05cb4a9 commit 9668870

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

dasp_interpolate/src/sinc/mod.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,15 @@ where
9393

9494
(0..max_depth).fold(Self::Frame::EQUILIBRIUM, |mut v, n| {
9595
v = {
96-
let a = PI * bandwidth * (phil + n as f64);
97-
let first = if a == 0.0 { 1.0 } else { sin(a) / a };
98-
let second = 0.5 + 0.5 * cos(a / (depth as f64 * bandwidth));
96+
let t = phil + n as f64;
97+
let a = PI * bandwidth * t;
98+
let b = PI * t / depth as f64;
99+
let first = if a.abs() < f64::EPSILON {
100+
bandwidth
101+
} else {
102+
bandwidth * sin(a) / a
103+
};
104+
let second = 0.5 + 0.5 * cos(b);
99105
v.zip_map(self.frames[nl - n], |vs, r_lag| {
100106
vs.add_amp(
101107
(first * second * r_lag.to_sample::<f64>())
@@ -105,9 +111,15 @@ where
105111
})
106112
};
107113

108-
let a = PI * bandwidth * (phir + n as f64);
109-
let first = if a == 0.0 { 1.0 } else { sin(a) / a };
110-
let second = 0.5 + 0.5 * cos(a / (depth as f64 * bandwidth));
114+
let t = phir + n as f64;
115+
let a = PI * bandwidth * t;
116+
let b = PI * t / depth as f64;
117+
let first = if a.abs() < f64::EPSILON {
118+
bandwidth
119+
} else {
120+
bandwidth * sin(a) / a
121+
};
122+
let second = 0.5 + 0.5 * cos(b);
111123
v.zip_map(self.frames[nr + n], |vs, r_lag| {
112124
vs.add_amp(
113125
(first * second * r_lag.to_sample::<f64>())

dasp_signal/tests/interpolate.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,28 +73,41 @@ fn test_sinc() {
7373
}
7474

7575
#[test]
76-
fn test_sinc_downsampling_antialiasing() {
76+
fn test_sinc_antialiasing() {
7777
const SOURCE_HZ: f64 = 2000.0;
7878
const TARGET_HZ: f64 = 1500.0;
7979
const SIGNAL_FREQ: f64 = 900.0;
80+
const TAPS: usize = 100;
8081

8182
let source_signal = signal::rate(SOURCE_HZ).const_hz(SIGNAL_FREQ).sine();
82-
83-
let ring_buffer = ring_buffer::Fixed::from(vec![0.0; 100]);
83+
let ring_buffer = ring_buffer::Fixed::from(vec![0.0; TAPS]);
8484
let sinc = Sinc::new(ring_buffer);
8585
let mut downsampled = source_signal.from_hz_to_hz(sinc, SOURCE_HZ, TARGET_HZ);
8686

87-
for _ in 0..50 {
87+
// Warm up the filter to reach steady state
88+
for _ in 0..TAPS / 2 {
8889
downsampled.next();
8990
}
9091

92+
// Measure peak amplitude in steady state
9193
let samples: Vec<f64> = downsampled.take(1500).collect();
92-
93-
let rms = (samples.iter().map(|&s| s * s).sum::<f64>() / samples.len() as f64).sqrt();
94+
let peak_amplitude = samples
95+
.iter()
96+
.map(|&s| s.abs())
97+
.max_by(|a, b| a.partial_cmp(b).unwrap())
98+
.unwrap();
99+
100+
// With Hann-windowed sinc, With 50 taps and 900 Hz being relatively close to the 750 Hz
101+
// cutoff, we're in the early stopband region where attenuation is less than -44 dB.
102+
// Empirical measurement shows ~41 dB for this configuration.
103+
const EXPECTED_ATTENUATION_DB: f64 = 41.0;
104+
let max_peak_amplitude = 10.0_f64.powf(-EXPECTED_ATTENUATION_DB / 20.0);
94105

95106
assert!(
96-
rms < 0.1,
97-
"Expected RMS < 0.1 (well-filtered), got {:.3}. Aliasing occurred.",
98-
rms
107+
peak_amplitude < max_peak_amplitude,
108+
"Expected ≥{:.0} dB attenuation (peak < {:.4}), got peak {:.4}",
109+
EXPECTED_ATTENUATION_DB,
110+
max_peak_amplitude,
111+
peak_amplitude
99112
);
100113
}

0 commit comments

Comments
 (0)