Most audio libraries hand you a bare buffer. In Python it is a NumPy array
whose dtype is explicit but whose meaning is not: the sample rate, amplitude
range, channel layout, and interleaving live elsewhere, if they are tracked at
all. Rust libraries give you fast, safe primitives but still leave you holding
the raw buffer, writing your own conversions, and carrying those conventions in
your head. Mismatches between them, such as a stale sample rate after
resampling or a buffer that was interleaved when the next stage expected planar,
are a recurring source of bugs.
audio_samples puts that metadata in the type. [AudioSamples<T>] pairs the
PCM data with its sample rate, channel count, and memory layout, and every
operation either preserves those invariants or updates them explicitly. The
sample type T (u8, i16, [I24], i32, f32, f64) is part of the type,
so format and bit-depth conversions are checked and scaled correctly rather than
left to ad hoc casts.
cargo add audio_samplesThe default feature set (bare-bones) is the core types and traits with no
optional dependencies. Add features for the operations you need; see
Features.
Upgrading from 1.x? See the migration guide.
Build AudioSamples from an ndarray, or generate a signal. Construction is
fallible because the data, channel count, and sample rate must agree.
use audio_samples::{AudioSamples, sample_rate, sine_wave};
use ndarray::array;
use std::time::Duration;
fn main() -> audio_samples::AudioSampleResult<()> {
// Mono, from a slice of samples.
let _mono = AudioSamples::new_mono(array![0.1f32, 0.5, -0.3, 0.8], sample_rate!(44_100))?;
// Stereo, from a 2-D (channels x samples) array.
let _stereo = AudioSamples::<f32>::new_multi_channel(
array![[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
sample_rate!(44_100),
)?;
// Or generate one: a 440 Hz sine, 1 second, amplitude 0.5.
let _tone = sine_wave::<f32>(440.0, Duration::from_secs(1), sample_rate!(44_100), 0.5);
Ok(())
}Generators cover the classic oscillators plus logarithmic chirps
(exponential_chirp), band-limited square/sawtooth/triangle waves, FM
(fm_signal), and white/pink/brown noise.
Operations carry the sample rate and channel layout with the data and chain
through the borrowing API. Here two tones are mixed, the low one is filtered
out, the result is peak-normalized, and its level is read back. Enable the
processing feature.
use audio_samples::{AudioProcessing, AudioStatistics, NormalizationConfig, sample_rate, sine_wave};
use std::time::Duration;
fn main() -> audio_samples::AudioSampleResult<()> {
let sr = sample_rate!(44_100);
let dur = Duration::from_secs(1);
// 100 Hz and 5 kHz, mixed.
let signal = sine_wave::<f32>(100.0, dur, sr, 0.5) + sine_wave::<f32>(5_000.0, dur, sr, 0.5);
// High-pass above 1 kHz, then peak-normalize. Each step returns a new value.
let cleaned = signal
.high_pass_filter(1_000.0)?
.normalize(NormalizationConfig::peak(1.0))?;
println!(
"{} channel(s) at {} Hz, peak {:.3}, rms {:.3}",
cleaned.num_channels().get(),
cleaned.sample_rate().get(),
cleaned.peak(),
cleaned.rms(),
);
// The `*_in_place` variants mutate instead of returning a new value.
let mut buf = cleaned;
buf.scale_in_place(0.5);
Ok(())
}Conversions are audio-aware: integer PCM is scaled to and from the float
[-1.0, 1.0] range rather than cast blindly.
use audio_samples::{AudioTypeConversion, sample_rate, sine_wave};
use std::time::Duration;
let pcm = sine_wave::<i16>(440.0, Duration::from_secs(1), sample_rate!(44_100), 0.8); // i16 PCM
let as_float = pcm.as_f32(); // scaled into [-1.0, 1.0]
let _back = as_float.as_i16(); // and back to i16Enable the transforms feature for FFT/STFT and the spectral feature suite.
use audio_samples::{AudioStatistics, AudioTransforms, ChannelReduction, nzu, sample_rate, sine_wave};
use spectrograms::{StftParams, WindowType};
use std::time::Duration;
fn main() -> audio_samples::AudioSampleResult<()> {
let audio = sine_wave::<f32>(440.0, Duration::from_millis(200), sample_rate!(44_100), 0.8);
// Short-time Fourier transform.
let params = StftParams::new(nzu!(1024), nzu!(256), WindowType::Hanning, true)?;
let _stft = audio.stft(¶ms)?;
// A derived spectral feature (brightness), in Hz.
let centroid = audio.spectral_centroid(ChannelReduction::Average)?;
println!("spectral centroid: {centroid:.0} Hz");
Ok(())
}The full transform set (FFT, MFCC, chromagram, CQT, PSD, inverse STFT) and the rest of the spectral features are listed under Features.
The default feature is bare-bones: the core types and traits, no optional
dependencies. Enable the rest as needed.
| Feature | Description |
|---|---|
statistics |
Peak, RMS, mean, variance, zero-crossings; the spectral feature suite (centroid, rolloff, bandwidth, flatness, contrast, slope, crest) when transforms is also enabled |
processing |
Normalization, scaling, clipping, DC-offset removal (requires statistics) |
editing |
Trim, pad, reverse, fade, concatenate, perturb (requires statistics, random-generation) |
channels |
Interleave/deinterleave, mono/stereo conversion, channel extraction |
iir-filtering |
Butterworth, Chebyshev I and II, Elliptic (Cauer), and Bessel filters; low-, high-, band-pass and band-stop responses; zero-phase filtfilt; design-once streaming SosFilter |
parametric-eq |
Parametric EQ bands and ThreeBandEqConfig (requires iir-filtering) |
dynamic-range |
Compression, limiting, gating, expansion via config structs, with side-chain support |
envelopes |
Amplitude, RMS, and attack-decay envelope followers |
vad |
Voice activity detection |
| Feature | Description |
|---|---|
transforms |
FFT, STFT and inverse STFT, MFCC, chromagram, CQT, power spectral density |
psychoacoustic |
Bark/Mel band layouts, absolute threshold of hearing, masking thresholds, SMR (requires transforms) |
pitch-analysis |
YIN and autocorrelation pitch detection and tracking (requires transforms) |
onset-detection |
Onset detection (requires transforms, peak-picking, processing) |
beat-tracking |
Beat tracking and tempo estimation (estimate_tempo) |
peak-picking |
Peak picking on onset-strength envelopes |
decomposition |
Harmonic/percussive source separation (requires onset-detection) |
| Feature | Description |
|---|---|
resampling |
Sample-rate conversion via rubato |
random-generation |
White, pink, and brown noise generators |
fixed-size-audio |
Stack-allocated fixed-size buffers |
plotting |
Interactive HTML plots (waveform, spectrum, phase, spectrogram, Lissajous) via plotly |
static-plots |
PNG/SVG export (requires plotting; see PLOTTING.md) |
simd |
SIMD-accelerated sample conversions via the wide crate (stable; results are bit-identical to the scalar path) |
| Feature | Description |
|---|---|
full |
Everything below |
full_no_plotting |
Everything except plotting |
Full API documentation: https://docs.rs/audio_samples. Architecture notes are in documentation/ARCHITECTURE.md.
The repository includes runnable examples in examples/, each annotated with
its required feature flags. Run one with, for example:
cargo run --example transforms --features transforms,statisticsA larger demo lives in a separate repository:
audio_samples_io: audio file decoding and encoding- `audio_samples_streaming: streaming functionality
- `audio_samples_ml: audio machine learning such as STT and TTS.
- `audio_samples_qoe: audio Quality of Experience (qoe) metrics.
audio_samples_python: Python bindingsspectrograms: spectrograms and time-frequency transforms (used by thetransformsfeature)i24: 24-bit signed integer type for Rustdtmf_tones:no_stdDTMF keypad frequencies
MIT.
If you use AudioSamples in research, please cite:
@inproceedings{geraghty2026audio,
author = {Geraghty, Jack and Golpayegani, Fatemeh and Hines, Andrew},
title = {Audio Made Simple: A Modern Framework for Audio Processing},
booktitle = {ACM Multimedia Systems Conference 2026 (MMSys '26)},
year = {2026},
month = apr,
publisher = {ACM},
address = {Hong Kong, Hong Kong},
doi = {10.1145/3793853.3799811},
note = {Accepted for publication}
}Contributions are welcome. Please open an issue or pull request, and see CONTRIBUTING.md for guidance.
