applied detune to the sampler and sin, added tests that need to be fixed
This commit is contained in:
parent
79c3e36eab
commit
a9ac28e351
6 changed files with 147 additions and 12 deletions
|
@ -254,8 +254,8 @@ macro_rules! d_pit { ($x: expr) => {
|
|||
// 5.0 * 0.1 => 12.0
|
||||
// 5.0 * 0.008333333 => 1.0
|
||||
// 5.0 * 0.000083333 => 0.001
|
||||
macro_rules! n_det { ($x: expr) => { ($x * 5.0 * 0.1) / 12.0 } }
|
||||
macro_rules! d_det { ($x: expr) => { $x * 0.2 * 120.0 } }
|
||||
macro_rules! n_det { ($x: expr) => { $x / 120.0 } }
|
||||
macro_rules! d_det { ($x: expr) => { $x * 120.0 } }
|
||||
|
||||
// Rounding function that does nothing
|
||||
macro_rules! r_id { ($x: expr) => { $x } }
|
||||
|
@ -344,9 +344,10 @@ macro_rules! node_list {
|
|||
(2 offs n_id n_id r_id f_def 0.0, 1.0, 0.0)
|
||||
(3 len n_id n_id r_id f_def 0.0, 1.0, 1.0)
|
||||
(4 dcms n_declick d_declick r_id f_def 0.0, 1.0, 3.14)
|
||||
{5 0 sample audio_unloaded("") f_def 0 0}
|
||||
{6 1 pmode setting(0) fa_sampl_pmode 0 1}
|
||||
{7 2 dclick setting(0) fa_sampl_dclick 0 1}
|
||||
(5 det n_det d_det r_id f_det -1.0, 1.0, 0.0)
|
||||
{6 0 sample audio_unloaded("") f_def 0 0}
|
||||
{7 1 pmode setting(0) fa_sampl_pmode 0 1}
|
||||
{8 2 dclick setting(0) fa_sampl_dclick 0 1}
|
||||
[0 sig],
|
||||
sin => Sin UIType::Generic UICategory::Osc
|
||||
(0 freq n_pit d_pit r_id f_freq -1.0, 1.0, 440.0)
|
||||
|
@ -857,6 +858,15 @@ macro_rules! make_node_info_enum {
|
|||
})+
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub mod denorm_offs {
|
||||
$(pub mod $variant {
|
||||
$(#[inline] pub fn $para(buf: &crate::dsp::ProcBuf, offs_val: f32, frame: usize) -> f32 {
|
||||
$d_fun!(buf.read(frame) + offs_val)
|
||||
})*
|
||||
})+
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub mod inp_dir {
|
||||
$(pub mod $variant {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals};
|
||||
use crate::dsp::{out, at, inp, denorm}; //, inp, denorm, denorm_v, inp_dir, at};
|
||||
use crate::dsp::{out, at, inp, denorm, denorm_offs}; //, inp, denorm, denorm_v, inp_dir, at};
|
||||
use super::helpers::Trigger;
|
||||
|
||||
#[macro_export]
|
||||
|
@ -64,6 +64,14 @@ impl Sampl {
|
|||
"Sampl len\nLength of the sample, after the offset has been applied.\nRange: (0..1)\n";
|
||||
pub const dcms : &'static str =
|
||||
"Sampl dcms\nDeclick fade time in milliseconds.\nNot audio rate!\nRange: (0..1)\n";
|
||||
pub const det : &'static str =
|
||||
"Sin det\nDetune the oscillator in semitones and cents. \
|
||||
the input of this value is rounded to semitones on coarse input. \
|
||||
Fine input lets you detune in cents (rounded). \
|
||||
A signal sent to this port is not rounded.\n\
|
||||
Note: The signal input allows detuning over +- 10 octaves.\
|
||||
\n\nKnob Range: (-0.2 .. 0.2)\n\
|
||||
Signal Range: (-1.0 .. 1.0)\n";
|
||||
|
||||
pub const sample : &'static str =
|
||||
"Sampl sample\nThe audio sample that is played back.\nRange: (-1..1)\n";
|
||||
|
@ -128,6 +136,7 @@ impl Sampl {
|
|||
let offs = inp::Sampl::offs(inputs);
|
||||
let len = inp::Sampl::len(inputs);
|
||||
let dcms = inp::Sampl::dcms(inputs);
|
||||
let det = inp::Sampl::det(inputs);
|
||||
|
||||
let sample_srate = sample_data[0] as f64;
|
||||
let sample_data = &sample_data[1..];
|
||||
|
@ -161,8 +170,10 @@ impl Sampl {
|
|||
|
||||
let s =
|
||||
if is_playing {
|
||||
let playback_speed =
|
||||
denorm::Sampl::freq(freq, frame) / 440.0;
|
||||
let freq =
|
||||
denorm_offs::Sampl::freq(
|
||||
freq, det.read(frame), frame);
|
||||
let playback_speed = freq / 440.0;
|
||||
|
||||
let prev_phase = self.phase;
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
use crate::dsp::{NodeId, SAtom, ProcBuf, denorm, out, inp, DspNode, LedPhaseVals};
|
||||
use crate::dsp::{
|
||||
NodeId, SAtom, ProcBuf, denorm, denorm_offs,
|
||||
out, inp, DspNode, LedPhaseVals
|
||||
};
|
||||
use crate::dsp::helpers::fast_sin;
|
||||
|
||||
|
||||
|
@ -31,8 +34,9 @@ impl Sin {
|
|||
"Sin det\nDetune the oscillator in semitones and cents. \
|
||||
the input of this value is rounded to semitones on coarse input. \
|
||||
Fine input lets you detune in cents (rounded). \
|
||||
A signal sent to this port is not rounded.\
|
||||
\n\nRange: (-1..1)\n";
|
||||
A signal sent to this port is not rounded.\n\
|
||||
Note: The signal input allows detune +-10 octaves.\
|
||||
\nRange: (Knob -0.2 .. 0.2) / (Signal -1.0 .. 1.0)\n";
|
||||
pub const sig : &'static str =
|
||||
"Sin sig\nOscillator signal output.\n\nRange: (-1..1)\n";
|
||||
}
|
||||
|
@ -56,11 +60,12 @@ impl DspNode for Sin {
|
|||
{
|
||||
let o = out::Sin::sig(outputs);
|
||||
let freq = inp::Sin::freq(inputs);
|
||||
let det = inp::Sin::det(inputs);
|
||||
let isr = 1.0 / self.srate;
|
||||
|
||||
let mut last_val = 0.0;
|
||||
for frame in 0..ctx.nframes() {
|
||||
let freq = denorm::Sin::freq(freq, frame);
|
||||
let freq = denorm_offs::Sampl::freq(freq, det.read(frame), frame);
|
||||
|
||||
last_val = fast_sin(self.phase * TWOPI);
|
||||
o.write(frame, last_val);
|
||||
|
|
|
@ -137,6 +137,59 @@ fn check_sine_pitch_change() {
|
|||
assert_eq!(fft_res[0], (4393, 251));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_detune_parameter() {
|
||||
let sin = NodeId::Sin(0);
|
||||
let det_param = sin.inp_param("det").unwrap();
|
||||
assert_float_eq!(det_param.norm(12.0), 0.1);
|
||||
assert_float_eq!(det_param.norm(-12.0), -0.1);
|
||||
assert_float_eq!(det_param.norm(24.0), 0.2);
|
||||
assert_float_eq!(det_param.norm(-24.0), -0.2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sine_freq_detune() {
|
||||
let (node_conf, mut node_exec) = new_node_engine();
|
||||
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||
|
||||
let sin = NodeId::Sin(0);
|
||||
let out = NodeId::Out(0);
|
||||
matrix.place(0, 0, Cell::empty(sin)
|
||||
.out(None, sin.out("sig"), None));
|
||||
matrix.place(1, 0, Cell::empty(out)
|
||||
.input(None, out.inp("ch1"), None));
|
||||
matrix.sync().unwrap();
|
||||
|
||||
let freq_param = sin.inp_param("freq").unwrap();
|
||||
let det_param = sin.inp_param("det").unwrap();
|
||||
|
||||
run_no_input(&mut node_exec, 50.0);
|
||||
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 100.0);
|
||||
assert_float_eq!(cfreq.floor(), 440.0);
|
||||
|
||||
matrix.set_param(freq_param, SAtom::param(freq_param.norm(4400.0)));
|
||||
run_no_input(&mut node_exec, 50.0);
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 1000.0);
|
||||
assert_float_eq!(cfreq.floor(), 4400.0);
|
||||
|
||||
matrix.set_param(freq_param, SAtom::param(freq_param.norm(50.0)));
|
||||
run_no_input(&mut node_exec, 50.0);
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 100.0);
|
||||
assert_float_eq!(cfreq.floor(), 50.0);
|
||||
|
||||
matrix.set_param(freq_param, SAtom::param(freq_param.norm(440.0)));
|
||||
matrix.set_param(det_param, SAtom::param(det_param.norm(12.0)));
|
||||
run_no_input(&mut node_exec, 50.0);
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 1000.0);
|
||||
assert_float_eq!(cfreq.floor(), 880.0);
|
||||
|
||||
matrix.set_param(det_param, SAtom::param(det_param.norm(1.0)));
|
||||
run_no_input(&mut node_exec, 50.0);
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 1000.0);
|
||||
assert_float_eq!(cfreq.floor(), 493.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_matrix_monitor() {
|
||||
let (node_conf, mut node_exec) = new_node_engine();
|
||||
|
|
|
@ -201,6 +201,40 @@ pub fn run_and_get_l_rms_mimax(
|
|||
rms_mimax[1]
|
||||
}
|
||||
|
||||
pub fn run_and_get_counted_freq(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor, ms: f32)
|
||||
-> f64
|
||||
{
|
||||
let (out_l, _out_r) =
|
||||
// +0.1 here for some extra samples
|
||||
// this is just for tuning the frequency counter, so that it detects
|
||||
// the last swing correctly. It's probably wrong, but the results
|
||||
// match up better this way.
|
||||
run_no_input(node_exec, (ms + 0.1) / 1000.0);
|
||||
|
||||
let mut zero_trans = 0;
|
||||
let mut last_val = 0.0;
|
||||
|
||||
for s in out_l.iter() {
|
||||
if last_val > 0.0 && *s < 0.0 {
|
||||
zero_trans += 1;
|
||||
} else if last_val < 0.0 && *s > 0.0 {
|
||||
zero_trans += 1;
|
||||
}
|
||||
|
||||
last_val = *s;
|
||||
}
|
||||
|
||||
//d// println!("SAMPLES: {}", out_l.len());
|
||||
//d// println!("ZERO TRANS: {}", zero_trans);
|
||||
|
||||
let trans_per_sample =
|
||||
// substract the extra samples applied earlier.
|
||||
(zero_trans as f64) / ((out_l.len() - 4) as f64);
|
||||
trans_per_sample * 44100.0 * 0.5
|
||||
}
|
||||
|
||||
|
||||
pub fn run_and_get_fft4096(
|
||||
node_exec: &mut hexodsp::nodes::NodeExecutor,
|
||||
thres: u32,
|
||||
|
|
|
@ -53,6 +53,28 @@ fn check_node_sampl_1() {
|
|||
assert_eq!(fft[0], (7127, 1029));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_detune() {
|
||||
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();
|
||||
let det_p = smpl.inp_param("det").unwrap();
|
||||
matrix.set_param(sample_p, SAtom::audio_unloaded("tests/sample_sin.wav"));
|
||||
|
||||
let cfreq = run_and_get_counted_freq(&mut node_exec, 1000.0);
|
||||
assert_float_eq!(cfreq, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sampl_reload() {
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue