implemented properties that can be used to store GUI related state in the patch

This commit is contained in:
Weird Constructor 2021-07-17 20:12:32 +02:00
parent 5a75e45f3f
commit f6223548c8
4 changed files with 137 additions and 9 deletions

View file

@ -15,7 +15,7 @@ pub use crate::monitor::MON_SIG_CNT;
use crate::matrix_repr::*;
use crate::dsp::tracker::PatternData;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
/// This is a cell/tile of the hexagonal [Matrix].
///
@ -297,6 +297,11 @@ pub struct Matrix {
/// by [Matrix::sync] and [Matrix::check].
edges: Vec<Edge>,
/// 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].
properties: HashMap<String, SAtom>,
/// Stores the [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
@ -326,6 +331,7 @@ impl Matrix {
graph_ordering: NodeGraphOrdering::new(),
edges: Vec::with_capacity(MAX_ALLOCATED_NODES * 2),
assigned_inputs: HashSet::new(),
properties: HashMap::new(),
config,
w,
h,
@ -455,6 +461,7 @@ impl Matrix {
self.matrix[x * self.h + y] = cell;
}
/// Clears the contents of the matrix. It's completely empty after this.
pub fn clear(&mut self) {
for cell in self.matrix.iter_mut() {
*cell = Cell::empty(NodeId::Nop);
@ -464,12 +471,15 @@ impl Matrix {
self.edges.clear();
self.assigned_inputs.clear();
self.saved_matrix = None;
self.properties.clear();
self.config.delete_nodes();
self.monitor_cell(Cell::empty(NodeId::Nop));
let _ = self.sync();
}
/// Iterates through all atoms. This is useful for reading
/// the atoms after a [MatrixRepr] has been loaded with [Matrix::from_repr].
pub fn for_each_atom<F: FnMut(usize, ParamId, &SAtom, Option<f32>)>(&self, f: F) {
self.config.for_each_param(f);
}
@ -533,11 +543,18 @@ impl Matrix {
tracker_id += 1;
}
let properties =
self.properties
.iter()
.map(|(k, v)| (k.to_string(), v.clone()))
.collect();
MatrixRepr {
cells,
params,
atoms,
patterns,
properties,
}
}
@ -553,6 +570,10 @@ impl Matrix {
&repr.params[..],
&repr.atoms[..]);
for (key, val) in repr.properties.iter() {
self.set_prop(key, val.clone());
}
for cell_repr in repr.cells.iter() {
let cell = Cell::from_repr(cell_repr);
self.place(cell.x as usize, cell.y as usize, cell);
@ -569,6 +590,38 @@ impl Matrix {
self.sync()
}
/// 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);
///```
pub fn set_prop(&mut self, key: &str, val: SAtom) {
self.properties.insert(key.to_string(), val);
}
/// Retrieves a matrix property. See also [Matrix::set_prop] for an
/// example and more information.
pub fn get_prop(&mut self, key: &str) -> Option<&SAtom> {
self.properties.get(key)
}
/// 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

View file

@ -145,6 +145,7 @@ pub struct MatrixRepr {
pub params: Vec<(ParamId, f32, Option<f32>)>,
pub atoms: Vec<(ParamId, SAtom)>,
pub patterns: Vec<Option<PatternRepr>>,
pub properties: Vec<(String, SAtom)>,
}
#[derive(Debug, Clone)]
@ -235,12 +236,14 @@ impl MatrixRepr {
let params = vec![];
let atoms = vec![];
let patterns = vec![];
let properties = vec![];
Self {
cells,
params,
atoms,
patterns,
properties,
}
}
@ -337,6 +340,15 @@ impl MatrixRepr {
}
}
let props = &v["props"];
if let Value::Array(props) = props {
for v in props.iter() {
let key = v[0].as_str().unwrap_or("");
m.properties.push(
(key.to_string(), deserialize_atom(&v[1])?));
}
}
let patterns = &v["patterns"];
if let Value::Array(patterns) = patterns {
for p in patterns.iter() {
@ -355,6 +367,7 @@ impl MatrixRepr {
"VERSION": 1,
});
self.properties.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
self.params.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
self.atoms.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
@ -395,6 +408,15 @@ impl MatrixRepr {
v["atoms"] = atoms;
let mut props = json!([]);
if let Value::Array(props) = &mut props {
for (k, v) in self.properties.iter() {
props.push(json!([k, serialize_atom(v)]));
}
}
v["props"] = props;
let mut cells = json!([]);
if let Value::Array(cells) = &mut cells {
for cell in self.cells.iter() {
@ -447,7 +469,7 @@ mod tests {
let s = matrix_repr.serialize();
assert_eq!(s,
"{\"VERSION\":1,\"atoms\":[],\"cells\":[],\"params\":[],\"patterns\":[]}");
"{\"VERSION\":1,\"atoms\":[],\"cells\":[],\"params\":[],\"patterns\":[],\"props\":[]}");
assert!(MatrixRepr::deserialize(&s).is_ok());
}
@ -477,7 +499,7 @@ mod tests {
let s = mr.serialize();
assert_eq!(s,
"{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,0,[-1,-1,-1],[-1,0,-1]],[\"out\",0,1,0,[-1,0,-1],[-1,-1,0]]],\"params\":[[\"out\",0,\"ch1\",0.0],[\"out\",0,\"ch2\",0.0],[\"sin\",0,\"det\",0.0],[\"sin\",1,\"det\",0.0],[\"sin\",2,\"det\",0.0],[\"sin\",0,\"freq\",0.0],[\"sin\",1,\"freq\",0.0],[\"sin\",2,\"freq\",-0.10000000149011612],[\"out\",0,\"gain\",0.5]],\"patterns\":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}");
"{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,0,[-1,-1,-1],[-1,0,-1]],[\"out\",0,1,0,[-1,0,-1],[-1,-1,0]]],\"params\":[[\"out\",0,\"ch1\",0.0],[\"out\",0,\"ch2\",0.0],[\"sin\",0,\"det\",0.0],[\"sin\",1,\"det\",0.0],[\"sin\",2,\"det\",0.0],[\"sin\",0,\"freq\",0.0],[\"sin\",1,\"freq\",0.0],[\"sin\",2,\"freq\",-0.10000000149011612],[\"out\",0,\"gain\",0.5]],\"patterns\":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],\"props\":[]}");
let mut mr2 = MatrixRepr::deserialize(&s).unwrap();
@ -660,7 +682,7 @@ mod tests {
fn check_matrix_repr_mod_amt_1() {
use crate::nodes::new_node_engine;
let mr = {
let s = {
let (node_conf, mut _node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
@ -680,13 +702,15 @@ mod tests {
matrix.set_param_modamt(
sin.inp_param("freq").unwrap(), Some(0.6)).unwrap();
matrix.to_repr()
let mut mr = matrix.to_repr();
mr.serialize().to_string()
};
{
let (node_conf, mut _node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
let mr = MatrixRepr::deserialize(&s).unwrap();
matrix.from_repr(&mr).unwrap();
let mut v = std::collections::HashMap::new();
@ -698,4 +722,29 @@ mod tests {
assert_eq!(*v.get("det").unwrap(), Some(-0.6));
}
}
#[test]
fn check_matrix_repr_properties() {
use crate::nodes::new_node_engine;
let s = {
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));
let mut mr = matrix.to_repr();
mr.serialize().to_string()
};
{
let (node_conf, mut _node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
let mr = MatrixRepr::deserialize(&s).unwrap();
matrix.from_repr(&mr).unwrap();
assert_eq!(matrix.get_prop("test").unwrap().i(), 31337);
}
}
}

BIN
tests/hr.wav Normal file

Binary file not shown.

View file

@ -627,3 +627,29 @@ fn check_node_sampl_rev_2() {
0.6802875, 0.79366875, 0.90705
]);
}
#[test]
fn check_node_sampl_4() {
let (node_conf, mut node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
let smpl = NodeId::Sampl(0);
let out = NodeId::Out(0);
matrix.place(0, 0, Cell::empty(smpl)
.out(None, None, smpl.out("sig")));
matrix.place(0, 1, Cell::empty(out)
.input(out.inp("ch1"), None, None));
matrix.sync().unwrap();
let sample_p = smpl.inp_param("sample").unwrap();
matrix.set_param(sample_p, SAtom::audio_unloaded("hr.wav"));
let fft = run_and_get_fft4096(&mut node_exec, 0, 1000.0);
assert_eq!(
avg_fft_freqs(4.0,
&[10, 100, 200, 300, 440, 800, 1000, 2000, 3000, 4000, 12000, 22050],
&fft[..]), vec![
(0, 32), (10, 204), (100, 104), (200, 16), (300, 32), (440, 8),
(800, 4), (1000, 0), (2000, 0), (3000, 0), (4000, 0), (12000, 0)
]);
}