Skip to main content

rubato/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "log")]
4extern crate log;
5
6pub use audioadapter;
7pub use audioadapter_buffers;
8
9use audioadapter::{Adapter, AdapterMut};
10use audioadapter_buffers::owned::InterleavedOwned;
11
12// Logging wrapper macros to avoid cluttering the code with conditionals.
13#[allow(unused)]
14macro_rules! trace { ($($x:tt)*) => (
15    #[cfg(feature = "log")] {
16        log::trace!($($x)*)
17    }
18) }
19#[allow(unused)]
20macro_rules! debug { ($($x:tt)*) => (
21    #[cfg(feature = "log")] {
22        log::debug!($($x)*)
23    }
24) }
25#[allow(unused)]
26macro_rules! info { ($($x:tt)*) => (
27    #[cfg(feature = "log")] {
28        log::info!($($x)*)
29    }
30) }
31#[allow(unused)]
32macro_rules! warn { ($($x:tt)*) => (
33    #[cfg(feature = "log")] {
34        log::warn!($($x)*)
35    }
36) }
37#[allow(unused)]
38macro_rules! error { ($($x:tt)*) => (
39    #[cfg(feature = "log")] {
40        log::error!($($x)*)
41    }
42) }
43
44mod asynchro;
45mod asynchro_fast;
46mod asynchro_sinc;
47mod error;
48mod interpolation;
49mod sample;
50mod sinc;
51mod slip;
52#[cfg(feature = "fft_resampler")]
53mod synchro;
54mod windows;
55
56pub mod sinc_interpolator;
57
58pub use crate::asynchro::{Async, FixedAsync};
59pub use crate::asynchro_fast::PolynomialDegree;
60pub use crate::asynchro_sinc::{SincInterpolationParameters, SincInterpolationType};
61pub use crate::error::{
62    CpuFeature, MissingCpuFeature, ResampleError, ResampleResult, ResamplerConstructionError,
63};
64pub use crate::sample::Sample;
65pub use crate::slip::Slip;
66#[cfg(feature = "fft_resampler")]
67pub use crate::synchro::{Fft, FixedSync};
68pub use crate::windows::{calculate_cutoff, WindowFunction};
69
70/// A struct for providing optional parameters when calling
71/// [process_into_buffer](Resampler::process_into_buffer).
72///
73/// All fields have sensible defaults: zero offsets, no partial length, and all channels active.
74/// Pass `None` as the `indexing` argument to use these defaults without constructing the struct.
75#[derive(Debug, Clone, Default)]
76pub struct Indexing {
77    /// Number of frames to skip at the beginning of the input buffer before reading.
78    /// Use this to process a sub-region of a larger buffer without copying data.
79    /// Defaults to `0` (read from the start of the buffer).
80    pub input_offset: usize,
81
82    /// Number of frames to skip at the beginning of the output buffer before writing.
83    /// Use this to write results into a sub-region of a larger buffer.
84    /// Defaults to `0` (write from the start of the buffer).
85    pub output_offset: usize,
86
87    /// Set to `Some(n)` when the input buffer contains fewer valid frames than
88    /// [Resampler::input_frames_next] requires.
89    /// The resampler will read the first `n` frames from the input buffer and
90    /// treat the remaining frames as silence.
91    /// This is useful for processing the very last (partial) chunk of a stream or audio clip.
92    ///
93    /// **Important:** even with `partial_len` set, the output buffer must still be large enough
94    /// to hold at least [Resampler::output_frames_next] frames (plus `output_offset`),
95    /// because the resampler always produces a full output chunk.
96    ///
97    /// Set to `Some(0)` to process a chunk of pure silence (e.g. to flush the resampler delay).
98    ///
99    /// Defaults to `None`, meaning the full [Resampler::input_frames_next] frames are read.
100    pub partial_len: Option<usize>,
101
102    /// Optional per-channel processing mask.
103    /// When `Some(vec)`, each element corresponds to one channel:
104    /// `true` means the channel is processed normally,
105    /// `false` means the channel is skipped and its output is left unchanged.
106    /// When `None`, all channels are processed.
107    pub active_channels_mask: Option<Vec<bool>>,
108}
109
110impl Indexing {
111    /// Create an [Indexing] with all fields at their defaults:
112    /// zero offsets, no partial length, and all channels active.
113    ///
114    /// Chain the setters to configure only the fields you need:
115    ///
116    /// ```
117    /// use rubato::Indexing;
118    ///
119    /// let indexing = Indexing::new()
120    ///     .input_offset(128)
121    ///     .partial_len(64);
122    /// ```
123    pub fn new() -> Self {
124        Self::default()
125    }
126
127    /// Set the number of frames to skip at the start of the input buffer.
128    #[must_use]
129    pub fn input_offset(mut self, frames: usize) -> Self {
130        self.input_offset = frames;
131        self
132    }
133
134    /// Set the number of frames to skip at the start of the output buffer.
135    #[must_use]
136    pub fn output_offset(mut self, frames: usize) -> Self {
137        self.output_offset = frames;
138        self
139    }
140
141    /// Set the number of valid input frames available for a partial (final) chunk.
142    #[must_use]
143    pub fn partial_len(mut self, frames: usize) -> Self {
144        self.partial_len = Some(frames);
145        self
146    }
147
148    /// Set the per-channel processing mask.
149    #[must_use]
150    pub fn active_channels_mask(mut self, mask: Vec<bool>) -> Self {
151        self.active_channels_mask = Some(mask);
152        self
153    }
154}
155
156pub(crate) fn get_offsets(indexing: &Option<&Indexing>) -> (usize, usize) {
157    indexing
158        .as_ref()
159        .map(|idx| (idx.input_offset, idx.output_offset))
160        .unwrap_or((0, 0))
161}
162
163pub(crate) fn get_partial_len(indexing: &Option<&Indexing>) -> Option<usize> {
164    indexing.as_ref().and_then(|idx| idx.partial_len)
165}
166
167/// Helper to update the mask from an optional Indexing struct.
168/// Returns [ResampleError::WrongNumberOfMaskChannels] if the provided mask has the wrong length.
169pub(crate) fn update_mask(indexing: &Option<&Indexing>, mask: &mut [bool]) -> ResampleResult<()> {
170    if let Some(idx) = indexing {
171        if let Some(new_mask) = &idx.active_channels_mask {
172            if new_mask.len() != mask.len() {
173                return Err(ResampleError::WrongNumberOfMaskChannels {
174                    expected: mask.len(),
175                    actual: new_mask.len(),
176                });
177            }
178            mask.copy_from_slice(new_mask);
179            return Ok(());
180        }
181    }
182    mask.iter_mut().for_each(|v| *v = true);
183    Ok(())
184}
185
186/// A resampler that is used to resample a chunk of audio to a new sample rate.
187/// For asynchronous resamplers, the rate can be adjusted as required.
188pub trait Resampler<T>: Send
189where
190    T: Sample,
191{
192    /// This is a convenience wrapper for [process_into_buffer](Resampler::process_into_buffer)
193    /// that allocates the output buffer with each call. For realtime applications, use
194    /// [process_into_buffer](Resampler::process_into_buffer) with a pre-allocated buffer
195    /// instead of this function.
196    ///
197    /// This resamples a single chunk of [input_frames_next](Resampler::input_frames_next)
198    /// frames. To resample a whole clip that is already in memory, use
199    /// [process_all](Resampler::process_all) instead. Do not size the resampler to the clip
200    /// length and call this once: that wastes memory and leaves the resampler's startup delay
201    /// as leading silence in the output.
202    ///
203    /// The output is returned as an [InterleavedOwned] struct that wraps a `Vec<T>`
204    /// of interleaved samples.
205    ///
206    /// The optional `indexing` parameter has the same meaning as in
207    /// [process_into_buffer](Resampler::process_into_buffer), with one exception: since the
208    /// output buffer is allocated here, its `output_offset` field is ignored (the output always
209    /// starts at frame zero). Use `input_offset` to start reading partway into a larger input
210    /// buffer, `partial_len` to feed a final chunk that is shorter than
211    /// [input_frames_next](Resampler::input_frames_next), and `active_channels_mask` to skip
212    /// channels.
213    fn process(
214        &mut self,
215        buffer_in: &dyn Adapter<T>,
216        indexing: Option<&Indexing>,
217    ) -> ResampleResult<InterleavedOwned<T>> {
218        let frames = self.output_frames_next();
219        let channels = self.nbr_channels();
220        let mut buffer_out = InterleavedOwned::<T>::new(T::coerce_from(0.0), channels, frames);
221
222        let indexing = Indexing {
223            input_offset: get_offsets(&indexing).0,
224            output_offset: 0,
225            partial_len: get_partial_len(&indexing),
226            active_channels_mask: indexing.and_then(|idx| idx.active_channels_mask.clone()),
227        };
228        self.process_into_buffer(buffer_in, &mut buffer_out, Some(&indexing))?;
229        Ok(buffer_out)
230    }
231
232    /// Resample a buffer of audio to a pre-allocated output buffer.
233    /// Use this in real-time applications where the unpredictable time required to allocate
234    /// memory from the heap can cause glitches. If this is not a problem, you may use
235    /// the [process](Resampler::process) method instead.
236    ///
237    /// The input and output buffers are buffers from the [audioadapter] crate.
238    /// The input buffer must implement the [Adapter] trait,
239    /// and the output the corresponding [AdapterMut] trait.
240    /// This ensures that this method is able to both read and write
241    /// audio data from and to buffers with different layout, as well as different sample formats.
242    ///
243    /// The `indexing` parameter is optional. When left out, the default values are used.
244    ///  - `input_offset` and `output_offset`: these determine how many frames at the beginning
245    ///    of the input and output buffers will be skipped before reading or writing starts.
246    ///    See the `process_raw` example for how these may be used to process a longer sound clip.
247    ///  - `partial_len`: If the input buffer has fewer frames than the required input length,
248    ///    set `partial_len` to the available number.
249    ///    The resampler will then insert silence in place of the missing frames.
250    ///    This is useful for processing a longer buffer with repeated process calls,
251    ///    where at the last iteration there may be fewer frames left than what the resampler needs.
252    ///  - `active_channels_mask`: A vector of booleans determining what channels are to be processed.
253    ///    Any channel marked as inactive by a false value will be skipped during processing
254    ///    and the corresponding output will be left unchanged.
255    ///    If `None` is given, all channels will be considered active.
256    ///
257    /// Before processing, the input and output buffer sizes are checked.
258    /// If either has the wrong number of channels, or if the buffer can hold too few frames,
259    /// a [ResampleError] is returned.
260    /// Both input and output are allowed to be longer than required.
261    /// The number of input samples consumed and the number output samples written
262    /// per channel is returned in a tuple, `(input_frames, output_frames)`.
263    fn process_into_buffer(
264        &mut self,
265        buffer_in: &dyn Adapter<T>,
266        buffer_out: &mut dyn AdapterMut<T>,
267        indexing: Option<&Indexing>,
268    ) -> ResampleResult<(usize, usize)>;
269
270    /// Convenience method for processing audio clips of arbitrary length
271    /// from and to buffers in memory.
272    /// This method repeatedly calls [process_into_buffer](Resampler::process_into_buffer)
273    /// until all frames of the input buffer have been processed.
274    /// The processed frames are written to the output buffer,
275    /// with the initial silence (caused by the resampler delay) trimmed off.
276    ///
277    /// Use [process_all_needed_output_len](Resampler::process_all_needed_output_len)
278    /// to get the minimal length of the output buffer required
279    /// to resample a clip of a given length.
280    ///
281    /// The `active_channels_mask` parameter has the same meaning as in
282    /// [process_into_buffer](Resampler::process_into_buffer).
283    ///
284    /// Returns the lengths of the original input and the resampled output.
285    ///
286    /// # Example
287    ///
288    /// Resample one second of 44.1 kHz audio to 48 kHz, into a buffer allocated
289    /// up front. This is the variant to use when allocating during processing is
290    /// not acceptable, such as in a realtime thread. See
291    /// [process_all](Resampler::process_all) for the allocating counterpart.
292    ///
293    /// ```
294    /// # #[cfg(not(feature = "fft_resampler"))]
295    /// # fn main() {}
296    /// # #[cfg(feature = "fft_resampler")]
297    /// # fn main() {
298    /// use audioadapter_buffers::owned::InterleavedOwned;
299    /// use rubato::{Fft, FixedSync, Resampler};
300    ///
301    /// let channels = 2;
302    /// let input_len = 44100;
303    /// let input = InterleavedOwned::<f64>::new(0.0, channels, input_len);
304    ///
305    /// let mut resampler =
306    ///     Fft::<f64>::new(44100, 48000, 1024, channels, FixedSync::Both).unwrap();
307    ///
308    /// // Allocate an output buffer that is guaranteed to be big enough.
309    /// let needed_len = resampler.process_all_needed_output_len(input_len);
310    /// let mut output = InterleavedOwned::<f64>::new(0.0, channels, needed_len);
311    ///
312    /// let (consumed, produced) = resampler
313    ///     .process_all_into_buffer(&input, &mut output, input_len, None)
314    ///     .unwrap();
315    ///
316    /// // The resampled audio is the first `produced` frames of the buffer.
317    /// // The rest is padding, since the buffer is sized for the worst case.
318    /// assert_eq!(consumed, input_len);
319    /// assert!(produced >= 48000);
320    /// assert!(produced <= needed_len);
321    /// # }
322    /// ```
323    fn process_all_into_buffer(
324        &mut self,
325        buffer_in: &dyn Adapter<T>,
326        buffer_out: &mut dyn AdapterMut<T>,
327        input_len: usize,
328        active_channels_mask: Option<&[bool]>,
329    ) -> ResampleResult<(usize, usize)> {
330        let expected_output_len = (self.resample_ratio() * input_len as f64).ceil() as usize;
331
332        let mut indexing = Indexing {
333            input_offset: 0,
334            output_offset: 0,
335            active_channels_mask: active_channels_mask.map(|m| m.to_vec()),
336            partial_len: None,
337        };
338
339        let mut frames_left = input_len;
340        let mut output_len = 0;
341        let mut frames_to_trim = self.output_delay();
342        debug!(
343            "resamping {} input frames to {} output frames, delay to trim off {} frames",
344            input_len, expected_output_len, frames_to_trim
345        );
346
347        let next_nbr_input_frames = self.input_frames_next();
348        while frames_left > next_nbr_input_frames {
349            debug!("process, {} input frames left", frames_left);
350            let (nbr_in, nbr_out) =
351                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
352            frames_left -= nbr_in;
353            output_len += nbr_out;
354            indexing.input_offset += nbr_in;
355            indexing.output_offset += nbr_out;
356            if frames_to_trim > 0 && output_len > frames_to_trim {
357                debug!(
358                    "output, {} is longer than delay to trim, {}, trimming..",
359                    output_len, frames_to_trim
360                );
361                // move useful output data to start of output buffer
362                buffer_out.copy_frames_within(frames_to_trim, 0, frames_to_trim);
363                // update counters
364                output_len -= frames_to_trim;
365                indexing.output_offset -= frames_to_trim;
366                frames_to_trim = 0;
367            }
368        }
369        if frames_left > 0 {
370            debug!("process the last partial chunk, len {}", frames_left);
371            indexing.partial_len = Some(frames_left);
372            let (_nbr_in, nbr_out) =
373                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
374            output_len += nbr_out;
375            indexing.output_offset += nbr_out;
376        }
377        indexing.partial_len = Some(0);
378        while output_len < expected_output_len {
379            debug!(
380                "output is still too short, {} < {}, pump zeros..",
381                output_len, expected_output_len
382            );
383            let (_nbr_in, nbr_out) =
384                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
385            output_len += nbr_out;
386            indexing.output_offset += nbr_out;
387        }
388        Ok((input_len, expected_output_len))
389    }
390
391    /// Resample a whole audio clip in a single call, returning the result in a freshly
392    /// allocated buffer.
393    ///
394    /// This is the allocating counterpart to
395    /// [process_all_into_buffer](Resampler::process_all_into_buffer), and the right method
396    /// for resampling a complete clip that is already in memory. It repeatedly calls
397    /// [process_into_buffer](Resampler::process_into_buffer) under the hood, trims the
398    /// resampler's startup delay, and returns an [InterleavedOwned] holding exactly the
399    /// resampled frames (no leading silence and no trailing padding).
400    ///
401    /// Prefer this over a single [process](Resampler::process) call: `process` resamples one
402    /// fixed-size chunk, so using it for a whole clip means oversizing the resampler to the
403    /// clip length and leaves the startup delay untrimmed.
404    ///
405    /// For realtime use, where allocating on each call can cause glitches, use
406    /// [process_all_into_buffer](Resampler::process_all_into_buffer) with a pre-allocated
407    /// buffer instead.
408    ///
409    /// The resampler is [reset](Resampler::reset) first, so the clip is always resampled from
410    /// a clean state regardless of any previous use.
411    ///
412    /// `input_len` is the length of the clip in frames. The `active_channels_mask` parameter
413    /// has the same meaning as in [process_into_buffer](Resampler::process_into_buffer).
414    fn process_all(
415        &mut self,
416        buffer_in: &dyn Adapter<T>,
417        input_len: usize,
418        active_channels_mask: Option<&[bool]>,
419    ) -> ResampleResult<InterleavedOwned<T>> {
420        self.reset();
421        let channels = self.nbr_channels();
422        let needed_len = self.process_all_needed_output_len(input_len);
423        let mut buffer_out = InterleavedOwned::<T>::new(T::coerce_from(0.0), channels, needed_len);
424        let (_input_len, output_len) = self.process_all_into_buffer(
425            buffer_in,
426            &mut buffer_out,
427            input_len,
428            active_channels_mask,
429        )?;
430
431        // The valid output is the first `output_len` frames; the rest is padding. Trim it off.
432        // The buffer is interleaved, so `output_len` frames are the first `output_len * channels`
433        // samples.
434        let mut data = buffer_out.take_data();
435        data.truncate(output_len * channels);
436        Ok(InterleavedOwned::new_from(data, channels, output_len)
437            .expect("trimmed length is consistent with the channel count"))
438    }
439
440    /// Calculate the minimal length of the output buffer
441    /// needed to process a clip of length `input_len` using the
442    /// [process_all_into_buffer](Resampler::process_all_into_buffer) method.
443    ///
444    /// Includes additional space needed by the resampler implementation. For
445    /// the length of resampled output, see the return value of
446    /// [process_all_into_buffer](Resampler::process_all_into_buffer).
447    fn process_all_needed_output_len(&mut self, input_len: usize) -> usize {
448        let delay_frames = self.output_delay();
449        let output_frames_max = self.output_frames_max();
450        let expected_output_len = (self.resample_ratio() * input_len as f64).ceil() as usize;
451        delay_frames + output_frames_max + expected_output_len
452    }
453
454    /// Get the maximum possible number of input frames per channel the resampler could require.
455    fn input_frames_max(&self) -> usize;
456
457    /// Get the number of frames per channel needed for the next call to
458    /// [process_into_buffer](Resampler::process_into_buffer) or [process](Resampler::process).
459    fn input_frames_next(&self) -> usize;
460
461    /// Get the number of channels this Resampler is configured for.
462    fn nbr_channels(&self) -> usize;
463
464    /// Get the maximum possible number of output frames per channel.
465    fn output_frames_max(&self) -> usize;
466
467    /// Get the number of frames per channel that will be output from the next call to
468    /// [process_into_buffer](Resampler::process_into_buffer) or [process](Resampler::process).
469    fn output_frames_next(&self) -> usize;
470
471    /// Get the delay for the resampler, reported as a number of output frames.
472    /// This gives how many frames any event in the input is delayed before it appears in the output.
473    fn output_delay(&self) -> usize;
474
475    /// Get the current resample ratio, defined as output sample rate divided by input sample rate.
476    fn resample_ratio(&self) -> f64;
477
478    /// Reset the resampler state and clear all internal buffers.
479    fn reset(&mut self);
480
481    /// If this resampler can change its resample ratio, borrow it as an [Adjustable],
482    /// otherwise return `None`.
483    ///
484    /// Asynchronous resamplers return `Some`, synchronous resamplers return `None`. This lets
485    /// you recover the adjust-ratio capability from a `&mut dyn Resampler` without knowing the
486    /// concrete type:
487    ///
488    /// ```ignore
489    /// if let Some(adjustable) = resampler.as_adjustable() {
490    ///     adjustable.set_resample_ratio(new_ratio, true)?;
491    /// }
492    /// ```
493    ///
494    /// Any implementor of [Adjustable] must return `Some(self)` here, otherwise the capability
495    /// is invisible through a trait object. This method is intentionally required rather than
496    /// defaulted so that implementing [Adjustable] and advertising it cannot drift apart.
497    fn as_adjustable(&mut self) -> Option<&mut dyn Adjustable<T>>;
498
499    /// Returns `true` if this resampler is [Adjustable], meaning that
500    /// [as_adjustable](Resampler::as_adjustable) returns `Some`.
501    ///
502    /// Unlike [as_adjustable](Resampler::as_adjustable) this takes a shared reference, so the
503    /// capability can be queried through a `&dyn Resampler`. Implementors of [Adjustable] must
504    /// override this to return `true`.
505    fn is_adjustable(&self) -> bool {
506        false
507    }
508
509    /// If this resampler can change its chunk size, borrow it as a [Resizable], otherwise
510    /// return `None`.
511    ///
512    /// Any implementor of [Resizable] must return `Some(self)` here, otherwise the capability
513    /// is invisible through a trait object. This method is intentionally required rather than
514    /// defaulted so that implementing [Resizable] and advertising it cannot drift apart.
515    fn as_resizable(&mut self) -> Option<&mut dyn Resizable<T>>;
516
517    /// Returns `true` if this resampler is [Resizable], meaning that
518    /// [as_resizable](Resampler::as_resizable) returns `Some`.
519    ///
520    /// Unlike [as_resizable](Resampler::as_resizable) this takes a shared reference, so the
521    /// capability can be queried through a `&dyn Resampler`. Implementors of [Resizable] must
522    /// override this to return `true`.
523    fn is_resizable(&self) -> bool {
524        false
525    }
526}
527
528/// A [Resampler] whose resample ratio can be changed after construction.
529///
530/// Implemented by the asynchronous resamplers. From a `&mut dyn Resampler` it can be recovered
531/// with [Resampler::as_adjustable].
532///
533/// The ratio is typically driven by a feedback loop that measures a buffer fill and nudges it to
534/// track a small clock difference. [Slip] carries a complete worked example of such a loop; the
535/// same pattern applies to any `Adjustable` resampler, including [Async].
536pub trait Adjustable<T>: Resampler<T>
537where
538    T: Sample,
539{
540    /// Update the resample ratio.
541    ///
542    /// The ratio must be within `original / maximum` to `original * maximum`, where the original
543    /// and maximum are the resampling ratios that were provided to the constructor. Trying to set
544    /// the ratio outside these bounds will return [ResampleError::RatioOutOfBounds].
545    ///
546    /// If the argument `ramp` is set to true, the ratio will be ramped from the old to the new value
547    /// during processing of the next chunk. This allows smooth transitions from one ratio to another.
548    /// If `ramp` is false, the new ratio will be applied from the start of the next chunk.
549    fn set_resample_ratio(&mut self, new_ratio: f64, ramp: bool) -> ResampleResult<()>;
550
551    /// Update the resample ratio as a factor relative to the original one.
552    ///
553    /// The relative ratio must be within `1 / maximum` to `maximum`, where `maximum` is the maximum
554    /// resampling ratio that was provided to the constructor. Trying to set the ratio outside these
555    /// bounds will return [ResampleError::RatioOutOfBounds].
556    ///
557    /// Ratios above 1.0 slow down the output and lower the pitch, while ratios
558    /// below 1.0 speed up the output and raise the pitch.
559    fn set_resample_ratio_relative(&mut self, rel_ratio: f64, ramp: bool) -> ResampleResult<()>;
560}
561
562/// A [Resampler] whose chunk size can be changed after construction.
563///
564/// From a `&mut dyn Resampler` it can be recovered with [Resampler::as_resizable].
565pub trait Resizable<T>: Resampler<T>
566where
567    T: Sample,
568{
569    /// Change the chunk size for the resampler.
570    /// The value must be equal to or smaller than the chunk size value
571    /// that the resampler was created with.
572    /// [ResampleError::InvalidChunkSize] is returned if the value is zero or too large.
573    ///
574    /// The meaning of chunk size depends on the resampler,
575    /// it refers to the input size for resamplers with fixed input size,
576    /// and output size for resamplers with fixed output size.
577    fn set_chunk_size(&mut self, chunksize: usize) -> ResampleResult<()>;
578}
579
580pub(crate) fn validate_buffers<T>(
581    wave_in: &dyn Adapter<T>,
582    wave_out: &dyn AdapterMut<T>,
583    channels: usize,
584    min_input_len: usize,
585    min_output_len: usize,
586) -> ResampleResult<()> {
587    if wave_in.channels() != channels {
588        return Err(ResampleError::WrongNumberOfInputChannels {
589            expected: channels,
590            actual: wave_in.channels(),
591        });
592    }
593    if wave_in.frames() < min_input_len {
594        return Err(ResampleError::InsufficientInputBufferSize {
595            expected: min_input_len,
596            actual: wave_in.frames(),
597        });
598    }
599    if wave_out.channels() != channels {
600        return Err(ResampleError::WrongNumberOfOutputChannels {
601            expected: channels,
602            actual: wave_out.channels(),
603        });
604    }
605    if wave_out.frames() < min_output_len {
606        return Err(ResampleError::InsufficientOutputBufferSize {
607            expected: min_output_len,
608            actual: wave_out.frames(),
609        });
610    }
611    Ok(())
612}
613
614#[cfg(test)]
615pub mod tests {
616    use crate::Resampler;
617    use crate::{
618        Async, FixedAsync, Indexing, ResampleError, SincInterpolationParameters,
619        SincInterpolationType, Slip, WindowFunction,
620    };
621    #[cfg(feature = "fft_resampler")]
622    use crate::{Fft, FixedSync};
623    use audioadapter::Adapter;
624    use audioadapter_buffers::direct::SequentialSliceOfVecs;
625
626    fn test_sinc_resampler() -> Async<f64> {
627        Async::<f64>::new_sinc(
628            88200.0 / 44100.0,
629            1.1,
630            &SincInterpolationParameters {
631                sinc_len: 64,
632                f_cutoff: Some(0.95),
633                interpolation: SincInterpolationType::Cubic,
634                oversampling_factor: 16,
635                window: WindowFunction::BlackmanHarris2,
636            },
637            1024,
638            2,
639            FixedAsync::Input,
640        )
641        .unwrap()
642    }
643
644    #[test_log::test]
645    fn process_single_chunk() {
646        let mut resampler = test_sinc_resampler();
647        let in_len = resampler.input_frames_next();
648        let samples: Vec<f64> = (0..in_len).map(|v| v as f64 / 10.0).collect();
649        let input_data = vec![samples; 2];
650        let input = SequentialSliceOfVecs::new(&input_data, 2, in_len).unwrap();
651
652        // A full chunk with no indexing returns output_frames_next frames.
653        let expected = resampler.output_frames_next();
654        let out = resampler.process(&input, None).unwrap();
655        assert_eq!(out.channels(), 2);
656        assert_eq!(out.frames(), expected);
657
658        // A final partial chunk fed via Indexing.partial_len still returns a full output chunk
659        // (process does not trim); the missing input frames are treated as silence.
660        let expected = resampler.output_frames_next();
661        let out = resampler
662            .process(&input, Some(&Indexing::new().partial_len(in_len / 2)))
663            .unwrap();
664        assert_eq!(out.frames(), expected);
665    }
666
667    #[test_log::test]
668    fn wrong_length_mask_returns_error() {
669        // A mask with the wrong number of channels must return an error, not panic.
670        let mut resampler = test_sinc_resampler();
671        let in_len = resampler.input_frames_next();
672        let input_data = vec![vec![0.0f64; in_len]; 2];
673        let input = SequentialSliceOfVecs::new(&input_data, 2, in_len).unwrap();
674
675        let indexing = Indexing::new().active_channels_mask(vec![true, true, true]);
676        let result = resampler.process(&input, Some(&indexing));
677        assert!(matches!(
678            result,
679            Err(ResampleError::WrongNumberOfMaskChannels {
680                expected: 2,
681                actual: 3
682            })
683        ));
684    }
685
686    #[test_log::test]
687    fn process_all() {
688        let mut resampler = Async::<f64>::new_sinc(
689            88200.0 / 44100.0,
690            1.1,
691            &SincInterpolationParameters {
692                sinc_len: 64,
693                f_cutoff: Some(0.95),
694                interpolation: SincInterpolationType::Cubic,
695                oversampling_factor: 16,
696                window: WindowFunction::BlackmanHarris2,
697            },
698            1024,
699            2,
700            FixedAsync::Input,
701        )
702        .unwrap();
703        let input_len = 12345;
704        let samples: Vec<f64> = (0..input_len).map(|v| v as f64 / 10.0).collect();
705        let input_data = vec![samples; 2];
706        // add a ramp to the input
707        let input = SequentialSliceOfVecs::new(&input_data, 2, input_len).unwrap();
708        let output_len = resampler.process_all_needed_output_len(input_len);
709        let mut output_data = vec![vec![0.0f64; output_len]; 2];
710        let mut output = SequentialSliceOfVecs::new_mut(&mut output_data, 2, output_len).unwrap();
711        let (nbr_in, nbr_out) = resampler
712            .process_all_into_buffer(&input, &mut output, input_len, None)
713            .unwrap();
714        assert_eq!(nbr_in, input_len);
715        // This is a simple ratio, output should be twice as long as input
716        assert_eq!(2 * nbr_in, nbr_out);
717
718        // check that the output follows the input ramp, within suitable margins
719        let increment = 0.1 / resampler.resample_ratio();
720        let delay = resampler.output_delay();
721        let margin = (delay as f64 * resampler.resample_ratio()) as usize;
722        let mut expected = margin as f64 * increment;
723        for frame in margin..(nbr_out - margin) {
724            for chan in 0..2 {
725                let val = output.read_sample(chan, frame).unwrap();
726                assert!(
727                    val - expected < 100.0 * increment,
728                    "frame: {}, value: {}, expected: {}",
729                    frame,
730                    val,
731                    expected
732                );
733                assert!(
734                    expected - val < 100.0 * increment,
735                    "frame: {}, value: {}, expected: {}",
736                    frame,
737                    val,
738                    expected
739                );
740            }
741            expected += increment;
742        }
743    }
744
745    #[test_log::test]
746    fn process_all_allocating() {
747        let mut resampler = Async::<f64>::new_sinc(
748            88200.0 / 44100.0,
749            1.1,
750            &SincInterpolationParameters {
751                sinc_len: 64,
752                f_cutoff: Some(0.95),
753                interpolation: SincInterpolationType::Cubic,
754                oversampling_factor: 16,
755                window: WindowFunction::BlackmanHarris2,
756            },
757            1024,
758            2,
759            FixedAsync::Input,
760        )
761        .unwrap();
762        let input_len = 12345;
763        let samples: Vec<f64> = (0..input_len).map(|v| v as f64 / 10.0).collect();
764        let input_data = vec![samples; 2];
765        let input = SequentialSliceOfVecs::new(&input_data, 2, input_len).unwrap();
766
767        let output = resampler.process_all(&input, input_len, None).unwrap();
768        // 2x upsampling, and the result must be trimmed to exactly the resampled length
769        // (not the oversized internal buffer).
770        let expected_len = 2 * input_len;
771        assert_eq!(output.channels(), 2);
772        assert_eq!(output.frames(), expected_len);
773
774        // The delay is trimmed, so output frame f follows input position f / ratio.
775        let increment = 0.1 / resampler.resample_ratio();
776        let margin = (resampler.output_delay() as f64 * resampler.resample_ratio()) as usize;
777        for frame in margin..(expected_len - margin) {
778            let expected = frame as f64 * increment;
779            for chan in 0..2 {
780                let val = output.read_sample(chan, frame).unwrap();
781                assert!(
782                    (val - expected).abs() < 100.0 * increment,
783                    "frame: {}, value: {}, expected: {}",
784                    frame,
785                    val,
786                    expected
787                );
788            }
789        }
790
791        // A second call must reset internally and produce an identical result.
792        let output2 = resampler.process_all(&input, input_len, None).unwrap();
793        assert_eq!(output2.frames(), expected_len);
794        for frame in 0..expected_len {
795            for chan in 0..2 {
796                assert_eq!(
797                    output.read_sample(chan, frame),
798                    output2.read_sample(chan, frame)
799                );
800            }
801        }
802    }
803
804    #[test_log::test]
805    fn capability_queries() {
806        // Async resamplers are adjustable and resizable.
807        let mut resampler = test_sinc_resampler();
808        assert!(resampler.is_adjustable());
809        assert!(resampler.is_resizable());
810        resampler
811            .as_adjustable()
812            .expect("Async should be adjustable")
813            .set_resample_ratio_relative(1.05, false)
814            .unwrap();
815        assert!(resampler.as_resizable().is_some());
816
817        // The capability is reachable through a trait object too, including through a shared
818        // reference via the `is_*` probes.
819        let mut boxed: Box<dyn Resampler<f64>> = Box::new(test_sinc_resampler());
820        let shared: &dyn Resampler<f64> = boxed.as_ref();
821        assert!(shared.is_adjustable());
822        assert!(shared.is_resizable());
823        assert!(boxed.as_adjustable().is_some());
824        assert!(boxed.as_resizable().is_some());
825
826        // Slip resamplers are adjustable and resizable, like the async resamplers.
827        let mut slip = Slip::<f64>::new(1024, 2, FixedAsync::Output).unwrap();
828        assert!(slip.is_adjustable());
829        assert!(slip.is_resizable());
830        assert!(slip.as_adjustable().is_some());
831        assert!(slip.as_resizable().is_some());
832
833        // Synchronous Fft resamplers are neither.
834        #[cfg(feature = "fft_resampler")]
835        {
836            let mut fft = Fft::<f64>::new(44100, 48000, 1024, 2, FixedSync::Both).unwrap();
837            assert!(!fft.is_adjustable());
838            assert!(!fft.is_resizable());
839            assert!(fft.as_adjustable().is_none());
840            assert!(fft.as_resizable().is_none());
841        }
842    }
843
844    // This tests that a Resampler can be boxed.
845    #[test_log::test]
846    fn boxed_resampler() {
847        let mut boxed: Box<dyn Resampler<f64>> = Box::new(
848            Async::<f64>::new_sinc(
849                88200.0 / 44100.0,
850                1.1,
851                &SincInterpolationParameters {
852                    sinc_len: 64,
853                    f_cutoff: Some(0.95),
854                    interpolation: SincInterpolationType::Cubic,
855                    oversampling_factor: 16,
856                    window: WindowFunction::BlackmanHarris2,
857                },
858                1024,
859                2,
860                FixedAsync::Input,
861            )
862            .unwrap(),
863        );
864        let max_frames_out = boxed.output_frames_max();
865        let nbr_frames_in_next = boxed.input_frames_next();
866        let waves = vec![vec![0.0f64; nbr_frames_in_next]; 2];
867        let mut waves_out = vec![vec![0.0f64; max_frames_out]; 2];
868        let input = SequentialSliceOfVecs::new(&waves, 2, nbr_frames_in_next).unwrap();
869        let mut output = SequentialSliceOfVecs::new_mut(&mut waves_out, 2, max_frames_out).unwrap();
870        process_with_boxed(&mut boxed, &input, &mut output);
871    }
872
873    fn process_with_boxed<'a>(
874        resampler: &mut Box<dyn Resampler<f64>>,
875        input: &SequentialSliceOfVecs<&'a [Vec<f64>]>,
876        output: &mut SequentialSliceOfVecs<&'a mut [Vec<f64>]>,
877    ) {
878        resampler.process_into_buffer(input, output, None).unwrap();
879    }
880
881    fn impl_send<T: Send>() {
882        fn is_send<T: Send>() {}
883        is_send::<Async<T>>();
884        is_send::<Slip<T>>();
885        #[cfg(feature = "fft_resampler")]
886        {
887            is_send::<Fft<T>>();
888        }
889    }
890
891    // This tests that all resamplers are Send.
892    #[test]
893    fn test_impl_send() {
894        impl_send::<f32>();
895        impl_send::<f64>();
896    }
897
898    pub fn expected_output_value(idx: usize, delay: usize, ratio: f64) -> f64 {
899        if idx <= delay {
900            return 0.0;
901        }
902        (idx - delay) as f64 * 0.1 / ratio
903    }
904
905    #[macro_export]
906    macro_rules! check_output {
907        ($resampler:ident, $fty:ty) => {
908            let mut ramp_value: $fty = 0.0;
909            let max_input_len = $resampler.input_frames_max();
910            let max_output_len = $resampler.output_frames_max();
911            let ratio = $resampler.resample_ratio() as $fty;
912            let delay = $resampler.output_delay();
913            let mut output_index = 0;
914
915            let out_incr = 0.1 / ratio;
916
917            let nbr_iterations =
918                100000 / ($resampler.output_frames_next() + $resampler.input_frames_next());
919            for _n in 0..nbr_iterations {
920                let expected_frames_in = $resampler.input_frames_next();
921                let expected_frames_out = $resampler.output_frames_next();
922                // Check that lengths are within the reported max values
923                assert!(expected_frames_in <= max_input_len);
924                assert!(expected_frames_out <= max_output_len);
925                let mut input_data = vec![vec![0.0 as $fty; expected_frames_in]; 2];
926                for m in 0..expected_frames_in {
927                    for ch in 0..2 {
928                        input_data[ch][m] = ramp_value;
929                    }
930                    ramp_value += 0.1;
931                }
932                let input = SequentialSliceOfVecs::new(&input_data, 2, expected_frames_in).unwrap();
933                let mut output_data = vec![vec![0.0 as $fty; expected_frames_out]; 2];
934                let mut output =
935                    SequentialSliceOfVecs::new_mut(&mut output_data, 2, expected_frames_out)
936                        .unwrap();
937
938                trace!("resample...");
939                let (input_frames, output_frames) = $resampler
940                    .process_into_buffer(&input, &mut output, None)
941                    .unwrap();
942                trace!("assert lengths");
943                assert_eq!(input_frames, expected_frames_in);
944                assert_eq!(output_frames, expected_frames_out);
945                trace!("check output");
946                for idx in 0..output_frames {
947                    let expected = expected_output_value(output_index + idx, delay, ratio) as $fty;
948                    for ch in 0..2 {
949                        let value = output_data[ch][idx];
950                        let margin = 3.0 * out_incr;
951                        assert!(
952                            value > expected - margin,
953                            "Value at frame {} is too small, {} < {} - {}",
954                            output_index + idx,
955                            value,
956                            expected,
957                            margin
958                        );
959                        assert!(
960                            value < expected + margin,
961                            "Value at frame {} is too large, {} > {} + {}",
962                            output_index + idx,
963                            value,
964                            expected,
965                            margin
966                        );
967                    }
968                }
969                output_index += output_frames;
970            }
971            assert!(output_index > 1000, "Too few frames checked!");
972        };
973    }
974
975    #[macro_export]
976    macro_rules! check_ratio {
977        ($resampler:ident, $repetitions:expr, $margin:expr, $fty:ty) => {
978            let ratio = $resampler.resample_ratio();
979            let max_input_len = $resampler.input_frames_max();
980            let max_output_len = $resampler.output_frames_max();
981            let waves_in = vec![vec![0.0 as $fty; max_input_len]; 2];
982            let input = SequentialSliceOfVecs::new(&waves_in, 2, max_input_len).unwrap();
983            let mut waves_out = vec![vec![0.0 as $fty; max_output_len]; 2];
984            let mut output =
985                SequentialSliceOfVecs::new_mut(&mut waves_out, 2, max_output_len).unwrap();
986            let mut total_in = 0;
987            let mut total_out = 0;
988            for _ in 0..$repetitions {
989                let out = $resampler
990                    .process_into_buffer(&input, &mut output, None)
991                    .unwrap();
992                total_in += out.0;
993                total_out += out.1
994            }
995            let measured_ratio = total_out as f64 / total_in as f64;
996            assert!(
997                measured_ratio / ratio > (1.0 - $margin),
998                "Measured ratio is too small, measured / expected = {}",
999                measured_ratio / ratio
1000            );
1001            assert!(
1002                measured_ratio / ratio < (1.0 + $margin),
1003                "Measured ratio is too large, measured / expected = {}",
1004                measured_ratio / ratio
1005            );
1006        };
1007    }
1008
1009    #[macro_export]
1010    macro_rules! assert_fi_len {
1011        ($resampler:ident, $chunksize:expr) => {
1012            let nbr_frames_in_next = $resampler.input_frames_next();
1013            let nbr_frames_in_max = $resampler.input_frames_max();
1014            assert_eq!(
1015                nbr_frames_in_next, $chunksize,
1016                "expected {} for next input samples, got {}",
1017                $chunksize, nbr_frames_in_next
1018            );
1019            assert_eq!(
1020                nbr_frames_in_next, $chunksize,
1021                "expected {} for max input samples, got {}",
1022                $chunksize, nbr_frames_in_max
1023            );
1024        };
1025    }
1026
1027    #[macro_export]
1028    macro_rules! assert_fo_len {
1029        ($resampler:ident, $chunksize:expr) => {
1030            let nbr_frames_out_next = $resampler.output_frames_next();
1031            let nbr_frames_out_max = $resampler.output_frames_max();
1032            assert_eq!(
1033                nbr_frames_out_next, $chunksize,
1034                "expected {} for next output samples, got {}",
1035                $chunksize, nbr_frames_out_next
1036            );
1037            assert_eq!(
1038                nbr_frames_out_next, $chunksize,
1039                "expected {} for max output samples, got {}",
1040                $chunksize, nbr_frames_out_max
1041            );
1042        };
1043    }
1044
1045    #[macro_export]
1046    macro_rules! assert_fb_len {
1047        ($resampler:ident) => {
1048            let nbr_frames_out_next = $resampler.output_frames_next();
1049            let nbr_frames_out_max = $resampler.output_frames_max();
1050            let nbr_frames_in_next = $resampler.input_frames_next();
1051            let nbr_frames_in_max = $resampler.input_frames_max();
1052            let ratio = $resampler.resample_ratio();
1053            assert_eq!(
1054                nbr_frames_out_next, nbr_frames_out_max,
1055                "next output frames, {}, is different than max, {}",
1056                nbr_frames_out_next, nbr_frames_out_next
1057            );
1058            assert_eq!(
1059                nbr_frames_in_next, nbr_frames_in_max,
1060                "next input frames, {}, is different than max, {}",
1061                nbr_frames_in_next, nbr_frames_in_max
1062            );
1063            let frames_ratio = nbr_frames_out_next as f64 / nbr_frames_in_next as f64;
1064            assert_abs_diff_eq!(frames_ratio, ratio, epsilon = 0.000001);
1065        };
1066    }
1067
1068    #[macro_export]
1069    macro_rules! check_reset {
1070        ($resampler:ident) => {
1071            let frames_in = $resampler.input_frames_next();
1072
1073            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1074            input_data
1075                .iter_mut()
1076                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1077
1078            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1079
1080            let frames_out = $resampler.output_frames_next();
1081            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1082            let mut output_1 =
1083                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1084            $resampler
1085                .process_into_buffer(&input, &mut output_1, None)
1086                .unwrap();
1087            $resampler.reset();
1088            assert_eq!(
1089                frames_in,
1090                $resampler.input_frames_next(),
1091                "Resampler requires different number of frames when new and after a reset."
1092            );
1093            let mut output_data_2 = vec![vec![0.0; frames_out]; 2];
1094            let mut output_2 =
1095                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out).unwrap();
1096            $resampler
1097                .process_into_buffer(&input, &mut output_2, None)
1098                .unwrap();
1099            assert_eq!(
1100                output_data_1, output_data_2,
1101                "Resampler gives different output when new and after a reset."
1102            );
1103        };
1104    }
1105
1106    #[macro_export]
1107    macro_rules! check_input_offset {
1108        ($resampler:ident) => {
1109            let frames_in = $resampler.input_frames_next();
1110
1111            let mut input_data_1 = vec![vec![0.0f64; frames_in]; 2];
1112            input_data_1
1113                .iter_mut()
1114                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1115
1116            let offset = 123;
1117            let mut input_data_2 = vec![vec![0.0f64; frames_in + offset]; 2];
1118            for (ch, data) in input_data_2.iter_mut().enumerate() {
1119                data[offset..offset + frames_in].clone_from_slice(&input_data_1[ch][..])
1120            }
1121
1122            let input_1 = SequentialSliceOfVecs::new(&input_data_1, 2, frames_in).unwrap();
1123            let input_2 = SequentialSliceOfVecs::new(&input_data_2, 2, frames_in + offset).unwrap();
1124
1125            let frames_out = $resampler.output_frames_next();
1126            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1127            let mut output_1 =
1128                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1129            $resampler
1130                .process_into_buffer(&input_1, &mut output_1, None)
1131                .unwrap();
1132            $resampler.reset();
1133            assert_eq!(
1134                frames_in,
1135                $resampler.input_frames_next(),
1136                "Resampler requires different number of frames when new and after a reset."
1137            );
1138            let mut output_data_2 = vec![vec![0.0; frames_out]; 2];
1139            let mut output_2 =
1140                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out).unwrap();
1141
1142            let indexing = Indexing {
1143                input_offset: offset,
1144                output_offset: 0,
1145                active_channels_mask: None,
1146                partial_len: None,
1147            };
1148            $resampler
1149                .process_into_buffer(&input_2, &mut output_2, Some(&indexing))
1150                .unwrap();
1151            assert_eq!(
1152                output_data_1, output_data_2,
1153                "Resampler gives different output when new and after a reset."
1154            );
1155        };
1156    }
1157
1158    #[macro_export]
1159    macro_rules! check_output_offset {
1160        ($resampler:ident) => {
1161            let frames_in = $resampler.input_frames_next();
1162
1163            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1164            input_data
1165                .iter_mut()
1166                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1167
1168            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1169
1170            let frames_out = $resampler.output_frames_next();
1171            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1172            let mut output_1 =
1173                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1174            $resampler
1175                .process_into_buffer(&input, &mut output_1, None)
1176                .unwrap();
1177            $resampler.reset();
1178            assert_eq!(
1179                frames_in,
1180                $resampler.input_frames_next(),
1181                "Resampler requires different number of frames when new and after a reset."
1182            );
1183            let offset = 123;
1184            let mut output_data_2 = vec![vec![0.0; frames_out + offset]; 2];
1185            let mut output_2 =
1186                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out + offset).unwrap();
1187            let indexing = Indexing {
1188                input_offset: 0,
1189                output_offset: offset,
1190                active_channels_mask: None,
1191                partial_len: None,
1192            };
1193            $resampler
1194                .process_into_buffer(&input, &mut output_2, Some(&indexing))
1195                .unwrap();
1196            assert_eq!(
1197                output_data_1[0][..],
1198                output_data_2[0][offset..],
1199                "Resampler gives different output when new and after a reset."
1200            );
1201            assert_eq!(
1202                output_data_1[1][..],
1203                output_data_2[1][offset..],
1204                "Resampler gives different output when new and after a reset."
1205            );
1206        };
1207    }
1208
1209    #[macro_export]
1210    macro_rules! check_masked {
1211        ($resampler:ident) => {
1212            let frames_in = $resampler.input_frames_next();
1213
1214            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1215            input_data
1216                .iter_mut()
1217                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1218
1219            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1220
1221            let frames_out = $resampler.output_frames_next();
1222            let mut output_data = vec![vec![0.0; frames_out]; 2];
1223            let mut output =
1224                SequentialSliceOfVecs::new_mut(&mut output_data, 2, frames_out).unwrap();
1225
1226            let indexing = Indexing {
1227                input_offset: 0,
1228                output_offset: 0,
1229                active_channels_mask: Some(vec![false, true]),
1230                partial_len: None,
1231            };
1232            $resampler
1233                .process_into_buffer(&input, &mut output, Some(&indexing))
1234                .unwrap();
1235
1236            let non_zero_chan_0 = output_data[0].iter().filter(|&v| *v != 0.0).count();
1237            let non_zero_chan_1 = output_data[1].iter().filter(|&v| *v != 0.0).count();
1238            // assert channel 0 is all zero
1239            assert_eq!(
1240                non_zero_chan_0, 0,
1241                "Some sample in the non-active channel has a non-zero value"
1242            );
1243            // assert channel 1 has some values
1244            assert!(
1245                non_zero_chan_1 > 0,
1246                "No sample in the active channel has a non-zero value"
1247            );
1248        };
1249    }
1250
1251    #[macro_export]
1252    macro_rules! check_resize {
1253        ($resampler:ident) => {};
1254    }
1255}