pub struct DspGraph {
pub arena: Arena<NodeRecord>,
pub buffers: BufferPool,
pub execution_order: Vec<NodeId>,
pub levels: Vec<Vec<NodeId>>,
pub output_node: Option<NodeId>,
/* private fields */
}Expand description
Directed Acyclic Graph (DAG) for DSP routing.
The graph owns the node arena and buffer pool. It maintains a topologically sorted execution order and BFS level structure for parallel processing.
§Structure
- Arena: Generational arena storing node records
- Buffer Pool: Pre-allocated audio buffers (no RT allocation)
- Execution Order: Flat topologically sorted node list
- BFS Levels: Nodes grouped by dependency depth for parallel execution
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct Gain { gain: f32 }
impl DspNode for Gain {
fn process(&mut self, inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _params: &mut ParamBlock, _sr: f32) {
if let Some(input) = inputs[0] {
for (i, out) in output.iter_mut().enumerate() {
*out = input[i] * self.gain;
}
}
}
fn type_name(&self) -> &'static str { "Gain" }
}
let mut graph = DspGraph::new();
let gain_id = graph.add_node(Box::new(Gain { gain: 0.5 })).unwrap();
graph.set_output_node(gain_id);Fields§
§arena: Arena<NodeRecord>§buffers: BufferPool§execution_order: Vec<NodeId>Topologically sorted execution order. Rebuilt on structural mutations.
levels: Vec<Vec<NodeId>>BFS wave levels: each inner Vec contains nodes that can execute in parallel. Level[i] nodes all depend only on nodes in levels 0..i.
output_node: Option<NodeId>The node whose output buffer is sent to the DAC.
Implementations§
Source§impl DspGraph
impl DspGraph
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new empty DSP graph.
Initializes the arena, buffer pool, and execution structures with
pre-allocated capacity for MAX_NODES nodes.
§Example
use aether_core::graph::DspGraph;
let graph = DspGraph::new();
assert_eq!(graph.execution_order.len(), 0);Sourcepub fn add_node(&mut self, processor: Box<dyn DspNode>) -> Option<NodeId>
pub fn add_node(&mut self, processor: Box<dyn DspNode>) -> Option<NodeId>
Adds a node to the graph and returns its ID.
Acquires a buffer from the pool, inserts the node into the arena, and rebuilds the topological execution order.
§Arguments
processor- Boxed DSP node implementation
§Returns
Some(NodeId)- The node’s unique identifierNone- If arena is full or buffer pool exhausted
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct Oscillator { frequency: f32, phase: f32 }
impl DspNode for Oscillator {
fn process(&mut self, _inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _params: &mut ParamBlock, sr: f32) {
let phase_inc = self.frequency / sr;
for sample in output.iter_mut() {
*sample = (self.phase * std::f32::consts::TAU).sin();
self.phase = (self.phase + phase_inc).fract();
}
}
fn type_name(&self) -> &'static str { "Oscillator" }
}
let mut graph = DspGraph::new();
let osc = Box::new(Oscillator { frequency: 440.0, phase: 0.0 });
let id = graph.add_node(osc).unwrap();§See Also
remove_node- Remove a node from the graphconnect- Connect two nodes
Sourcepub fn remove_node(&mut self, id: NodeId) -> bool
pub fn remove_node(&mut self, id: NodeId) -> bool
Removes a node from the graph and releases its buffer.
Removes the node from the arena, releases its output buffer back to the pool, and removes all edges connected to this node. Rebuilds the topological execution order.
§Arguments
id- Node ID to remove
§Returns
true- Node removed successfullyfalse- Node doesn’t exist (invalid ID or already removed)
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct SimpleNode;
impl DspNode for SimpleNode {
fn process(&mut self, _: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _: &mut ParamBlock, _: f32) {
output.fill(0.0);
}
fn type_name(&self) -> &'static str { "SimpleNode" }
}
let mut graph = DspGraph::new();
let node_id = graph.add_node(Box::new(SimpleNode)).unwrap();
assert!(graph.remove_node(node_id)); // Returns true
assert!(!graph.remove_node(node_id)); // Returns false (already removed)§See Also
add_node- Add a node to the graph
Sourcepub fn connect(&mut self, src: NodeId, dst: NodeId, slot: usize) -> bool
pub fn connect(&mut self, src: NodeId, dst: NodeId, slot: usize) -> bool
Connects the output of one node to the input of another.
Creates an edge in the DAG from src to dst, routing audio from
the source node’s output buffer to the destination node’s input slot.
Rebuilds the topological execution order to maintain DAG invariants.
§Arguments
src- Source node ID (output)dst- Destination node ID (input)slot- Input slot index on destination node (0 to MAX_INPUTS-1)
§Returns
true- Connection successfulfalse- One or both nodes don’t exist
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct SimpleNode;
impl DspNode for SimpleNode {
fn process(&mut self, _: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _: &mut ParamBlock, _: f32) {
output.fill(0.0);
}
fn type_name(&self) -> &'static str { "SimpleNode" }
}
let mut graph = DspGraph::new();
let node_a = graph.add_node(Box::new(SimpleNode)).unwrap();
let node_b = graph.add_node(Box::new(SimpleNode)).unwrap();
// Connect node_a output → node_b input slot 0
graph.connect(node_a, node_b, 0);§See Also
disconnect- Remove a connectionadd_node- Add nodes to connect
Sourcepub fn disconnect(&mut self, dst: NodeId, slot: usize) -> bool
pub fn disconnect(&mut self, dst: NodeId, slot: usize) -> bool
Disconnects an input slot on a destination node.
Removes the connection to the specified input slot, clearing the audio routing. The slot will receive silence until reconnected. Rebuilds the topological execution order.
§Arguments
dst- Destination node IDslot- Input slot index to disconnect (0 to MAX_INPUTS-1)
§Returns
true- Disconnection successfulfalse- Node doesn’t exist or slot was already empty
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct SimpleNode;
impl DspNode for SimpleNode {
fn process(&mut self, _: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _: &mut ParamBlock, _: f32) {
output.fill(0.0);
}
fn type_name(&self) -> &'static str { "SimpleNode" }
}
let mut graph = DspGraph::new();
let node_a = graph.add_node(Box::new(SimpleNode)).unwrap();
let node_b = graph.add_node(Box::new(SimpleNode)).unwrap();
graph.connect(node_a, node_b, 0);
graph.disconnect(node_b, 0); // Disconnect slot 0§See Also
connect- Create a connection
Sourcepub fn set_output_node(&mut self, id: NodeId)
pub fn set_output_node(&mut self, id: NodeId)
Sets the output node whose buffer is sent to the DAC.
Designates which node’s output buffer should be copied to the audio device output. Only one node can be the output node at a time.
§Arguments
id- Node ID to use as output
§Example
use aether_core::graph::DspGraph;
use aether_core::node::DspNode;
use aether_core::param::ParamBlock;
use aether_core::{BUFFER_SIZE, MAX_INPUTS};
struct Oscillator { phase: f32 }
impl DspNode for Oscillator {
fn process(&mut self, _: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
output: &mut [f32; BUFFER_SIZE], _: &mut ParamBlock, _: f32) {
for sample in output.iter_mut() {
*sample = (self.phase * std::f32::consts::TAU).sin();
self.phase = (self.phase + 0.01).fract();
}
}
fn type_name(&self) -> &'static str { "Oscillator" }
}
let mut graph = DspGraph::new();
let osc_id = graph.add_node(Box::new(Oscillator { phase: 0.0 })).unwrap();
graph.set_output_node(osc_id);Trait Implementations§
Auto Trait Implementations§
impl Freeze for DspGraph
impl !RefUnwindSafe for DspGraph
impl Send for DspGraph
impl !Sync for DspGraph
impl Unpin for DspGraph
impl UnsafeUnpin for DspGraph
impl !UnwindSafe for DspGraph
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more