650 lines
20 KiB
Rust
650 lines
20 KiB
Rust
// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
|
|
// This is a part of HexoDSP. Released under (A)GPLv3 or any later.
|
|
// See README.md and COPYING for details.
|
|
|
|
use crate::dsp::{NodeId, ParamId, SAtom};
|
|
use serde_json::{Value, json};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct CellRepr {
|
|
pub node_id: NodeId,
|
|
pub x: usize,
|
|
pub y: usize,
|
|
pub inp: [i16; 3],
|
|
pub out: [i16; 3],
|
|
}
|
|
|
|
fn deserialize_node_id(v: &Value, i1: usize, i2: usize)
|
|
-> Result<NodeId, MatrixDeserError>
|
|
{
|
|
let nid = NodeId::from_str(v[i1].as_str().unwrap_or("???"));
|
|
if nid == NodeId::Nop {
|
|
return Err(
|
|
MatrixDeserError::UnknownNode(
|
|
v[i1].as_str().unwrap_or("???").to_string()));
|
|
}
|
|
|
|
Ok(nid.to_instance(v[i2].as_i64().unwrap_or(0) as usize))
|
|
}
|
|
|
|
impl CellRepr {
|
|
pub fn serialize(&self) -> Value {
|
|
json!([
|
|
self.node_id.name(),
|
|
self.node_id.instance(),
|
|
self.x,
|
|
self.y,
|
|
[self.inp[0], self.inp[1], self.inp[2]],
|
|
[self.out[0], self.out[1], self.out[2]],
|
|
])
|
|
}
|
|
|
|
pub fn deserialize(v: &Value) -> Result<Self, MatrixDeserError> {
|
|
Ok(Self {
|
|
node_id: deserialize_node_id(v, 0, 1)?,
|
|
x: v[2].as_i64().unwrap_or(0) as usize,
|
|
y: v[3].as_i64().unwrap_or(0) as usize,
|
|
inp: [
|
|
v[4][0].as_i64().unwrap_or(-1) as i16,
|
|
v[4][1].as_i64().unwrap_or(-1) as i16,
|
|
v[4][2].as_i64().unwrap_or(-1) as i16,
|
|
],
|
|
out: [
|
|
v[5][0].as_i64().unwrap_or(-1) as i16,
|
|
v[5][1].as_i64().unwrap_or(-1) as i16,
|
|
v[5][2].as_i64().unwrap_or(-1) as i16,
|
|
],
|
|
})
|
|
}
|
|
}
|
|
|
|
use crate::dsp::tracker::{MAX_PATTERN_LEN, MAX_COLS};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PatternRepr {
|
|
pub col_types: [u8; MAX_COLS],
|
|
pub data: Vec<Vec<i32>>,
|
|
pub rows: usize,
|
|
pub edit_step: usize,
|
|
pub cursor: (usize, usize),
|
|
}
|
|
|
|
impl PatternRepr {
|
|
fn serialize(&self) -> Value {
|
|
let mut ret = json!({
|
|
"rows": self.rows,
|
|
"edit_step": self.edit_step,
|
|
"cursor_row": self.cursor.0,
|
|
"cursor_col": self.cursor.1,
|
|
});
|
|
|
|
let mut cts = json!([]);
|
|
if let Value::Array(cts) = &mut cts {
|
|
for ct in self.col_types.iter() {
|
|
cts.push(json!(*ct as i64));
|
|
}
|
|
}
|
|
ret["col_types"] = cts;
|
|
|
|
let mut data = json!([]);
|
|
if let Value::Array(data) = &mut data {
|
|
for row in self.data.iter() {
|
|
let mut out_col = json!([]);
|
|
if let Value::Array(out_col) = &mut out_col {
|
|
for col in row.iter() {
|
|
out_col.push(json!(*col as i64));
|
|
}
|
|
}
|
|
data.push(out_col);
|
|
}
|
|
}
|
|
ret["data"] = data;
|
|
|
|
ret
|
|
}
|
|
|
|
fn deserialize(v: &Value) -> Result<Self, MatrixDeserError> {
|
|
let mut col_types = [0; MAX_COLS];
|
|
|
|
let cts = &v["col_types"];
|
|
if let Value::Array(cts) = cts {
|
|
for (i, ct) in cts.iter().enumerate() {
|
|
col_types[i] = ct.as_i64().unwrap_or(0) as u8;
|
|
}
|
|
}
|
|
|
|
let mut data = vec![vec![-1; MAX_COLS]; MAX_PATTERN_LEN];
|
|
let dt = &v["data"];
|
|
if let Value::Array(dt) = dt {
|
|
for (row_idx, row) in dt.iter().enumerate() {
|
|
if let Value::Array(row) = row {
|
|
for (col_idx, c) in row.iter().enumerate() {
|
|
data[row_idx][col_idx] = c.as_i64().unwrap_or(-1) as i32;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(Self {
|
|
col_types,
|
|
data,
|
|
rows: v["rows"] .as_i64().unwrap_or(0) as usize,
|
|
edit_step: v["edit_step"].as_i64().unwrap_or(0) as usize,
|
|
cursor: (
|
|
v["cursor_row"].as_i64().unwrap_or(0) as usize,
|
|
v["cursor_col"].as_i64().unwrap_or(0) as usize
|
|
),
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MatrixRepr {
|
|
pub cells: Vec<CellRepr>,
|
|
pub params: Vec<(ParamId, f32)>,
|
|
pub atoms: Vec<(ParamId, SAtom)>,
|
|
pub patterns: Vec<Option<PatternRepr>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum MatrixDeserError {
|
|
BadVersion,
|
|
UnknownNode(String),
|
|
UnknownParamId(String),
|
|
Deserialization(String),
|
|
IO(String),
|
|
InvalidAtom(String),
|
|
MatrixError(crate::matrix::MatrixError),
|
|
}
|
|
|
|
impl From<crate::matrix::MatrixError> for MatrixDeserError {
|
|
fn from(err: crate::matrix::MatrixError) -> Self {
|
|
MatrixDeserError::MatrixError(err)
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for MatrixDeserError {
|
|
fn from(err: serde_json::Error) -> MatrixDeserError {
|
|
MatrixDeserError::Deserialization(format!("{}", err))
|
|
}
|
|
}
|
|
|
|
impl From<std::str::Utf8Error> for MatrixDeserError {
|
|
fn from(err: std::str::Utf8Error) -> MatrixDeserError {
|
|
MatrixDeserError::Deserialization(format!("{}", err))
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for MatrixDeserError {
|
|
fn from(err: std::io::Error) -> MatrixDeserError {
|
|
MatrixDeserError::IO(format!("{}", err))
|
|
}
|
|
}
|
|
|
|
fn deserialize_atom(v: &Value) -> Result<SAtom, MatrixDeserError> {
|
|
match v[0].as_str().unwrap_or("?") {
|
|
"i" => {
|
|
if let Some(v) = v[1].as_i64() { Ok(SAtom::setting(v)) }
|
|
else { Err(MatrixDeserError::InvalidAtom(v.to_string())) }
|
|
},
|
|
"p" => {
|
|
if let Some(v) = v[1].as_f64() { Ok(SAtom::param(v as f32)) }
|
|
else { Err(MatrixDeserError::InvalidAtom(v.to_string())) }
|
|
},
|
|
"s" => {
|
|
if let Some(v) = v[1].as_str() { Ok(SAtom::str(v)) }
|
|
else { Err(MatrixDeserError::InvalidAtom(v.to_string())) }
|
|
},
|
|
"as" => {
|
|
if let Some(v) = v[1].as_str() { Ok(SAtom::audio_unloaded(v)) }
|
|
else { Err(MatrixDeserError::InvalidAtom(v.to_string())) }
|
|
},
|
|
"ms" => {
|
|
let mut buf : [f32; 8] = [0.0; 8];
|
|
|
|
for i in 0..8 {
|
|
if let Some(v) = v[i + 1].as_f64() {
|
|
buf[i] = v as f32;
|
|
} else {
|
|
return Err(MatrixDeserError::InvalidAtom(v.to_string()));
|
|
}
|
|
}
|
|
|
|
Ok(SAtom::micro(&buf))
|
|
},
|
|
_ => Err(MatrixDeserError::InvalidAtom(v.to_string())),
|
|
}
|
|
}
|
|
|
|
fn serialize_atom(atom: &SAtom) -> Value {
|
|
match atom {
|
|
SAtom::MicroSample(s) => json!(["ms",
|
|
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
|
|
]),
|
|
SAtom::Str(s) => json!(["s", s]),
|
|
SAtom::AudioSample((s, _)) => json!(["as", s]),
|
|
SAtom::Setting(i) => json!(["i", i]),
|
|
SAtom::Param(p) => json!(["p", p]),
|
|
}
|
|
}
|
|
|
|
impl MatrixRepr {
|
|
pub fn empty() -> Self {
|
|
let cells = vec![];
|
|
let params = vec![];
|
|
let atoms = vec![];
|
|
let patterns = vec![];
|
|
|
|
Self {
|
|
cells,
|
|
params,
|
|
atoms,
|
|
patterns,
|
|
}
|
|
}
|
|
|
|
pub fn write_to_file(&mut self, filepath: &str) -> std::io::Result<()> {
|
|
use std::io::prelude::*;
|
|
use std::fs::OpenOptions;
|
|
|
|
let tmp_filepath = format!("{}~", filepath);
|
|
|
|
let mut ser = self.serialize();
|
|
ser.push('\n');
|
|
|
|
let mut file =
|
|
OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.truncate(true)
|
|
.open(&tmp_filepath)?;
|
|
file.write_all(ser.as_bytes())?;
|
|
std::fs::rename(&tmp_filepath, &filepath)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn read_from_file(filepath: &str) -> Result<MatrixRepr, MatrixDeserError> {
|
|
use std::io::prelude::*;
|
|
use std::fs::OpenOptions;
|
|
|
|
let mut file =
|
|
OpenOptions::new()
|
|
.write(false)
|
|
.create(false)
|
|
.read(true)
|
|
.open(&filepath)?;
|
|
|
|
let mut contents : Vec<u8> = Vec::new();
|
|
file.read_to_end(&mut contents)?;
|
|
|
|
let s = std::str::from_utf8(&contents)?;
|
|
|
|
MatrixRepr::deserialize(s)
|
|
}
|
|
|
|
pub fn deserialize(s: &str) -> Result<MatrixRepr, MatrixDeserError> {
|
|
let v : Value = serde_json::from_str(s)?;
|
|
|
|
if let Some(version) = v.get("VERSION") {
|
|
let version : i64 = version.as_i64().unwrap_or(0);
|
|
|
|
if version != 1 {
|
|
return Err(MatrixDeserError::BadVersion);
|
|
}
|
|
}
|
|
|
|
let mut m = MatrixRepr::empty();
|
|
|
|
let cells = &v["cells"];
|
|
if let Value::Array(cells) = cells {
|
|
for c in cells.iter() {
|
|
m.cells.push(CellRepr::deserialize(c)?);
|
|
}
|
|
}
|
|
|
|
let params = &v["params"];
|
|
if let Value::Array(params) = params {
|
|
for v in params.iter() {
|
|
let node_id = deserialize_node_id(&v, 0, 1)?;
|
|
let param_id = node_id.inp_param(v[2].as_str().unwrap_or(""));
|
|
|
|
if let Some(param_id) = param_id {
|
|
m.params.push(
|
|
(param_id, v[3].as_f64().unwrap_or(0.0) as f32));
|
|
} else {
|
|
return Err(
|
|
MatrixDeserError::UnknownParamId(v.to_string()));
|
|
}
|
|
}
|
|
}
|
|
|
|
let atoms = &v["atoms"];
|
|
if let Value::Array(atoms) = atoms {
|
|
for v in atoms.iter() {
|
|
let node_id = deserialize_node_id(&v, 0, 1)?;
|
|
let param_id = node_id.inp_param(v[2].as_str().unwrap_or(""));
|
|
|
|
if let Some(param_id) = param_id {
|
|
m.atoms.push((param_id, deserialize_atom(&v[3])?))
|
|
} else {
|
|
return Err(
|
|
MatrixDeserError::UnknownParamId(v.to_string()));
|
|
}
|
|
}
|
|
}
|
|
|
|
let patterns = &v["patterns"];
|
|
if let Value::Array(patterns) = patterns {
|
|
for p in patterns.iter() {
|
|
m.patterns.push(
|
|
if p.is_object() {
|
|
Some(PatternRepr::deserialize(&p)?)
|
|
} else { None });
|
|
}
|
|
}
|
|
|
|
Ok(m)
|
|
}
|
|
|
|
pub fn serialize(&mut self) -> String {
|
|
let mut v = json!({
|
|
"VERSION": 1,
|
|
});
|
|
|
|
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());
|
|
|
|
let mut params = json!([]);
|
|
if let Value::Array(params) = &mut params {
|
|
for (p, v) in self.params.iter() {
|
|
params.push(
|
|
json!([
|
|
p.node_id().name(),
|
|
p.node_id().instance(),
|
|
p.name(),
|
|
v
|
|
]));
|
|
}
|
|
}
|
|
|
|
v["params"] = params;
|
|
|
|
let mut atoms = json!([]);
|
|
if let Value::Array(atoms) = &mut atoms {
|
|
for (p, v) in self.atoms.iter() {
|
|
atoms.push(
|
|
json!([
|
|
p.node_id().name(),
|
|
p.node_id().instance(),
|
|
p.name(),
|
|
serialize_atom(v),
|
|
]));
|
|
}
|
|
}
|
|
|
|
v["atoms"] = atoms;
|
|
|
|
let mut cells = json!([]);
|
|
if let Value::Array(cells) = &mut cells {
|
|
for cell in self.cells.iter() {
|
|
cells.push(cell.serialize());
|
|
}
|
|
}
|
|
|
|
v["cells"] = cells;
|
|
|
|
let mut patterns = json!([]);
|
|
if let Value::Array(patterns) = &mut patterns {
|
|
for p in self.patterns.iter() {
|
|
patterns.push(
|
|
if let Some(p) = p { p.serialize() }
|
|
else { Value::Null });
|
|
}
|
|
}
|
|
|
|
v["patterns"] = patterns;
|
|
|
|
v.to_string()
|
|
}
|
|
}
|
|
|
|
pub fn load_patch_from_file(matrix: &mut crate::matrix::Matrix, filepath: &str)
|
|
-> Result<(), MatrixDeserError>
|
|
{
|
|
let mr = MatrixRepr::read_from_file(filepath)?;
|
|
matrix.from_repr(&mr)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn save_patch_to_file(matrix: &mut crate::matrix::Matrix, filepath: &str)
|
|
-> std::io::Result<()>
|
|
{
|
|
let mut mr = matrix.to_repr();
|
|
mr.write_to_file(filepath)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use crate::matrix::{Matrix, Cell};
|
|
|
|
#[test]
|
|
fn check_empty_repr_serialization() {
|
|
let mut matrix_repr = MatrixRepr::empty();
|
|
|
|
let s = matrix_repr.serialize();
|
|
|
|
assert_eq!(s,
|
|
"{\"VERSION\":1,\"atoms\":[],\"cells\":[],\"params\":[],\"patterns\":[]}");
|
|
assert!(MatrixRepr::deserialize(&s).is_ok());
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn check_repr_serialization() {
|
|
use crate::nodes::new_node_engine;
|
|
|
|
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));
|
|
matrix.place(1, 0,
|
|
Cell::empty(NodeId::Out(0))
|
|
.input(None, Some(0), None)
|
|
.out(None, None, Some(0)));
|
|
matrix.sync().unwrap();
|
|
|
|
let freq_param = sin.inp_param("freq").unwrap();
|
|
matrix.set_param(freq_param, SAtom::param(-0.1));
|
|
|
|
let mut mr = matrix.to_repr();
|
|
|
|
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]}");
|
|
|
|
let mut mr2 = MatrixRepr::deserialize(&s).unwrap();
|
|
|
|
let s2 = mr2.serialize();
|
|
|
|
assert_eq!(s, s2);
|
|
}
|
|
|
|
#[test]
|
|
fn check_atom_repr() {
|
|
let v = serialize_atom(&SAtom::str("foo"));
|
|
assert_eq!(v.to_string(), "[\"s\",\"foo\"]");
|
|
let s = serialize_atom(&deserialize_atom(&v).unwrap()).to_string();
|
|
assert_eq!(s, v.to_string());
|
|
|
|
let v = serialize_atom(&SAtom::setting(1337));
|
|
assert_eq!(v.to_string(), "[\"i\",1337]");
|
|
let s = serialize_atom(&deserialize_atom(&v).unwrap()).to_string();
|
|
assert_eq!(s, v.to_string());
|
|
|
|
let v = serialize_atom(&SAtom::param(1.0));
|
|
assert_eq!(v.to_string(), "[\"p\",1.0]");
|
|
let s = serialize_atom(&deserialize_atom(&v).unwrap()).to_string();
|
|
assert_eq!(s, v.to_string());
|
|
|
|
let v =
|
|
serialize_atom(
|
|
&SAtom::micro(&[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0]));
|
|
assert_eq!(v.to_string(), "[\"ms\",1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0]");
|
|
let s = serialize_atom(&deserialize_atom(&v).unwrap()).to_string();
|
|
assert_eq!(s, v.to_string());
|
|
|
|
let v =
|
|
serialize_atom(
|
|
&SAtom::audio(
|
|
"lol.wav",
|
|
std::sync::Arc::new(vec![1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0])));
|
|
assert_eq!(v.to_string(), "[\"as\",\"lol.wav\"]");
|
|
let s = serialize_atom(&deserialize_atom(&v).unwrap()).to_string();
|
|
assert_eq!(s, v.to_string());
|
|
}
|
|
|
|
#[test]
|
|
fn check_cell_repr() {
|
|
let cell =
|
|
Cell::empty(NodeId::Out(2))
|
|
.input(Some(2), Some(0), Some(3))
|
|
.out(Some(11), Some(4), Some(1));
|
|
let cr = cell.to_repr();
|
|
|
|
let s = cr.serialize().to_string();
|
|
|
|
let v : Value = serde_json::from_str(&s).unwrap();
|
|
let cr2 = CellRepr::deserialize(&v).unwrap();
|
|
|
|
let s2 = cr2.serialize().to_string();
|
|
assert_eq!(s, s2);
|
|
}
|
|
|
|
#[test]
|
|
fn check_file_repr() {
|
|
let orig_serial = {
|
|
use crate::nodes::new_node_engine;
|
|
|
|
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));
|
|
matrix.place(1, 0,
|
|
Cell::empty(NodeId::Out(0))
|
|
.input(None, Some(0), None)
|
|
.out(None, None, Some(0)));
|
|
matrix.sync().unwrap();
|
|
|
|
let freq_param = sin.inp_param("freq").unwrap();
|
|
matrix.set_param(freq_param, SAtom::param(-0.1));
|
|
|
|
let mut mr = matrix.to_repr();
|
|
let s2 = mr.serialize().to_string();
|
|
|
|
save_patch_to_file(
|
|
&mut matrix, "hexosynth_test_patch.hxy").unwrap();
|
|
|
|
s2
|
|
};
|
|
|
|
{
|
|
use crate::nodes::new_node_engine;
|
|
|
|
let (node_conf, mut _node_exec) = new_node_engine();
|
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
|
|
|
load_patch_from_file(
|
|
&mut matrix, "hexosynth_test_patch.hxy").unwrap();
|
|
|
|
let mut mr = matrix.to_repr();
|
|
let s = mr.serialize().to_string();
|
|
|
|
assert_eq!(s, orig_serial);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn check_matrix_track_repr() {
|
|
use crate::dsp::tracker::UIPatternModel;
|
|
|
|
let orig_serial = {
|
|
use crate::nodes::new_node_engine;
|
|
|
|
let (node_conf, mut _node_exec) = new_node_engine();
|
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
|
|
|
let ts = NodeId::TSeq(0);
|
|
|
|
matrix.place(0, 0,
|
|
Cell::empty(ts)
|
|
.out(None, Some(0), None));
|
|
matrix.sync().unwrap();
|
|
|
|
{
|
|
let pat_ref = matrix.get_pattern_data(0).unwrap();
|
|
let mut pat = pat_ref.borrow_mut();
|
|
|
|
for col in 0..MAX_COLS {
|
|
pat.set_col_note_type(col);
|
|
for v in 1..(MAX_PATTERN_LEN + 1) {
|
|
pat.set_cell_value(v - 1, col, v as u16);
|
|
}
|
|
|
|
pat.set_cursor(16, 3);
|
|
pat.set_edit_step(5);
|
|
pat.set_rows(133);
|
|
}
|
|
}
|
|
|
|
let mut mr = matrix.to_repr();
|
|
let s2 = mr.serialize().to_string();
|
|
|
|
save_patch_to_file(
|
|
&mut matrix, "hexosynth_test_patch_2.hxy").unwrap();
|
|
|
|
s2
|
|
};
|
|
|
|
{
|
|
use crate::nodes::new_node_engine;
|
|
|
|
let (node_conf, mut _node_exec) = new_node_engine();
|
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
|
|
|
load_patch_from_file(
|
|
&mut matrix, "hexosynth_test_patch_2.hxy").unwrap();
|
|
|
|
let mut mr = matrix.to_repr();
|
|
let s = mr.serialize().to_string();
|
|
|
|
assert_eq!(s, orig_serial);
|
|
|
|
let pat_ref = matrix.get_pattern_data(0).unwrap();
|
|
let mut pat = pat_ref.borrow_mut();
|
|
|
|
for col in 0..MAX_COLS {
|
|
assert!(pat.is_col_note(col));
|
|
for v in 1..(MAX_PATTERN_LEN + 1) {
|
|
assert_eq!(pat.get_cell_value(v - 1, col), v as u16);
|
|
}
|
|
|
|
assert_eq!(pat.get_cursor(), (16, 3));
|
|
assert_eq!(pat.get_edit_step(), 5);
|
|
assert_eq!(pat.rows(), 133);
|
|
}
|
|
}
|
|
}
|
|
}
|