diff --git a/src/dsp/node_midip.rs b/src/dsp/node_midip.rs index 2713038..e1dbbd0 100644 --- a/src/dsp/node_midip.rs +++ b/src/dsp/node_midip.rs @@ -5,7 +5,7 @@ use crate::dsp::{ 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_rules! fa_midip_chan { @@ -30,12 +30,15 @@ macro_rules! fa_midip_gmode { /// The (stereo) output port of the plugin #[derive(Debug, Clone)] pub struct MidiP { - prev_gate: u8, + next_gate: i8, + cur_note: u8, + cur_gate: u8, + cur_vel: f32, } impl MidiP { 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"; @@ -106,29 +109,54 @@ impl DspNode for MidiP { let gate = &mut gate[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() { - 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; - let note = note + det.read(frame); - freq.write(frame, note); + while let Some(ev) = ptr.next_at(frame) { + println!("MIDIP EV: {} {:?}", frame, ev); + match ev { + HxMidiEvent::NoteOn { channel, note, vel } => { + if channel != midip_channel { + continue; + } - if chan.gate & 0x10 > 0 { - // insert a single sample of silence, for retriggering - // any envelopes if the note changed but no note-off came. - if self.prev_gate > 0 && self.prev_gate != chan.gate { - gate.write(frame, 0.0); - } else { - gate.write(frame, 1.0); + if self.cur_gate > 0 { + self.next_gate = 1; + self.cur_gate = 0; + } else { + self.cur_gate = 1; + } + 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; - vel.write(frame, chan.vel as f32); + let note = (self.cur_note as f32 - 69.0) / 120.0; + 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); diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index a85f3f5..3c02f9f 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -31,7 +31,9 @@ pub use node_conf::*; pub use node_exec::*; pub use node_graph_ordering::NodeGraphOrdering; 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}; pub use crate::monitor::MinMaxMonitorSamples; diff --git a/src/nodes/node_exec.rs b/src/nodes/node_exec.rs index 2a54166..a28b30e 100644 --- a/src/nodes/node_exec.rs +++ b/src/nodes/node_exec.rs @@ -25,6 +25,9 @@ use core::arch::x86_64::{ // _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 /// the program. New Nodes or the program is /// not newly allocated in the audio backend, but it is @@ -171,13 +174,17 @@ impl Default for FeedbackBuffer { pub struct NodeExecContext { pub feedback_delay_buffers: Vec, pub note_buffer: NoteBuffer, + pub midi_notes: Vec, + pub midi_ccs: Vec, } impl NodeExecContext { fn new() -> Self { let mut fbdb = vec![]; 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) { @@ -354,6 +361,27 @@ impl NodeExecutor { } } + #[inline] + pub fn feed_midi_events_from Option>(&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] pub fn get_note_buffer(&mut self) -> &mut NoteBuffer { &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 }; nframes -= cur_nframes; - let buf = self.get_note_buffer(); - buf.reset(); - loop { + self.feed_midi_events_from(|| { if ev_win.feed_me() { if events.is_empty() { - break; + return None; } ev_win.feed(events.remove(0)); } - 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); + ev_win.next_event_in_range(offs, cur_nframes) + }); let mut context = crate::Context { nframes: cur_nframes, diff --git a/src/nodes/note_buffer.rs b/src/nodes/note_buffer.rs index 4b272ad..37e3138 100644 --- a/src/nodes/note_buffer.rs +++ b/src/nodes/note_buffer.rs @@ -12,6 +12,14 @@ pub struct 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 { 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 { + 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)] pub enum HxMidiEvent { NoteOn { channel: u8, note: u8, vel: f32 }, NoteOff { channel: u8, note: u8 }, + CC { channel: u8, cc: u8, value: f32 }, } #[derive(Debug, Clone, Copy)] @@ -128,10 +157,14 @@ impl EventWindowing { } #[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 { if let Some(event) = self.event.take() { - if event.timing < to_time { - return Some((event.timing, event.kind)); + if event.timing < (to_time + block_size) { + return Some(HxTimedEvent { timing: event.timing - to_time, kind: event.kind }); } else { self.event = Some(event); }