Wrote gmode Trigger and a test for it
This commit is contained in:
parent
c175594b1f
commit
5be2c93c45
3 changed files with 85 additions and 5 deletions
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use crate::dsp::{at, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{at, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{HxMidiEvent, MidiEventPointer, NodeAudioContext, NodeExecContext};
|
use crate::nodes::{HxMidiEvent, MidiEventPointer, NodeAudioContext, NodeExecContext};
|
||||||
|
use synfx_dsp::TrigSignal;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! fa_midip_chan {
|
macro_rules! fa_midip_chan {
|
||||||
|
@ -32,16 +33,20 @@ pub struct MidiP {
|
||||||
cur_note: u8,
|
cur_note: u8,
|
||||||
cur_gate: u8,
|
cur_gate: u8,
|
||||||
cur_vel: f32,
|
cur_vel: f32,
|
||||||
|
trig_sig: TrigSignal,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiP {
|
impl MidiP {
|
||||||
pub fn new(_nid: &NodeId) -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self { next_gate: 0, cur_note: 0, cur_gate: 0, cur_vel: 0.0 }
|
Self { next_gate: 0, cur_note: 0, cur_gate: 0, cur_vel: 0.0, trig_sig: TrigSignal::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const chan: &'static str = "MidiP chan\nMIDI Channel 0 to 15\n";
|
pub const chan: &'static str = "MidiP chan\nMIDI Channel 0 to 15\n";
|
||||||
pub const gmode: &'static str = "MidiP gmode\nMIDI gate mode.\n- 'MIDI' gate same as MIDI input\n- 'Trigger' output only triggers on 'gate' output\n- 'Gate Len' output gate with the length of the 'gatel' parameter\n";
|
pub const gmode: &'static str = "MidiP gmode\nMIDI gate mode.\n- 'MIDI' gate same as MIDI input\n- 'Trigger' output only triggers on 'gate' output\n- 'Gate Len' output gate with the length of the 'gatel' parameter\n";
|
||||||
pub const glen: &'static str = "MidiP glen\nMIDI gate length\nIf 'gmode' is set to 'Gate Len' this controls and overrides the gate length on a MIDI note event.";
|
pub const glen: &'static str = "MidiP glen\nMIDI gate length\n\
|
||||||
|
If 'gmode' is set to 'Gate Len' this controls and overrides the gate length on a MIDI \
|
||||||
|
note event. 'Trigger' will just send a short trigger when a note event is received. \
|
||||||
|
'MIDI' means the gate reflects the note on/off duration.";
|
||||||
pub const det: &'static str = "MidiP det\nDetune input pitch a bit\nRange: (-1..1)";
|
pub const det: &'static str = "MidiP det\nDetune input pitch a bit\nRange: (-1..1)";
|
||||||
pub const freq: &'static str =
|
pub const freq: &'static str =
|
||||||
"MidiP freq\nMIDI note frequency, detuned by 'det'.\nRange: (-1..1)";
|
"MidiP freq\nMIDI note frequency, detuned by 'det'.\nRange: (-1..1)";
|
||||||
|
@ -84,8 +89,12 @@ impl DspNode for MidiP {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_sample_rate(&mut self, _srate: f32) {}
|
fn set_sample_rate(&mut self, srate: f32) {
|
||||||
fn reset(&mut self) {}
|
self.trig_sig.set_sample_rate(srate);
|
||||||
|
}
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.trig_sig.reset();
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process<T: NodeAudioContext>(
|
fn process<T: NodeAudioContext>(
|
||||||
|
@ -100,6 +109,7 @@ impl DspNode for MidiP {
|
||||||
) {
|
) {
|
||||||
let det = inp::MidiP::det(inputs);
|
let det = inp::MidiP::det(inputs);
|
||||||
let chan = at::MidiP::chan(atoms);
|
let chan = at::MidiP::chan(atoms);
|
||||||
|
let gmode = at::MidiP::gmode(atoms);
|
||||||
let out_i = out_idx::MidiP::gate();
|
let out_i = out_idx::MidiP::gate();
|
||||||
let (freq, r) = outputs.split_at_mut(out_i);
|
let (freq, r) = outputs.split_at_mut(out_i);
|
||||||
let (gate, vel) = r.split_at_mut(1);
|
let (gate, vel) = r.split_at_mut(1);
|
||||||
|
@ -111,6 +121,8 @@ impl DspNode for MidiP {
|
||||||
|
|
||||||
let mut ptr = MidiEventPointer::new(&ectx.midi_notes[..]);
|
let mut ptr = MidiEventPointer::new(&ectx.midi_notes[..]);
|
||||||
|
|
||||||
|
let gmode = gmode.i();
|
||||||
|
|
||||||
for frame in 0..ctx.nframes() {
|
for frame in 0..ctx.nframes() {
|
||||||
if self.next_gate > 0 {
|
if self.next_gate > 0 {
|
||||||
self.cur_gate = 1;
|
self.cur_gate = 1;
|
||||||
|
@ -132,6 +144,7 @@ impl DspNode for MidiP {
|
||||||
} else {
|
} else {
|
||||||
self.cur_gate = 1;
|
self.cur_gate = 1;
|
||||||
}
|
}
|
||||||
|
self.trig_sig.trigger();
|
||||||
self.cur_note = note;
|
self.cur_note = note;
|
||||||
self.cur_vel = vel;
|
self.cur_vel = vel;
|
||||||
}
|
}
|
||||||
|
@ -148,11 +161,19 @@ impl DspNode for MidiP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match gmode {
|
||||||
|
1 => {
|
||||||
|
gate.write(frame, self.trig_sig.next());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
gate.write(frame, if self.cur_gate > 0 { 1.0 } else { 0.0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let note = (self.cur_note as f32 - 69.0) / 120.0;
|
let note = (self.cur_note as f32 - 69.0) / 120.0;
|
||||||
let note = note + det.read(frame);
|
let note = note + det.read(frame);
|
||||||
//d// println!("FRAME: {} => gate={}, freq={}, next_gate={}", frame, self.cur_gate, note, self.next_gate);
|
//d// println!("FRAME: {} => gate={}, freq={}, next_gate={}", frame, self.cur_gate, note, self.next_gate);
|
||||||
freq.write(frame, note);
|
freq.write(frame, note);
|
||||||
gate.write(frame, if self.cur_gate > 0 { 1.0 } else { 0.0 });
|
|
||||||
vel.write(frame, self.cur_vel as f32);
|
vel.write(frame, self.cur_vel as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,6 +335,27 @@ pub fn collect_signal_changes(inp: &[f32], thres: i64) -> Vec<(usize, i64)> {
|
||||||
return idxs_big;
|
return idxs_big;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn collect_signal_changes_both_edges(inp: &[f32], thres: i64) -> Vec<(usize, i64)> {
|
||||||
|
let mut idxs = vec![];
|
||||||
|
let mut last_sig = 0.0;
|
||||||
|
for i in 0..inp.len() {
|
||||||
|
if (inp[i] - last_sig).abs() > 0.1 {
|
||||||
|
idxs.push((i, ((inp[i] - last_sig) * 100.0).floor() as i64));
|
||||||
|
last_sig = inp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut idxs_big = vec![];
|
||||||
|
for v in idxs.iter() {
|
||||||
|
if v.1.abs() > thres {
|
||||||
|
idxs_big.push(*v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idxs_big;
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn collect_signal_changes_flt(inp: &[f32], delta: f32) -> Vec<(usize, f32)> {
|
pub fn collect_signal_changes_flt(inp: &[f32], delta: f32) -> Vec<(usize, f32)> {
|
||||||
let mut idxs = vec![];
|
let mut idxs = vec![];
|
||||||
|
|
|
@ -151,3 +151,41 @@ fn check_node_midip_off_on_test() {
|
||||||
let changes = collect_signal_changes(&ch1[..], -1);
|
let changes = collect_signal_changes(&ch1[..], -1);
|
||||||
assert_eq!(changes, vec![(0, 100), (5, 0), (6, 100)]);
|
assert_eq!(changes, vec![(0, 100), (5, 0), (6, 100)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_midip_trigger_test() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
|
let mut chain = MatrixCellChain::new(CellDir::B);
|
||||||
|
chain
|
||||||
|
.node_out("midip", "gate")
|
||||||
|
.set_denorm("det", 0.1)
|
||||||
|
.set_atom("gmode", SAtom::setting(1))
|
||||||
|
.node_inp("out", "ch1")
|
||||||
|
.place(&mut matrix, 0, 0)
|
||||||
|
.unwrap();
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let (ch1, _) = node_exec.test_run(
|
||||||
|
0.015,
|
||||||
|
false,
|
||||||
|
&[HxTimedEvent::note_on(100, 0, 69, 1.0), HxTimedEvent::note_off(105, 0, 69)],
|
||||||
|
);
|
||||||
|
|
||||||
|
let changes = collect_signal_changes_both_edges(&ch1[..], 1);
|
||||||
|
assert_eq!(changes, vec![(100, 100), (189, -100)]);
|
||||||
|
|
||||||
|
// Now test without the trigger signal:
|
||||||
|
node_pset_s(&mut matrix, "midip", 0, "gmode", 0);
|
||||||
|
|
||||||
|
let (ch1, _) = node_exec.test_run(
|
||||||
|
0.015,
|
||||||
|
false,
|
||||||
|
&[HxTimedEvent::note_on(100, 0, 69, 1.0), HxTimedEvent::note_off(105, 0, 69)],
|
||||||
|
);
|
||||||
|
|
||||||
|
let changes = collect_signal_changes_both_edges(&ch1[..], 1);
|
||||||
|
// As expected, now end of note at 106:
|
||||||
|
assert_eq!(changes, vec![(100, 100), (106, -100)]);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue