Skip to main content

Graph

Struct Graph 

Source
pub struct Graph { /* private fields */ }
Expand description

A real-time-safe directed acyclic graph of audio nodes.

A Graph moves through three phases:

  1. Build — call Graph::add_node to register nodes and Graph::link to wire output ports to input ports.
  2. Compile — call Graph::compile with a GraphConfig. This runs the topological sort, rejects cycles, and pre-allocates every scratch frame.
  3. Run — call Graph::process_cycle once per audio cycle on the RT thread. Use Graph::feed to seed external inputs before a cycle and Graph::read_output / Graph::read_input to tap results after.

See the crate-level documentation for the real-time safety contract.

Implementations§

Source§

impl Graph

Source

pub fn new() -> Self

Creates an empty graph.

Source

pub fn add_node(&mut self, node: Box<dyn AudioNode>) -> NodeId

Adds a node to the graph and returns its stable NodeId.

The returned id equals the node’s index in insertion order and never changes for the lifetime of the graph. Scratch frames for the node’s ports are allocated later in Graph::compile.

Links an output port to an input port, validating both endpoints and their compatibility.

The from port must be an output and the to port an input; their channel counts and sample formats must match. The edge is stored but the graph is not recompiled — call Graph::compile (or design the full topology before compiling) before processing.

§Errors

Returns GraphError::NodeNotFound if either node does not exist, GraphError::PortNotFound if a port index is out of range, GraphError::PortDirectionMismatch if the directions are wrong, or GraphError::PortIncompatible if channel/format differ.

Source

pub fn compile(&mut self, config: GraphConfig) -> Result<(), GraphError>

Compiles the graph: topologically sorts the nodes and pre-allocates every scratch frame.

This is the only place allocation is permitted. After compile succeeds, Graph::process_cycle is guaranteed to be allocation-free.

§Errors

Returns GraphError::AlreadyCompiled if called twice, or GraphError::CycleDetected if the topology contains a cycle.

Source

pub fn process_cycle(&self, ctx: &mut ProcessContext) -> Result<(), GraphError>

Processes one audio cycle on the real-time thread.

For each node in dependency order this copies connected upstream outputs into the node’s input scratch (or zeroes unconnected inputs), then invokes the node’s process. The whole pass is bounded and allocation-free: every slice is pre-sized by Graph::compile and only bounded for loops / slice copies are used.

§Safety (caller invariant)

This method takes &self (not &mut self) so it can be called on a Graph loaded from a shared handle such as ArcSwap<Graph>. The interior mutability of the scratch cell is sound only because the caller guarantees the real-time thread is single and exclusive — no other process_cycle, compile, feed, or read_* may run concurrently on the same Graph. In practice the control thread builds and compiles a new Graph before swapping it in, so the old instance being processed is never touched from another thread.

§Real-time safety

This method performs no allocation, locking, panicking, or system call. The single Err(NotCompiled) return on the uncompiled path is a stack-only branch (the variant carries no heap data); on the happy path the method returns Ok(()).

§Errors

Returns GraphError::NotCompiled if Graph::compile has not been called.

Source

pub fn feed(&mut self, node: NodeId, port: PortIdx, src: &AudioFrame)

Seeds a node’s output port from an external frame, before a cycle.

Performs a bounded copy into the pre-sized scratch slot — no reallocation. Used to inject audio into a source node’s output before calling Graph::process_cycle. Out-of-range node/port is a silent no-op (never panics).

Source

pub fn read_output(&self, node: NodeId, port: PortIdx) -> Option<&AudioFrame>

Borrows a node’s output frame after a cycle (tapping a node’s output).

Returns None if the node or port is out of range — never panics.

Source

pub fn read_input(&self, node: NodeId, port: PortIdx) -> Option<&AudioFrame>

Borrows the input frame that reached a node after a cycle.

Sinks have zero outputs, so callers read a sink’s consumed audio through its input slot. Returns None if the node or port is out of range.

Source

pub fn node_count(&self) -> usize

Returns the number of nodes in the graph.

Returns the number of links in the graph.

Source

pub fn is_compiled(&self) -> bool

Returns true if the graph has been compiled.

Source

pub fn config(&self) -> GraphConfig

Returns the compile-time GraphConfig.

Before Graph::compile this returns the default (zeroed) config.

Source§

impl Graph

Source

pub fn from_snapshot( snapshot: &TopologySnapshot, factory: &mut dyn FnMut(NodeId) -> Option<Box<dyn AudioNode>>, ) -> Result<Graph, GraphError>

Available on crate feature topology only.

Builds a NEW Graph from a TopologySnapshot plus a node factory.

This is a non-RT, control-thread operation. For each NodeSnapshot the factory is asked to supply a concrete Box<dyn AudioNode> for that node’s id; the snapshot’s port metadata is informational (the real node’s ports, as reported by its inputs() / outputs(), are what link validates against). Edges are re-linked by remapping each snapshot NodeId to its new id in the rebuilt graph.

The returned graph is not compiled — the caller compiles it with their own GraphConfig.

§Errors

Returns GraphError::NodeNotFound if the factory returns None for a node the snapshot requires, or if an edge references a node id the factory did not supply. Returns the underlying GraphError from Graph::link if a rebuilt edge fails port validation.

Source

pub fn subscribe_topology(&mut self) -> Receiver<TopologyEvent>

Available on crate feature topology only.

Subscribes to topology events via a std::sync::mpsc channel (non-RT).

Returns a Receiver that receives a TopologyEvent whenever the graph mutates on the control thread (a node is added/removed or a link is added/removed). The RT Graph::process_cycle path never emits events.

Senders whose receivers have been dropped are pruned automatically on the next emission.

§Real-time safety (G3)

Subscription is a control-thread mutation of the subscriber set, so it takes &mut self — an exclusive borrow. process_cycle is now &self (Phase-C prep), so this &mut self is the stronger constraint: it is statically impossible to hold the &mut self needed to subscribe while a &self process_cycle borrow is live on the same binding. Combined with the single-RT-thread invariant on GraphScratch, the RT path is never disrupted by a concurrent subscription on the same Graph.

Source§

impl Graph

Source

pub fn partition_hints( &self, boundaries: &[(NodeId, PortIdx, RemoteNode)], ) -> Vec<PartitionHint>

Available on crate feature distributed only.

Derives partition hints from a set of (node, port, remote) boundary declarations.

Every node present in this graph is treated as a local node (the graph is the local partition). Each declared port becomes a BoundaryPort with PortKind::Network. The port’s direction is resolved from the node’s own port descriptors: output ports are checked first, so a port index valid on both sides is treated as an output. A declaration whose port is out of range on both sides is skipped (the method never panics).

This is a hint generator — sonicbrew’s session-store (M07) decides the actual partitioning. Exactly one PartitionHint is returned, describing this single local partition.

Trait Implementations§

Source§

impl Default for Graph

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl SnapshotSource for Graph

Available on crate feature topology only.
Source§

fn topology_snapshot(&self) -> TopologySnapshot

Returns a serializable, metadata-only snapshot of the current topology.
Source§

impl Sync for Graph

Auto Trait Implementations§

§

impl !Freeze for Graph

§

impl !RefUnwindSafe for Graph

§

impl !UnwindSafe for Graph

§

impl Send for Graph

§

impl Unpin for Graph

§

impl UnsafeUnpin for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.