Audiograph
A realtime audio processing graph library for Rust
[!WARNING] Not production ready yet - breaking changes may occur, some features are not implemented yet, and the API is not fully stabilized.
Audiograph provides abstractions for audio processors, audio buffers and channel routing, and enables the construction and management of directed signal processing graphs. Graph edges can explicitly encode the channel-based routing between processor nodes, allowing for flexible channel selection and reordering with minimal runtime overhead.
Audiograph supports modifying DSP graphs at runtime under real-time constraints, including adding or removing processor nodes, and changing connections between nodes.
Installation
Basic Usage
The most basic functionality is shown below. For more advanced usage, refer to the documentation.
Create a graph instance
use ;
let num_channels = 2; // stereo
let frame_size = FrameSize; // maximum number of samples per frame
// intended maximum number of edges in the graph,
// used for the preallocation of internal graph structures
let max_num_edges = Some;
// we use f32 as the sample type
let mut dsp_graph = new;
Define a processing node
You have to implement the Processor trait for your custom processor types.
use ;
;
// We can implement for a generic (floating point) sample type T
Add processor nodes to the graph
use MultiChannelBuffer;
// allocate a sample buffer that the processor node can write into
let node_buffer = new;
let my_processor_node = dsp_graph.add_processor.unwrap;
Connect nodes in the graph
use GraphNode;
// connect graph input to a processor node
dsp_graph
.connect
.unwrap;
// connect two processor nodes
dsp_graph
.connect
.unwrap;
// connect a processor node to the graph output
dsp_graph
.connect
.unwrap;
Execute the graph
// The input and output buffers may come from an audio I/O callback.
// There are buffer view wrappers that can be used in this case.
// Or you define and store your own MultiChannelBuffer instances instead:
let input_buffer = new;
let mut output_buffer = new;
// Do the processing
dsp_graph.process;
Technical Notes
-
Audiograph operates on channel-based floating-point audio sample structures. Convenience utilities are provided for interleaving and deinterleaving buffers when interfacing with APIs that use interleaved layouts.
-
Realtime safety is guaranteed for most of the graph operations, including processing and modifying the graph structure. Some methods are not realtime safe (currently e.g. the
rewiremethod). These methods are documented as such. Some operations require audio buffers to be preallocated (such as adding nodes to the graph), which is the responsibility of the user of the library. -
There is a maximum number of channels the
DspGraphcan process. The default value is 64, but it can be changed at compile time by setting theMAX_CHANNELSenvironment variable when building the library. It is not worth to make this value smaller. Higher values will introduce slightly more processing overhead. -
The directed graph structure is implemented using the
petgraphcrate (https://github.com/petgraph/petgraph).
A note on performance
Audiograph is designed with flexibility and composability as its primary goals — enabling complex channel routing, runtime graph modification, and context-agnostic processors. The graph infrastructure is engineered to keep overhead low, and for typical use cases the vast majority of CPU time is spent in the audio processing code. There are, however, some performance penalties to be aware of: branching, lookups and dynamic dispatching are part of the graph traversal. For extremely performance-critical applications, it may be better to define larger-scope processing nodes or to not use a graph structure at all.
Iterating over a ChannelSelection involves bit set operations, which can impose some performance overhead. This is the trade-off for its compact and flexible channel selection representation.
Alternatives
The dasp_graph module of the DASP crate (https://github.com/RustAudio/dasp/tree/master/dasp_graph) provides an alternative audio graph implementation in Rust, also built on top of petgraph. It is more generic and lower-level, with certain structural constraints.
Audiograph places more responsibility on the graph itself to express e.g. complex routing, allowing processing nodes to remain context-agnostic. This design introduces slightly higher graph overhead.
TODO
- Make rewiring of edges realtime-safe (e.g. by using fixed-size arrays for channel mappings)