From 2a6273940845513f9838f11288d8de70eb1afb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bojan=20Janjatovi=C4=87?= Date: Wed, 6 May 2026 20:30:11 +0200 Subject: [PATCH] Add equilibrium-padded interpolator constructors --- CHANGELOG.md | 2 ++ dasp_interpolate/src/floor.rs | 21 ++++++++++++++++ dasp_interpolate/src/linear.rs | 24 +++++++++++++++++++ dasp_interpolate/src/sinc/mod.rs | 33 +++++++++++++++++++++++++ dasp_signal/tests/interpolate.rs | 41 +++++++++++++++++++++++++++++++- 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb66e2d..69c4df56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Added `equilibrium_padded` constructors for `Floor`, `Linear`, and `Sinc` + interpolators. - Renamed `window-hanning` to `window-hann` - Made `IntoInterleavedSamples` and `IntoInterleavedSamplesIterator` stop yielding samples when the underlying signal gets exhausted. This is a breaking diff --git a/dasp_interpolate/src/floor.rs b/dasp_interpolate/src/floor.rs index 8bef380b..53d39f66 100644 --- a/dasp_interpolate/src/floor.rs +++ b/dasp_interpolate/src/floor.rs @@ -29,6 +29,27 @@ impl Floor { pub fn new(left: F) -> Floor { Floor { left: left } } + + /// Create a new Floor Interpolator padded with equilibrium. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **floor** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-floor** feature to be enabled. + /// + /// ``` + /// use dasp_interpolate::floor::Floor; + /// + /// let interp = Floor::::equilibrium_padded(); + /// ``` + pub fn equilibrium_padded() -> Floor + where + F: Frame, + { + Floor { + left: F::EQUILIBRIUM, + } + } } impl Interpolator for Floor diff --git a/dasp_interpolate/src/linear.rs b/dasp_interpolate/src/linear.rs index a0da4b0f..35be36e6 100644 --- a/dasp_interpolate/src/linear.rs +++ b/dasp_interpolate/src/linear.rs @@ -34,6 +34,30 @@ impl Linear { right: right, } } + + /// Create a new Linear Interpolator padded with equilibrium. + /// + /// Both the left and right frames are initialized to `Frame::EQUILIBRIUM`. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **linear** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-linear** feature to be enabled. + /// + /// ``` + /// use dasp_interpolate::linear::Linear; + /// + /// let interp = Linear::::equilibrium_padded(); + /// ``` + pub fn equilibrium_padded() -> Linear + where + F: Frame, + { + Linear { + left: F::EQUILIBRIUM, + right: F::EQUILIBRIUM, + } + } } impl Interpolator for Linear diff --git a/dasp_interpolate/src/sinc/mod.rs b/dasp_interpolate/src/sinc/mod.rs index dd6d6029..1a6bc842 100644 --- a/dasp_interpolate/src/sinc/mod.rs +++ b/dasp_interpolate/src/sinc/mod.rs @@ -54,6 +54,39 @@ impl Sinc { } } + /// Create a new **Sinc** interpolator with the given ring buffer padded with equilibrium. + /// + /// The given ring buffer should have a length twice that of the desired sinc interpolation + /// `depth`. + /// + /// The initial contents of the ring_buffer are replaced with `Frame::EQUILIBRIUM`. + /// + /// **panic!**s if the given ring buffer's length is not a multiple of `2`. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **sinc** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-sinc** feature to be enabled. + /// + /// ``` + /// use dasp_interpolate::sinc::Sinc; + /// use dasp_ring_buffer as ring_buffer; + /// + /// let frames = ring_buffer::Fixed::from([1.0; 8]); + /// let interp = Sinc::equilibrium_padded(frames); + /// ``` + pub fn equilibrium_padded(mut frames: ring_buffer::Fixed) -> Self + where + S: ring_buffer::SliceMut, + S::Element: Frame, + { + assert!(frames.len() % 2 == 0); + for frame in frames.iter_mut() { + *frame = ::EQUILIBRIUM; + } + Sinc { frames, idx: 0 } + } + fn depth(&self) -> usize where S: ring_buffer::Slice, diff --git a/dasp_signal/tests/interpolate.rs b/dasp_signal/tests/interpolate.rs index 065ad74d..4b34aace 100644 --- a/dasp_signal/tests/interpolate.rs +++ b/dasp_signal/tests/interpolate.rs @@ -1,6 +1,6 @@ //! Tests for the `Converter` and `Interpolator` traits -use dasp_interpolate::{floor::Floor, linear::Linear, sinc::Sinc}; +use dasp_interpolate::{floor::Floor, linear::Linear, sinc::Sinc, Interpolator}; use dasp_ring_buffer as ring_buffer; use dasp_signal::{self as signal, interpolate::Converter, Signal}; @@ -23,6 +23,17 @@ fn test_floor_converter() { assert_eq!(conv.next(), 2.0); } +#[test] +fn test_floor_equilibrium_padded() { + let mut interp = Floor::::equilibrium_padded(); + + assert_eq!(interp.interpolate(0.0), 0.0); + assert_eq!(interp.interpolate(0.5), 0.0); + + interp.next_source_frame(1.0); + assert_eq!(interp.interpolate(0.5), 1.0); +} + #[test] fn test_linear_converter() { let frames: [f64; 3] = [0.0, 1.0, 2.0]; @@ -42,6 +53,21 @@ fn test_linear_converter() { assert_eq!(conv.next(), 1.0); } +#[test] +fn test_linear_equilibrium_padded() { + let mut interp = Linear::::equilibrium_padded(); + + assert_eq!(interp.interpolate(0.0), 0.0); + assert_eq!(interp.interpolate(0.5), 0.0); + + interp.next_source_frame(1.0); + assert_eq!(interp.interpolate(0.0), 0.0); + assert_eq!(interp.interpolate(0.5), 0.5); + + interp.next_source_frame(2.0); + assert_eq!(interp.interpolate(0.5), 1.5); +} + #[test] fn test_scale_playback_rate() { // Scale the playback rate by `0.5` @@ -71,3 +97,16 @@ fn test_sinc() { None ); } + +#[test] +fn test_sinc_equilibrium_padded_clears_supplied_buffer() { + let frames = ring_buffer::Fixed::from(vec![1.0_f64; 50]); + let mut interp = Sinc::equilibrium_padded(frames); + + assert_eq!(interp.interpolate(0.0), 0.0); + assert_eq!(interp.interpolate(0.5), 0.0); + + interp.next_source_frame(1.0); + let sample = interp.interpolate(0.0); + assert!(sample.is_finite()); +}