implemented properties that can be used to store GUI related state in the patch
This commit is contained in:
parent
5a75e45f3f
commit
f6223548c8
4 changed files with 137 additions and 9 deletions
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
@ -231,16 +232,18 @@ fn serialize_atom(atom: &SAtom) -> Value {
|
|||
|
||||
impl MatrixRepr {
|
||||
pub fn empty() -> Self {
|
||||
let cells = vec![];
|
||||
let params = vec![];
|
||||
let atoms = vec![];
|
||||
let patterns = vec![];
|
||||
let cells = vec![];
|
||||
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
BIN
tests/hr.wav
Normal file
Binary file not shown.
|
@ -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)
|
||||
]);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue