Added Mix3 node, and changed serialization format to port names.
This commit is contained in:
parent
4f4f3aed25
commit
f4bc7341f6
5 changed files with 621 additions and 14 deletions
|
@ -30,6 +30,8 @@ mod node_map;
|
||||||
mod node_smap;
|
mod node_smap;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
mod node_sfilter;
|
mod node_sfilter;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
mod node_mix3;
|
||||||
|
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
mod satom;
|
mod satom;
|
||||||
|
@ -75,6 +77,7 @@ use node_noise::Noise;
|
||||||
use node_map::Map;
|
use node_map::Map;
|
||||||
use node_smap::SMap;
|
use node_smap::SMap;
|
||||||
use node_sfilter::SFilter;
|
use node_sfilter::SFilter;
|
||||||
|
use node_mix3::Mix3;
|
||||||
|
|
||||||
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
||||||
|
|
||||||
|
@ -484,6 +487,15 @@ macro_rules! node_list {
|
||||||
(2 att n_att d_att r_id f_def stp_d 0.0, 1.0, 1.0)
|
(2 att n_att d_att r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||||
{3 0 neg_att setting(1) fa_amp_neg_att 0 1}
|
{3 0 neg_att setting(1) fa_amp_neg_att 0 1}
|
||||||
[0 sig],
|
[0 sig],
|
||||||
|
mix3 => Mix3 UIType::Generic UICategory::NtoM
|
||||||
|
(0 ch1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
|
(1 ch2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
|
(2 ch3 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
|
(3 gain1 n_gain d_gain r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||||
|
(4 gain2 n_gain d_gain r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||||
|
(5 gain3 n_gain d_gain r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||||
|
(6 ogain n_gain d_gain r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||||
|
[0 sig],
|
||||||
smap => SMap UIType::Generic UICategory::CV
|
smap => SMap UIType::Generic UICategory::CV
|
||||||
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
(1 min n_id d_id r_s f_def stp_d -1.0, 1.0, -1.0)
|
(1 min n_id d_id r_s f_def stp_d -1.0, 1.0, -1.0)
|
||||||
|
@ -1033,6 +1045,19 @@ macro_rules! make_node_info_enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn inp_name_by_idx(&self, idx: u8) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
NodeId::$v1 => None,
|
||||||
|
$(NodeId::$variant(_) => {
|
||||||
|
match idx {
|
||||||
|
$($in_idx => Some(stringify!($para)),)*
|
||||||
|
$($in_at_idx => Some(stringify!($atom)),)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}),+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn out_name_by_idx(&self, idx: u8) -> Option<&'static str> {
|
pub fn out_name_by_idx(&self, idx: u8) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
NodeId::$v1 => None,
|
NodeId::$v1 => None,
|
||||||
|
|
98
src/dsp/node_mix3.rs
Normal file
98
src/dsp/node_mix3.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext};
|
||||||
|
|
||||||
|
//#[macro_export]
|
||||||
|
//macro_rules! fa_amp_neg_att { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
||||||
|
// let s =
|
||||||
|
// match ($v.round() as usize) {
|
||||||
|
// 0 => "Allow",
|
||||||
|
// 1 => "Clip",
|
||||||
|
// _ => "?",
|
||||||
|
// };
|
||||||
|
// write!($formatter, "{}", s)
|
||||||
|
//} } }
|
||||||
|
|
||||||
|
/// A simple amplifier
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Mix3 {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mix3 {
|
||||||
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
|
Self {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const ch1 : &'static str =
|
||||||
|
"Mix3 inp\nChannel 1 Signal input\nRange: (-1..1)\n";
|
||||||
|
pub const ch2 : &'static str =
|
||||||
|
"Mix3 inp2\nChannel 2 Signal input\nRange: (-1..1)\n";
|
||||||
|
pub const ch3 : &'static str =
|
||||||
|
"Mix3 inp3\nChannel 3 Signal input\nRange: (-1..1)\n";
|
||||||
|
pub const gain1 : &'static str =
|
||||||
|
"Mix3 gain1\nChannel 1 gain\nRange: (0..1)";
|
||||||
|
pub const gain2 : &'static str =
|
||||||
|
"Mix3 gain2\nChannel 2 gain\nRange: (0..1)";
|
||||||
|
pub const gain3 : &'static str =
|
||||||
|
"Mix3 gain3\nChannel 3 gain\nRange: (0..1)";
|
||||||
|
pub const ogain : &'static str =
|
||||||
|
"Mix3 ogain\nOutput gain of the sum\nRange: (0..1)";
|
||||||
|
pub const sig : &'static str =
|
||||||
|
"Mix3 sig\nMixed signal output\nRange: (-1..1)\n";
|
||||||
|
pub const DESC : &'static str =
|
||||||
|
r#"3 Ch. Signal Mixer
|
||||||
|
|
||||||
|
A very simple 3 channel signal mixer.
|
||||||
|
You can mix anything, from audio signals to control signals.
|
||||||
|
"#;
|
||||||
|
pub const HELP : &'static str =
|
||||||
|
r#"Mix3 - 3 Channel Signal Mixer
|
||||||
|
|
||||||
|
Just a small 3 channel mixer to create a sum of multiple signals.
|
||||||
|
You can mix anything, from audio signals to control signals.
|
||||||
|
|
||||||
|
There is even a convenient output gain knob,
|
||||||
|
to turn down the output.
|
||||||
|
"#;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DspNode for Mix3 {
|
||||||
|
fn outputs() -> usize { 1 }
|
||||||
|
|
||||||
|
fn set_sample_rate(&mut self, _srate: f32) { }
|
||||||
|
fn reset(&mut self) { }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn process<T: NodeAudioContext>(
|
||||||
|
&mut self, ctx: &mut T, _ectx: &mut NodeExecContext,
|
||||||
|
_nctx: &NodeContext,
|
||||||
|
_atoms: &[SAtom], inputs: &[ProcBuf],
|
||||||
|
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
||||||
|
{
|
||||||
|
use crate::dsp::{out, inp, denorm};
|
||||||
|
|
||||||
|
let inp1 = inp::Mix3::ch1(inputs);
|
||||||
|
let inp2 = inp::Mix3::ch2(inputs);
|
||||||
|
let inp3 = inp::Mix3::ch3(inputs);
|
||||||
|
let g1 = inp::Mix3::gain1(inputs);
|
||||||
|
let g2 = inp::Mix3::gain2(inputs);
|
||||||
|
let g3 = inp::Mix3::gain3(inputs);
|
||||||
|
let og = inp::Mix3::ogain(inputs);
|
||||||
|
let out = out::Mix3::sig(outputs);
|
||||||
|
|
||||||
|
for frame in 0..ctx.nframes() {
|
||||||
|
let sum =
|
||||||
|
inp1.read(frame) * denorm::Mix3::gain1(g1, frame)
|
||||||
|
+ inp2.read(frame) * denorm::Mix3::gain2(g2, frame)
|
||||||
|
+ inp3.read(frame) * denorm::Mix3::gain3(g3, frame);
|
||||||
|
out.write(frame, sum * denorm::Mix3::ogain(og, frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_vals[0].set(
|
||||||
|
out.read(ctx.nframes() - 1));
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,7 +98,7 @@ impl Cell {
|
||||||
/// let repr = some_cell.to_repr();
|
/// let repr = some_cell.to_repr();
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// repr.serialize().to_string(),
|
/// repr.serialize().to_string(),
|
||||||
/// "[\"sin\",0,0,0,[-1,0,0],[-1,0,0]]");
|
/// "[\"sin\",0,0,0,[-1,\"freq\",\"freq\"],[-1,\"sig\",\"sig\"]]");
|
||||||
///```
|
///```
|
||||||
pub fn to_repr(&self) -> CellRepr {
|
pub fn to_repr(&self) -> CellRepr {
|
||||||
CellRepr {
|
CellRepr {
|
||||||
|
@ -518,7 +518,7 @@ impl Matrix {
|
||||||
///
|
///
|
||||||
/// let mut serialized = matrix.to_repr().serialize().to_string();
|
/// let mut serialized = matrix.to_repr().serialize().to_string();
|
||||||
///
|
///
|
||||||
/// assert!(serialized.find("\"sin\",2,0,0,[-1,-1,-1],[-1,0,-1]").is_some());
|
/// assert!(serialized.find("\"sin\",2,0,0,[-1,-1,-1],[-1,\"sig\",-1]").is_some());
|
||||||
/// assert!(serialized.find("\"freq\",-0.100").is_some());
|
/// assert!(serialized.find("\"freq\",-0.100").is_some());
|
||||||
///```
|
///```
|
||||||
///
|
///
|
||||||
|
|
|
@ -27,6 +27,49 @@ fn deserialize_node_id(v: &Value, i1: usize, i2: usize)
|
||||||
Ok(nid.to_instance(v[i2].as_i64().unwrap_or(0) as usize))
|
Ok(nid.to_instance(v[i2].as_i64().unwrap_or(0) as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn inp2idx(node_id: NodeId, v: &Value) -> i16 {
|
||||||
|
let inp = v.as_i64().unwrap_or(-2) as i16;
|
||||||
|
|
||||||
|
if inp > -2 { inp }
|
||||||
|
else {
|
||||||
|
v.as_str()
|
||||||
|
.map(|s| node_id.inp(s).map(|i| i as i16).unwrap_or(-1))
|
||||||
|
.unwrap_or(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out2idx(node_id: NodeId, v: &Value) -> i16 {
|
||||||
|
let out = v.as_i64().unwrap_or(-2) as i16;
|
||||||
|
|
||||||
|
if out > -2 { out }
|
||||||
|
else {
|
||||||
|
v.as_str()
|
||||||
|
.map(|s| node_id.out(s).map(|i| i as i16).unwrap_or(-1))
|
||||||
|
.unwrap_or(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inp_idx2value(node_id: NodeId, idx: i16) -> Value {
|
||||||
|
if idx == -1 {
|
||||||
|
json!(-1)
|
||||||
|
} else {
|
||||||
|
node_id.inp_name_by_idx(idx as u8)
|
||||||
|
.map(|n| json!(n))
|
||||||
|
.unwrap_or_else(|| json!(-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_idx2value(node_id: NodeId, idx: i16) -> Value {
|
||||||
|
if idx == -1 {
|
||||||
|
json!(-1)
|
||||||
|
} else {
|
||||||
|
node_id.out_name_by_idx(idx as u8)
|
||||||
|
.map(|n| json!(n))
|
||||||
|
.unwrap_or_else(|| json!(-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl CellRepr {
|
impl CellRepr {
|
||||||
pub fn serialize(&self) -> Value {
|
pub fn serialize(&self) -> Value {
|
||||||
json!([
|
json!([
|
||||||
|
@ -34,25 +77,31 @@ impl CellRepr {
|
||||||
self.node_id.instance(),
|
self.node_id.instance(),
|
||||||
self.x,
|
self.x,
|
||||||
self.y,
|
self.y,
|
||||||
[self.inp[0], self.inp[1], self.inp[2]],
|
[inp_idx2value(self.node_id, self.inp[0]),
|
||||||
[self.out[0], self.out[1], self.out[2]],
|
inp_idx2value(self.node_id, self.inp[1]),
|
||||||
|
inp_idx2value(self.node_id, self.inp[2])],
|
||||||
|
[out_idx2value(self.node_id, self.out[0]),
|
||||||
|
out_idx2value(self.node_id, self.out[1]),
|
||||||
|
out_idx2value(self.node_id, self.out[2])],
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize(v: &Value) -> Result<Self, MatrixDeserError> {
|
pub fn deserialize(v: &Value) -> Result<Self, MatrixDeserError> {
|
||||||
|
let node_id = deserialize_node_id(v, 0, 1)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
node_id: deserialize_node_id(v, 0, 1)?,
|
node_id,
|
||||||
x: v[2].as_i64().unwrap_or(0) as usize,
|
x: v[2].as_i64().unwrap_or(0) as usize,
|
||||||
y: v[3].as_i64().unwrap_or(0) as usize,
|
y: v[3].as_i64().unwrap_or(0) as usize,
|
||||||
inp: [
|
inp: [
|
||||||
v[4][0].as_i64().unwrap_or(-1) as i16,
|
inp2idx(node_id, &v[4][0]),
|
||||||
v[4][1].as_i64().unwrap_or(-1) as i16,
|
inp2idx(node_id, &v[4][1]),
|
||||||
v[4][2].as_i64().unwrap_or(-1) as i16,
|
inp2idx(node_id, &v[4][2])
|
||||||
],
|
],
|
||||||
out: [
|
out: [
|
||||||
v[5][0].as_i64().unwrap_or(-1) as i16,
|
out2idx(node_id, &v[5][0]),
|
||||||
v[5][1].as_i64().unwrap_or(-1) as i16,
|
out2idx(node_id, &v[5][1]),
|
||||||
v[5][2].as_i64().unwrap_or(-1) as i16,
|
out2idx(node_id, &v[5][2])
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -499,8 +548,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],\"props\":[]}");
|
"{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,0,[-1,-1,-1],[-1,\"sig\",-1]],[\"out\",0,1,0,[-1,\"ch1\",-1],[-1,-1,-1]]],\"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();
|
||||||
|
|
||||||
let s2 = mr2.serialize();
|
let s2 = mr2.serialize();
|
||||||
|
@ -546,7 +594,7 @@ mod tests {
|
||||||
fn check_cell_repr() {
|
fn check_cell_repr() {
|
||||||
let cell =
|
let cell =
|
||||||
Cell::empty(NodeId::Out(2))
|
Cell::empty(NodeId::Out(2))
|
||||||
.input(Some(2), Some(0), Some(3))
|
.input(Some(2), Some(0), Some(1))
|
||||||
.out(Some(11), Some(4), Some(1));
|
.out(Some(11), Some(4), Some(1));
|
||||||
let cr = cell.to_repr();
|
let cr = cell.to_repr();
|
||||||
|
|
||||||
|
@ -747,4 +795,44 @@ mod tests {
|
||||||
assert_eq!(matrix.get_prop("test").unwrap().i(), 31337);
|
assert_eq!(matrix.get_prop("test").unwrap().i(), 31337);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_matrix_repr_old_format2new() {
|
||||||
|
let old_format =
|
||||||
|
"{\"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 mr = MatrixRepr::deserialize(old_format).unwrap();
|
||||||
|
let new_format = mr.serialize().to_string();
|
||||||
|
|
||||||
|
assert_eq!(new_format,
|
||||||
|
"{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,\
|
||||||
|
0,[-1,-1,-1],[-1,\"sig\",-1]],[\"out\",0,1,0,[-1,\"ch1\",-1],[-1,-1,-1]]],\"params\":[[\"o\
|
||||||
|
ut\",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,nul\
|
||||||
|
l,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,\
|
||||||
|
null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,nu\
|
||||||
|
ll,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null\
|
||||||
|
,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,n\
|
||||||
|
ull,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,nul\
|
||||||
|
l,null,null,null,null,null,null,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\":[]}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
396
tests/node_mix3.rs
Normal file
396
tests/node_mix3.rs
Normal file
|
@ -0,0 +1,396 @@
|
||||||
|
mod common;
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_1() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let trig_p = ad.inp_param("trig").unwrap();
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 50, vec![
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.007558584, 0.007558584, 0.007558584,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.0022675395,
|
||||||
|
-0.0022675693, -0.0022675693, -0.0022675842, -0.0022675693,
|
||||||
|
-0.0022675726,
|
||||||
|
0.0, 0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 10.0);
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
//d// println!("RES: {:?}", res);
|
||||||
|
let start = res.0[330];
|
||||||
|
assert_float_eq!(start, 0.0075585);
|
||||||
|
let peak = res.0[330 + ((44.1_f64 * 3.0).floor() as usize)];
|
||||||
|
assert_float_eq!(peak, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_retrig() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let test = NodeId::Test(0);
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(test)
|
||||||
|
.out(None, None, test.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(ad)
|
||||||
|
.input(ad.inp("trig"), None, None)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 2, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let trig_p = test.inp_param("p").unwrap();
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 50, vec![
|
||||||
|
// XXX: Direct trigger!
|
||||||
|
// Due to Test node outputting an unsmoothed value!
|
||||||
|
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.007558584, 0.007558584, 0.007558584,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.0022675395,
|
||||||
|
-0.0022675693, -0.0022675693, -0.0022675842, -0.0022675693,
|
||||||
|
-0.0022675726,
|
||||||
|
0.0, 0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 0.1);
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 1.5);
|
||||||
|
assert_decimated_feq!(res.0, 2, vec![
|
||||||
|
0.0075585, 0.022675736, 0.03779289, 0.05291005, 0.068027206, 0.08314436,
|
||||||
|
0.09826152, 0.113378674, 0.12849583, 0.143613, 0.15873015,
|
||||||
|
0.1738473, 0.18896446, 0.20408161, 0.21919878, 0.23431593,
|
||||||
|
0.24943309, 0.26455024, 0.2796674, 0.29478455, 0.3099017,
|
||||||
|
0.32501888, 0.34013602, 0.3552532, 0.37037033, 0.3854875,
|
||||||
|
0.40060467, 0.4157218, 0.43083897, 0.4459561, 0.46107328,
|
||||||
|
0.47619045, 0.4913076
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Reset trigger
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 0.1);
|
||||||
|
assert_slope_feq!(res.0, vec![0.00755; 3]);
|
||||||
|
|
||||||
|
// Retrigger attack (should do nothing)
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 0.1);
|
||||||
|
assert_slope_feq!(res.0, vec![0.00755; 7]);
|
||||||
|
|
||||||
|
// Wait into decay phase
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 1.4);
|
||||||
|
let mut v = vec![0.00755; 57];
|
||||||
|
v.append(&mut vec![0.002267, -0.002267, -0.002267]);
|
||||||
|
assert_slope_feq!(res.0, v);
|
||||||
|
|
||||||
|
// Decay some more
|
||||||
|
let res = run_for_ms(&mut node_exec, 0.8);
|
||||||
|
assert_slope_feq!(res.0, vec![-0.002267; 100]);
|
||||||
|
|
||||||
|
// Retrigger right in the decay phase
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
let res = run_for_ms(&mut node_exec, 1.0);
|
||||||
|
assert_slope_feq!(res.0, vec![
|
||||||
|
// Re-attack until we are at 1.0 again
|
||||||
|
0.007558584, 0.007558584, 0.007558584, 0.0075585246, 0.007558584,
|
||||||
|
0.007558584, 0.007558584, 0.007558584, 0.007558584, 0.007558584,
|
||||||
|
0.0007558465,
|
||||||
|
// Restart decay after 1.0 was reached:
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599,
|
||||||
|
-0.0022675395, -0.002267599, -0.0022675395, -0.002267599,
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.0022675395,
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.002267599,
|
||||||
|
-0.0022675395, -0.002267599, -0.0022675395, -0.002267599,
|
||||||
|
-0.0022675395, -0.002267599, -0.002267599, -0.0022675395,
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.0022675395,
|
||||||
|
-0.002267599, -0.002267599, -0.0022675395, -0.002267599, -0.0022675395
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_inp_sin() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let sin = NodeId::Sin(0);
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(sin)
|
||||||
|
.out(None, None, sin.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(ad)
|
||||||
|
.input(ad.inp("inp"), None, None)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 2, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let trig_p = ad.inp_param("trig").unwrap();
|
||||||
|
let atk_p = ad.inp_param("atk").unwrap();
|
||||||
|
let dcy_p = ad.inp_param("dcy").unwrap();
|
||||||
|
|
||||||
|
// check if we have any frequencies resembling 440Hz
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
run_for_ms(&mut node_exec, 7.0);
|
||||||
|
|
||||||
|
let fft = run_and_get_fft4096_now(&mut node_exec, 6);
|
||||||
|
assert_eq!(fft[0], (420, 6));
|
||||||
|
assert_eq!(fft[1], (431, 6));
|
||||||
|
assert_eq!(fft[2], (441, 6));
|
||||||
|
assert_eq!(fft[3], (452, 6));
|
||||||
|
assert_eq!(fft[4], (463, 6));
|
||||||
|
|
||||||
|
// Next we test if lengthening the attack has
|
||||||
|
// effect on the captured frequencies.
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 8.0);
|
||||||
|
|
||||||
|
matrix.set_param(atk_p, SAtom::param(atk_p.norm(40.0)));
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
run_for_ms(&mut node_exec, 7.0);
|
||||||
|
|
||||||
|
let fft = run_and_get_fft4096_now(&mut node_exec, 300);
|
||||||
|
assert_eq!(fft[0], (431, 322));
|
||||||
|
assert_eq!(fft[1], (441, 360));
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 8.0);
|
||||||
|
|
||||||
|
// Next we test if lengthening the decay too has
|
||||||
|
// effect on the captured frequencies.
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 8.0);
|
||||||
|
|
||||||
|
matrix.set_param(dcy_p, SAtom::param(dcy_p.norm(40.0)));
|
||||||
|
matrix.set_param(trig_p, SAtom::param(1.0));
|
||||||
|
run_for_ms(&mut node_exec, 7.0);
|
||||||
|
|
||||||
|
let fft = run_and_get_fft4096_now(&mut node_exec, 300);
|
||||||
|
assert_eq!(fft[0], (431, 489));
|
||||||
|
assert_eq!(fft[1], (441, 647));
|
||||||
|
assert_eq!(fft[2], (452, 398));
|
||||||
|
|
||||||
|
matrix.set_param(trig_p, SAtom::param(0.0));
|
||||||
|
run_for_ms(&mut node_exec, 8.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_shp_log() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
pset_n(&mut matrix, ad, "trig", 1.0);
|
||||||
|
pset_n(&mut matrix, ad, "ashp", 1.0);
|
||||||
|
pset_n(&mut matrix, ad, "dshp", 1.0);
|
||||||
|
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 50, vec![
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.009243369, 0.003936231, 0.0020142794,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
-0.0006071329, -0.00067061186, -0.000752151, -0.0008612871,
|
||||||
|
-0.0010163188, -0.0012571216, -0.0016934872, -0.0027971864,
|
||||||
|
-0.027684271,
|
||||||
|
0.0, 0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_shp_exp() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
pset_n(&mut matrix, ad, "trig", 1.0);
|
||||||
|
pset_n(&mut matrix, ad, "ashp", 0.0);
|
||||||
|
pset_n(&mut matrix, ad, "dshp", 0.0);
|
||||||
|
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 50, vec![
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.0009576017, 0.0044435486, 0.023418367,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
-0.0068960786, -0.004632175, -0.002927363, -0.0017025322,
|
||||||
|
-0.00087817386, -0.0003750762, -0.000113890506, -0.0000153047,
|
||||||
|
-0.0000000017186217,
|
||||||
|
0.0, 0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_eoet() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.place(1, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("eoet")));
|
||||||
|
matrix.place(1, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch2"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
pset_n(&mut matrix, ad, "trig", 1.0);
|
||||||
|
let res = run_for_ms(&mut node_exec, 25.0);
|
||||||
|
// just make sure we are running an env:
|
||||||
|
assert_decimated_slope_feq!(res.0, 50, vec![
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.007558584, 0.007558584, 0.007558584,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
-0.002267599, -0.0022675395, -0.002267599, -0.0022675395,
|
||||||
|
-0.0022675693, -0.0022675693, -0.0022675842, -0.0022675693,
|
||||||
|
-0.0022675726,
|
||||||
|
0.0, // <- EOET expected here
|
||||||
|
0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
|
||||||
|
// check if trigger appears:
|
||||||
|
assert_decimated_feq!(res.1, 50, vec![
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, attack is default 3.0ms (roughly 3 * 50 samples):
|
||||||
|
0.0, 0.0, 0.0,
|
||||||
|
// 44.1 per ms, decay is default 10.0ms (=> roughly 9 * 50 samples):
|
||||||
|
0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0,
|
||||||
|
1.0, // <- End of envelope!
|
||||||
|
0.0, 0.0, 0.0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_atk_dcy() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let test = NodeId::Test(0);
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(test)
|
||||||
|
.out(None, None, test.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(ad)
|
||||||
|
.input(ad.inp("trig"), None, None)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 2, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
pset_d(&mut matrix, ad, "atk", 20.0);
|
||||||
|
pset_n(&mut matrix, test, "p", 0.0);
|
||||||
|
run_for_ms(&mut node_exec, 10.0);
|
||||||
|
|
||||||
|
pset_n(&mut matrix, test, "p", 1.0);
|
||||||
|
let res = run_for_ms(&mut node_exec, 10.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 10, vec![0.001133787; 50]);
|
||||||
|
|
||||||
|
pset_d(&mut matrix, ad, "atk", 50.0);
|
||||||
|
let res = run_for_ms(&mut node_exec, 20.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 40, vec![
|
||||||
|
// Slope is getting less and less steep, as expected:
|
||||||
|
0.0011277795, 0.0010179877, 0.00092345476, 0.00084143877,
|
||||||
|
0.0007699132, 0.0007072091, 0.0006517768, 0.00060266256,
|
||||||
|
0.00055885315, 0.0005196929, 0.00048446655, 0.00045353174,
|
||||||
|
// Slope does not change after the "atk" change has been smoothed
|
||||||
|
0.00045353174, 0.00045353174, 0.00045353174, 0.00045347214,
|
||||||
|
0.00045353174, 0.00045353174, 0.00045353174, 0.00045353174,
|
||||||
|
0.00045347214, 0.00045353174,
|
||||||
|
// attack phase ended, and now we decay:
|
||||||
|
-0.002267599
|
||||||
|
]);
|
||||||
|
|
||||||
|
// check if decay stays stable:
|
||||||
|
let res = run_for_ms(&mut node_exec, 2.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 40, vec![-0.002267599; 3]);
|
||||||
|
|
||||||
|
pset_d(&mut matrix, ad, "dcy", 200.0);
|
||||||
|
let res = run_for_ms(&mut node_exec, 20.0);
|
||||||
|
assert_decimated_slope_feq!(res.0, 40, vec![
|
||||||
|
// Slope is getting less and less steep, as expected:
|
||||||
|
-0.002197802, -0.0012806058, -0.00083732605, -0.0005899668,
|
||||||
|
-0.00043797493, -0.00033789873, -0.00026863813, -0.00021868944,
|
||||||
|
-0.00018143654, -0.00015294552, -0.00013071299,
|
||||||
|
// Slope does not change after the "dcy" change has been smoothed
|
||||||
|
-0.000113368034, -0.000113368034, -0.00011339784, -0.00011339784,
|
||||||
|
-0.000113368034, -0.000113368034, -0.00011339784, -0.000113368034,
|
||||||
|
-0.000113368034, -0.00011339784, -0.000113368034, -0.000113368034
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_ad_mult() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let ad = NodeId::Ad(0);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(ad)
|
||||||
|
.out(None, None, ad.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
pset_n(&mut matrix, ad, "trig", 1.0);
|
||||||
|
pset_n(&mut matrix, ad, "mult", 2.0);
|
||||||
|
let res = run_for_ms(&mut node_exec, 2000.0);
|
||||||
|
assert_decimated_slope_feq_fine!(res.0, 1800, vec![
|
||||||
|
0.0,
|
||||||
|
// looong attack:
|
||||||
|
0.00007558, 0.00007558, 0.00007558, 0.00007558,
|
||||||
|
0.00007558, 0.00007558, 0.00007558,
|
||||||
|
// looong decay:
|
||||||
|
-0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709,
|
||||||
|
-0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709,
|
||||||
|
-0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709,
|
||||||
|
-0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709,
|
||||||
|
-0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue