pub struct Matrix {
    config: NodeConfigurator,
    matrix: Vec<Cell>,
    w: usize,
    h: usize,
    graph_ordering: NodeGraphOrdering,
    saved_matrix: Option<Vec<Cell>>,
    edges: Vec<Edge>,
    properties: HashMap<String, SAtom>,
    assigned_inputs: HashSet<ParamId>,
    monitored_cell: Cell,
    gen_counter: usize,
    observer: Option<Arc<dyn MatrixObserver>>,
}

Fields

config: NodeConfigurator

The node configurator to control the backend.

matrix: Vec<Cell>

Holds the actual 2 dimensional matrix cells in one big vector.

w: usize

Width of the matrix.

h: usize

Height of the matrix.

graph_ordering: NodeGraphOrdering

The retained data structure of the graph topology. This is used by sync() and check() to determine the order and cycle freeness of the graph. We store it in this field, so we don’t have to reallocate it all the time.

saved_matrix: Option<Vec<Cell>>

Holds a saved version of the matrix field to roll back changes that might introduce cycles or other invalid topology.

edges: Vec<Edge>

Stores the edges which are extracted from the matrix field by Matrix::update_graph_ordering_and_edges, which is used by Matrix::sync and Matrix::check.

properties: HashMap<String, SAtom>

Holds custom user defined properties. They are saved with the MatrixRepr and you can set and retrieve these properties using Matrix::set_prop and Matrix::get_prop.

assigned_inputs: HashSet<ParamId>

Stores the crate::dsp::ParamId of the inputs that have an output assigned to them. It’s updates when Matrix::edges is updated and used by Matrix::param_input_is_used to return whether a parameter is controlled by some output port.

monitored_cell: Cell

Holds the currently monitored cell.

gen_counter: usize

A counter that increases for each sync(), it can be used by other components of the application to detect changes in the matrix to resync their own data.

observer: Option<Arc<dyn MatrixObserver>>

A trait object that tracks changed on the Matrix.

Implementations

Assigns the MatrixObserver to observe changes on the Matrix.

Returns the PatternData handle for the tracker tracker_id. Implicitly allocates the [Tracker] instance.

Returns true if the tracker or pattern data for tracker_id has been allocated/used yet.

Retrieve the oscilloscope handle for the scope index scope.

Checks if there are any updates to send for the pattern data that belongs to the tracker tracker_id. Call this repeatedly, eg. once per frame in a GUI, in case the user modified the pattern data. It will make sure that the modifications are sent to the audio thread.

Checks the block function for the id id. If the block function did change, updates are then sent to the audio thread. See also Matrix::get_block_function.

Returns true if the code engine or block function for tracker_id has been allocated/used yet.

Retrieve a handle to the block function id. In case you modify the block function, make sure to call Matrix::check_block_function.

Saves the state of the hexagonal grid layout. This is usually used together with Matrix::check and Matrix::restore_matrix to try if changes on the matrix using Matrix::place (or other grid changing functions).

It is advised to use convenience functions such as Matrix::change_matrix.

See also Matrix::change_matrix, Matrix::check and Matrix::sync.

Restores the previously via Matrix::save_matrix saved matrix.

It is advised to use convenience functions such as Matrix::change_matrix.

See also Matrix::change_matrix, Matrix::check.

Helps encapsulating changes of the matrix and wraps them into a Matrix::save_matrix, Matrix::check and Matrix::restore_matrix.

 use hexodsp::*;

 let (node_conf, mut node_exec) = new_node_engine();
 let mut matrix = Matrix::new(node_conf, 3, 3);

 let res = matrix.change_matrix(|matrix| {
     matrix.place(0, 1,
         Cell::empty(NodeId::Sin(1))
         .input(Some(0), None, None));
     matrix.place(0, 0,
         Cell::empty(NodeId::Sin(1))
         .out(None, None, Some(0)));
 });

 // In this examples case there is an error, as we created
 // a cycle:
 assert!(res.is_err());

Like Matrix::change_matrix but the function passed to this needs to return a Result<(), MatrixError>.

Tries to place all cells at once, if they are placed in empty cells only! Returns an error of the destination cell is not empty or out of range, or if the placement of the cluster results in any other inconsistencies.

This action must be wrapped with Matrix::change_matrix_err!

Restores the matrix to the previous state if placing fails.

Inserts a cell into the hexagonal grid of the matrix. You have to make sure that the resulting DSP graph topology does not have cycles, otherwise an upload to the DSP thread via Matrix::sync will fail.

If you try to place a cell outside the grid, it will not be placed and just silently ignored.

You can safely check the DSP topology of changes using the convenience function Matrix::change_matrix or alternatively: Matrix::save_matrix, Matrix::restore_matrix and Matrix::check.

See also the example in Matrix::change_matrix and Matrix::check.

Set the cell at it’s assigned position. This is basically a shorthand for Matrix::place. As if you would call: m.place(cell.pos().0, cell.pos().1, cell).

Clears the contents of the matrix. It’s completely empty after this.

Iterates through all atoms. This is useful for reading all the atoms after a MatrixRepr has been loaded with Matrix::from_repr.

Returns the DSP graph generation, which is increased after each call to Matrix::sync.

This can be used by external components to track if they should update their knowledge of the nodes in the DSP graph. Such as parameter values.

HexoSynth for instance updates the UI by tracking this value.

Returns a serializable representation of the matrix. This representation contains all parameters, created nodes, connections and the tracker’s pattern data.

 use hexodsp::*;

 let (node_conf, mut _node_exec) = new_node_engine();
 let mut matrix = Matrix::new(node_conf, 3, 3);

 let sin = NodeId::Sin(2);

 matrix.place(0, 0,
     Cell::empty(sin)
     .out(None, Some(0), None));

 let freq_param = sin.inp_param("freq").unwrap();
 matrix.set_param(freq_param, SAtom::param(-0.1));

 let mut serialized = matrix.to_repr().serialize().to_string();

 assert!(serialized.find("\"sin\",2,0,0,[-1,-1,-1],[-1,\"sig\",-1]").is_some());
 assert!(serialized.find("\"freq\",220.0").is_some());

See also MatrixRepr::serialize.

Loads the matrix from a previously my Matrix::to_repr generated matrix representation.

This function will call Matrix::sync after loading and overwriting the current matrix contents.

Saves a property in the matrix, these can be retrieved using Matrix::get_prop and are saved/loaded along with the MatrixRepr. See also Matrix::to_repr and Matrix::from_repr.

 use hexodsp::*;

 let repr = {
     let (node_conf, mut _node_exec) = new_node_engine();
     let mut matrix = Matrix::new(node_conf, 3, 3);

     matrix.set_prop("test", SAtom::setting(31337));

     matrix.to_repr()
 };

 let (node_conf, mut _node_exec) = new_node_engine();
 let mut matrix2 = Matrix::new(node_conf, 3, 3);

 matrix2.from_repr(&repr).unwrap();
 assert_eq!(matrix2.get_prop("test").unwrap().i(), 31337);

Retrieves a matrix property. See also Matrix::set_prop for an example and more information.

Receives the most recent data for the monitored signal at index idx. Might introduce a short wait, because internally a mutex is still locked. If this leads to stuttering in the UI, we need to change the internal handling to a triple buffer.

Returns the currently monitored cell.

Sets the cell to monitor next. Please bear in mind, that you need to call sync before retrieving the cell from the matrix, otherwise the node instance might not have been created in the backend yet and we can not start monitoring the cell.

Is called by Matrix::sync to refresh the monitored cell. In case the matrix has changed (inputs/outputs of a cell) we show the current state.

Note, that if the UI actually moved a cell, it needs to monitor the newly moved cell anyways.

Retrieve SAtom values for input parameters and atoms.

Assign SAtom values to input parameters and atoms.

Retrieve the modulation amount of the input parameter.

Assign or remove modulation of an input parameter.

Retrieves the immediate connections to adjacent cells and returns a list. Returns none if there is no cell at the given position.

Returns a vector with pairs of this content:

( (center_cell, center_connection_dir, center_node_io_index), ( other_cell, other_connection_dir, other__node_io_index, (other_cell_x, other_cell_y) ) )

Compiles a NodeProg from the data collected by the previous call to Matrix::update_graph_ordering_and_edges.

May return an error if the graph topology is invalid (cycles) or something else happened.

Checks the topology of the DSP graph represented by the hexagonal matrix.

Use Matrix::save_matrix and Matrix::restore_matrix for trying out changes before committing them to the DSP thread using Matrix::sync.

Note that there is a convenience function with Matrix::change_matrix to make it easier to test and rollback changes if they are faulty.

 use hexodsp::*;

 let (node_conf, mut node_exec) = new_node_engine();
 let mut matrix = Matrix::new(node_conf, 3, 3);

 matrix.save_matrix();

 // ...
 matrix.place(0, 1,
     Cell::empty(NodeId::Sin(1))
     .input(Some(0), None, None));
 matrix.place(0, 0,
     Cell::empty(NodeId::Sin(1))
     .out(None, None, Some(0)));
 // ...

 let error =
     if let Err(_) = matrix.check() {
        matrix.restore_matrix();
        true
     } else {
        matrix.sync().unwrap();
        false
     };

 // In this examples case there is an error, as we created
 // a cycle:
 assert!(error);

Synchronizes the matrix with the DSP thread. Call this everytime you changed any of the matrix Cells eg. with Matrix::place and want to publish the changes to the DSP thread.

This method might return an error, for instance if the DSP graph topology contains cycles or has other errors.

You can check any changes and roll them back using the method Matrix::change_matrix.

Retrieves the output port feedback for a specific output of the given NodeId.

See also NodeConfigurator::out_fb_for.

Updates the output port feedback. Call this every UI frame or whenever you want to get the most recent values from Matrix::out_fb_for.

See also NodeConfigurator::update_output_feedback.

Updates the dynamic node for the Rust1x1 nodes. The index refers to the instance NodeId::Rust1x1(index).

See also crate::SynthConstructor::set_dynamic_node1x1.

Injects a HxMidiEvent directly into audio thread, so that it can trickle back to the GUI thread the standard way. This is mostly used for automated testing. And maybe some day for some kind of remote control script from WLambda?

Handles events from the DSP graph. Such as MIDI events for MIDI learn functionality! Call this regularily (every UI frame) if you want to have MIDI learn to work and receive events such as MIDI events via the MatrixObserver.

Trait Implementations

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.