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

Use const generics in graph module #152

Open
wants to merge 3 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
27 changes: 12 additions & 15 deletions dasp_graph/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,53 @@ use core::ops::{Deref, DerefMut};

/// The fixed-size buffer used for processing the graph.
#[derive(Clone)]
pub struct Buffer {
data: [f32; Self::LEN],
pub struct Buffer<const N: usize> {
data: [f32; N],
}

impl Buffer {
/// The fixed length of the **Buffer** type.
pub const LEN: usize = 64;
impl<const N: usize> Buffer<N> {
pub const LEN: usize = N;
/// A silent **Buffer**.
pub const SILENT: Self = Buffer {
data: [0.0; Self::LEN],
};
pub const SILENT: Self = Buffer { data: [0.0; N] };

/// Short-hand for writing silence to the whole buffer.
pub fn silence(&mut self) {
self.data.copy_from_slice(&Self::SILENT)
}
}

impl Default for Buffer {
impl<const N: usize> Default for Buffer<N> {
fn default() -> Self {
Self::SILENT
}
}

impl From<[f32; Self::LEN]> for Buffer {
fn from(data: [f32; Self::LEN]) -> Self {
impl<const N: usize> From<[f32; N]> for Buffer<N> {
fn from(data: [f32; N]) -> Self {
Buffer { data }
}
}

impl fmt::Debug for Buffer {
impl<const N: usize> fmt::Debug for Buffer<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.data[..], f)
}
}

impl PartialEq for Buffer {
impl<const N: usize> PartialEq for Buffer<N> {
fn eq(&self, other: &Self) -> bool {
&self[..] == &other[..]
}
}

impl Deref for Buffer {
impl<const N: usize> Deref for Buffer<N> {
type Target = [f32];
fn deref(&self) -> &Self::Target {
&self.data[..]
}
}

impl DerefMut for Buffer {
impl<const N: usize> DerefMut for Buffer<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data[..]
}
Expand Down
53 changes: 28 additions & 25 deletions dasp_graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,16 @@ pub mod node;
/// use petgraph;
/// #
/// # // The node type. (Hint: Use existing node impls by enabling their associated features).
/// # struct MyNode;
/// # struct MyNode<const N: usize>;
///
/// // Chose a type of graph for audio processing.
/// type Graph = petgraph::graph::DiGraph<NodeData<MyNode>, (), u32>;
/// type Graph = petgraph::graph::DiGraph<NodeData<MyNode::<128>, 128>, (), u32>;
/// // Create a short-hand for our processor type.
/// type Processor = dasp_graph::Processor<Graph>;
/// type Processor = dasp_graph::Processor<Graph, 128>;
/// #
/// # impl Node for MyNode {
/// # impl<const N: usize> Node<N> for MyNode<N> {
/// # // ...
/// # fn process(&mut self, _inputs: &[Input], _output: &mut [Buffer]) {
/// # fn process(&mut self, _inputs: &[Input<N>], _output: &mut [Buffer<N>]) {
/// # }
/// # }
///
Expand All @@ -173,37 +173,37 @@ pub mod node;
/// let mut p = Processor::with_capacity(max_nodes);
///
/// // Add some nodes and edges...
/// # let n_id = g.add_node(NodeData::new1(MyNode));
/// # let n_id = g.add_node(NodeData::new1(MyNode::<128>));
///
/// // Process all nodes within the graph that output to the node at `n_id`.
/// p.process(&mut g, n_id);
/// }
/// ```
pub struct Processor<G>
pub struct Processor<G, const N: usize>
where
G: Visitable,
{
// 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<node::Input<N>>,
}

/// For use as the node weight within a dasp graph. Contains the node and its buffers.
///
/// For a graph to be compatible with a graph **Processor**, its node weights must be of type
/// `NodeData<T>`, where `T` is some type that implements the `Node` trait.
pub struct NodeData<T: ?Sized> {
pub struct NodeData<T: ?Sized, const N: usize> {
/// The buffers to which the `node` writes audio data during a call to its `process` method.
///
/// Generally, each buffer stored within `buffers` corresponds to a unique audio channel. E.g.
/// a node processing mono data would store one buffer, a node processing stereo data would
/// store two, and so on.
pub buffers: Vec<Buffer>,
pub buffers: Vec<Buffer<N>>,
pub node: T,
}

impl<G> Processor<G>
impl<G, const N: usize> Processor<G, N>
where
G: Visitable,
{
Expand Down Expand Up @@ -242,17 +242,17 @@ where
/// **Panics** if there is no node for the given index.
pub fn process<T>(&mut self, graph: &mut G, node: G::NodeId)
where
G: Data<NodeWeight = NodeData<T>> + DataMapMut,
G: Data<NodeWeight = NodeData<T, N>> + DataMapMut,
for<'a> &'a G: GraphBase<NodeId = G::NodeId> + IntoNeighborsDirected,
T: Node,
T: Node<N>,
{
process(self, graph, node)
}
}

impl<T> NodeData<T> {
impl<T, const N: usize> NodeData<T, N> {
/// Construct a new **NodeData** from an instance of its node type and buffers.
pub fn new(node: T, buffers: Vec<Buffer>) -> Self {
pub fn new(node: T, buffers: Vec<Buffer<N>>) -> Self {
NodeData { node, buffers }
}

Expand All @@ -268,27 +268,27 @@ impl<T> NodeData<T> {
}

#[cfg(feature = "node-boxed")]
impl NodeData<BoxedNode> {
impl<const N: usize> NodeData<BoxedNode<N>, N> {
/// The same as **new**, but boxes the given node data before storing it.
pub fn boxed<T>(node: T, buffers: Vec<Buffer>) -> Self
pub fn boxed<T>(node: T, buffers: Vec<Buffer<N>>) -> Self
where
T: 'static + Node,
T: 'static + Node<N>,
{
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<N>,
{
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<N>,
{
Self::boxed(node, vec![Buffer::SILENT, Buffer::SILENT])
}
Expand All @@ -310,17 +310,20 @@ impl NodeData<BoxedNode> {
/// 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,
pub fn process<G, T, const N: usize>(
processor: &mut Processor<G, N>,
graph: &mut G,
node: G::NodeId,
) where
G: Data<NodeWeight = NodeData<T, N>> + DataMapMut + Visitable,
for<'a> &'a G: GraphBase<NodeId = G::NodeId> + IntoNeighborsDirected,
T: Node,
T: Node<N>,
{
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 _;
let data: *mut NodeData<T, N> = graph.node_weight_mut(n).expect(NO_NODE) as *mut _;
processor.inputs.clear();
for in_n in (&*graph).neighbors_directed(n, Incoming) {
// Skip edges that connect the node to itself to avoid aliasing `node`.
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<const N: usize>(pub Box<dyn Node<N>>);

/// 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<const N: usize>(pub Box<dyn Node<N> + Send>);

impl BoxedNode {
impl<const N: usize> BoxedNode<N> {
/// 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<N>,
{
Self::from(Box::new(node))
}
}

impl BoxedNodeSend {
impl<const N: usize> BoxedNodeSend<N> {
/// 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<N> + Send,
{
Self::from(Box::new(node))
}
}

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

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

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

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

impl Into<Box<dyn Node>> for BoxedNode {
fn into(self) -> Box<dyn Node> {
impl<const N: usize> Into<Box<dyn Node<N>>> for BoxedNode<N> {
fn into(self) -> Box<dyn Node<N>> {
self.0
}
}

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

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

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

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

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

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

impl DerefMut for BoxedNodeSend {
impl<const N: usize> DerefMut for BoxedNodeSend<N> {
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, const N: usize> Node<N> 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<N>], output: &mut [Buffer<N>]) {
// Retrieve the single input, ignore any others.
let input = match inputs.get(0) {
Some(input) => input,
Expand Down
Loading