fon
A no_std Rust crate for type-safe PCM audio sample types, multi-channel frames, and Kaiser-windowed resampling.
Repository Health
Technical Analysis
Fōn is a no_std Rust crate providing type-safe audio primitives — bit-depth-aware sample types, multi-channel frames, and buffer conversions — that let you resample, mix, and interoperate between raw sample formats (16-bit integer PCM, 24-bit integer PCM, 32-bit float PCM, and 64-bit float PCM) without hand-rolling raw byte math. It supports up to 8-channel speaker layouts following FLAC/SMPTE/ITU-R conventions and includes a Speex-derived, Kaiser-windowed resampler for converting between arbitrary sample rates.
Built entirely in pure, #![deny(unsafe_code)] Rust and released under an Apache-2.0/BSL-1.0/MIT tri-license, fon is designed as a small, embeddable interchange layer that other audio crates can build on rather than a full audio engine itself — the same maintainer’s twang (synthesis) and wavy (recording/playback) crates use it as their common sample format.
What You Get
- Type-safe channel samples for 16-bit, 24-bit, 32-bit float, and 64-bit float PCM (
Samp16,Samp24,Samp32,Samp64) - An
Audio<C, COUNT>buffer type generic over channel type and channel count, backed by a boxed slice of frames - A Speex-derived, Kaiser-windowed
Resamplerfor converting between arbitrary sample rates - Speaker position types (
Mono,Left,Right,Center,FrontL/FrontR,SurroundL/SurroundR,Lfe,Back, etc.) covering up to 7.1 surround layouts Sink/SinkTotraits for streaming converted audio into custom destinations such as a mixer
Common Use Cases
- Converting decoded audio buffers between sample formats before handing them to a playback or recording backend
- Downsampling or upsampling audio between two different source and target sample rates
- Mixing multiple audio buffers of different formats and sample rates into a single output buffer
- Building a no_std-compatible audio pipeline for embedded or resource-constrained targets
Under The Hood
Architecture
The crate is organized as a flat set of modules (audio, chan, frame, math, pos, private, resampler, sink) exposed through a thin lib.rs facade that re-exports Audio, AudioSink, Frame, Resampler, Sink, and SinkTo, plus public chan and pos submodules; data flows from raw sample types in chan.rs (implementing a sealed Channel trait for Samp16/Samp24/Samp32/Samp64) into Frame<C, COUNT> (frame.rs), which groups channels by speaker position, into the top-level Audio<C, COUNT> buffer (audio.rs) that owns a Box<[Frame<C, COUNT>]> plus a sample rate, with format and sample-rate conversion routed through Resampler<N> (resampler.rs, itself built on a private Speex-derived speex.rs submodule) and streamed out via the Sink/SinkTo traits (sink.rs) — a private Sealed trait in private.rs closes the Channel/position type sets to prevent external implementations, so changing the core Channel trait’s associated constants or its conversion bounds would ripple through every sample type, Frame, and the resampler’s per-channel state.
Tech Stack
fon is a pure-Rust, #![no_std] crate (2024 edition, MSRV 1.85) with a single runtime dependency, libm, providing floating-point math without std; it pulls in alloc for Vec/Box via extern crate alloc, and uses const generics (Audio<C, const COUNT: usize>) for compile-time channel-count typing rather than any async runtime, database, or web framework, since it’s purely a data-types library. CI (ci.yml) runs cargo test --all --all-features across Linux/macOS/Windows on stable, beta, nightly, and the pinned MSRV toolchain, plus separate cross-compilation jobs for Android, wasm32, FreeBSD, Redox, iOS, and Windows-GNU targets, and the crate ships three license texts (Apache-2.0, BSL-1.0, MIT) selectable at the consumer’s option.
Code Quality
Tests are colocated inline via #[test] functions in chan.rs and math.rs rather than spread across every module — audio.rs, frame.rs, resampler.rs, sink.rs, and pos.rs carry no dedicated unit tests, relying instead on the crate-level doctest in lib.rs and the examples/ directory (mix.rs, resample.rs, sawtooth.rs) for integration-style verification. Error handling favors assert!/assert_ne! panics on invariant violations (for example Resampler::new panics on a zero target sample rate) rather than Result types, which is typical for a low-level numeric library; naming is consistent and terse (Samp16, Frame, Sink), the crate enables a broad #![warn(...)] lint set including missing_docs and unreachable_pub, and CI exercises the test suite across three OSes and four toolchains — though there’s no explicit clippy/formatting gate visible in CI beyond the checked-in .rustfmt.toml.
API Design
The public API is small and ergonomic for what it does — Audio::<Samp32, 1>::with_silence(hz, len) allocates a buffer, frames are indexed by typed position markers (f[Mono]) instead of raw channel numbers, and Audio::with_audio() converts between differently-typed or differently-rated buffers in a single call — which reads close to idiomatic Rust and needs almost no boilerplate to get started, as shown in the crate-level doctest. Documentation is thorough at the module and type level, with extensive doc comments cross-linked via [Type]: crate::path reference syntax, though the crate offers no dedicated guide beyond docs.rs; encoding channel count and speaker position at the type level (rather than as runtime channel indices) is a genuinely distinctive design choice next to audio libraries that pass around raw sample slices, catching channel-count/position mismatches at compile time.