Digital Audio Signal Processing in Rust.
A suite of crates providing the fundamentals for working with PCM (pulse-code
modulation) DSP (digital signal processing). In other words,
dasp provides a
suite of low-level, high-performance tools including types, traits and functions
for working with digital audio signals.
dasp libraries require no dynamic allocations1 and have
no dependencies. The goal is to design a library akin to the std, but for
audio DSP; keeping the focus on portable and fast fundamentals.
1: Besides the feature-gated
SignalBus trait, which is occasionally
useful when converting a
Signal tree into a directed acyclic graph.
Find the API documentation here.
dasp is a modular collection of crates, allowing users to select the precise
set of tools required for their project. The following crates are included
within this repository:
Red dotted lines indicate optional dependencies, while black lines indicate
Use the Sample trait to convert between and remain generic over any
bit-depth in an optimal, performance-sensitive manner. Implementations are
provided for all signed integer, unsigned integer and floating point primitive
types along with some custom types including 11, 20, 24 and 48-bit signed and
unsigned unpacked integers. For example:
assert_eq!((-1.0).to_sample:: <u8>(), 0); assert_eq!(0.0.to_sample:: <u8>(), 128); assert_eq!(0i32.to_sample:: <u32>(), 2_147_483_648); assert_eq!(I24:: new(0).unwrap(), Sample:: from_sample(0.0)); assert_eq!(0.0, Sample:: EQUILIBRIUM);
Use the Frame trait to remain generic over the number of channels at a
discrete moment in time. Implementations are provided for all fixed-size arrays
up to 32 elements in length.
let foo = [0.1, 0.2, -0.1, -0.2]; let bar = foo.scale_amp(2.0); assert_eq!(bar, [0.2, 0.4, -0.2, -0.4]); assert_eq!(Mono:: <f32>:: EQUILIBRIUM, [0.0]); assert_eq!(Stereo:: <f32>:: EQUILIBRIUM, [0.0, 0.0]); assert_eq!(<[f32; 3]>:: EQUILIBRIUM, [0.0, 0.0, 0.0]); let foo = [0i16, 0]; let bar: [u8; 2] = foo.map(Sample:: to_sample); assert_eq!(bar, [128u8, 128]);
Use the Signal trait (enabled by the “signal” feature) for working with
infinite-iterator-like types that yield
Frames. Signal provides methods
for adding, scaling, offsetting, multiplying, clipping, generating, monitoring
and buffering streams of
Frames. Working with Signals allows for easy,
readable creation of rich and complex DSP graphs with a simple and familiar API.
// Clip to an amplitude of 0.9. let frames = [[1.2, 0.8], [-0.7, -1.4]]; let clipped: Vec<_> = signal:: from_iter(frames.iter().cloned()).clip_amp(0.9).take(2).collect(); assert_eq!(clipped, vec![[0.9, 0.8], [-0.7, -0.9]]); // Add `a` with `b` and yield the result. let a = [0.2, -0.6, 0.5]; let b = [0.2, 0.1, -0.8]; let a_signal = signal:: from_iter(a.iter().cloned()); let b_signal = signal:: from_iter(b.iter().cloned()); let added: Vec<f32> = a_signal.add_amp(b_signal).take(3).collect(); assert_eq!(added, vec![0.4, -0.5, -0.3]); // Scale the playback rate by `0.5`. let foo = [0.0, 1.0, 0.0, -1.0]; let mut source = signal:: from_iter(foo.iter().cloned()); let a = source.next(); let b = source.next(); let interp = Linear:: new(a, b); let frames: Vec<_> = source.scale_hz(interp, 0.5).take(8).collect(); assert_eq!(&frames[..], &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..]); // Convert a signal to its RMS. let signal = signal:: rate(44_100.0).const_hz(440.0).sine();; let ring_buffer = ring_buffer:: Fixed:: from([0.0; WINDOW_SIZE]); let mut rms_signal = signal.rms(ring_buffer);
The signal module also provides a series of Signal source types,
Gen(generate frames from a Fn() -> F)
GenMut(generate frames from a FnMut() -> F)
Use the slice module functions (enabled via the “slice” feature) for
processing chunks of
Frames. Conversion functions are provided for safely
converting between slices of interleaved
Samples and slices of
without requiring any allocation. For example:
let frames = &[[0.0, 0.5], [0.0, -0.5]][..]; let samples = slice:: to_sample_slice(frames); assert_eq!(samples, &[0.0, 0.5, 0.0, -0.5][..]); let samples = &[0.0, 0.5, 0.0, -0.5][..]; let frames = slice:: to_frame_slice(samples); assert_eq!(frames, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); let samples = &[0.0, 0.5, 0.0][..]; let frames = slice:: to_frame_slice(samples); assert_eq!(frames, None:: <&[[f32; 2]]>);
The signal::interpolate module provides a Converter type, for converting
and interpolating the rate of Signals. This can be useful for both sample
rate conversion and playback rate multiplication. Converters can use a range
of interpolation methods, with Floor, Linear, and Sinc interpolation provided in
The ring_buffer module provides generic Fixed and Bounded ring
buffer types, both of which may be used with owned, borrowed, stack and
The peak module can be used for monitoring the peak of a signal. Provided
peak rectifiers include
The rms module provides a flexible Rms type that can be used for RMS
(root mean square) detection. Any Fixed ring buffer can be used as the
window for the RMS detection.
The envelope module provides a Detector type (also known as a
Follower) that allows for detecting the envelope of a signal. Detector is
generic over the type of Detection – Rms and Peak detection are
provided. For example:
let signal = signal:: rate(4.0).const_hz(1.0).sine(); let attack = 1.0; let release = 1.0; let detector = envelope:: Detector:: peak(attack, release); let mut envelope = signal.detect_envelope(detector); assert_eq!( envelope.take(4).collect:: <Vec<_>>(), vec![0.0, 0.6321205496788025, 0.23254416035257117, 0.7176687675647109] );
All crates may be compiled with and without the std library. The std library is
enabled by default, however it may be disabled via
To enable all of a crate’s features without the std library, you may use
--no-default-features --features "all-no-std".
Please note that some of the crates require the
core_intrinsics feature in
order to be able to perform operations like
powf32 in a
no_std context. This means that these crates require the nightly toolchain in
order to build in a
If dasp is missing types, conversions or other fundamental functionality
that you wish it had, feel free to open an issue or pull request! The more
hands on deck, the merrier 🙂
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.