new buffer less and simpler style of midi event processing

This commit is contained in:
Weird Constructor 2022-08-14 00:32:48 +02:00
parent 1d595fe52f
commit 0f5b44141d
4 changed files with 119 additions and 44 deletions

View file

@ -5,7 +5,7 @@
use crate::dsp::{ use crate::dsp::{
at, denorm, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, at, denorm, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
}; };
use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::nodes::{HxMidiEvent, MidiEventPointer, NodeAudioContext, NodeExecContext};
#[macro_export] #[macro_export]
macro_rules! fa_midip_chan { macro_rules! fa_midip_chan {
@ -30,12 +30,15 @@ macro_rules! fa_midip_gmode {
/// The (stereo) output port of the plugin /// The (stereo) output port of the plugin
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MidiP { pub struct MidiP {
prev_gate: u8, next_gate: i8,
cur_note: u8,
cur_gate: u8,
cur_vel: f32,
} }
impl MidiP { impl MidiP {
pub fn new(_nid: &NodeId) -> Self { pub fn new(_nid: &NodeId) -> Self {
Self { prev_gate: 0 } Self { next_gate: 0, cur_note: 0, cur_gate: 0, cur_vel: 0.0 }
} }
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";
@ -106,29 +109,54 @@ impl DspNode for MidiP {
let gate = &mut gate[0]; let gate = &mut gate[0];
let vel = &mut vel[0]; let vel = &mut vel[0];
let channel = (chan.i() as usize % 16) as u8; let midip_channel = (chan.i() as usize % 16) as u8;
let mut ptr = MidiEventPointer::new(&ectx.midi_notes[..]);
for frame in 0..ctx.nframes() { for frame in 0..ctx.nframes() {
let chan = ectx.note_buffer.get_chan_at(channel, frame as u8); if self.next_gate > 0 {
self.cur_gate = 1;
} else if self.next_gate < 0 {
self.cur_gate = 0;
}
self.next_gate = 0;
let note = (chan.note as f32 - 69.0) / 120.0; while let Some(ev) = ptr.next_at(frame) {
let note = note + det.read(frame); println!("MIDIP EV: {} {:?}", frame, ev);
freq.write(frame, note); match ev {
HxMidiEvent::NoteOn { channel, note, vel } => {
if channel != midip_channel {
continue;
}
if chan.gate & 0x10 > 0 { if self.cur_gate > 0 {
// insert a single sample of silence, for retriggering self.next_gate = 1;
// any envelopes if the note changed but no note-off came. self.cur_gate = 0;
if self.prev_gate > 0 && self.prev_gate != chan.gate { } else {
gate.write(frame, 0.0); self.cur_gate = 1;
} else { }
gate.write(frame, 1.0); self.cur_note = note;
self.cur_vel = vel;
}
HxMidiEvent::NoteOff { channel, note } => {
if channel != midip_channel {
continue;
}
if self.cur_note == note {
self.next_gate = -1;
}
}
_ => (),
} }
} else {
gate.write(frame, 0.0);
} }
self.prev_gate = chan.gate; let note = (self.cur_note as f32 - 69.0) / 120.0;
vel.write(frame, chan.vel as f32); let note = note + det.read(frame);
println!("FRAME: {} => gate={}, freq={}, next_gate={}", frame, self.cur_gate, note, self.next_gate);
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);
} }
let last_val = gate.read(ctx.nframes() - 1); let last_val = gate.read(ctx.nframes() - 1);

View file

@ -31,7 +31,9 @@ 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::{EventWindowing, HxMidiEvent, HxTimedEvent, NoteBuffer, NoteChannelState}; pub use note_buffer::{
EventWindowing, HxMidiEvent, HxTimedEvent, MidiEventPointer, NoteBuffer, NoteChannelState,
};
use crate::dsp::{Node, SAtom}; use crate::dsp::{Node, SAtom};
pub use crate::monitor::MinMaxMonitorSamples; pub use crate::monitor::MinMaxMonitorSamples;

View file

@ -25,6 +25,9 @@ use core::arch::x86_64::{
// _MM_GET_FLUSH_ZERO_MODE // _MM_GET_FLUSH_ZERO_MODE
}; };
pub const MAX_MIDI_NOTES_PER_BLOCK: usize = 512;
pub const MAX_MIDI_CC_PER_BLOCK: usize = 1024;
/// Holds the complete allocation of nodes and /// Holds the complete allocation of nodes and
/// the program. New Nodes or the program is /// the program. New Nodes or the program is
/// not newly allocated in the audio backend, but it is /// not newly allocated in the audio backend, but it is
@ -171,13 +174,17 @@ impl Default for FeedbackBuffer {
pub struct NodeExecContext { pub struct NodeExecContext {
pub feedback_delay_buffers: Vec<FeedbackBuffer>, pub feedback_delay_buffers: Vec<FeedbackBuffer>,
pub note_buffer: NoteBuffer, pub note_buffer: NoteBuffer,
pub midi_notes: Vec<HxTimedEvent>,
pub midi_ccs: Vec<HxTimedEvent>,
} }
impl NodeExecContext { impl NodeExecContext {
fn new() -> Self { fn new() -> Self {
let mut fbdb = vec![]; let mut fbdb = vec![];
fbdb.resize_with(MAX_ALLOCATED_NODES, FeedbackBuffer::new); fbdb.resize_with(MAX_ALLOCATED_NODES, FeedbackBuffer::new);
Self { feedback_delay_buffers: fbdb, note_buffer: NoteBuffer::new() } let midi_notes = Vec::with_capacity(MAX_MIDI_NOTES_PER_BLOCK);
let midi_ccs = Vec::with_capacity(MAX_MIDI_CC_PER_BLOCK);
Self { feedback_delay_buffers: fbdb, note_buffer: NoteBuffer::new(), midi_notes, midi_ccs }
} }
fn set_sample_rate(&mut self, srate: f32) { fn set_sample_rate(&mut self, srate: f32) {
@ -354,6 +361,27 @@ impl NodeExecutor {
} }
} }
#[inline]
pub fn feed_midi_events_from<F: FnMut() -> Option<HxTimedEvent>>(&mut self, mut f: F) {
self.exec_ctx.midi_notes.clear();
self.exec_ctx.midi_ccs.clear();
while let Some(ev) = f() {
if ev.is_cc() {
self.exec_ctx.midi_ccs.push(ev);
} else {
self.exec_ctx.midi_notes.push(ev);
}
if self.exec_ctx.midi_ccs.len() == MAX_MIDI_CC_PER_BLOCK {
break;
}
if self.exec_ctx.midi_notes.len() == MAX_MIDI_NOTES_PER_BLOCK {
break;
}
}
}
#[inline] #[inline]
pub fn get_note_buffer(&mut self) -> &mut NoteBuffer { pub fn get_note_buffer(&mut self) -> &mut NoteBuffer {
&mut self.exec_ctx.note_buffer &mut self.exec_ctx.note_buffer
@ -548,33 +576,17 @@ 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(); self.feed_midi_events_from(|| {
buf.reset();
loop {
if ev_win.feed_me() { if ev_win.feed_me() {
if events.is_empty() { if events.is_empty() {
break; return None;
} }
ev_win.feed(events.remove(0)); ev_win.feed(events.remove(0));
} }
if let Some((timing, event)) = ev_win.next_event_in_range(offs + cur_nframes) { 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,

View file

@ -12,6 +12,14 @@ pub struct HxTimedEvent {
} }
impl HxTimedEvent { impl HxTimedEvent {
pub fn is_cc(&self) -> bool {
if let HxMidiEvent::CC { .. } = self.kind {
true
} else {
false
}
}
pub fn note_on(timing: usize, channel: u8, note: u8, vel: f32) -> Self { pub fn note_on(timing: usize, channel: u8, note: u8, vel: f32) -> Self {
Self { timing, kind: HxMidiEvent::NoteOn { channel, note, vel } } Self { timing, kind: HxMidiEvent::NoteOn { channel, note, vel } }
} }
@ -21,10 +29,31 @@ impl HxTimedEvent {
} }
} }
pub struct MidiEventPointer<'a> {
buf: &'a [HxTimedEvent],
idx: usize,
}
impl<'a> MidiEventPointer<'a> {
pub fn new(buf: &'a [HxTimedEvent]) -> Self {
Self { buf, idx: 0 }
}
pub fn next_at(&mut self, time: usize) -> Option<HxMidiEvent> {
if self.idx < self.buf.len() && self.buf[self.idx].timing <= time {
self.idx += 1;
Some(self.buf[self.idx - 1].kind)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum HxMidiEvent { pub enum HxMidiEvent {
NoteOn { channel: u8, note: u8, vel: f32 }, NoteOn { channel: u8, note: u8, vel: f32 },
NoteOff { channel: u8, note: u8 }, NoteOff { channel: u8, note: u8 },
CC { channel: u8, cc: u8, value: f32 },
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -128,10 +157,14 @@ impl EventWindowing {
} }
#[inline] #[inline]
pub fn next_event_in_range(&mut self, to_time: usize) -> Option<(usize, HxMidiEvent)> { pub fn next_event_in_range(
&mut self,
to_time: usize,
block_size: usize,
) -> Option<HxTimedEvent> {
if let Some(event) = self.event.take() { if let Some(event) = self.event.take() {
if event.timing < to_time { if event.timing < (to_time + block_size) {
return Some((event.timing, event.kind)); return Some(HxTimedEvent { timing: event.timing - to_time, kind: event.kind });
} else { } else {
self.event = Some(event); self.event = Some(event);
} }