dasp
A modular, allocation-free Rust toolkit of Sample, Frame, and Signal traits for digital audio PCM signal processing.
Repository Health
Technical Analysis
dasp (formerly the sample crate) is a suite of composable Rust crates for digital audio DSP work, built around three core abstractions: Sample (bit-depth-generic values), Frame (multi-channel arrays of samples), and Signal (an iterator-like trait for streams of frames). The top-level dasp crate re-exports functionality from focused sub-crates — dasp_sample, dasp_frame, dasp_signal, dasp_interpolate, dasp_ring_buffer, dasp_rms, dasp_peak, dasp_envelope, dasp_slice, dasp_window, and dasp_graph — each gated behind its own cargo feature, so a project only compiles the pieces it actually uses.
It targets both std and no_std environments, needs no dynamic allocation outside an optional signal-bus feature, and ships ready building blocks: floor/linear/sinc sample-rate interpolation, RMS and peak envelope detection, Hann and rectangle windowing functions, fixed and bounded ring buffers, and an optional dynamic audio-graph module for wiring processing nodes together. As the successor to sample, it’s widely used as the low-level DSP layer beneath higher-level Rust audio tooling.
What You Get
- Sample trait with bit-depth conversions across integer, float, and custom packed types (I24, I48, U24, U48)
- Frame trait generic over channel count, with array implementations up to 32 channels
- Signal trait giving an iterator-like API for generating, combining, and transforming streams of audio frames
- Interpolation module with floor, linear, and sinc rate converters for sample-rate conversion and playback speed changes
- RMS and peak envelope detection via a configurable Detector type
- Fixed and bounded ring buffer types usable with owned, borrowed, stack, or heap storage
- Optional dasp_graph module for composing DSP nodes into a dynamic, modular audio graph
Common Use Cases
- Sample-rate conversion - an audio engine developer resamples incoming PCM streams to a target output rate using the Converter type with sinc interpolation.
- Synthesizer signal generation - a synth author builds oscillator chains (sine, saw, square, noise) with the Signal trait’s combinator methods like scale_amp and add_amp.
- Envelope followers for dynamics processing - an audio-effects author tracks RMS or peak envelope of a signal to drive compressors, gates, or visual meters.
- Bit-depth-agnostic audio pipelines - a plugin developer writes DSP code once using the Sample trait and lets it work across u8, i16, i32, and f32 formats without duplicating logic.
- no_std embedded audio processing - a firmware engineer builds DSP code for a DSP chip or embedded device using dasp’s no_std feature set without a heap allocator.
Under The Hood
Architecture
dasp is a Cargo workspace of eleven independently published sub-crates unified through the dasp facade crate (dasp/src/lib.rs), which re-exports each sub-crate as a module gated behind its own cargo feature. The layering is strict: dasp_sample (bit-depth-generic values) underpins dasp_frame (multi-channel frames built on Sample), which underpins dasp_signal (an iterator-like stream abstraction built on Frame); higher-level modules — dasp_interpolate, dasp_ring_buffer, dasp_rms, dasp_peak, dasp_envelope, dasp_slice, dasp_window — consume Signal/Frame/Sample, and dasp_graph sits on top as an optional dynamic node-graph layer built on petgraph. Every abstraction is a trait plus a generic struct composed at compile time with no runtime state or global config, so a change to the core Sample trait in dasp_sample would ripple through every downstream crate’s trait bounds.
Tech Stack The core no_std path (dasp_sample, dasp_frame) has zero external dependencies; std-enabled features add heap-backed types such as dasp_ring_buffer’s boxed variant and dasp_signal’s boxed/bus features. dasp_graph depends on petgraph for its node-graph representation. The separate examples crate links cpal and hound to demonstrate real playback and WAV I/O (play_wav.rs, resample.rs, synth.rs). CI (.github/workflows/dasp.yml) runs cargo fmt —check, cargo test —all on stable, and a —no-default-features build on nightly to validate the no_std path.
Code Quality Core sub-crates (dasp_sample, dasp_signal, dasp_slice, dasp_ring_buffer, dasp_graph) each carry dedicated test suites under tests/, and the public API is extensively documented with runnable doctest examples embedded directly in doc comments — dasp_signal/src/lib.rs alone is roughly a third documentation by line count. Naming follows idiomatic Rust conventions (snake_case functions, PascalCase traits), and custom numeric types like I24 use unchecked-constructor newtype patterns for compile-time safety. There’s no dedicated clippy CI job or rustfmt config file beyond defaults, and no CONTRIBUTING.md, though the README covers contribution expectations directly.
What Makes It Unique dasp’s Sample/Frame/Signal trait trio gives it a genuinely distinct position among Rust audio crates: DSP code is written once and works generically across any bit-depth or channel count, resolved at compile time with no runtime dispatch cost. Combined with no_std support that requires no allocator, and a per-feature crate split that lets consumers pull in only what they need, it reads more like a domain-specific standard library for audio than a typical single-purpose crate. The optional dasp_graph module extends this further by letting DSP nodes be composed into dynamic graphs rather than hand-wired call chains.