Improved documentation of MatrixCellChain

This commit is contained in:
Weird Constructor 2022-07-24 07:27:18 +02:00
parent eff41e7ad5
commit ed1ac581ce
5 changed files with 66 additions and 69 deletions

View file

@ -1,6 +1,28 @@
// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com> // Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
// This file is a part of HexoDSP. Released under GPL-3.0-or-later. // This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details. // 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 crate::{Cell, CellDir, Matrix, NodeId, ParamId, SAtom};
use std::collections::HashMap; use std::collections::HashMap;
@ -12,16 +34,16 @@ struct MatrixChainLink {
params: Vec<(ParamId, SAtom)>, 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 /// 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. /// by test cases to quickly fill the hexagonal Matrix.
/// ///
///``` ///```
/// use hexodsp::*; /// use hexodsp::*;
/// let mut chain = MatrixCellChain::new(CellDir::BR); /// let mut chain = MatrixCellChain::new(CellDir::BR);
/// chain.node_out("sin") /// chain.node_out("sin", "sig")
/// .set_denorm("freq", 220.0) /// .set_denorm("freq", 220.0)
/// .node_io("amp", "inp", "sig") /// .node_io("amp", "inp", "sig")
/// .set_denorm("att", 0.5) /// .set_denorm("att", 0.5)
@ -42,6 +64,7 @@ pub struct MatrixCellChain {
param_idx: usize, param_idx: usize,
} }
/// Error type for the [crate::MatrixCellChain].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ChainError { pub enum ChainError {
UnknownOutput(NodeId, String), UnknownOutput(NodeId, String),
@ -122,14 +145,14 @@ impl MatrixCellChain {
self 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 { pub fn spawn_cell_from_node_id_name(&mut self, node_id: &str) -> Cell {
let node_id = NodeId::from_str(node_id); let node_id = NodeId::from_str(node_id);
Cell::empty(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. /// This also sets the current parameter cell.
pub fn add_link(&mut self, cell: Cell) { pub fn add_link(&mut self, cell: Cell) {

View file

@ -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 (node_conf, mut node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3); let mut matrix = Matrix::new(node_conf, 3, 3);
let ad = NodeId::Ad(0); let mut chain = MatrixCellChain::new(CellDir::B);
let out = NodeId::Out(0); chain.node_out("ad", "sig")
matrix.place(0, 0, Cell::empty(ad).out(None, None, ad.out("sig"))); .node_inp("out", "ch1")
matrix.place(0, 1, Cell::empty(out).input(out.inp("ch1"), None, None)); .place(&mut matrix, 0, 0).unwrap();
matrix.sync().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); 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 Next you usually want to define short variable names for the [NodeId] that refer to the DSP
node instances: node instances:
```ignore ```ignore
let ad = NodeId::Ad(0); 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 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")`. 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 After you have setup everything for the test, you usually want to modify a paramter
and look at the values the graph returns. and look at the values the graph returns.
```ignore ```ignore
#[test] #[test]
fn check_node_ad_1() { fn check_node_ad_1() {
// ...
// matrix setup code above // matrix setup code above
// ...
let ad = NodeId::Ad(0);
// Fetch parameter id: // Fetch parameter id:
let trig_p = ad.inp_param("trig").unwrap(); let trig_p = ad.inp_param("trig").unwrap();

View file

@ -295,6 +295,7 @@ pub use matrix_repr::load_patch_from_file;
pub use matrix_repr::save_patch_to_file; pub use matrix_repr::save_patch_to_file;
pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor}; pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor};
pub use sample_lib::{SampleLibrary, SampleLoadError}; pub use sample_lib::{SampleLibrary, SampleLoadError};
pub use chain_builder::MatrixCellChain;
pub struct Context<'a, 'b, 'c, 'd> { pub struct Context<'a, 'b, 'c, 'd> {
pub nframes: usize, pub nframes: usize,

View file

@ -6,6 +6,7 @@ pub use hexodsp::dsp::*;
pub use hexodsp::matrix::*; pub use hexodsp::matrix::*;
pub use hexodsp::nodes::new_node_engine; pub use hexodsp::nodes::new_node_engine;
pub use hexodsp::NodeExecutor; pub use hexodsp::NodeExecutor;
pub use hexodsp::MatrixCellChain;
use hound; use hound;

View file

@ -10,8 +10,10 @@ fn check_node_ad_1() {
let (node_conf, mut node_exec) = new_node_engine(); let (node_conf, mut node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3); let mut matrix = Matrix::new(node_conf, 3, 3);
let mut chain = hexodsp::chain_builder::MatrixCellChain::new(CellDir::B); let mut chain = MatrixCellChain::new(CellDir::B);
chain.node_out("ad", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); chain.node_out("ad", "sig")
.node_inp("out", "ch1")
.place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap(); matrix.sync().unwrap();
let ad = NodeId::Ad(0); let ad = NodeId::Ad(0);