audio-resample-bsd 0.2.1

RT-safe audio resampling crate (rubato-based) — the only processing interface callable directly from the real-time audio thread
Documentation
//! Public resampling interface — the RT-safe processing boundary of this crate.
//!
//! This module defines [`Resampler`], the only processing trait that may be
//! called directly from the real-time (RT) audio thread. See the crate-level
//! docs and the [`Resampler`] trait docs for the full RT-safety boundary.

use crate::ResampleError;

/// Public contract for audio sample-rate conversion (resampling).
///
/// This trait is the **only processing interface that may be called directly
/// from the RT (real-time) thread**. The rubato-backed implementation
/// ([`crate::RubatoResampler`]) is provided as the standard reference.
///
/// # Data layout — planar flat
///
/// `input`/`output` are flat buffers in **planar** layout:
/// `[ch0_frames..., ch1_frames...]`. This matches the layout of
/// [`audio_core_bsd::AudioFrame::samples`]. The channel count is fixed when the
/// implementation is constructed (e.g. `RubatoResampler::new(in, out, channels,
/// chunk)`).
///
/// # Real-time safety — CRITICAL
///
/// [`Resampler::process_into_buffer`] is invoked on the RT thread. The
/// following are forbidden (same principle as the `audio-core-bsd` `AudioNode`
/// RT contract):
///
/// - **No heap allocation** — every buffer must be pre-allocated at
///   construction.
/// - **No locking** — only wait-free primitives are permitted.
/// - **No panicking** — no `unwrap`/`expect`/`assert`/out-of-bounds indexing.
///   Degrade gracefully (e.g. emit silence) instead.
/// - **No system calls / I/O**.
///
/// # `set_rate` is NOT real-time safe
///
/// [`Resampler::set_rate`] rebuilds the inner resampler to change the ratio and
/// **therefore allocates**. It must be called only from a configuration/worker
/// thread — never the RT thread, and only between audio cycles. The RT-alloc=0
/// guarantee applies only to `process_into_buffer` (the fixed-ratio path).
pub trait Resampler: Send {
    /// Writes resampled samples into the pre-allocated `output` buffer
    /// (RT-safe, allocation-free).
    ///
    /// `input` is planar-flat data for `channels × N_in` frames
    /// (`[ch0.., ch1..]`); `output` is a planar-flat buffer holding at least
    /// `channels × N_out_max` frames. The implementation writes as many frames
    /// as it can and returns the number of **output frames written (per
    /// channel)**.
    ///
    /// This trait chooses an explicit `Result` return over silent degradation:
    /// the caller handles the `Result` between cycles and decides whether to
    /// retry. Within the RT processing loop itself, ignoring the return value
    /// and falling back to silence is recommended (a cycle cannot be aborted).
    ///
    /// # Errors
    ///
    /// Returns [`ResampleError`] when:
    /// - the input frame count does not match the implementation's fixed chunk
    ///   size ([`ResampleError::InputLengthMismatch`]);
    /// - the output buffer is smaller than required
    ///   ([`ResampleError::BufferTooSmall`]);
    /// - a rubato runtime error occurs ([`ResampleError::ResampleFailed`]).
    fn process_into_buffer(
        &mut self,
        input: &[f32],
        output: &mut [f32],
    ) -> Result<usize, ResampleError>;

    /// Changes the input/output sample rates. **NOT RT-safe — allocates**
    /// (call only between audio cycles).
    ///
    /// This method rebuilds the inner resampler (e.g. the rubato `Fft`
    /// synchronous resampler) at the new ratio, which performs heap allocation.
    /// It must be called from a configuration/worker thread, never the RT
    /// thread. The RT-alloc=0 guarantee applies only to
    /// [`process_into_buffer`][Resampler::process_into_buffer].
    ///
    /// An implementation may ignore an invalid ratio (non-positive, non-finite,
    /// or unsupported) rather than report it; since this method is not RT-safe,
    /// graceful no-op behaviour (ignoring) is acceptable in place of a panic.
    fn set_rate(&mut self, input_rate: f64, output_rate: f64);
}