Made the first MidiP test work properly
This commit is contained in:
parent
22698c3a1a
commit
60281a86b7
6 changed files with 97 additions and 17 deletions
|
@ -94,6 +94,7 @@ impl DspNode for MidiP {
|
||||||
|
|
||||||
let note = (chan.note as f32 - 57.0) / 120.0;
|
let note = (chan.note as f32 - 57.0) / 120.0;
|
||||||
freq.write(frame, note);
|
freq.write(frame, note);
|
||||||
|
println!("FRAME: {}, gate={}, freq={}", frame, chan.gate, chan.note);
|
||||||
|
|
||||||
if chan.gate > 0 {
|
if chan.gate > 0 {
|
||||||
// insert a single sample of silence, for retriggering
|
// insert a single sample of silence, for retriggering
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub use node_conf::*;
|
||||||
pub use node_exec::*;
|
pub use node_exec::*;
|
||||||
pub use node_graph_ordering::NodeGraphOrdering;
|
pub use node_graph_ordering::NodeGraphOrdering;
|
||||||
pub use node_prog::*;
|
pub use node_prog::*;
|
||||||
pub use note_buffer::{NoteBuffer, NoteChannelState};
|
pub use note_buffer::{NoteBuffer, NoteChannelState, HxTimedEvent, HxMidiEvent, EventWindowing};
|
||||||
|
|
||||||
use crate::dsp::{Node, SAtom};
|
use crate::dsp::{Node, SAtom};
|
||||||
pub use crate::monitor::MinMaxMonitorSamples;
|
pub use crate::monitor::MinMaxMonitorSamples;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use super::NoteBuffer;
|
use super::NoteBuffer;
|
||||||
use super::{
|
use super::{
|
||||||
DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, MAX_FB_DELAY_SIZE,
|
DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, MAX_FB_DELAY_SIZE,
|
||||||
MAX_SMOOTHERS, UNUSED_MONITOR_IDX,
|
MAX_SMOOTHERS, UNUSED_MONITOR_IDX, HxTimedEvent, EventWindowing, HxMidiEvent,
|
||||||
};
|
};
|
||||||
use crate::dsp::{Node, NodeContext, NodeId, MAX_BLOCK_SIZE};
|
use crate::dsp::{Node, NodeContext, NodeId, MAX_BLOCK_SIZE};
|
||||||
use crate::monitor::{MonitorBackend, MON_SIG_CNT};
|
use crate::monitor::{MonitorBackend, MON_SIG_CNT};
|
||||||
|
@ -521,11 +521,13 @@ impl NodeExecutor {
|
||||||
///
|
///
|
||||||
/// You can use it's source as reference for your own audio
|
/// You can use it's source as reference for your own audio
|
||||||
/// DSP thread processing function.
|
/// DSP thread processing function.
|
||||||
pub fn test_run(&mut self, seconds: f32, realtime: bool) -> (Vec<f32>, Vec<f32>) {
|
pub fn test_run(&mut self, seconds: f32, realtime: bool, mut events: Vec<HxTimedEvent>) -> (Vec<f32>, Vec<f32>) {
|
||||||
const SAMPLE_RATE: f32 = 44100.0;
|
const SAMPLE_RATE: f32 = 44100.0;
|
||||||
self.set_sample_rate(SAMPLE_RATE);
|
self.set_sample_rate(SAMPLE_RATE);
|
||||||
self.process_graph_updates();
|
self.process_graph_updates();
|
||||||
|
|
||||||
|
let mut ev_win = EventWindowing::new();
|
||||||
|
|
||||||
let mut nframes = (seconds * SAMPLE_RATE) as usize;
|
let mut nframes = (seconds * SAMPLE_RATE) as usize;
|
||||||
|
|
||||||
let input = vec![0.0; nframes];
|
let input = vec![0.0; nframes];
|
||||||
|
@ -541,6 +543,37 @@ impl NodeExecutor {
|
||||||
let cur_nframes = if nframes >= MAX_BLOCK_SIZE { MAX_BLOCK_SIZE } else { nframes };
|
let cur_nframes = if nframes >= MAX_BLOCK_SIZE { MAX_BLOCK_SIZE } else { nframes };
|
||||||
nframes -= cur_nframes;
|
nframes -= cur_nframes;
|
||||||
|
|
||||||
|
let buf = self.get_note_buffer();
|
||||||
|
buf.reset();
|
||||||
|
loop {
|
||||||
|
if ev_win.feed_me() {
|
||||||
|
if events.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev_win.feed(events.remove(0));
|
||||||
|
println!("FEED {}", offs);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("CHECK {}", offs);
|
||||||
|
if let Some((timing, event)) = ev_win.next_event_in_range(offs + cur_nframes) {
|
||||||
|
buf.step_to(timing);
|
||||||
|
match event {
|
||||||
|
HxMidiEvent::NoteOn { channel, note, vel } => {
|
||||||
|
buf.note_on(channel, note);
|
||||||
|
buf.set_velocity(channel, vel);
|
||||||
|
}
|
||||||
|
HxMidiEvent::NoteOff { channel, note } => {
|
||||||
|
buf.note_off(channel, note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.get_note_buffer().step_to(cur_nframes - 1);
|
||||||
|
|
||||||
let mut context = crate::Context {
|
let mut context = crate::Context {
|
||||||
nframes: cur_nframes,
|
nframes: cur_nframes,
|
||||||
output: &mut [
|
output: &mut [
|
||||||
|
|
|
@ -5,16 +5,32 @@
|
||||||
use crate::dsp::MAX_BLOCK_SIZE;
|
use crate::dsp::MAX_BLOCK_SIZE;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TimedEvent {
|
pub struct HxTimedEvent {
|
||||||
/// The frame number in the current block by the audio driver or plugin API/DAW
|
/// The frame number in the current block by the audio driver or plugin API/DAW
|
||||||
timing: usize,
|
timing: usize,
|
||||||
kind: MidiEvent,
|
kind: HxMidiEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HxTimedEvent {
|
||||||
|
pub fn note_on(timing: usize, channel: u8, note: u8, vel: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
timing,
|
||||||
|
kind: HxMidiEvent::NoteOn { channel, note, vel }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn note_off(timing: usize, channel: u8, note: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
timing,
|
||||||
|
kind: HxMidiEvent::NoteOff { channel, note }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum MidiEvent {
|
pub enum HxMidiEvent {
|
||||||
NoteOn { channel: u8, note: u8, vel: f32 },
|
NoteOn { channel: u8, note: u8, vel: f32 },
|
||||||
NoteOff { channel: u8, note: u8, vel: f32 },
|
NoteOff { channel: u8, note: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -74,7 +90,8 @@ impl NoteBuffer {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn note_on(&mut self, channel: u8, note: u8) {
|
pub fn note_on(&mut self, channel: u8, note: u8) {
|
||||||
let mut chan = &mut self.interleaved_chans[(self.buf_idx * 16) + (channel as usize % 16)];
|
let mut chan = &mut self.interleaved_chans[(self.buf_idx * 16) + (channel as usize % 16)];
|
||||||
chan.gate = (chan.gate + 1) % 2 + 1;
|
println!("NOTE ON {}, GATE={}", note, chan.gate);
|
||||||
|
chan.gate = chan.gate % 2 + 1;
|
||||||
chan.note = note;
|
chan.note = note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +114,8 @@ impl NoteBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventWindowing {
|
pub struct EventWindowing {
|
||||||
pub event: Option<TimedEvent>,
|
pub event: Option<HxTimedEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventWindowing {
|
impl EventWindowing {
|
||||||
|
@ -114,17 +131,15 @@ impl EventWindowing {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn feed(&mut self, event: TimedEvent) {
|
pub fn feed(&mut self, event: HxTimedEvent) {
|
||||||
self.event = Some(event);
|
self.event = Some(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_event_in_range(&mut self, to_time: usize) -> Option<NoteEvent> {
|
pub fn next_event_in_range(&mut self, to_time: usize) -> Option<(usize, HxMidiEvent)> {
|
||||||
let to_time = to_time as u32;
|
|
||||||
|
|
||||||
if let Some(event) = self.event.take() {
|
if let Some(event) = self.event.take() {
|
||||||
if event.timing() < to_time {
|
if event.timing < to_time {
|
||||||
return Some(event);
|
return Some((event.timing, event.kind));
|
||||||
} else {
|
} else {
|
||||||
self.event = Some(event);
|
self.event = Some(event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub use hexodsp::matrix::*;
|
||||||
pub use hexodsp::nodes::new_node_engine;
|
pub use hexodsp::nodes::new_node_engine;
|
||||||
pub use hexodsp::MatrixCellChain;
|
pub use hexodsp::MatrixCellChain;
|
||||||
pub use hexodsp::NodeExecutor;
|
pub use hexodsp::NodeExecutor;
|
||||||
|
pub use hexodsp::nodes::{HxTimedEvent, HxMidiEvent};
|
||||||
|
|
||||||
use hound;
|
use hound;
|
||||||
|
|
||||||
|
@ -518,7 +519,7 @@ pub fn run_realtime_no_input(
|
||||||
seconds: f32,
|
seconds: f32,
|
||||||
sleep_a_bit: bool,
|
sleep_a_bit: bool,
|
||||||
) -> (Vec<f32>, Vec<f32>) {
|
) -> (Vec<f32>, Vec<f32>) {
|
||||||
node_exec.test_run(seconds, sleep_a_bit)
|
node_exec.test_run(seconds, sleep_a_bit, vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calc_rms_mimax_each_ms(buf: &[f32], ms: f32) -> Vec<(f32, f32, f32)> {
|
pub fn calc_rms_mimax_each_ms(buf: &[f32], ms: f32) -> Vec<(f32, f32, f32)> {
|
||||||
|
|
30
tests/node_midip.rs
Normal file
30
tests/node_midip.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
|
||||||
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_node_midip_gate_inserts() {
|
||||||
|
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").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap();
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let (ch1, _) = node_exec.test_run(0.005, false, vec![
|
||||||
|
HxTimedEvent::note_on(5, 0, 69, 1.0),
|
||||||
|
HxTimedEvent::note_on(10, 0, 68, 1.0),
|
||||||
|
HxTimedEvent::note_on(130, 0, 57, 1.0),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let changes = collect_signal_changes(&ch1[..], 0);
|
||||||
|
|
||||||
|
assert_eq!(changes, vec![
|
||||||
|
(5, 100),
|
||||||
|
(11, 100),
|
||||||
|
(131, 100),
|
||||||
|
]);
|
||||||
|
}
|
Loading…
Reference in a new issue