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_f64` 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    fn process_all_into_buffer(
286        &mut self,
287        buffer_in: &dyn Adapter<T>,
288        buffer_out: &mut dyn AdapterMut<T>,
289        input_len: usize,
290        active_channels_mask: Option<&[bool]>,
291    ) -> ResampleResult<(usize, usize)> {
292        let expected_output_len = (self.resample_ratio() * input_len as f64).ceil() as usize;
293
294        let mut indexing = Indexing {
295            input_offset: 0,
296            output_offset: 0,
297            active_channels_mask: active_channels_mask.map(|m| m.to_vec()),
298            partial_len: None,
299        };
300
301        let mut frames_left = input_len;
302        let mut output_len = 0;
303        let mut frames_to_trim = self.output_delay();
304        debug!(
305            "resamping {} input frames to {} output frames, delay to trim off {} frames",
306            input_len, expected_output_len, frames_to_trim
307        );
308
309        let next_nbr_input_frames = self.input_frames_next();
310        while frames_left > next_nbr_input_frames {
311            debug!("process, {} input frames left", frames_left);
312            let (nbr_in, nbr_out) =
313                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
314            frames_left -= nbr_in;
315            output_len += nbr_out;
316            indexing.input_offset += nbr_in;
317            indexing.output_offset += nbr_out;
318            if frames_to_trim > 0 && output_len > frames_to_trim {
319                debug!(
320                    "output, {} is longer than delay to trim, {}, trimming..",
321                    output_len, frames_to_trim
322                );
323                // move useful output data to start of output buffer
324                buffer_out.copy_frames_within(frames_to_trim, 0, frames_to_trim);
325                // update counters
326                output_len -= frames_to_trim;
327                indexing.output_offset -= frames_to_trim;
328                frames_to_trim = 0;
329            }
330        }
331        if frames_left > 0 {
332            debug!("process the last partial chunk, len {}", frames_left);
333            indexing.partial_len = Some(frames_left);
334            let (_nbr_in, nbr_out) =
335                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
336            output_len += nbr_out;
337            indexing.output_offset += nbr_out;
338        }
339        indexing.partial_len = Some(0);
340        while output_len < expected_output_len {
341            debug!(
342                "output is still too short, {} < {}, pump zeros..",
343                output_len, expected_output_len
344            );
345            let (_nbr_in, nbr_out) =
346                self.process_into_buffer(buffer_in, buffer_out, Some(&indexing))?;
347            output_len += nbr_out;
348            indexing.output_offset += nbr_out;
349        }
350        Ok((input_len, expected_output_len))
351    }
352
353    /// Resample a whole audio clip in a single call, returning the result in a freshly
354    /// allocated buffer.
355    ///
356    /// This is the allocating counterpart to
357    /// [process_all_into_buffer](Resampler::process_all_into_buffer), and the right method
358    /// for resampling a complete clip that is already in memory. It repeatedly calls
359    /// [process_into_buffer](Resampler::process_into_buffer) under the hood, trims the
360    /// resampler's startup delay, and returns an [InterleavedOwned] holding exactly the
361    /// resampled frames (no leading silence and no trailing padding).
362    ///
363    /// Prefer this over a single [process](Resampler::process) call: `process` resamples one
364    /// fixed-size chunk, so using it for a whole clip means oversizing the resampler to the
365    /// clip length and leaves the startup delay untrimmed.
366    ///
367    /// For realtime use, where allocating on each call can cause glitches, use
368    /// [process_all_into_buffer](Resampler::process_all_into_buffer) with a pre-allocated
369    /// buffer instead.
370    ///
371    /// The resampler is [reset](Resampler::reset) first, so the clip is always resampled from
372    /// a clean state regardless of any previous use.
373    ///
374    /// `input_len` is the length of the clip in frames. The `active_channels_mask` parameter
375    /// has the same meaning as in [process_into_buffer](Resampler::process_into_buffer).
376    fn process_all(
377        &mut self,
378        buffer_in: &dyn Adapter<T>,
379        input_len: usize,
380        active_channels_mask: Option<&[bool]>,
381    ) -> ResampleResult<InterleavedOwned<T>> {
382        self.reset();
383        let channels = self.nbr_channels();
384        let needed_len = self.process_all_needed_output_len(input_len);
385        let mut buffer_out = InterleavedOwned::<T>::new(T::coerce_from(0.0), channels, needed_len);
386        let (_input_len, output_len) = self.process_all_into_buffer(
387            buffer_in,
388            &mut buffer_out,
389            input_len,
390            active_channels_mask,
391        )?;
392
393        // The valid output is the first `output_len` frames; the rest is padding. Trim it off.
394        // The buffer is interleaved, so `output_len` frames are the first `output_len * channels`
395        // samples.
396        let mut data = buffer_out.take_data();
397        data.truncate(output_len * channels);
398        Ok(InterleavedOwned::new_from(data, channels, output_len)
399            .expect("trimmed length is consistent with the channel count"))
400    }
401
402    /// Calculate the minimal length of the output buffer
403    /// needed to process a clip of length `input_len` using the
404    /// [process_all_into_buffer](Resampler::process_all_into_buffer) method.
405    ///
406    /// Includes additional space needed by the resampler implementation. For
407    /// the length of resampled output, see the return value of
408    /// [process_all_into_buffer](Resampler::process_all_into_buffer).
409    fn process_all_needed_output_len(&mut self, input_len: usize) -> usize {
410        let delay_frames = self.output_delay();
411        let output_frames_max = self.output_frames_max();
412        let expected_output_len = (self.resample_ratio() * input_len as f64).ceil() as usize;
413        delay_frames + output_frames_max + expected_output_len
414    }
415
416    /// Get the maximum possible number of input frames per channel the resampler could require.
417    fn input_frames_max(&self) -> usize;
418
419    /// Get the number of frames per channel needed for the next call to
420    /// [process_into_buffer](Resampler::process_into_buffer) or [process](Resampler::process).
421    fn input_frames_next(&self) -> usize;
422
423    /// Get the number of channels this Resampler is configured for.
424    fn nbr_channels(&self) -> usize;
425
426    /// Get the maximum possible number of output frames per channel.
427    fn output_frames_max(&self) -> usize;
428
429    /// Get the number of frames per channel that will be output from the next call to
430    /// [process_into_buffer](Resampler::process_into_buffer) or [process](Resampler::process).
431    fn output_frames_next(&self) -> usize;
432
433    /// Get the delay for the resampler, reported as a number of output frames.
434    /// This gives how many frames any event in the input is delayed before it appears in the output.
435    fn output_delay(&self) -> usize;
436
437    /// Get the current resample ratio, defined as output sample rate divided by input sample rate.
438    fn resample_ratio(&self) -> f64;
439
440    /// Reset the resampler state and clear all internal buffers.
441    fn reset(&mut self);
442
443    /// If this resampler can change its resample ratio, borrow it as an [Adjustable],
444    /// otherwise return `None`.
445    ///
446    /// Asynchronous resamplers return `Some`, synchronous resamplers return `None`. This lets
447    /// you recover the adjust-ratio capability from a `&mut dyn Resampler` without knowing the
448    /// concrete type:
449    ///
450    /// ```ignore
451    /// if let Some(adjustable) = resampler.as_adjustable() {
452    ///     adjustable.set_resample_ratio(new_ratio, true)?;
453    /// }
454    /// ```
455    ///
456    /// Any implementor of [Adjustable] must return `Some(self)` here, otherwise the capability
457    /// is invisible through a trait object. This method is intentionally required rather than
458    /// defaulted so that implementing [Adjustable] and advertising it cannot drift apart.
459    fn as_adjustable(&mut self) -> Option<&mut dyn Adjustable<T>>;
460
461    /// Returns `true` if this resampler is [Adjustable], meaning that
462    /// [as_adjustable](Resampler::as_adjustable) returns `Some`.
463    ///
464    /// Unlike [as_adjustable](Resampler::as_adjustable) this takes a shared reference, so the
465    /// capability can be queried through a `&dyn Resampler`. Implementors of [Adjustable] must
466    /// override this to return `true`.
467    fn is_adjustable(&self) -> bool {
468        false
469    }
470
471    /// If this resampler can change its chunk size, borrow it as a [Resizable], otherwise
472    /// return `None`.
473    ///
474    /// Any implementor of [Resizable] must return `Some(self)` here, otherwise the capability
475    /// is invisible through a trait object. This method is intentionally required rather than
476    /// defaulted so that implementing [Resizable] and advertising it cannot drift apart.
477    fn as_resizable(&mut self) -> Option<&mut dyn Resizable<T>>;
478
479    /// Returns `true` if this resampler is [Resizable], meaning that
480    /// [as_resizable](Resampler::as_resizable) returns `Some`.
481    ///
482    /// Unlike [as_resizable](Resampler::as_resizable) this takes a shared reference, so the
483    /// capability can be queried through a `&dyn Resampler`. Implementors of [Resizable] must
484    /// override this to return `true`.
485    fn is_resizable(&self) -> bool {
486        false
487    }
488}
489
490/// A [Resampler] whose resample ratio can be changed after construction.
491///
492/// Implemented by the asynchronous resamplers. From a `&mut dyn Resampler` it can be recovered
493/// with [Resampler::as_adjustable].
494///
495/// The ratio is typically driven by a feedback loop that measures a buffer fill and nudges it to
496/// track a small clock difference. [Slip] carries a complete worked example of such a loop; the
497/// same pattern applies to any `Adjustable` resampler, including [Async].
498pub trait Adjustable<T>: Resampler<T>
499where
500    T: Sample,
501{
502    /// Update the resample ratio.
503    ///
504    /// The ratio must be within `original / maximum` to `original * maximum`, where the original
505    /// and maximum are the resampling ratios that were provided to the constructor. Trying to set
506    /// the ratio outside these bounds will return [ResampleError::RatioOutOfBounds].
507    ///
508    /// If the argument `ramp` is set to true, the ratio will be ramped from the old to the new value
509    /// during processing of the next chunk. This allows smooth transitions from one ratio to another.
510    /// If `ramp` is false, the new ratio will be applied from the start of the next chunk.
511    fn set_resample_ratio(&mut self, new_ratio: f64, ramp: bool) -> ResampleResult<()>;
512
513    /// Update the resample ratio as a factor relative to the original one.
514    ///
515    /// The relative ratio must be within `1 / maximum` to `maximum`, where `maximum` is the maximum
516    /// resampling ratio that was provided to the constructor. Trying to set the ratio outside these
517    /// bounds will return [ResampleError::RatioOutOfBounds].
518    ///
519    /// Ratios above 1.0 slow down the output and lower the pitch, while ratios
520    /// below 1.0 speed up the output and raise the pitch.
521    fn set_resample_ratio_relative(&mut self, rel_ratio: f64, ramp: bool) -> ResampleResult<()>;
522}
523
524/// A [Resampler] whose chunk size can be changed after construction.
525///
526/// From a `&mut dyn Resampler` it can be recovered with [Resampler::as_resizable].
527pub trait Resizable<T>: Resampler<T>
528where
529    T: Sample,
530{
531    /// Change the chunk size for the resampler.
532    /// The value must be equal to or smaller than the chunk size value
533    /// that the resampler was created with.
534    /// [ResampleError::InvalidChunkSize] is returned if the value is zero or too large.
535    ///
536    /// The meaning of chunk size depends on the resampler,
537    /// it refers to the input size for resamplers with fixed input size,
538    /// and output size for resamplers with fixed output size.
539    fn set_chunk_size(&mut self, chunksize: usize) -> ResampleResult<()>;
540}
541
542pub(crate) fn validate_buffers<T>(
543    wave_in: &dyn Adapter<T>,
544    wave_out: &dyn AdapterMut<T>,
545    channels: usize,
546    min_input_len: usize,
547    min_output_len: usize,
548) -> ResampleResult<()> {
549    if wave_in.channels() != channels {
550        return Err(ResampleError::WrongNumberOfInputChannels {
551            expected: channels,
552            actual: wave_in.channels(),
553        });
554    }
555    if wave_in.frames() < min_input_len {
556        return Err(ResampleError::InsufficientInputBufferSize {
557            expected: min_input_len,
558            actual: wave_in.frames(),
559        });
560    }
561    if wave_out.channels() != channels {
562        return Err(ResampleError::WrongNumberOfOutputChannels {
563            expected: channels,
564            actual: wave_out.channels(),
565        });
566    }
567    if wave_out.frames() < min_output_len {
568        return Err(ResampleError::InsufficientOutputBufferSize {
569            expected: min_output_len,
570            actual: wave_out.frames(),
571        });
572    }
573    Ok(())
574}
575
576#[cfg(test)]
577pub mod tests {
578    use crate::Resampler;
579    use crate::{
580        Async, FixedAsync, Indexing, ResampleError, SincInterpolationParameters,
581        SincInterpolationType, Slip, WindowFunction,
582    };
583    #[cfg(feature = "fft_resampler")]
584    use crate::{Fft, FixedSync};
585    use audioadapter::Adapter;
586    use audioadapter_buffers::direct::SequentialSliceOfVecs;
587
588    fn test_sinc_resampler() -> Async<f64> {
589        Async::<f64>::new_sinc(
590            88200.0 / 44100.0,
591            1.1,
592            &SincInterpolationParameters {
593                sinc_len: 64,
594                f_cutoff: Some(0.95),
595                interpolation: SincInterpolationType::Cubic,
596                oversampling_factor: 16,
597                window: WindowFunction::BlackmanHarris2,
598            },
599            1024,
600            2,
601            FixedAsync::Input,
602        )
603        .unwrap()
604    }
605
606    #[test_log::test]
607    fn process_single_chunk() {
608        let mut resampler = test_sinc_resampler();
609        let in_len = resampler.input_frames_next();
610        let samples: Vec<f64> = (0..in_len).map(|v| v as f64 / 10.0).collect();
611        let input_data = vec![samples; 2];
612        let input = SequentialSliceOfVecs::new(&input_data, 2, in_len).unwrap();
613
614        // A full chunk with no indexing returns output_frames_next frames.
615        let expected = resampler.output_frames_next();
616        let out = resampler.process(&input, None).unwrap();
617        assert_eq!(out.channels(), 2);
618        assert_eq!(out.frames(), expected);
619
620        // A final partial chunk fed via Indexing.partial_len still returns a full output chunk
621        // (process does not trim); the missing input frames are treated as silence.
622        let expected = resampler.output_frames_next();
623        let out = resampler
624            .process(&input, Some(&Indexing::new().partial_len(in_len / 2)))
625            .unwrap();
626        assert_eq!(out.frames(), expected);
627    }
628
629    #[test_log::test]
630    fn wrong_length_mask_returns_error() {
631        // A mask with the wrong number of channels must return an error, not panic.
632        let mut resampler = test_sinc_resampler();
633        let in_len = resampler.input_frames_next();
634        let input_data = vec![vec![0.0f64; in_len]; 2];
635        let input = SequentialSliceOfVecs::new(&input_data, 2, in_len).unwrap();
636
637        let indexing = Indexing::new().active_channels_mask(vec![true, true, true]);
638        let result = resampler.process(&input, Some(&indexing));
639        assert!(matches!(
640            result,
641            Err(ResampleError::WrongNumberOfMaskChannels {
642                expected: 2,
643                actual: 3
644            })
645        ));
646    }
647
648    #[test_log::test]
649    fn process_all() {
650        let mut resampler = Async::<f64>::new_sinc(
651            88200.0 / 44100.0,
652            1.1,
653            &SincInterpolationParameters {
654                sinc_len: 64,
655                f_cutoff: Some(0.95),
656                interpolation: SincInterpolationType::Cubic,
657                oversampling_factor: 16,
658                window: WindowFunction::BlackmanHarris2,
659            },
660            1024,
661            2,
662            FixedAsync::Input,
663        )
664        .unwrap();
665        let input_len = 12345;
666        let samples: Vec<f64> = (0..input_len).map(|v| v as f64 / 10.0).collect();
667        let input_data = vec![samples; 2];
668        // add a ramp to the input
669        let input = SequentialSliceOfVecs::new(&input_data, 2, input_len).unwrap();
670        let output_len = resampler.process_all_needed_output_len(input_len);
671        let mut output_data = vec![vec![0.0f64; output_len]; 2];
672        let mut output = SequentialSliceOfVecs::new_mut(&mut output_data, 2, output_len).unwrap();
673        let (nbr_in, nbr_out) = resampler
674            .process_all_into_buffer(&input, &mut output, input_len, None)
675            .unwrap();
676        assert_eq!(nbr_in, input_len);
677        // This is a simple ratio, output should be twice as long as input
678        assert_eq!(2 * nbr_in, nbr_out);
679
680        // check that the output follows the input ramp, within suitable margins
681        let increment = 0.1 / resampler.resample_ratio();
682        let delay = resampler.output_delay();
683        let margin = (delay as f64 * resampler.resample_ratio()) as usize;
684        let mut expected = margin as f64 * increment;
685        for frame in margin..(nbr_out - margin) {
686            for chan in 0..2 {
687                let val = output.read_sample(chan, frame).unwrap();
688                assert!(
689                    val - expected < 100.0 * increment,
690                    "frame: {}, value: {}, expected: {}",
691                    frame,
692                    val,
693                    expected
694                );
695                assert!(
696                    expected - val < 100.0 * increment,
697                    "frame: {}, value: {}, expected: {}",
698                    frame,
699                    val,
700                    expected
701                );
702            }
703            expected += increment;
704        }
705    }
706
707    #[test_log::test]
708    fn process_all_allocating() {
709        let mut resampler = Async::<f64>::new_sinc(
710            88200.0 / 44100.0,
711            1.1,
712            &SincInterpolationParameters {
713                sinc_len: 64,
714                f_cutoff: Some(0.95),
715                interpolation: SincInterpolationType::Cubic,
716                oversampling_factor: 16,
717                window: WindowFunction::BlackmanHarris2,
718            },
719            1024,
720            2,
721            FixedAsync::Input,
722        )
723        .unwrap();
724        let input_len = 12345;
725        let samples: Vec<f64> = (0..input_len).map(|v| v as f64 / 10.0).collect();
726        let input_data = vec![samples; 2];
727        let input = SequentialSliceOfVecs::new(&input_data, 2, input_len).unwrap();
728
729        let output = resampler.process_all(&input, input_len, None).unwrap();
730        // 2x upsampling, and the result must be trimmed to exactly the resampled length
731        // (not the oversized internal buffer).
732        let expected_len = 2 * input_len;
733        assert_eq!(output.channels(), 2);
734        assert_eq!(output.frames(), expected_len);
735
736        // The delay is trimmed, so output frame f follows input position f / ratio.
737        let increment = 0.1 / resampler.resample_ratio();
738        let margin = (resampler.output_delay() as f64 * resampler.resample_ratio()) as usize;
739        for frame in margin..(expected_len - margin) {
740            let expected = frame as f64 * increment;
741            for chan in 0..2 {
742                let val = output.read_sample(chan, frame).unwrap();
743                assert!(
744                    (val - expected).abs() < 100.0 * increment,
745                    "frame: {}, value: {}, expected: {}",
746                    frame,
747                    val,
748                    expected
749                );
750            }
751        }
752
753        // A second call must reset internally and produce an identical result.
754        let output2 = resampler.process_all(&input, input_len, None).unwrap();
755        assert_eq!(output2.frames(), expected_len);
756        for frame in 0..expected_len {
757            for chan in 0..2 {
758                assert_eq!(
759                    output.read_sample(chan, frame),
760                    output2.read_sample(chan, frame)
761                );
762            }
763        }
764    }
765
766    #[test_log::test]
767    fn capability_queries() {
768        // Async resamplers are adjustable and resizable.
769        let mut resampler = test_sinc_resampler();
770        assert!(resampler.is_adjustable());
771        assert!(resampler.is_resizable());
772        resampler
773            .as_adjustable()
774            .expect("Async should be adjustable")
775            .set_resample_ratio_relative(1.05, false)
776            .unwrap();
777        assert!(resampler.as_resizable().is_some());
778
779        // The capability is reachable through a trait object too, including through a shared
780        // reference via the `is_*` probes.
781        let mut boxed: Box<dyn Resampler<f64>> = Box::new(test_sinc_resampler());
782        let shared: &dyn Resampler<f64> = boxed.as_ref();
783        assert!(shared.is_adjustable());
784        assert!(shared.is_resizable());
785        assert!(boxed.as_adjustable().is_some());
786        assert!(boxed.as_resizable().is_some());
787
788        // Slip resamplers are adjustable and resizable, like the async resamplers.
789        let mut slip = Slip::<f64>::new(1024, 2, FixedAsync::Output).unwrap();
790        assert!(slip.is_adjustable());
791        assert!(slip.is_resizable());
792        assert!(slip.as_adjustable().is_some());
793        assert!(slip.as_resizable().is_some());
794
795        // Synchronous Fft resamplers are neither.
796        #[cfg(feature = "fft_resampler")]
797        {
798            let mut fft = Fft::<f64>::new(44100, 48000, 1024, 2, FixedSync::Both).unwrap();
799            assert!(!fft.is_adjustable());
800            assert!(!fft.is_resizable());
801            assert!(fft.as_adjustable().is_none());
802            assert!(fft.as_resizable().is_none());
803        }
804    }
805
806    // This tests that a Resampler can be boxed.
807    #[test_log::test]
808    fn boxed_resampler() {
809        let mut boxed: Box<dyn Resampler<f64>> = Box::new(
810            Async::<f64>::new_sinc(
811                88200.0 / 44100.0,
812                1.1,
813                &SincInterpolationParameters {
814                    sinc_len: 64,
815                    f_cutoff: Some(0.95),
816                    interpolation: SincInterpolationType::Cubic,
817                    oversampling_factor: 16,
818                    window: WindowFunction::BlackmanHarris2,
819                },
820                1024,
821                2,
822                FixedAsync::Input,
823            )
824            .unwrap(),
825        );
826        let max_frames_out = boxed.output_frames_max();
827        let nbr_frames_in_next = boxed.input_frames_next();
828        let waves = vec![vec![0.0f64; nbr_frames_in_next]; 2];
829        let mut waves_out = vec![vec![0.0f64; max_frames_out]; 2];
830        let input = SequentialSliceOfVecs::new(&waves, 2, nbr_frames_in_next).unwrap();
831        let mut output = SequentialSliceOfVecs::new_mut(&mut waves_out, 2, max_frames_out).unwrap();
832        process_with_boxed(&mut boxed, &input, &mut output);
833    }
834
835    fn process_with_boxed<'a>(
836        resampler: &mut Box<dyn Resampler<f64>>,
837        input: &SequentialSliceOfVecs<&'a [Vec<f64>]>,
838        output: &mut SequentialSliceOfVecs<&'a mut [Vec<f64>]>,
839    ) {
840        resampler.process_into_buffer(input, output, None).unwrap();
841    }
842
843    fn impl_send<T: Send>() {
844        fn is_send<T: Send>() {}
845        is_send::<Async<T>>();
846        is_send::<Slip<T>>();
847        #[cfg(feature = "fft_resampler")]
848        {
849            is_send::<Fft<T>>();
850        }
851    }
852
853    // This tests that all resamplers are Send.
854    #[test]
855    fn test_impl_send() {
856        impl_send::<f32>();
857        impl_send::<f64>();
858    }
859
860    pub fn expected_output_value(idx: usize, delay: usize, ratio: f64) -> f64 {
861        if idx <= delay {
862            return 0.0;
863        }
864        (idx - delay) as f64 * 0.1 / ratio
865    }
866
867    #[macro_export]
868    macro_rules! check_output {
869        ($resampler:ident, $fty:ty) => {
870            let mut ramp_value: $fty = 0.0;
871            let max_input_len = $resampler.input_frames_max();
872            let max_output_len = $resampler.output_frames_max();
873            let ratio = $resampler.resample_ratio() as $fty;
874            let delay = $resampler.output_delay();
875            let mut output_index = 0;
876
877            let out_incr = 0.1 / ratio;
878
879            let nbr_iterations =
880                100000 / ($resampler.output_frames_next() + $resampler.input_frames_next());
881            for _n in 0..nbr_iterations {
882                let expected_frames_in = $resampler.input_frames_next();
883                let expected_frames_out = $resampler.output_frames_next();
884                // Check that lengths are within the reported max values
885                assert!(expected_frames_in <= max_input_len);
886                assert!(expected_frames_out <= max_output_len);
887                let mut input_data = vec![vec![0.0 as $fty; expected_frames_in]; 2];
888                for m in 0..expected_frames_in {
889                    for ch in 0..2 {
890                        input_data[ch][m] = ramp_value;
891                    }
892                    ramp_value += 0.1;
893                }
894                let input = SequentialSliceOfVecs::new(&input_data, 2, expected_frames_in).unwrap();
895                let mut output_data = vec![vec![0.0 as $fty; expected_frames_out]; 2];
896                let mut output =
897                    SequentialSliceOfVecs::new_mut(&mut output_data, 2, expected_frames_out)
898                        .unwrap();
899
900                trace!("resample...");
901                let (input_frames, output_frames) = $resampler
902                    .process_into_buffer(&input, &mut output, None)
903                    .unwrap();
904                trace!("assert lengths");
905                assert_eq!(input_frames, expected_frames_in);
906                assert_eq!(output_frames, expected_frames_out);
907                trace!("check output");
908                for idx in 0..output_frames {
909                    let expected = expected_output_value(output_index + idx, delay, ratio) as $fty;
910                    for ch in 0..2 {
911                        let value = output_data[ch][idx];
912                        let margin = 3.0 * out_incr;
913                        assert!(
914                            value > expected - margin,
915                            "Value at frame {} is too small, {} < {} - {}",
916                            output_index + idx,
917                            value,
918                            expected,
919                            margin
920                        );
921                        assert!(
922                            value < expected + margin,
923                            "Value at frame {} is too large, {} > {} + {}",
924                            output_index + idx,
925                            value,
926                            expected,
927                            margin
928                        );
929                    }
930                }
931                output_index += output_frames;
932            }
933            assert!(output_index > 1000, "Too few frames checked!");
934        };
935    }
936
937    #[macro_export]
938    macro_rules! check_ratio {
939        ($resampler:ident, $repetitions:expr, $margin:expr, $fty:ty) => {
940            let ratio = $resampler.resample_ratio();
941            let max_input_len = $resampler.input_frames_max();
942            let max_output_len = $resampler.output_frames_max();
943            let waves_in = vec![vec![0.0 as $fty; max_input_len]; 2];
944            let input = SequentialSliceOfVecs::new(&waves_in, 2, max_input_len).unwrap();
945            let mut waves_out = vec![vec![0.0 as $fty; max_output_len]; 2];
946            let mut output =
947                SequentialSliceOfVecs::new_mut(&mut waves_out, 2, max_output_len).unwrap();
948            let mut total_in = 0;
949            let mut total_out = 0;
950            for _ in 0..$repetitions {
951                let out = $resampler
952                    .process_into_buffer(&input, &mut output, None)
953                    .unwrap();
954                total_in += out.0;
955                total_out += out.1
956            }
957            let measured_ratio = total_out as f64 / total_in as f64;
958            assert!(
959                measured_ratio / ratio > (1.0 - $margin),
960                "Measured ratio is too small, measured / expected = {}",
961                measured_ratio / ratio
962            );
963            assert!(
964                measured_ratio / ratio < (1.0 + $margin),
965                "Measured ratio is too large, measured / expected = {}",
966                measured_ratio / ratio
967            );
968        };
969    }
970
971    #[macro_export]
972    macro_rules! assert_fi_len {
973        ($resampler:ident, $chunksize:expr) => {
974            let nbr_frames_in_next = $resampler.input_frames_next();
975            let nbr_frames_in_max = $resampler.input_frames_max();
976            assert_eq!(
977                nbr_frames_in_next, $chunksize,
978                "expected {} for next input samples, got {}",
979                $chunksize, nbr_frames_in_next
980            );
981            assert_eq!(
982                nbr_frames_in_next, $chunksize,
983                "expected {} for max input samples, got {}",
984                $chunksize, nbr_frames_in_max
985            );
986        };
987    }
988
989    #[macro_export]
990    macro_rules! assert_fo_len {
991        ($resampler:ident, $chunksize:expr) => {
992            let nbr_frames_out_next = $resampler.output_frames_next();
993            let nbr_frames_out_max = $resampler.output_frames_max();
994            assert_eq!(
995                nbr_frames_out_next, $chunksize,
996                "expected {} for next output samples, got {}",
997                $chunksize, nbr_frames_out_next
998            );
999            assert_eq!(
1000                nbr_frames_out_next, $chunksize,
1001                "expected {} for max output samples, got {}",
1002                $chunksize, nbr_frames_out_max
1003            );
1004        };
1005    }
1006
1007    #[macro_export]
1008    macro_rules! assert_fb_len {
1009        ($resampler:ident) => {
1010            let nbr_frames_out_next = $resampler.output_frames_next();
1011            let nbr_frames_out_max = $resampler.output_frames_max();
1012            let nbr_frames_in_next = $resampler.input_frames_next();
1013            let nbr_frames_in_max = $resampler.input_frames_max();
1014            let ratio = $resampler.resample_ratio();
1015            assert_eq!(
1016                nbr_frames_out_next, nbr_frames_out_max,
1017                "next output frames, {}, is different than max, {}",
1018                nbr_frames_out_next, nbr_frames_out_next
1019            );
1020            assert_eq!(
1021                nbr_frames_in_next, nbr_frames_in_max,
1022                "next input frames, {}, is different than max, {}",
1023                nbr_frames_in_next, nbr_frames_in_max
1024            );
1025            let frames_ratio = nbr_frames_out_next as f64 / nbr_frames_in_next as f64;
1026            assert_abs_diff_eq!(frames_ratio, ratio, epsilon = 0.000001);
1027        };
1028    }
1029
1030    #[macro_export]
1031    macro_rules! check_reset {
1032        ($resampler:ident) => {
1033            let frames_in = $resampler.input_frames_next();
1034
1035            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1036            input_data
1037                .iter_mut()
1038                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1039
1040            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1041
1042            let frames_out = $resampler.output_frames_next();
1043            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1044            let mut output_1 =
1045                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1046            $resampler
1047                .process_into_buffer(&input, &mut output_1, None)
1048                .unwrap();
1049            $resampler.reset();
1050            assert_eq!(
1051                frames_in,
1052                $resampler.input_frames_next(),
1053                "Resampler requires different number of frames when new and after a reset."
1054            );
1055            let mut output_data_2 = vec![vec![0.0; frames_out]; 2];
1056            let mut output_2 =
1057                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out).unwrap();
1058            $resampler
1059                .process_into_buffer(&input, &mut output_2, None)
1060                .unwrap();
1061            assert_eq!(
1062                output_data_1, output_data_2,
1063                "Resampler gives different output when new and after a reset."
1064            );
1065        };
1066    }
1067
1068    #[macro_export]
1069    macro_rules! check_input_offset {
1070        ($resampler:ident) => {
1071            let frames_in = $resampler.input_frames_next();
1072
1073            let mut input_data_1 = vec![vec![0.0f64; frames_in]; 2];
1074            input_data_1
1075                .iter_mut()
1076                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1077
1078            let offset = 123;
1079            let mut input_data_2 = vec![vec![0.0f64; frames_in + offset]; 2];
1080            for (ch, data) in input_data_2.iter_mut().enumerate() {
1081                data[offset..offset + frames_in].clone_from_slice(&input_data_1[ch][..])
1082            }
1083
1084            let input_1 = SequentialSliceOfVecs::new(&input_data_1, 2, frames_in).unwrap();
1085            let input_2 = SequentialSliceOfVecs::new(&input_data_2, 2, frames_in + offset).unwrap();
1086
1087            let frames_out = $resampler.output_frames_next();
1088            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1089            let mut output_1 =
1090                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1091            $resampler
1092                .process_into_buffer(&input_1, &mut output_1, None)
1093                .unwrap();
1094            $resampler.reset();
1095            assert_eq!(
1096                frames_in,
1097                $resampler.input_frames_next(),
1098                "Resampler requires different number of frames when new and after a reset."
1099            );
1100            let mut output_data_2 = vec![vec![0.0; frames_out]; 2];
1101            let mut output_2 =
1102                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out).unwrap();
1103
1104            let indexing = Indexing {
1105                input_offset: offset,
1106                output_offset: 0,
1107                active_channels_mask: None,
1108                partial_len: None,
1109            };
1110            $resampler
1111                .process_into_buffer(&input_2, &mut output_2, Some(&indexing))
1112                .unwrap();
1113            assert_eq!(
1114                output_data_1, output_data_2,
1115                "Resampler gives different output when new and after a reset."
1116            );
1117        };
1118    }
1119
1120    #[macro_export]
1121    macro_rules! check_output_offset {
1122        ($resampler:ident) => {
1123            let frames_in = $resampler.input_frames_next();
1124
1125            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1126            input_data
1127                .iter_mut()
1128                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1129
1130            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1131
1132            let frames_out = $resampler.output_frames_next();
1133            let mut output_data_1 = vec![vec![0.0; frames_out]; 2];
1134            let mut output_1 =
1135                SequentialSliceOfVecs::new_mut(&mut output_data_1, 2, frames_out).unwrap();
1136            $resampler
1137                .process_into_buffer(&input, &mut output_1, None)
1138                .unwrap();
1139            $resampler.reset();
1140            assert_eq!(
1141                frames_in,
1142                $resampler.input_frames_next(),
1143                "Resampler requires different number of frames when new and after a reset."
1144            );
1145            let offset = 123;
1146            let mut output_data_2 = vec![vec![0.0; frames_out + offset]; 2];
1147            let mut output_2 =
1148                SequentialSliceOfVecs::new_mut(&mut output_data_2, 2, frames_out + offset).unwrap();
1149            let indexing = Indexing {
1150                input_offset: 0,
1151                output_offset: offset,
1152                active_channels_mask: None,
1153                partial_len: None,
1154            };
1155            $resampler
1156                .process_into_buffer(&input, &mut output_2, Some(&indexing))
1157                .unwrap();
1158            assert_eq!(
1159                output_data_1[0][..],
1160                output_data_2[0][offset..],
1161                "Resampler gives different output when new and after a reset."
1162            );
1163            assert_eq!(
1164                output_data_1[1][..],
1165                output_data_2[1][offset..],
1166                "Resampler gives different output when new and after a reset."
1167            );
1168        };
1169    }
1170
1171    #[macro_export]
1172    macro_rules! check_masked {
1173        ($resampler:ident) => {
1174            let frames_in = $resampler.input_frames_next();
1175
1176            let mut input_data = vec![vec![0.0f64; frames_in]; 2];
1177            input_data
1178                .iter_mut()
1179                .for_each(|ch| ch.iter_mut().for_each(|s| *s = rand::random()));
1180
1181            let input = SequentialSliceOfVecs::new(&input_data, 2, frames_in).unwrap();
1182
1183            let frames_out = $resampler.output_frames_next();
1184            let mut output_data = vec![vec![0.0; frames_out]; 2];
1185            let mut output =
1186                SequentialSliceOfVecs::new_mut(&mut output_data, 2, frames_out).unwrap();
1187
1188            let indexing = Indexing {
1189                input_offset: 0,
1190                output_offset: 0,
1191                active_channels_mask: Some(vec![false, true]),
1192                partial_len: None,
1193            };
1194            $resampler
1195                .process_into_buffer(&input, &mut output, Some(&indexing))
1196                .unwrap();
1197
1198            let non_zero_chan_0 = output_data[0].iter().filter(|&v| *v != 0.0).count();
1199            let non_zero_chan_1 = output_data[1].iter().filter(|&v| *v != 0.0).count();
1200            // assert channel 0 is all zero
1201            assert_eq!(
1202                non_zero_chan_0, 0,
1203                "Some sample in the non-active channel has a non-zero value"
1204            );
1205            // assert channel 1 has some values
1206            assert!(
1207                non_zero_chan_1 > 0,
1208                "No sample in the active channel has a non-zero value"
1209            );
1210        };
1211    }
1212
1213    #[macro_export]
1214    macro_rules! check_resize {
1215        ($resampler:ident) => {};
1216    }
1217}