Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dasp_graph: Pass edge weights to Node::process #182

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 30 additions & 24 deletions dasp_graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -181,12 +182,12 @@ pub mod node;
/// ```
pub struct Processor<G>
where
G: Visitable,
G: Visitable + Data,
{
// State related to the traversal of the audio graph starting from the output node.
dfs_post_order: DfsPostOrder<G::NodeId, G::Map>,
// Solely for collecting the inputs of a node in order to apply its `Node::process` method.
inputs: Vec<node::Input>,
inputs: Vec<Input<G::EdgeWeight>>,
}

/// For use as the node weight within a dasp graph. Contains the node and its buffers.
Expand All @@ -205,7 +206,7 @@ pub struct NodeData<T: ?Sized> {

impl<G> Processor<G>
where
G: Visitable,
G: Visitable + Data,
{
/// Construct a new graph processor from the given maximum anticipated node count.
///
Expand Down Expand Up @@ -243,8 +244,10 @@ where
pub fn process<T>(&mut self, graph: &mut G, node: G::NodeId)
where
G: Data<NodeWeight = NodeData<T>> + DataMapMut,
for<'a> &'a G: GraphBase<NodeId = G::NodeId> + IntoNeighborsDirected,
T: Node,
for<'b> &'b G:
GraphBase<NodeId = G::NodeId> + IntoEdgesDirected + Data<EdgeWeight = G::EdgeWeight>,
G::EdgeWeight: Clone,
T: Node<G::EdgeWeight>,
{
process(self, graph, node)
}
Expand All @@ -268,27 +271,27 @@ impl<T> NodeData<T> {
}

#[cfg(feature = "node-boxed")]
impl NodeData<BoxedNode> {
impl<W> NodeData<BoxedNode<W>> {
/// The same as **new**, but boxes the given node data before storing it.
pub fn boxed<T>(node: T, buffers: Vec<Buffer>) -> Self
where
T: 'static + Node,
T: 'static + Node<W>,
{
NodeData::new(BoxedNode(Box::new(node)), buffers)
}

/// The same as **new1**, but boxes the given node data before storing it.
pub fn boxed1<T>(node: T) -> Self
where
T: 'static + Node,
T: 'static + Node<W>,
{
Self::boxed(node, vec![Buffer::SILENT])
}

/// The same as **new2**, but boxes the given node data before storing it.
pub fn boxed2<T>(node: T) -> Self
where
T: 'static + Node,
T: 'static + Node<W>,
{
Self::boxed(node, vec![Buffer::SILENT, Buffer::SILENT])
}
Expand All @@ -301,34 +304,37 @@ impl NodeData<BoxedNode> {
/// 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<T>` where `T` implements the `Node` trait.
/// type `NodeData<T>` where `T` implements the [`Node`] trait.
///
/// **Panics** if there is no node for the given index.
pub fn process<G, T>(processor: &mut Processor<G>, graph: &mut G, node: G::NodeId)
where
G: Data<NodeWeight = NodeData<T>> + DataMapMut + Visitable,
for<'a> &'a G: GraphBase<NodeId = G::NodeId> + IntoNeighborsDirected,
T: Node,
for<'b> &'b G:
GraphBase<NodeId = G::NodeId> + IntoEdgesDirected + Data<EdgeWeight = G::EdgeWeight>,
G::EdgeWeight: Clone,
T: Node<G::EdgeWeight>,
{
const NO_NODE: &str = "no node exists for the given index";
processor.dfs_post_order.reset(Reversed(&*graph));
processor.dfs_post_order.move_to(node);
while let Some(n) = processor.dfs_post_order.next(Reversed(&*graph)) {
let data: *mut NodeData<T> = 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
Expand Down
56 changes: 28 additions & 28 deletions dasp_graph/src/node/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Node>);
pub struct BoxedNode<W = ()>(pub Box<dyn Node<W>>);

/// A wrapper around a `Box<dyn Node>`.
///
Expand All @@ -16,107 +16,107 @@ pub struct BoxedNode(pub Box<dyn Node>);
/// 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<dyn Node + Send>);
pub struct BoxedNodeSend<W = ()>(pub Box<dyn Node<W> + Send>);

impl BoxedNode {
impl<W> BoxedNode<W> {
/// Create a new `BoxedNode` around the given `node`.
///
/// This is short-hand for `BoxedNode::from(Box::new(node))`.
pub fn new<T>(node: T) -> Self
where
T: 'static + Node,
T: 'static + Node<W>,
{
Self::from(Box::new(node))
}
}

impl BoxedNodeSend {
impl<W> BoxedNodeSend<W> {
/// Create a new `BoxedNode` around the given `node`.
///
/// This is short-hand for `BoxedNode::from(Box::new(node))`.
pub fn new<T>(node: T) -> Self
where
T: 'static + Node + Send,
T: 'static + Node<W> + Send,
{
Self::from(Box::new(node))
}
}

impl Node for BoxedNode {
fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) {
impl<W> Node<W> for BoxedNode<W> {
fn process(&mut self, inputs: &[Input<W>], output: &mut [Buffer]) {
self.0.process(inputs, output)
}
}

impl Node for BoxedNodeSend {
fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) {
impl<W> Node<W> for BoxedNodeSend<W> {
fn process(&mut self, inputs: &[Input<W>], output: &mut [Buffer]) {
self.0.process(inputs, output)
}
}

impl<T> From<Box<T>> for BoxedNode
impl<T, W> From<Box<T>> for BoxedNode<W>
where
T: 'static + Node,
T: 'static + Node<W>,
{
fn from(n: Box<T>) -> Self {
BoxedNode(n as Box<dyn Node>)
BoxedNode(n as Box<dyn Node<W>>)
}
}

impl<T> From<Box<T>> for BoxedNodeSend
impl<T, W> From<Box<T>> for BoxedNodeSend<W>
where
T: 'static + Node + Send,
T: 'static + Node<W> + Send,
{
fn from(n: Box<T>) -> Self {
BoxedNodeSend(n as Box<dyn Node + Send>)
BoxedNodeSend(n as Box<dyn Node<W> + Send>)
}
}

impl Into<Box<dyn Node>> for BoxedNode {
fn into(self) -> Box<dyn Node> {
impl<W> Into<Box<dyn Node<W>>> for BoxedNode<W> {
fn into(self) -> Box<dyn Node<W>> {
self.0
}
}

impl Into<Box<dyn Node + Send>> for BoxedNodeSend {
fn into(self) -> Box<dyn Node + Send> {
impl<W> Into<Box<dyn Node<W> + Send>> for BoxedNodeSend<W> {
fn into(self) -> Box<dyn Node<W> + Send> {
self.0
}
}

impl fmt::Debug for BoxedNode {
impl<W> fmt::Debug for BoxedNode<W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BoxedNode").finish()
}
}

impl fmt::Debug for BoxedNodeSend {
impl<W> fmt::Debug for BoxedNodeSend<W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BoxedNodeSend").finish()
}
}

impl Deref for BoxedNode {
type Target = Box<dyn Node>;
impl<W> Deref for BoxedNode<W> {
type Target = Box<dyn Node<W>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Deref for BoxedNodeSend {
type Target = Box<dyn Node + Send>;
impl<W> Deref for BoxedNodeSend<W> {
type Target = Box<dyn Node<W> + Send>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for BoxedNode {
impl<W> DerefMut for BoxedNode<W> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl DerefMut for BoxedNodeSend {
impl<W> DerefMut for BoxedNodeSend<W> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
Expand Down
4 changes: 2 additions & 2 deletions dasp_graph/src/node/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use dasp_ring_buffer as ring_buffer;
#[derive(Clone, Debug, PartialEq)]
pub struct Delay<S>(pub Vec<ring_buffer::Fixed<S>>);

impl<S> Node for Delay<S>
impl<S, W> Node<W> for Delay<S>
where
S: ring_buffer::SliceMut<Element = f32>,
{
fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) {
fn process(&mut self, inputs: &[Input<W>], output: &mut [Buffer]) {
// Retrieve the single input, ignore any others.
let input = match inputs.get(0) {
Some(input) => input,
Expand Down
14 changes: 8 additions & 6 deletions dasp_graph/src/node/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<G, T>
where
G: Visitable,
G: Visitable + Data,
{
pub processor: Processor<G>,
pub graph: G,
Expand All @@ -18,13 +18,15 @@ where
pub node_type: PhantomData<T>,
}

impl<G, T> Node for GraphNode<G, T>
impl<G, T, W> Node<W> for GraphNode<G, T>
where
G: Data<NodeWeight = NodeData<T>> + DataMapMut + Visitable,
for<'a> &'a G: GraphBase<NodeId = G::NodeId> + IntoNeighborsDirected,
T: Node,
for<'a> &'a G:
GraphBase<NodeId = G::NodeId> + IntoEdgesDirected + Data<EdgeWeight = G::EdgeWeight>,
G::EdgeWeight: Clone,
T: Node<G::EdgeWeight>,
{
fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) {
fn process(&mut self, inputs: &[Input<W>], output: &mut [Buffer]) {
let GraphNode {
ref mut processor,
ref mut graph,
Expand Down
Loading
Loading