implemented offset and len for sampler, and unified the oneshot and loop implementation.
This commit is contained in:
parent
aff9984f08
commit
3530b6df2d
5 changed files with 677 additions and 469 deletions
|
@ -276,8 +276,8 @@ macro_rules! node_list {
|
|||
sampl => Sampl UIType::Generic UICategory::Osc
|
||||
(0 freq n_pit d_pit -1.0, 1.0, 440.0)
|
||||
(1 trig n_id n_id -1.0, 1.0, 0.0)
|
||||
(2 spos n_id n_id -1.0, 1.0, 0.0)
|
||||
(3 epos n_id n_id -1.0, 1.0, 0.0)
|
||||
(2 offs n_id n_id 0.0, 1.0, 0.0)
|
||||
(3 len n_id n_id 0.0, 1.0, 1.0)
|
||||
{4 0 sample audio_unloaded("") 0 0}
|
||||
{5 1 pmode setting(0) 0 1}
|
||||
{6 2 dclick setting(1) 0 1}
|
||||
|
|
|
@ -32,10 +32,10 @@ impl Sampl {
|
|||
pub const trig : &'static str =
|
||||
"Sampl trig\nThe trigger input causes a resync of the playback phase \
|
||||
and triggers the playback if the 'pmode' is 'OneShot'";
|
||||
pub const spos : &'static str =
|
||||
"Sampl spos\nStart position offset.\nRange: (-1..1)\n";
|
||||
pub const epos : &'static str =
|
||||
"Sampl epos\nEnd position offset.\nRange: (-1..1)\n";
|
||||
pub const offs : &'static str =
|
||||
"Sampl offs\nStart position offset.\nRange: (0..1)\n";
|
||||
pub const len : &'static str =
|
||||
"Sampl len\nLength of the sample, after the offset has been applied.\nRange: (0..1)\n";
|
||||
|
||||
pub const sample : &'static str =
|
||||
"Sampl sample\nThe audio sample that is played back.\nRange: (-1..1)\n";
|
||||
|
@ -59,6 +59,7 @@ impl Sampl {
|
|||
#[inline]
|
||||
fn next_sample(&mut self, sr_factor: f64, speed: f64, sample_data: &[f32]) -> f32 {
|
||||
let sd_len = sample_data.len();
|
||||
if sd_len < 1 { return 0.0; }
|
||||
|
||||
let i = self.phase.floor() as usize + sd_len;
|
||||
|
||||
|
@ -89,30 +90,46 @@ impl Sampl {
|
|||
(((a * f) - b_neg) * f + c) * f + x0
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
// fn play_loop(&mut self, inputs: &[ProcBuf], nframes: usize, sample_data: &[f32], out: &mut ProcBuf) {
|
||||
// let freq = inp::Sampl::freq(inputs);
|
||||
// let offs = inp::Sampl::offs(inputs);
|
||||
// let len = inp::Sampl::len(inputs);
|
||||
//
|
||||
// let sample_srate = sample_data[0] as f64;
|
||||
// let sample_data = &sample_data[1..];
|
||||
// let sr_factor = sample_srate / self.srate;
|
||||
//
|
||||
// for frame in 0..nframes {
|
||||
// let playback_speed =
|
||||
// denorm::Sampl::freq(freq, frame) / 440.0;
|
||||
//
|
||||
// let start_idx =
|
||||
// (sample_data.len() as f32
|
||||
// * denorm::Sampl::offs(offs, frame).abs().min(0.999999))
|
||||
// .floor() as usize;
|
||||
//
|
||||
// let end_idx_plus1 =
|
||||
// ((sample_data.len() - start_idx) as f32
|
||||
// * denorm::Sampl::len(offs, frame).abs().min(0.999999))
|
||||
// .ceil() as usize;
|
||||
//
|
||||
// out.write(frame,
|
||||
// self.next_sample(
|
||||
// sr_factor,
|
||||
// playback_speed as f64,
|
||||
// sample_data[start_idx..end_idx_plus1]));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
#[inline]
|
||||
fn play_loop(&mut self, inputs: &[ProcBuf], nframes: usize, sample_data: &[f32], out: &mut ProcBuf) {
|
||||
let freq = inp::Sampl::freq(inputs);
|
||||
|
||||
let sample_srate = sample_data[0] as f64;
|
||||
let sample_data = &sample_data[1..];
|
||||
let sr_factor = sample_srate / self.srate;
|
||||
|
||||
for frame in 0..nframes {
|
||||
let playback_speed =
|
||||
denorm::Sampl::freq(freq, frame) / 440.0;
|
||||
|
||||
out.write(frame,
|
||||
self.next_sample(
|
||||
sr_factor, playback_speed as f64, sample_data));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn play_oneshot(&mut self, inputs: &[ProcBuf], nframes: usize,
|
||||
sample_data: &[f32], out: &mut ProcBuf)
|
||||
fn play(&mut self, inputs: &[ProcBuf], nframes: usize,
|
||||
sample_data: &[f32], out: &mut ProcBuf, do_loop: bool)
|
||||
{
|
||||
let freq = inp::Sampl::freq(inputs);
|
||||
let trig = inp::Sampl::trig(inputs);
|
||||
let offs = inp::Sampl::offs(inputs);
|
||||
let len = inp::Sampl::len(inputs);
|
||||
|
||||
let sample_srate = sample_data[0] as f64;
|
||||
let sample_data = &sample_data[1..];
|
||||
|
@ -120,6 +137,16 @@ impl Sampl {
|
|||
|
||||
let mut is_playing = self.is_playing;
|
||||
|
||||
if do_loop {
|
||||
is_playing = true;
|
||||
}
|
||||
|
||||
let mut prev_offs = -10.0;
|
||||
let mut prev_len = -10.0;
|
||||
|
||||
let mut start_idx = 0;
|
||||
let mut end_idx_plus1 = sample_data.len();
|
||||
|
||||
for frame in 0..nframes {
|
||||
let trig_val = denorm::Sampl::trig(trig, frame);
|
||||
let triggered = self.trig.check_trigger(trig_val);
|
||||
|
@ -135,12 +162,36 @@ impl Sampl {
|
|||
|
||||
let prev_phase = self.phase;
|
||||
|
||||
let cur_offs =
|
||||
denorm::Sampl::offs(offs, frame)
|
||||
.abs().min(0.999999);
|
||||
if prev_offs != cur_offs {
|
||||
start_idx =
|
||||
(sample_data.len() as f32 * cur_offs)
|
||||
.floor() as usize;
|
||||
prev_offs = cur_offs;
|
||||
}
|
||||
|
||||
let cur_len =
|
||||
denorm::Sampl::len(len, frame)
|
||||
.abs().min(0.999999);
|
||||
if prev_len != cur_len {
|
||||
end_idx_plus1 =
|
||||
((sample_data.len() - start_idx) as f32
|
||||
* denorm::Sampl::len(len, frame).abs().min(0.999999))
|
||||
.ceil() as usize;
|
||||
prev_len = cur_len;
|
||||
}
|
||||
|
||||
let s = self.next_sample(
|
||||
sr_factor, playback_speed as f64, sample_data);
|
||||
sr_factor,
|
||||
playback_speed as f64,
|
||||
&sample_data[start_idx..(start_idx + end_idx_plus1)]);
|
||||
|
||||
out.write(frame, s);
|
||||
|
||||
if !do_loop && prev_phase > self.phase {
|
||||
// played past end => stop playing.
|
||||
if prev_phase > self.phase {
|
||||
is_playing = false;
|
||||
}
|
||||
} else {
|
||||
|
@ -171,11 +222,20 @@ impl DspNode for Sampl {
|
|||
let out = out::Sampl::sig(outputs);
|
||||
|
||||
if let SAtom::AudioSample((_, Some(sample_data))) = sample {
|
||||
if pmode.i() == 0 {
|
||||
self.play_loop(inputs, ctx.nframes(), &sample_data[..], out);
|
||||
} else {
|
||||
self.play_oneshot(inputs, ctx.nframes(), &sample_data[..], out);
|
||||
// 3 is for sample-sample-rate and at least 2 audio samples.
|
||||
if sample_data.len() < 3 {
|
||||
for frame in 0..ctx.nframes() {
|
||||
out.write(frame, 0.0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.play(
|
||||
inputs,
|
||||
ctx.nframes(),
|
||||
&sample_data[..],
|
||||
out,
|
||||
pmode.i() == 0);
|
||||
} else {
|
||||
for frame in 0..ctx.nframes() {
|
||||
out.write(frame, 0.0);
|
||||
|
|
437
tests/basics.rs
437
tests/basics.rs
|
@ -1,211 +1,5 @@
|
|||
use hexodsp::matrix::*;
|
||||
use hexodsp::nodes::new_node_engine;
|
||||
use hexodsp::dsp::*;
|
||||
|
||||
use hound;
|
||||
//use num_complex::Complex;
|
||||
use microfft;
|
||||
|
||||
macro_rules! assert_float_eq {
|
||||
($a:expr, $b:expr) => {
|
||||
if ($a - $b).abs() > 0.0001 {
|
||||
panic!(r#"assertion failed: `(left == right)`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#, $a, $b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SAMPLE_RATE : f32 = 44100.0;
|
||||
|
||||
fn save_wav(name: &str, buf: &[f32]) {
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate: SAMPLE_RATE as u32,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
|
||||
let mut writer = hound::WavWriter::create(name, spec).unwrap();
|
||||
for s in buf.iter() {
|
||||
let amp = i16::MAX as f32;
|
||||
writer.write_sample((amp * s) as i16).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_no_input(node_exec: &mut hexodsp::nodes::NodeExecutor, seconds: f32) -> (Vec<f32>, Vec<f32>) {
|
||||
run_realtime_no_input(node_exec, seconds, false)
|
||||
}
|
||||
|
||||
fn run_realtime_no_input(node_exec: &mut hexodsp::nodes::NodeExecutor, seconds: f32, sleep_a_bit: bool) -> (Vec<f32>, Vec<f32>) {
|
||||
node_exec.test_run(seconds, sleep_a_bit)
|
||||
}
|
||||
|
||||
fn calc_rms_mimax_each_ms(buf: &[f32], ms: f32) -> Vec<(f32, f32, f32)> {
|
||||
let ms_samples = ms * SAMPLE_RATE / 1000.0;
|
||||
let len_ms = ms_samples as usize;
|
||||
|
||||
let mut idx = 0;
|
||||
let mut res = vec![];
|
||||
loop {
|
||||
if (idx + len_ms) > buf.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut max = -1000.0;
|
||||
let mut min = 1000.0;
|
||||
for s in buf[idx..(idx + len_ms)].iter() {
|
||||
max = s.max(max);
|
||||
min = s.min(min);
|
||||
}
|
||||
|
||||
let rms : f32 =
|
||||
buf[idx..(idx + len_ms)]
|
||||
.iter()
|
||||
.map(|s: &f32| s * s).sum::<f32>()
|
||||
/ ms_samples;
|
||||
|
||||
res.push((rms, min, max));
|
||||
|
||||
idx += len_ms;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn run_and_undersample(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
run_len_ms: f32, samples: usize) -> Vec<f32>
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, run_len_ms / 1000.0);
|
||||
|
||||
let sample_interval = out_l.len() / samples;
|
||||
let mut out_samples = vec![];
|
||||
|
||||
for i in 0..samples {
|
||||
let idx = i * sample_interval;
|
||||
out_samples.push(out_l[idx]);
|
||||
}
|
||||
|
||||
out_samples
|
||||
}
|
||||
|
||||
fn run_and_get_each_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> Vec<(f32, f32, f32)>
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
calc_rms_mimax_each_ms(&out_l[..], len_ms)
|
||||
}
|
||||
|
||||
fn run_and_get_first_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> (f32, f32, f32)
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
let rms_mimax = calc_rms_mimax_each_ms(&out_l[..], len_ms);
|
||||
rms_mimax[0]
|
||||
}
|
||||
|
||||
fn run_and_get_l_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> (f32, f32, f32)
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
let rms_mimax = calc_rms_mimax_each_ms(&out_l[..], len_ms);
|
||||
rms_mimax[1]
|
||||
}
|
||||
|
||||
fn run_and_get_fft4096(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
thres: u32,
|
||||
offs_ms: f32) -> Vec<(u16, u32)>
|
||||
{
|
||||
let min_samples_for_fft = 4096.0;
|
||||
let offs_samples = (offs_ms * (SAMPLE_RATE / 1000.0)).ceil();
|
||||
let min_len_samples =
|
||||
offs_samples
|
||||
// 2.0 * for safety margin
|
||||
+ 2.0 * min_samples_for_fft;
|
||||
let run_len_s = min_len_samples / SAMPLE_RATE;
|
||||
let (mut out_l, _out_r) = run_no_input(node_exec, run_len_s);
|
||||
fft_thres_at_ms(&mut out_l[..], FFT::F4096, thres, offs_ms)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
enum FFT {
|
||||
F16,
|
||||
F32,
|
||||
F64,
|
||||
F128,
|
||||
F512,
|
||||
F1024,
|
||||
F2048,
|
||||
F4096,
|
||||
}
|
||||
|
||||
fn fft_thres_at_ms(buf: &mut [f32], size: FFT, amp_thres: u32, ms_idx: f32) -> Vec<(u16, u32)> {
|
||||
let ms_sample_offs = ms_idx * (SAMPLE_RATE / 1000.0);
|
||||
let fft_nbins = match size {
|
||||
FFT::F16 => 16,
|
||||
FFT::F32 => 32,
|
||||
FFT::F64 => 64,
|
||||
FFT::F128 => 128,
|
||||
FFT::F512 => 512,
|
||||
FFT::F1024 => 1024,
|
||||
FFT::F2048 => 2048,
|
||||
FFT::F4096 => 4096,
|
||||
};
|
||||
let len = fft_nbins;
|
||||
|
||||
let idx = ms_sample_offs as usize;
|
||||
let mut res = vec![];
|
||||
|
||||
if (idx + len) > buf.len() {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Hann window:
|
||||
for (i, s) in buf[idx..(idx + len)].iter_mut().enumerate() {
|
||||
let w =
|
||||
0.5
|
||||
* (1.0
|
||||
- ((2.0 * std::f32::consts::PI * i as f32)
|
||||
/ (fft_nbins as f32 - 1.0))
|
||||
.cos());
|
||||
*s *= w;
|
||||
}
|
||||
|
||||
let spec =
|
||||
match size {
|
||||
FFT::F16 =>
|
||||
microfft::real::rfft_16(&mut buf[idx..(idx + len)]),
|
||||
FFT::F32 =>
|
||||
microfft::real::rfft_32(&mut buf[idx..(idx + len)]),
|
||||
FFT::F64 =>
|
||||
microfft::real::rfft_64(&mut buf[idx..(idx + len)]),
|
||||
FFT::F128 =>
|
||||
microfft::real::rfft_128(&mut buf[idx..(idx + len)]),
|
||||
FFT::F512 =>
|
||||
microfft::real::rfft_512(&mut buf[idx..(idx + len)]),
|
||||
FFT::F1024 =>
|
||||
microfft::real::rfft_1024(&mut buf[idx..(idx + len)]),
|
||||
FFT::F2048 =>
|
||||
microfft::real::rfft_2048(&mut buf[idx..(idx + len)]),
|
||||
FFT::F4096 =>
|
||||
microfft::real::rfft_4096(&mut buf[idx..(idx + len)]),
|
||||
};
|
||||
let amplitudes: Vec<_> = spec.iter().map(|c| c.norm() as u32).collect();
|
||||
|
||||
for (i, amp) in amplitudes.iter().enumerate() {
|
||||
if *amp >= amp_thres {
|
||||
let freq = (i as f32 * SAMPLE_RATE) / fft_nbins as f32;
|
||||
res.push((freq.round() as u16, *amp));
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
#[test]
|
||||
fn check_matrix_sine() {
|
||||
|
@ -1122,230 +916,3 @@ fn check_matrix_output_feedback() {
|
|||
assert_float_eq!(fo_amp.1, 0.11627);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_1() {
|
||||
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();
|
||||
let freq_p = smpl.inp_param("freq").unwrap();
|
||||
matrix.set_param(sample_p, SAtom::audio_unloaded("tests/sample_sin.wav"));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.505);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (441, 940));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.1));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (894, 988));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.1));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (226, 966));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.2));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (108, 953));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.4));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (22, 818));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.5));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (11, 964));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.2));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (1776, 877));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.4));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (7127, 1029));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_reload() {
|
||||
{
|
||||
let (node_conf, _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("tests/sample_sin.wav"));
|
||||
|
||||
hexodsp::save_patch_to_file(&mut matrix, "check_matrix_serialize.hxy")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let (node_conf, mut node_exec) = new_node_engine();
|
||||
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||
|
||||
hexodsp::load_patch_from_file(
|
||||
&mut matrix, "check_matrix_serialize.hxy").unwrap();
|
||||
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.505);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (441, 940));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_load_err() {
|
||||
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("tests/sample_NOSIN.wav"));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
let err = matrix.pop_error();
|
||||
assert_eq!(err.unwrap(), "Sample Loading Error\nCouldn't load sample 'tests/sample_NOSIN.wav':\nLoadError(IoError(Os { code: 2, kind: NotFound, message: \"No such file or directory\" }))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_trigger() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
matrix.set_param(sample_p, SAtom::audio_unloaded("tests/sample_sin.wav"));
|
||||
matrix.set_param(pmode_p, SAtom::setting(1));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let (rms, min, max) = run_and_get_first_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.1136);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 20.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, -0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_trigger_reset_phase() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
|
||||
let mut test_sample_ramp = vec![0.0; 44101];
|
||||
test_sample_ramp[0] = 44100.0;
|
||||
for i in 0..(test_sample_ramp.len() - 1) {
|
||||
test_sample_ramp[i + 1] =
|
||||
(i as f32) / ((test_sample_ramp.len() - 2) as f32)
|
||||
}
|
||||
|
||||
matrix.set_param(sample_p,
|
||||
SAtom::audio(
|
||||
"1second_ramp.wav",
|
||||
std::sync::Arc::new(test_sample_ramp)));
|
||||
matrix.set_param(pmode_p, SAtom::setting(1));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.092496);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.19252);
|
||||
assert_float_eq!(max, 0.29250);
|
||||
|
||||
// lower trigger level, for retrigger later
|
||||
matrix.set_param(trig_p, (0.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 10.0);
|
||||
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.31252);
|
||||
assert_float_eq!(max, 0.32250);
|
||||
|
||||
// retrigger the phase sample
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
// this is the start of the phase
|
||||
assert_float_eq!(min, 0.0);
|
||||
// this is the last value of the previous triggering
|
||||
assert_float_eq!(max, 0.32998);
|
||||
|
||||
let (_rms, min, max) = rmsvec[1];
|
||||
assert_float_eq!(min, 0.09251);
|
||||
assert_float_eq!(max, 0.19249);
|
||||
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.19252);
|
||||
assert_float_eq!(max, 0.29250);
|
||||
}
|
||||
|
||||
|
||||
|
|
211
tests/common/mod.rs
Normal file
211
tests/common/mod.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
pub use hexodsp::matrix::*;
|
||||
pub use hexodsp::nodes::new_node_engine;
|
||||
pub use hexodsp::dsp::*;
|
||||
|
||||
use hound;
|
||||
//use num_complex::Complex;
|
||||
use microfft;
|
||||
|
||||
pub const SAMPLE_RATE : f32 = 44100.0;
|
||||
pub const SAMPLE_RATE_US : usize = 44100;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_float_eq {
|
||||
($a:expr, $b:expr) => {
|
||||
if ($a - $b).abs() > 0.0001 {
|
||||
panic!(r#"assertion failed: `(left == right)`
|
||||
left: `{:?}`,
|
||||
right: `{:?}`"#, $a, $b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_wav(name: &str, buf: &[f32]) {
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate: SAMPLE_RATE as u32,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
|
||||
let mut writer = hound::WavWriter::create(name, spec).unwrap();
|
||||
for s in buf.iter() {
|
||||
let amp = i16::MAX as f32;
|
||||
writer.write_sample((amp * s) as i16).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_no_input(node_exec: &mut hexodsp::nodes::NodeExecutor, seconds: f32) -> (Vec<f32>, Vec<f32>) {
|
||||
run_realtime_no_input(node_exec, seconds, false)
|
||||
}
|
||||
|
||||
pub fn run_realtime_no_input(node_exec: &mut hexodsp::nodes::NodeExecutor, seconds: f32, sleep_a_bit: bool) -> (Vec<f32>, Vec<f32>) {
|
||||
node_exec.test_run(seconds, sleep_a_bit)
|
||||
}
|
||||
|
||||
pub fn calc_rms_mimax_each_ms(buf: &[f32], ms: f32) -> Vec<(f32, f32, f32)> {
|
||||
let ms_samples = ms * SAMPLE_RATE / 1000.0;
|
||||
let len_ms = ms_samples as usize;
|
||||
|
||||
let mut idx = 0;
|
||||
let mut res = vec![];
|
||||
loop {
|
||||
if (idx + len_ms) > buf.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut max = -1000.0;
|
||||
let mut min = 1000.0;
|
||||
for s in buf[idx..(idx + len_ms)].iter() {
|
||||
max = s.max(max);
|
||||
min = s.min(min);
|
||||
}
|
||||
|
||||
let rms : f32 =
|
||||
buf[idx..(idx + len_ms)]
|
||||
.iter()
|
||||
.map(|s: &f32| s * s).sum::<f32>()
|
||||
/ ms_samples;
|
||||
|
||||
res.push((rms, min, max));
|
||||
|
||||
idx += len_ms;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn run_and_undersample(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
run_len_ms: f32, samples: usize) -> Vec<f32>
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, run_len_ms / 1000.0);
|
||||
|
||||
let sample_interval = out_l.len() / samples;
|
||||
let mut out_samples = vec![];
|
||||
|
||||
for i in 0..samples {
|
||||
let idx = i * sample_interval;
|
||||
out_samples.push(out_l[idx]);
|
||||
}
|
||||
|
||||
out_samples
|
||||
}
|
||||
|
||||
pub fn run_and_get_each_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> Vec<(f32, f32, f32)>
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
calc_rms_mimax_each_ms(&out_l[..], len_ms)
|
||||
}
|
||||
|
||||
pub fn run_and_get_first_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> (f32, f32, f32)
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
let rms_mimax = calc_rms_mimax_each_ms(&out_l[..], len_ms);
|
||||
rms_mimax[0]
|
||||
}
|
||||
|
||||
pub fn run_and_get_l_rms_mimax(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
len_ms: f32) -> (f32, f32, f32)
|
||||
{
|
||||
let (out_l, _out_r) = run_no_input(node_exec, (len_ms * 3.0) / 1000.0);
|
||||
let rms_mimax = calc_rms_mimax_each_ms(&out_l[..], len_ms);
|
||||
rms_mimax[1]
|
||||
}
|
||||
|
||||
pub fn run_and_get_fft4096(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
thres: u32,
|
||||
offs_ms: f32) -> Vec<(u16, u32)>
|
||||
{
|
||||
let min_samples_for_fft = 4096.0;
|
||||
let offs_samples = (offs_ms * (SAMPLE_RATE / 1000.0)).ceil();
|
||||
let min_len_samples =
|
||||
offs_samples
|
||||
// 2.0 * for safety margin
|
||||
+ 2.0 * min_samples_for_fft;
|
||||
let run_len_s = min_len_samples / SAMPLE_RATE;
|
||||
let (mut out_l, _out_r) = run_no_input(node_exec, run_len_s);
|
||||
fft_thres_at_ms(&mut out_l[..], FFT::F4096, thres, offs_ms)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum FFT {
|
||||
F16,
|
||||
F32,
|
||||
F64,
|
||||
F128,
|
||||
F512,
|
||||
F1024,
|
||||
F2048,
|
||||
F4096,
|
||||
}
|
||||
|
||||
pub fn fft_thres_at_ms(buf: &mut [f32], size: FFT, amp_thres: u32, ms_idx: f32) -> Vec<(u16, u32)> {
|
||||
let ms_sample_offs = ms_idx * (SAMPLE_RATE / 1000.0);
|
||||
let fft_nbins = match size {
|
||||
FFT::F16 => 16,
|
||||
FFT::F32 => 32,
|
||||
FFT::F64 => 64,
|
||||
FFT::F128 => 128,
|
||||
FFT::F512 => 512,
|
||||
FFT::F1024 => 1024,
|
||||
FFT::F2048 => 2048,
|
||||
FFT::F4096 => 4096,
|
||||
};
|
||||
let len = fft_nbins;
|
||||
|
||||
let idx = ms_sample_offs as usize;
|
||||
let mut res = vec![];
|
||||
|
||||
if (idx + len) > buf.len() {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Hann window:
|
||||
for (i, s) in buf[idx..(idx + len)].iter_mut().enumerate() {
|
||||
let w =
|
||||
0.5
|
||||
* (1.0
|
||||
- ((2.0 * std::f32::consts::PI * i as f32)
|
||||
/ (fft_nbins as f32 - 1.0))
|
||||
.cos());
|
||||
*s *= w;
|
||||
}
|
||||
|
||||
let spec =
|
||||
match size {
|
||||
FFT::F16 =>
|
||||
microfft::real::rfft_16(&mut buf[idx..(idx + len)]),
|
||||
FFT::F32 =>
|
||||
microfft::real::rfft_32(&mut buf[idx..(idx + len)]),
|
||||
FFT::F64 =>
|
||||
microfft::real::rfft_64(&mut buf[idx..(idx + len)]),
|
||||
FFT::F128 =>
|
||||
microfft::real::rfft_128(&mut buf[idx..(idx + len)]),
|
||||
FFT::F512 =>
|
||||
microfft::real::rfft_512(&mut buf[idx..(idx + len)]),
|
||||
FFT::F1024 =>
|
||||
microfft::real::rfft_1024(&mut buf[idx..(idx + len)]),
|
||||
FFT::F2048 =>
|
||||
microfft::real::rfft_2048(&mut buf[idx..(idx + len)]),
|
||||
FFT::F4096 =>
|
||||
microfft::real::rfft_4096(&mut buf[idx..(idx + len)]),
|
||||
};
|
||||
let amplitudes: Vec<_> = spec.iter().map(|c| c.norm() as u32).collect();
|
||||
|
||||
for (i, amp) in amplitudes.iter().enumerate() {
|
||||
if *amp >= amp_thres {
|
||||
let freq = (i as f32 * SAMPLE_RATE) / fft_nbins as f32;
|
||||
res.push((freq.round() as u16, *amp));
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
370
tests/node_sampl.rs
Normal file
370
tests/node_sampl.rs
Normal file
|
@ -0,0 +1,370 @@
|
|||
mod common;
|
||||
use common::*;
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_1() {
|
||||
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();
|
||||
let freq_p = smpl.inp_param("freq").unwrap();
|
||||
matrix.set_param(sample_p, SAtom::audio_unloaded("tests/sample_sin.wav"));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.505);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (441, 940));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.1));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (894, 988));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.1));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (226, 966));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.2));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (108, 953));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.4));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (22, 818));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(-0.5));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (11, 964));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.2));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (1776, 877));
|
||||
|
||||
matrix.set_param(freq_p, SAtom::param(0.4));
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (7127, 1029));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_reload() {
|
||||
{
|
||||
let (node_conf, _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("tests/sample_sin.wav"));
|
||||
|
||||
hexodsp::save_patch_to_file(&mut matrix, "check_matrix_serialize.hxy")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let (node_conf, mut node_exec) = new_node_engine();
|
||||
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||
|
||||
hexodsp::load_patch_from_file(
|
||||
&mut matrix, "check_matrix_serialize.hxy").unwrap();
|
||||
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.505);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let fft = run_and_get_fft4096(&mut node_exec, 800, 20.0);
|
||||
assert_eq!(fft[0], (441, 940));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_load_err() {
|
||||
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("tests/sample_NOSIN.wav"));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 50.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
let err = matrix.pop_error();
|
||||
assert_eq!(err.unwrap(), "Sample Loading Error\nCouldn't load sample 'tests/sample_NOSIN.wav':\nLoadError(IoError(Os { code: 2, kind: NotFound, message: \"No such file or directory\" }))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_trigger() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
matrix.set_param(sample_p, SAtom::audio_unloaded("tests/sample_sin.wav"));
|
||||
matrix.set_param(pmode_p, SAtom::setting(1));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let (rms, min, max) = run_and_get_first_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.1136);
|
||||
assert_float_eq!(min, -0.9998);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 20.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, -0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
}
|
||||
|
||||
fn create_1sec_ramp() -> SAtom {
|
||||
let mut test_sample_ramp = vec![0.0; (SAMPLE_RATE_US) + 1];
|
||||
test_sample_ramp[0] = SAMPLE_RATE;
|
||||
for i in 0..(test_sample_ramp.len() - 1) {
|
||||
test_sample_ramp[i + 1] =
|
||||
(i as f32) / ((test_sample_ramp.len() - 2) as f32)
|
||||
}
|
||||
|
||||
SAtom::audio(
|
||||
"1second_ramp.wav",
|
||||
std::sync::Arc::new(test_sample_ramp))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_trigger_reset_phase() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
|
||||
matrix.set_param(sample_p, create_1sec_ramp());
|
||||
// One Shot Mode
|
||||
matrix.set_param(pmode_p, SAtom::setting(1));
|
||||
|
||||
let (rms, min, max) = run_and_get_l_rms_mimax(&mut node_exec, 10.0);
|
||||
assert_float_eq!(rms, 0.0);
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.092496);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.19252);
|
||||
assert_float_eq!(max, 0.29250);
|
||||
|
||||
// lower trigger level, for retrigger later
|
||||
matrix.set_param(trig_p, (0.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 10.0);
|
||||
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.31252);
|
||||
assert_float_eq!(max, 0.32250);
|
||||
|
||||
// retrigger the phase sample
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
// this is the start of the phase
|
||||
assert_float_eq!(min, 0.0);
|
||||
// this is the last value of the previous triggering
|
||||
assert_float_eq!(max, 0.32998);
|
||||
|
||||
let (_rms, min, max) = rmsvec[1];
|
||||
assert_float_eq!(min, 0.09251);
|
||||
assert_float_eq!(max, 0.19249);
|
||||
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.19252);
|
||||
assert_float_eq!(max, 0.29250);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_trigger_loop_reset_phase() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
|
||||
matrix.set_param(sample_p, create_1sec_ramp());
|
||||
// Loop mode:
|
||||
matrix.set_param(pmode_p, SAtom::setting(0));
|
||||
|
||||
matrix.set_param(trig_p, (0.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.09999);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.2);
|
||||
assert_float_eq!(max, 0.2999);
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 100.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.3074);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.1925);
|
||||
assert_float_eq!(max, 0.2925);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_offs_len() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let offs_p = smpl.inp_param("offs").unwrap();
|
||||
let len_p = smpl.inp_param("len").unwrap();
|
||||
|
||||
matrix.set_param(sample_p, create_1sec_ramp());
|
||||
matrix.set_param(pmode_p, SAtom::setting(0));
|
||||
|
||||
// Select part 0.5 to 0.75 of the sample:
|
||||
matrix.set_param(offs_p, SAtom::param(0.5));
|
||||
matrix.set_param(len_p, SAtom::param(0.5));
|
||||
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0011133);
|
||||
assert_float_eq!(max, 0.54999);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.6);
|
||||
assert_float_eq!(max, 0.65);
|
||||
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.65);
|
||||
assert_float_eq!(max, 0.6999);
|
||||
let (_rms, min, max) = rmsvec[1];
|
||||
assert_float_eq!(min, 0.70);
|
||||
assert_float_eq!(max, 0.75);
|
||||
let (_rms, min, max) = rmsvec[2];
|
||||
assert_float_eq!(min, 0.5);
|
||||
assert_float_eq!(max, 0.55);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_offs_len_zero_crash() {
|
||||
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();
|
||||
let pmode_p = smpl.inp_param("pmode").unwrap();
|
||||
let offs_p = smpl.inp_param("offs").unwrap();
|
||||
let len_p = smpl.inp_param("len").unwrap();
|
||||
let trig_p = smpl.inp_param("trig").unwrap();
|
||||
|
||||
matrix.set_param(sample_p, create_1sec_ramp());
|
||||
matrix.set_param(pmode_p, SAtom::setting(0));
|
||||
|
||||
// Select part 0.5 to 0.75 of the sample:
|
||||
matrix.set_param(offs_p, SAtom::param(1.0));
|
||||
matrix.set_param(len_p, SAtom::param(0.0));
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 1.0);
|
||||
|
||||
// Select part 0.5 to 0.75 of the sample:
|
||||
matrix.set_param(offs_p, SAtom::param(0.9));
|
||||
matrix.set_param(len_p, SAtom::param(0.0));
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
|
||||
// Select part 0.5 to 0.75 of the sample:
|
||||
matrix.set_param(offs_p, SAtom::param(1.0));
|
||||
matrix.set_param(len_p, SAtom::param(0.0));
|
||||
|
||||
matrix.set_param(trig_p, (1.0).into());
|
||||
let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0);
|
||||
let (_rms, min, max) = rmsvec[0];
|
||||
assert_float_eq!(min, 0.0);
|
||||
assert_float_eq!(max, 0.0);
|
||||
}
|
Loading…
Reference in a new issue