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::matrix_repr::*;
|
||||||
use crate::dsp::tracker::PatternData;
|
use crate::dsp::tracker::PatternData;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
/// This is a cell/tile of the hexagonal [Matrix].
|
/// This is a cell/tile of the hexagonal [Matrix].
|
||||||
///
|
///
|
||||||
|
@ -297,6 +297,11 @@ pub struct Matrix {
|
||||||
/// by [Matrix::sync] and [Matrix::check].
|
/// by [Matrix::sync] and [Matrix::check].
|
||||||
edges: Vec<Edge>,
|
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
|
/// Stores the [dsp::ParamId] of the inputs that have an output
|
||||||
/// assigned to them. It's updates when [Matrix::edges] is updated and used
|
/// 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
|
/// by [Matrix::param_input_is_used] to return whether a parameter is
|
||||||
|
@ -326,6 +331,7 @@ impl Matrix {
|
||||||
graph_ordering: NodeGraphOrdering::new(),
|
graph_ordering: NodeGraphOrdering::new(),
|
||||||
edges: Vec::with_capacity(MAX_ALLOCATED_NODES * 2),
|
edges: Vec::with_capacity(MAX_ALLOCATED_NODES * 2),
|
||||||
assigned_inputs: HashSet::new(),
|
assigned_inputs: HashSet::new(),
|
||||||
|
properties: HashMap::new(),
|
||||||
config,
|
config,
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
|
@ -455,6 +461,7 @@ impl Matrix {
|
||||||
self.matrix[x * self.h + y] = cell;
|
self.matrix[x * self.h + y] = cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears the contents of the matrix. It's completely empty after this.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
for cell in self.matrix.iter_mut() {
|
for cell in self.matrix.iter_mut() {
|
||||||
*cell = Cell::empty(NodeId::Nop);
|
*cell = Cell::empty(NodeId::Nop);
|
||||||
|
@ -464,12 +471,15 @@ impl Matrix {
|
||||||
self.edges.clear();
|
self.edges.clear();
|
||||||
self.assigned_inputs.clear();
|
self.assigned_inputs.clear();
|
||||||
self.saved_matrix = None;
|
self.saved_matrix = None;
|
||||||
|
self.properties.clear();
|
||||||
|
|
||||||
self.config.delete_nodes();
|
self.config.delete_nodes();
|
||||||
self.monitor_cell(Cell::empty(NodeId::Nop));
|
self.monitor_cell(Cell::empty(NodeId::Nop));
|
||||||
let _ = self.sync();
|
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) {
|
pub fn for_each_atom<F: FnMut(usize, ParamId, &SAtom, Option<f32>)>(&self, f: F) {
|
||||||
self.config.for_each_param(f);
|
self.config.for_each_param(f);
|
||||||
}
|
}
|
||||||
|
@ -533,11 +543,18 @@ impl Matrix {
|
||||||
tracker_id += 1;
|
tracker_id += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
self.properties
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.to_string(), v.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
MatrixRepr {
|
MatrixRepr {
|
||||||
cells,
|
cells,
|
||||||
params,
|
params,
|
||||||
atoms,
|
atoms,
|
||||||
patterns,
|
patterns,
|
||||||
|
properties,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,6 +570,10 @@ impl Matrix {
|
||||||
&repr.params[..],
|
&repr.params[..],
|
||||||
&repr.atoms[..]);
|
&repr.atoms[..]);
|
||||||
|
|
||||||
|
for (key, val) in repr.properties.iter() {
|
||||||
|
self.set_prop(key, val.clone());
|
||||||
|
}
|
||||||
|
|
||||||
for cell_repr in repr.cells.iter() {
|
for cell_repr in repr.cells.iter() {
|
||||||
let cell = Cell::from_repr(cell_repr);
|
let cell = Cell::from_repr(cell_repr);
|
||||||
self.place(cell.x as usize, cell.y as usize, cell);
|
self.place(cell.x as usize, cell.y as usize, cell);
|
||||||
|
@ -569,6 +590,38 @@ impl Matrix {
|
||||||
self.sync()
|
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`.
|
/// Receives the most recent data for the monitored signal at index `idx`.
|
||||||
/// Might introduce a short wait, because internally a mutex is still locked.
|
/// 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
|
/// 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 params: Vec<(ParamId, f32, Option<f32>)>,
|
||||||
pub atoms: Vec<(ParamId, SAtom)>,
|
pub atoms: Vec<(ParamId, SAtom)>,
|
||||||
pub patterns: Vec<Option<PatternRepr>>,
|
pub patterns: Vec<Option<PatternRepr>>,
|
||||||
|
pub properties: Vec<(String, SAtom)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -231,16 +232,18 @@ fn serialize_atom(atom: &SAtom) -> Value {
|
||||||
|
|
||||||
impl MatrixRepr {
|
impl MatrixRepr {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
let cells = vec![];
|
let cells = vec![];
|
||||||
let params = vec![];
|
let params = vec![];
|
||||||
let atoms = vec![];
|
let atoms = vec![];
|
||||||
let patterns = vec![];
|
let patterns = vec![];
|
||||||
|
let properties = vec![];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
cells,
|
cells,
|
||||||
params,
|
params,
|
||||||
atoms,
|
atoms,
|
||||||
patterns,
|
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"];
|
let patterns = &v["patterns"];
|
||||||
if let Value::Array(patterns) = patterns {
|
if let Value::Array(patterns) = patterns {
|
||||||
for p in patterns.iter() {
|
for p in patterns.iter() {
|
||||||
|
@ -355,6 +367,7 @@ impl MatrixRepr {
|
||||||
"VERSION": 1,
|
"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.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());
|
self.atoms.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
||||||
|
|
||||||
|
@ -395,6 +408,15 @@ impl MatrixRepr {
|
||||||
|
|
||||||
v["atoms"] = atoms;
|
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!([]);
|
let mut cells = json!([]);
|
||||||
if let Value::Array(cells) = &mut cells {
|
if let Value::Array(cells) = &mut cells {
|
||||||
for cell in self.cells.iter() {
|
for cell in self.cells.iter() {
|
||||||
|
@ -447,7 +469,7 @@ mod tests {
|
||||||
let s = matrix_repr.serialize();
|
let s = matrix_repr.serialize();
|
||||||
|
|
||||||
assert_eq!(s,
|
assert_eq!(s,
|
||||||
"{\"VERSION\":1,\"atoms\":[],\"cells\":[],\"params\":[],\"patterns\":[]}");
|
"{\"VERSION\":1,\"atoms\":[],\"cells\":[],\"params\":[],\"patterns\":[],\"props\":[]}");
|
||||||
assert!(MatrixRepr::deserialize(&s).is_ok());
|
assert!(MatrixRepr::deserialize(&s).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,7 +499,7 @@ mod tests {
|
||||||
let s = mr.serialize();
|
let s = mr.serialize();
|
||||||
|
|
||||||
assert_eq!(s,
|
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();
|
let mut mr2 = MatrixRepr::deserialize(&s).unwrap();
|
||||||
|
|
||||||
|
@ -660,7 +682,7 @@ mod tests {
|
||||||
fn check_matrix_repr_mod_amt_1() {
|
fn check_matrix_repr_mod_amt_1() {
|
||||||
use crate::nodes::new_node_engine;
|
use crate::nodes::new_node_engine;
|
||||||
|
|
||||||
let mr = {
|
let s = {
|
||||||
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);
|
||||||
|
|
||||||
|
@ -680,13 +702,15 @@ mod tests {
|
||||||
matrix.set_param_modamt(
|
matrix.set_param_modamt(
|
||||||
sin.inp_param("freq").unwrap(), Some(0.6)).unwrap();
|
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 (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 mr = MatrixRepr::deserialize(&s).unwrap();
|
||||||
matrix.from_repr(&mr).unwrap();
|
matrix.from_repr(&mr).unwrap();
|
||||||
|
|
||||||
let mut v = std::collections::HashMap::new();
|
let mut v = std::collections::HashMap::new();
|
||||||
|
@ -698,4 +722,29 @@ mod tests {
|
||||||
assert_eq!(*v.get("det").unwrap(), Some(-0.6));
|
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
|
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