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
// 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 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)
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 block = unsafe ; // 2 channels, 512 frames
Planar requires adapter:
let mut adapter = unsafe ; // 2 channels, 512 frames
let block = adapter.planar_view;
Common Operations
use AudioBlockOps;
block.copy_from_block;
block.fill_with;
block.clear;
block.for_each;
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:
;
;
;
Sample access:
;
Iteration:
;
;
;
;
Generic view (zero-allocation):
;
Downcast to concrete type:
;
;
;
AudioBlockMut
Everything from AudioBlock plus:
Resizing:
;
;
;
Mutable access:
;
;
+ '_;
;
+ '_;
Generic view (zero-allocation):
;
Downcast to concrete type:
;
;
;
Operations:
;
;
;
;
;
;
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 view = from_slice_limited;
Auto-resize when copying:
Query allocation:
block.num_channels_allocated;
block.num_frames_allocated;
Advanced: Access Non-Visible Samples
For operations that can safely process all allocated memory:
block.for_each_including_non_visible;
block.enumerate_including_non_visible;
Direct memory access:
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.