diff --git a/dasp_graph/src/lib.rs b/dasp_graph/src/lib.rs index 2dcb145..3ceee9b 100644 --- a/dasp_graph/src/lib.rs +++ b/dasp_graph/src/lib.rs @@ -17,9 +17,10 @@ //! popular node implementations out of the box, each of which may be accessed by enabling [their //! associated features](./index.html#optional-features). //! -//! The edges of a `dasp` graph are empty and simply describe the direction of audio flow -//! through the graph. That is, the edge *a -> b* describes that the audio output of node *a* will -//! be used as an input to node *b*. +//! The edges of a `dasp` graph describe the direction of audio flow through the graph. That is, +//! the edge *a -> b* describes that the audio output of node *a* will be used as an input to node *b*. +//! Edges can also contain weights of any `Clone` type, which are provided to destination nodes and can +//! allow them to distinguish between different types of connections. //! //! Once we have added our nodes and edges describing the flow of audio through our graph, we can //! repeatedly process and retrieve audio from it using the [`Processor`](./struct.Processor.html) @@ -120,15 +121,15 @@ //! //! ### no_std //! -//! *TODO: Adding support for `no_std` is pending the addition of support for `no_std` in petgraph. -//! See https://github.com/petgraph/petgraph/pull/238. +//! **TODO:** Adding support for `no_std` is pending the addition of support for `no_std` in petgraph. +//! [See this pull request](https://github.com/petgraph/petgraph/pull/238). pub use buffer::Buffer; pub use node::{Input, Node}; use petgraph::data::{DataMap, DataMapMut}; use petgraph::visit::{ - Data, DfsPostOrder, GraphBase, IntoNeighborsDirected, NodeCount, NodeIndexable, Reversed, - Visitable, + Data, DfsPostOrder, EdgeRef, GraphBase, IntoEdgesDirected, IntoNeighborsDirected, NodeCount, + NodeIndexable, Reversed, Visitable, }; use petgraph::{Incoming, Outgoing}; @@ -181,12 +182,12 @@ pub mod node; /// ``` pub struct Processor where - G: Visitable, + G: Visitable + Data, { // State related to the traversal of the audio graph starting from the output node. dfs_post_order: DfsPostOrder, // Solely for collecting the inputs of a node in order to apply its `Node::process` method. - inputs: Vec, + inputs: Vec>, } /// For use as the node weight within a dasp graph. Contains the node and its buffers. @@ -205,7 +206,7 @@ pub struct NodeData { impl Processor where - G: Visitable, + G: Visitable + Data, { /// Construct a new graph processor from the given maximum anticipated node count. /// @@ -243,8 +244,10 @@ where pub fn process(&mut self, graph: &mut G, node: G::NodeId) where G: Data> + DataMapMut, - for<'a> &'a G: GraphBase + IntoNeighborsDirected, - T: Node, + for<'b> &'b G: + GraphBase + IntoEdgesDirected + Data, + G::EdgeWeight: Clone, + T: Node, { process(self, graph, node) } @@ -268,11 +271,11 @@ impl NodeData { } #[cfg(feature = "node-boxed")] -impl NodeData { +impl NodeData> { /// The same as **new**, but boxes the given node data before storing it. pub fn boxed(node: T, buffers: Vec) -> Self where - T: 'static + Node, + T: 'static + Node, { NodeData::new(BoxedNode(Box::new(node)), buffers) } @@ -280,7 +283,7 @@ impl NodeData { /// The same as **new1**, but boxes the given node data before storing it. pub fn boxed1(node: T) -> Self where - T: 'static + Node, + T: 'static + Node, { Self::boxed(node, vec![Buffer::SILENT]) } @@ -288,7 +291,7 @@ impl NodeData { /// The same as **new2**, but boxes the given node data before storing it. pub fn boxed2(node: T) -> Self where - T: 'static + Node, + T: 'static + Node, { Self::boxed(node, vec![Buffer::SILENT, Buffer::SILENT]) } @@ -301,20 +304,22 @@ impl NodeData { /// connected to the inputs of the given `node`. This ensures that all inputs of each node are /// visited before the node itself. /// -/// The `Node::process` method is called on each node as they are visited in the traversal. +/// The [`Node::process`] method is called on each node as they are visited in the traversal. /// /// Upon returning, the buffers of each visited node will contain the audio processed by their /// respective nodes. /// /// Supports all graphs that implement the necessary petgraph traits and whose nodes are of -/// type `NodeData` where `T` implements the `Node` trait. +/// type `NodeData` where `T` implements the [`Node`] trait. /// /// **Panics** if there is no node for the given index. pub fn process(processor: &mut Processor, graph: &mut G, node: G::NodeId) where G: Data> + DataMapMut + Visitable, - for<'a> &'a G: GraphBase + IntoNeighborsDirected, - T: Node, + for<'b> &'b G: + GraphBase + IntoEdgesDirected + Data, + G::EdgeWeight: Clone, + T: Node, { const NO_NODE: &str = "no node exists for the given index"; processor.dfs_post_order.reset(Reversed(&*graph)); @@ -322,13 +327,14 @@ where while let Some(n) = processor.dfs_post_order.next(Reversed(&*graph)) { let data: *mut NodeData = graph.node_weight_mut(n).expect(NO_NODE) as *mut _; processor.inputs.clear(); - for in_n in (&*graph).neighbors_directed(n, Incoming) { + for in_edge in (&*graph).edges_directed(n, Incoming) { + let source_id = in_edge.source(); // Skip edges that connect the node to itself to avoid aliasing `node`. - if n == in_n { + if n == source_id { continue; } - let input_container = graph.node_weight(in_n).expect(NO_NODE); - let input = node::Input::new(&input_container.buffers); + let input_container = graph.node_weight(source_id).expect(NO_NODE); + let input = Input::new(&input_container.buffers, in_edge.weight().clone()); processor.inputs.push(input); } // Here we deference our raw pointer to the `NodeData`. The only references to the graph at diff --git a/dasp_graph/src/node/boxed.rs b/dasp_graph/src/node/boxed.rs index 7501d55..a3ebaa4 100644 --- a/dasp_graph/src/node/boxed.rs +++ b/dasp_graph/src/node/boxed.rs @@ -6,7 +6,7 @@ use core::ops::{Deref, DerefMut}; /// /// Provides the necessary `Sized` implementation to allow for compatibility with the graph process /// function. -pub struct BoxedNode(pub Box); +pub struct BoxedNode(pub Box>); /// A wrapper around a `Box`. /// @@ -16,107 +16,107 @@ pub struct BoxedNode(pub Box); /// Useful when the ability to send nodes from one thread to another is required. E.g. this is /// common when initialising nodes or the audio graph itself on one thread before sending them to /// the audio thread. -pub struct BoxedNodeSend(pub Box); +pub struct BoxedNodeSend(pub Box + Send>); -impl BoxedNode { +impl BoxedNode { /// Create a new `BoxedNode` around the given `node`. /// /// This is short-hand for `BoxedNode::from(Box::new(node))`. pub fn new(node: T) -> Self where - T: 'static + Node, + T: 'static + Node, { Self::from(Box::new(node)) } } -impl BoxedNodeSend { +impl BoxedNodeSend { /// Create a new `BoxedNode` around the given `node`. /// /// This is short-hand for `BoxedNode::from(Box::new(node))`. pub fn new(node: T) -> Self where - T: 'static + Node + Send, + T: 'static + Node + Send, { Self::from(Box::new(node)) } } -impl Node for BoxedNode { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for BoxedNode { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { self.0.process(inputs, output) } } -impl Node for BoxedNodeSend { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for BoxedNodeSend { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { self.0.process(inputs, output) } } -impl From> for BoxedNode +impl From> for BoxedNode where - T: 'static + Node, + T: 'static + Node, { fn from(n: Box) -> Self { - BoxedNode(n as Box) + BoxedNode(n as Box>) } } -impl From> for BoxedNodeSend +impl From> for BoxedNodeSend where - T: 'static + Node + Send, + T: 'static + Node + Send, { fn from(n: Box) -> Self { - BoxedNodeSend(n as Box) + BoxedNodeSend(n as Box + Send>) } } -impl Into> for BoxedNode { - fn into(self) -> Box { +impl Into>> for BoxedNode { + fn into(self) -> Box> { self.0 } } -impl Into> for BoxedNodeSend { - fn into(self) -> Box { +impl Into + Send>> for BoxedNodeSend { + fn into(self) -> Box + Send> { self.0 } } -impl fmt::Debug for BoxedNode { +impl fmt::Debug for BoxedNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BoxedNode").finish() } } -impl fmt::Debug for BoxedNodeSend { +impl fmt::Debug for BoxedNodeSend { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BoxedNodeSend").finish() } } -impl Deref for BoxedNode { - type Target = Box; +impl Deref for BoxedNode { + type Target = Box>; fn deref(&self) -> &Self::Target { &self.0 } } -impl Deref for BoxedNodeSend { - type Target = Box; +impl Deref for BoxedNodeSend { + type Target = Box + Send>; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for BoxedNode { +impl DerefMut for BoxedNode { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl DerefMut for BoxedNodeSend { +impl DerefMut for BoxedNodeSend { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/dasp_graph/src/node/delay.rs b/dasp_graph/src/node/delay.rs index 8e18d6b..a833d57 100644 --- a/dasp_graph/src/node/delay.rs +++ b/dasp_graph/src/node/delay.rs @@ -9,11 +9,11 @@ use dasp_ring_buffer as ring_buffer; #[derive(Clone, Debug, PartialEq)] pub struct Delay(pub Vec>); -impl Node for Delay +impl Node for Delay where S: ring_buffer::SliceMut, { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { // Retrieve the single input, ignore any others. let input = match inputs.get(0) { Some(input) => input, diff --git a/dasp_graph/src/node/graph.rs b/dasp_graph/src/node/graph.rs index e4b6101..f3095ea 100644 --- a/dasp_graph/src/node/graph.rs +++ b/dasp_graph/src/node/graph.rs @@ -5,11 +5,11 @@ use crate::{Buffer, Input, Node, NodeData, Processor}; use core::marker::PhantomData; use petgraph::data::DataMapMut; -use petgraph::visit::{Data, GraphBase, IntoNeighborsDirected, Visitable}; +use petgraph::visit::{Data, GraphBase, IntoEdgesDirected, Visitable}; pub struct GraphNode where - G: Visitable, + G: Visitable + Data, { pub processor: Processor, pub graph: G, @@ -18,13 +18,15 @@ where pub node_type: PhantomData, } -impl Node for GraphNode +impl Node for GraphNode where G: Data> + DataMapMut + Visitable, - for<'a> &'a G: GraphBase + IntoNeighborsDirected, - T: Node, + for<'a> &'a G: + GraphBase + IntoEdgesDirected + Data, + G::EdgeWeight: Clone, + T: Node, { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { let GraphNode { ref mut processor, ref mut graph, diff --git a/dasp_graph/src/node/mod.rs b/dasp_graph/src/node/mod.rs index a9d5262..f135197 100644 --- a/dasp_graph/src/node/mod.rs +++ b/dasp_graph/src/node/mod.rs @@ -68,13 +68,14 @@ mod sum; /// } /// } /// ``` -pub trait Node { +pub trait Node { /// Process some audio given a list of the node's `inputs` and write the result to the `output` /// buffers. /// /// `inputs` represents a list of all nodes with direct edges toward this node. Each /// [`Input`](./struct.Input.html) within the list can providee a reference to the output - /// buffers of their corresponding node. + /// buffers of its corresponding node, as well as the weight of the edge connecting it + /// to this node. /// /// The `inputs` may be ignored if the implementation is for a source node. Alternatively, if /// the `Node` only supports a specific number of `input`s, it is up to the user to decide how @@ -82,27 +83,29 @@ pub trait Node { /// /// This `process` method is called by the [`Processor`](../struct.Processor.html) as it /// traverses the graph during audio rendering. - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]); + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]); } /// A reference to another node that is an input to the current node. /// -/// *TODO: It may be useful to provide some information that can uniquely identify the input node. -/// This could be useful to allow to distinguish between side-chained and regular inputs for -/// example.* -pub struct Input { +/// The edge weight from the graph is provided to support differentiating between inputs. +/// For example, you can use an enum to identify main vs sidechain inputs, or label the edges +/// with the source node's ID. +pub struct Input { buffers_ptr: *const Buffer, buffers_len: usize, + pub edge_weight: W, } -impl Input { +impl Input { // Constructor solely for use within the graph `process` function. - pub(crate) fn new(slice: &[Buffer]) -> Self { + pub(crate) fn new(slice: &[Buffer], edge_weight: W) -> Input { let buffers_ptr = slice.as_ptr(); let buffers_len = slice.len(); Input { buffers_ptr, buffers_len, + edge_weight, } } @@ -118,46 +121,46 @@ impl Input { // Inputs can only be created by the `dasp_graph::process` implementation and only ever live as // long as the lifetime of the call to the function. Thus, it's safe to implement this so that // `Send` closures can be stored within the graph and sent between threads. -unsafe impl Send for Input {} +unsafe impl Send for Input {} -impl fmt::Debug for Input { +impl fmt::Debug for Input { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.buffers(), f) } } -impl<'a, T> Node for &'a mut T +impl<'a, T, W> Node for &'a mut T where - T: Node + ?Sized, + T: Node + ?Sized, { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { (**self).process(inputs, output) } } -impl Node for Box +impl Node for Box where - T: Node + ?Sized, + T: Node + ?Sized, { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { (**self).process(inputs, output) } } -impl Node for dyn Fn(&[Input], &mut [Buffer]) { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for dyn Fn(&[Input], &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { (*self)(inputs, output) } } -impl Node for dyn FnMut(&[Input], &mut [Buffer]) { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for dyn FnMut(&[Input], &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { (*self)(inputs, output) } } -impl Node for fn(&[Input], &mut [Buffer]) { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for fn(&[Input], &mut [Buffer]) { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { (*self)(inputs, output) } } diff --git a/dasp_graph/src/node/pass.rs b/dasp_graph/src/node/pass.rs index 8872719..f7049b1 100644 --- a/dasp_graph/src/node/pass.rs +++ b/dasp_graph/src/node/pass.rs @@ -10,8 +10,8 @@ use crate::{Buffer, Input, Node}; #[derive(Clone, Debug, PartialEq)] pub struct Pass; -impl Node for Pass { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for Pass { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { let input = match inputs.get(0) { None => return, Some(input) => input, diff --git a/dasp_graph/src/node/signal.rs b/dasp_graph/src/node/signal.rs index cd8285b..2101dd0 100644 --- a/dasp_graph/src/node/signal.rs +++ b/dasp_graph/src/node/signal.rs @@ -2,11 +2,11 @@ use crate::{Buffer, Input, Node}; use dasp_frame::Frame; use dasp_signal::Signal; -impl Node for dyn Signal +impl Node for dyn Signal where F: Frame, { - fn process(&mut self, _inputs: &[Input], output: &mut [Buffer]) { + fn process(&mut self, _inputs: &[Input], output: &mut [Buffer]) { let channels = std::cmp::min(F::CHANNELS, output.len()); for ix in 0..Buffer::LEN { let frame = self.next(); diff --git a/dasp_graph/src/node/sum.rs b/dasp_graph/src/node/sum.rs index 9a078d4..8a06bee 100644 --- a/dasp_graph/src/node/sum.rs +++ b/dasp_graph/src/node/sum.rs @@ -22,8 +22,8 @@ pub struct Sum; #[derive(Clone, Debug, PartialEq)] pub struct SumBuffers; -impl Node for Sum { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for Sum { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { // Fill the output with silence. for out_buffer in output.iter_mut() { out_buffer.silence(); @@ -40,8 +40,8 @@ impl Node for Sum { } } -impl Node for SumBuffers { - fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { +impl Node for SumBuffers { + fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { // Get the first output buffer. let mut out_buffers = output.iter_mut(); let out_buffer_first = match out_buffers.next() {