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>
// 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) {

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 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();

View file

@ -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,

View file

@ -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;

View file

@ -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);