audio-blocks
Real-time safe abstractions over audio data with support for all common layouts.
Quick Start
Install:
Basic planar usage (most common for DSP):
use *;
// Create a planar block - each channel gets its own buffer
let mut block = new; // 2 channels, 512 frames
// Process per channel
for channel in block.channels_mut
Generic function that accepts any layout:
Block Types
Three multi-channel layouts supported:
Planar - [[ch0, ch0, ch0], [ch1, ch1, ch1]]
Each channel has its own separate buffer. Standard for real-time DSP. Optimal for SIMD/vectorization.
Sequential - [ch0, ch0, ch0, ch1, ch1, ch1]
Single contiguous buffer with all samples for channel 0, then all samples for channel 1. Channel-contiguous in one allocation.
Interleaved - [ch0, ch1, ch0, ch1, ch0, ch1]
Channels alternate sample-by-sample. Common in audio APIs and hardware interfaces.
Plus a dedicated mono type:
Mono - [sample0, sample1, sample2, ...]
Simplified single-channel block with a streamlined API that doesn't require channel indexing.
Core Traits
Write functions that accept any layout:
Generic across float types:
Creating Blocks
Owned Blocks
use *;
// Allocate with default values (zero)
let mut block = new; // 2 channels, 512 frames
let mut block = new; // 2 channels, 512 frames
let mut block = new; // 2 channels, 512 frames
let mut block = new; // 512 frames
// Copy from existing data
let channel_data = vec!;
let data = vec!;
let mut block = from_slice; // channels derived from slice
let mut block = from_slice; // 2 channels
let mut block = from_slice; // 2 channels
let mut block = from_slice;
Allocation only happens when creating owned blocks. Never do that in real-time contexts.
Views (zero-allocation, borrows data)
use *;
let channel_data = vec!;
let data = vec!;
let block = from_slice; // channels derived from slice
let block = from_slice; // 2 channels
let block = from_slice; // 2 channels
let block = from_slice;
From raw pointers:
let data = vec!;
let block = unsafe ; // 2 channels, 512 frames
Planar requires adapter:
let mut adapter = unsafe ; // 2 channels, 512 frames
let block = adapter.planar_view;
Common Operations
Import the extension traits for additional operations:
use ;
Copying and Clearing
let other_block = new;
let mut block = new;
// Copy from another block (flexible - copies min of both sizes)
let result = block.copy_from_block;
// Returns None if exact match, Some((channels, frames)) if partial
// Copy with exact size requirement (panics on mismatch)
block.copy_from_block_exact;
// Fill all samples with a value
block.fill_with;
// Clear to zero
block.clear;
Per-Sample Processing
let mut block = new;
// Process each sample
block.for_each;
// Process with channel/frame indices
block.enumerate;
// Apply gain to all samples
block.gain;
Mono Conversions
let mut block = new;
let mut mono_data = vec!;
let mut mono_view = from_slice;
// Mix all channels to mono (averages channels)
let result = block.mix_to_mono;
// Returns None if exact match, Some(frames_processed) if partial
// Or with exact size requirement
block.mix_to_mono_exact;
// Copy a specific channel to mono
block.copy_channel_to_mono; // channel 0
// Copy mono to all channels of a block
let mono_ro = from_slice;
block.copy_mono_to_all_channels;
Working with Slices
Convert generic blocks to concrete types for slice access:
Direct slice access on concrete types:
let mut block = new; // 2 channels, 512 frames
let channel: & = block.channel;
let raw_data: & = block.raw_data;
let mut block = new; // 2 channels, 512 frames
let frame: & = block.frame;
let raw_data: & = block.raw_data;
Trait API Reference
AudioBlock
Size and layout:
let channels: u16 = audio.num_channels;
let frames: usize = audio.num_frames;
let layout: BlockLayout = audio.layout;
Sample access:
let s: f32 = audio.sample;
Iteration:
for s in audio.channel_iter
for ch in audio.channels_iter
for s in audio.frame_iter
for fr in audio.frames_iter
Generic view (zero-allocation):
let view = audio.as_view;
Downcast to concrete type:
let interleaved: = audio.as_interleaved_view;
let sequential: = audio.as_sequential_view;
AudioBlockMut
Everything from AudioBlock plus:
Resizing:
audio.set_visible;
audio.set_num_channels_visible;
audio.set_num_frames_visible;
Mutable access:
let s: &mut f32 = audio.sample_mut;
for s in audio.channel_iter_mut
for ch in audio.channels_iter_mut
for s in audio.frame_iter_mut
for fr in audio.frames_iter_mut
Generic view (zero-allocation):
let view = audio.as_view_mut;
Downcast to concrete type:
let interleaved: = audio.as_interleaved_view_mut;
let sequential: = audio.as_sequential_view_mut;
AudioBlockOps (extension trait)
Read-only operations on audio blocks:
let _: = block.mix_to_mono;
block.mix_to_mono_exact;
let _: = block.copy_channel_to_mono;
block.copy_channel_to_mono_exact;
AudioBlockOpsMut (extension trait)
Mutable operations on audio blocks:
let _: = block.copy_from_block;
block.copy_from_block_exact;
let _: = block.copy_mono_to_all_channels;
block.copy_mono_to_all_channels_exact;
block.for_each;
block.enumerate;
block.for_each_allocated;
block.fill_with;
block.clear;
block.gain;
Advanced: Variable Buffer Sizes
Blocks separate allocated capacity from visible size. Resize visible portion without reallocation:
let mut block = new; // 2 channels, 512 frames
block.set_num_frames_visible; // use only 256 frames
Create views with limited visibility:
let block = from_slice_limited;
Query allocation:
let _ = block.num_channels_allocated;
let _ = block.num_frames_allocated;
Advanced: Access Allocated Samples
For operations that process all allocated memory (including non-visible samples):
use AudioBlockOpsMut;
block.for_each_allocated;
block.enumerate_allocated;
Note: fill_with, clear, and gain also operate on the entire allocated buffer for efficiency.
Direct memory access:
let block = new;
let data: & = block.raw_data; // Includes non-visible samples
Performance
Iterator performance varies by layout:
- Sequential/Planar: Channel iteration faster
- Interleaved (many channels): Frame iteration faster
raw_data() access is fastest but exposes non-visible samples. For simple operations like gain, processing all samples (including non-visible) can be more efficient.
Check layout before optimization:
match block.layout
no_std Support
Disable default features. Owned blocks require alloc or std feature.