resampler/lib.rs
1//! # Audio resampling library
2//!
3//! Resampler is a small, zero-dependency crate for high-quality audio resampling between common
4//! sample rates. It provides both FFT-based and FIR-based resamplers optimized for different use
5//! cases.
6//!
7//! ## Usage Examples
8//!
9//! ### FFT-Based Resampler (Highest Quality)
10//!
11//! ```rust
12//! use resampler::{ResamplerFft, SampleRate};
13//!
14//! // Create a stereo resampler (2 channels) from 44.1 kHz to 48 kHz.
15//! let mut resampler = ResamplerFft::new(2, SampleRate::Hz44100, SampleRate::Hz48000);
16//!
17//! // Get required buffer sizes (already includes all channels).
18//! let input_size = resampler.chunk_size_input();
19//! let output_size = resampler.chunk_size_output();
20//!
21//! // Create input and output buffers (interleaved format: [L0, R0, L1, R1, ...]).
22//! let input = vec![0.0f32; input_size];
23//! let mut output = vec![0.0f32; output_size];
24//!
25//! resampler.resample(&input, &mut output).unwrap();
26//! ```
27//!
28//! ### FIR-Based Resampler (Low Latency, Streaming)
29//!
30//! ```rust
31//! use resampler::{Attenuation, Latency, ResamplerFir, SampleRate};
32//!
33//! // Create a stereo resampler with configurable latency (16, 32, or 64 samples).
34//! let mut resampler = ResamplerFir::new(
35//! 2,
36//! SampleRate::Hz48000,
37//! SampleRate::Hz44100,
38//! Latency::Sample64,
39//! Attenuation::Db90,
40//! );
41//!
42//! // Streaming API - accepts arbitrary input buffer sizes.
43//! let input = vec![0.0f32; 512];
44//! let mut output = vec![0.0f32; resampler.buffer_size_output()];
45//!
46//! let (consumed, produced) = resampler.resample(&input, &mut output).unwrap();
47//! println!("Consumed {consumed} samples, produced {produced} samples");
48//! ```
49//!
50//! ## Choosing a Resampler
51//!
52//! Both resamplers provide good quality, but are optimized for different use cases:
53//!
54//! | Feature | [`ResamplerFft`] | [`ResamplerFir`] |
55//! |-------------|----------------------------------|------------------------------|
56//! | Quality | Very good (sharp rolloff) | Good (slow rolloff) |
57//! | Performance | Very fast | Fast (configurable) |
58//! | Latency | ~256 samples | 16-64 samples (configurable) |
59//! | API | Fixed chunk size | Flexible streaming |
60//! | Best for | Non-latency sensitive processing | Low-latency processing |
61//!
62//! Use [`ResamplerFft`] when:
63//! - You need the absolute highest quality
64//! - Latency is not a concern
65//! - Processing pre-recorded audio files
66//!
67//! Use [`ResamplerFir`] when:
68//! - You need low latency (real-time audio)
69//! - You can live with a slower rolloff
70//! - Working with streaming data
71//!
72//! ## FFT-Based Implementation
73//!
74//! The resampler uses an FFT-based overlap-add algorithm with Kaiser windowing for high-quality
75//! audio resampling. Key technical details:
76//!
77//! - Custom mixed-radix FFT with the Stockham Autosort algorithm.
78//! - SIMD optimizations: All butterflies have SSE2, SSE4.2, AVX+FMA, and ARM NEON implementations.
79//! - Stopband attenuation of -100 dB using the Kaiser windows function.
80//! - Latency around 256 samples.
81//!
82//! ## FIR-Based Implementation
83//!
84//! The FIR resampler uses a polyphase filter with linear interpolation for high-quality audio
85//! resampling with low latency. Key technical details:
86//!
87//! - Polyphase decomposition: 1024 phases with linear interpolation between phases.
88//! - SIMD optimizations: Convolution kernels optimized with SSE2, SSE4.2, AVX+FMA, AVX-512
89//! and ARM NEON.
90//! - Configurable filter length: 32, 64, or 128 taps (16, 32, or 64 samples latency).
91//! - Adjustable rolloff and stopband attenuation,
92//! - Streaming API: Accepts arbitrary input buffer sizes for flexible real-time processing,
93//!
94//! ## Performance
95//!
96//! Both resamplers include SIMD optimizations with runtime CPU feature detection for maximum
97//! performance and compatibility.
98//!
99//! But for up to 25% better performance on x86_64, compile with `target-cpu=x86-64-v3`
100//! (enables AVX2, FMA, and other optimizations).
101//!
102//! Overall the SIMD for x86_64 have four levels implemented, targeting four possible CPU
103//! generations that build up on each other:
104//!
105//! * x86-64-v1: 128-bit SSE2 (around 2003-2004)
106//! * x86-64-v2: 128-bit SSE4.2 (around 2008-2011)
107//! * x86-64-v3: 256-bit AVX+FMA (around 2013-2015)
108//! * x86-64-v4: 512-bit AVX-512 (around 2017-2022)
109//!
110//! ## no-std Compatibility
111//!
112//! The library supports `no-std` environments with `alloc`. To use the library in a `no-std`
113//! environment, enable the `no_std` feature:
114//!
115//! ```toml
116//! [dependencies]
117//! resampler = { version = "0.2", features = ["no_std"] }
118//! ```
119//!
120//! ### Behavior Differences
121//!
122//! When the `no_std` feature is enabled:
123//!
124//! - Caching: The library will not cache FFT and FIR objects globally to shorten resampler creation
125//! time and lower overall memory consumption for multiple resamplers.
126//!
127//! - No runtime detection of SIMD functionality. You need to activate SIMD via compile time target
128//! features.
129//!
130//! The default build (without `no_std` feature) has zero dependencies and uses the standard
131//! library for optimal performance and memory efficiency through global caching.
132//!
133//! ## Alternatives
134//!
135//! Other high-quality audio resampling libraries in Rust are:
136//!
137//! - [Rubato](https://github.com/HEnquist/rubato): The overlap-add resampling approach used in this
138//! library is based on Rubato's implementation.
139//!
140//! ## License
141//!
142//! Licensed under either of
143//!
144//! - Apache License, Version 2.0
145//! - MIT license
146//!
147//! at your option.
148#![cfg_attr(feature = "no_std", no_std)]
149#![forbid(missing_docs)]
150
151extern crate alloc;
152
153mod error;
154mod fft;
155mod fir;
156mod resampler_fft;
157mod resampler_fir;
158mod window;
159
160pub use error::ResampleError;
161pub(crate) use fft::*;
162pub use resampler_fft::*;
163pub use resampler_fir::{Attenuation, Latency, ResamplerFir};
164
165/// All sample rates the resampler can operate on.
166#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
167pub enum SampleRate {
168 /// 22.5 kHz
169 Hz22050,
170 /// 16 kHz
171 Hz16000,
172 /// 32 kHz
173 Hz32000,
174 /// 44.1 kHz
175 Hz44100,
176 /// 48 kHz
177 Hz48000,
178 /// 88.2 kHz
179 Hz88200,
180 /// 96 kHz
181 Hz96000,
182 /// 176.4 kHz
183 Hz176400,
184 /// 192 kHz
185 Hz192000,
186 /// 384 kHz
187 Hz384000,
188}
189
190impl SampleRate {
191 pub(crate) fn family(self) -> SampleRateFamily {
192 match self {
193 SampleRate::Hz22050 => SampleRateFamily::Hz22050,
194 SampleRate::Hz16000 => SampleRateFamily::Hz16000,
195 SampleRate::Hz32000 => SampleRateFamily::Hz16000,
196 SampleRate::Hz44100 => SampleRateFamily::Hz22050,
197 SampleRate::Hz48000 => SampleRateFamily::Hz48000,
198 SampleRate::Hz88200 => SampleRateFamily::Hz22050,
199 SampleRate::Hz96000 => SampleRateFamily::Hz48000,
200 SampleRate::Hz176400 => SampleRateFamily::Hz22050,
201 SampleRate::Hz192000 => SampleRateFamily::Hz48000,
202 SampleRate::Hz384000 => SampleRateFamily::Hz48000,
203 }
204 }
205
206 /// Returns the multiplier of the actual sample rate relative to its base family.
207 ///
208 /// For example:
209 /// - 22050 is the base of its family, so it returns 1
210 /// - 44100 is 2× the base (22050), so it returns 2
211 /// - 96000 is 2× the base (48000), so it returns 2
212 pub(crate) fn family_multiplier(self) -> u32 {
213 let actual_rate: u32 = self.into();
214 let family_rate: u32 = self.family().into();
215 actual_rate / family_rate
216 }
217}
218
219impl From<SampleRate> for u32 {
220 fn from(value: SampleRate) -> Self {
221 match value {
222 SampleRate::Hz22050 => 22050,
223 SampleRate::Hz16000 => 16000,
224 SampleRate::Hz32000 => 32000,
225 SampleRate::Hz44100 => 44100,
226 SampleRate::Hz48000 => 48000,
227 SampleRate::Hz88200 => 88200,
228 SampleRate::Hz96000 => 96000,
229 SampleRate::Hz176400 => 176400,
230 SampleRate::Hz192000 => 192000,
231 SampleRate::Hz384000 => 384000,
232 }
233 }
234}
235
236impl TryFrom<u32> for SampleRate {
237 type Error = ();
238
239 fn try_from(value: u32) -> Result<Self, Self::Error> {
240 match value {
241 22050 => Ok(SampleRate::Hz22050),
242 16000 => Ok(SampleRate::Hz16000),
243 32000 => Ok(SampleRate::Hz32000),
244 44100 => Ok(SampleRate::Hz44100),
245 48000 => Ok(SampleRate::Hz48000),
246 88200 => Ok(SampleRate::Hz88200),
247 96000 => Ok(SampleRate::Hz96000),
248 176400 => Ok(SampleRate::Hz176400),
249 192000 => Ok(SampleRate::Hz192000),
250 384000 => Ok(SampleRate::Hz384000),
251 _ => Err(()),
252 }
253 }
254}
255
256/// The "family" of "lineage" that every sample rate must be a multiple of.
257#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
258enum SampleRateFamily {
259 /// 22.5 kHz Family
260 Hz22050,
261 /// 16.0 kHz Family
262 Hz16000,
263 /// 48 kHz Family
264 Hz48000,
265}
266
267impl From<SampleRateFamily> for u32 {
268 fn from(value: SampleRateFamily) -> Self {
269 match value {
270 SampleRateFamily::Hz22050 => 22050,
271 SampleRateFamily::Hz16000 => 16000,
272 SampleRateFamily::Hz48000 => 48000,
273 }
274 }
275}