diff --git a/src/chain_builder.rs b/src/chain_builder.rs index 88b6e0c..f1c10c5 100644 --- a/src/chain_builder.rs +++ b/src/chain_builder.rs @@ -1,6 +1,28 @@ // Copyright (c) 2021-2022 Weird Constructor // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. +/*! Defines an API for easy DSP chain building with the hexagonal [crate::Matrix]. + +The [crate::MatrixCellChain] abstractions allows very easy placement of DSP signal chains: + +``` + use hexodsp::*; + let mut chain = MatrixCellChain::new(CellDir::BR); + chain.node_out("sin", "sig") + .set_denorm("freq", 220.0) + .node_io("amp", "inp", "sig") + .set_denorm("att", 0.5) + .node_inp("out", "ch1"); + + // use crate::nodes::new_node_engine; + let (node_conf, _node_exec) = new_node_engine(); + let mut matrix = Matrix::new(node_conf, 7, 7); + + chain.place(&mut matrix, 2, 2).expect("no error in this case"); +``` +*/ + + use crate::{Cell, CellDir, Matrix, NodeId, ParamId, SAtom}; use std::collections::HashMap; @@ -12,16 +34,16 @@ struct MatrixChainLink { params: Vec<(ParamId, SAtom)>, } -/// A DSP chain builder for the [hexodsp::Matrix]. +/// A DSP chain builder for the [crate::Matrix]. /// -/// This is an extremely easy API to create and place new DSP chains into the [hexodsp::Matrix]. +/// This is an extremely easy API to create and place new DSP chains into the [crate::Matrix]. /// It can be used by frontends to place DSP chains on user request or it can be used /// by test cases to quickly fill the hexagonal Matrix. /// ///``` /// use hexodsp::*; /// let mut chain = MatrixCellChain::new(CellDir::BR); -/// chain.node_out("sin") +/// chain.node_out("sin", "sig") /// .set_denorm("freq", 220.0) /// .node_io("amp", "inp", "sig") /// .set_denorm("att", 0.5) @@ -42,6 +64,7 @@ pub struct MatrixCellChain { param_idx: usize, } +/// Error type for the [crate::MatrixCellChain]. #[derive(Debug, Clone)] pub enum ChainError { UnknownOutput(NodeId, String), @@ -122,14 +145,14 @@ impl MatrixCellChain { self } - /// Utility function for creating [hexodsp::Cell] for this chain. + /// Utility function for creating [crate::Cell] for this chain. pub fn spawn_cell_from_node_id_name(&mut self, node_id: &str) -> Cell { let node_id = NodeId::from_str(node_id); Cell::empty(node_id) } - /// Utility function to add a pre-built [hexodsp::Cell] as next link. + /// Utility function to add a pre-built [crate::Cell] as next link. /// /// This also sets the current parameter cell. pub fn add_link(&mut self, cell: Cell) { diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index f0a671b..f079960 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -366,12 +366,13 @@ The start of your `tests/node_*.rs` file usually should look like this: let (node_conf, mut node_exec) = new_node_engine(); let mut matrix = Matrix::new(node_conf, 3, 3); - let ad = NodeId::Ad(0); - let out = NodeId::Out(0); - matrix.place(0, 0, Cell::empty(ad).out(None, None, ad.out("sig"))); - matrix.place(0, 1, Cell::empty(out).input(out.inp("ch1"), None, None)); + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("ad", "sig") + .node_inp("out", "ch1") + .place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); + let ad = NodeId::Ad(0); // ... } ``` @@ -393,81 +394,50 @@ The two parameters to _new_ are the width and height of the hex grid. let mut matrix = Matrix::new(node_conf, 3, 3); ``` +Next step is to create a DSP chain of nodes and place that onto the hexagonal matrix. +Luckily a simpler API has been created with the [crate::MatrixCellChain], that lets +you build DSP chains on the fly using only names of the nodes and the corresponding +input/output ports: + +```ignore + // Create a new cell chain that points in to the given direction (CellDir::B => to bottom). + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("ad", "sig") // Add a Node::Ad(0) cell, with the "sig" output set + .node_inp("out", "ch1") // Add a Node::Out(0) cell, with the "ch1" input set + .place(&mut matrix, 0, 0).unwrap(); +``` + +After placing the new cells, we need to synchronize it with the audio backend: + +```ignore + matrix.sync().unwrap(); +``` + +The `sync` is necessary to update the DSP graph. + Next you usually want to define short variable names for the [NodeId] that refer to the DSP node instances: ```ignore let ad = NodeId::Ad(0); - let out = NodeId::Out(0); -``` - -You can have multiple instances for a node. The number in the parenthesis are -the instance index of that node. - -Next you want to layout the nodes adjacent to each other on the hexagonal grid. -This is a bit more tricky than with a rectangular grid. - -```ignore - matrix.place(0, 0, Cell::empty(ad).out(None, None, ad.out("sig"))); - matrix.place(0, 1, Cell::empty(out).input(out.inp("ch1"), None, None)); - matrix.sync().unwrap(); -``` - -The `sync` is necessary to update the DSP graph. -When doing this, keep the following grid layout in mind: - -```text - _____ _____ - / \ / \ - / 0,0 \_____/ 2,0 \_____ - \ / \ / \ - \_____/ 1,0 \_____/ 3,0 \ - / \ / \ / - / 0,1 \_____/ 2,1 \_____/ - \ / \ / \ - \_____/ 1,1 \_____/ 3,1 \ - / \ / \ / - / 0,2 \_____/ 2,2 \_____/ - \ / \ / - \_____/ 1,2 \_____/ - \ / - \_____/ -``` - -Defining the outputs of a cell is done like this: - -```ignore - Cell::empty(ad).out(None, None, ad.out("sig")) -``` - -[crate::Cell::empty] takes a [NodeId] as first argument. The [crate::Cell] -structure then allows you to specify the output ports using the [crate::Cell::out] -function. The 3 arguments of that function are for the 3 edges of that hex tile: - -```ignore - // TopRight BottomRight Bottom - Cell::empty(ad).out(None, None, ad.out("sig")) -``` - -[crate::Cell::input] works the same way, but the 3 arguments refer to the 3 input -edges of a hex tile: - -```ignore - // Top TopLeft BottomLeft - Cell::empty(out).input(out.inp("ch1"), None, None) ``` The [NodeId] interface offers you functions to get the input parameter index from a name like `out.inp("ch1")` or the output port index from a name: `ad.out("sig")`. +You can have multiple instances for a node. The number in the parenthesis are +the instance index of that node. After you have setup everything for the test, you usually want to modify a paramter and look at the values the graph returns. - ```ignore #[test] fn check_node_ad_1() { + // ... // matrix setup code above + // ... + + let ad = NodeId::Ad(0); // Fetch parameter id: let trig_p = ad.inp_param("trig").unwrap(); diff --git a/src/lib.rs b/src/lib.rs index ed6096d..7704886 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,6 +295,7 @@ pub use matrix_repr::load_patch_from_file; pub use matrix_repr::save_patch_to_file; pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor}; pub use sample_lib::{SampleLibrary, SampleLoadError}; +pub use chain_builder::MatrixCellChain; pub struct Context<'a, 'b, 'c, 'd> { pub nframes: usize, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 0b1a3af..d6ff59b 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -6,6 +6,7 @@ pub use hexodsp::dsp::*; pub use hexodsp::matrix::*; pub use hexodsp::nodes::new_node_engine; pub use hexodsp::NodeExecutor; +pub use hexodsp::MatrixCellChain; use hound; diff --git a/tests/node_ad.rs b/tests/node_ad.rs index 0c5cf68..ba1e3d8 100644 --- a/tests/node_ad.rs +++ b/tests/node_ad.rs @@ -10,8 +10,10 @@ fn check_node_ad_1() { let (node_conf, mut node_exec) = new_node_engine(); let mut matrix = Matrix::new(node_conf, 3, 3); - let mut chain = hexodsp::chain_builder::MatrixCellChain::new(CellDir::B); - chain.node_out("ad", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("ad", "sig") + .node_inp("out", "ch1") + .place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); let ad = NodeId::Ad(0);