new buffer less and simpler style of midi event processing
This commit is contained in:
parent
1d595fe52f
commit
0f5b44141d
4 changed files with 119 additions and 44 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<FeedbackBuffer>,
|
||||
pub note_buffer: NoteBuffer,
|
||||
pub midi_notes: Vec<HxTimedEvent>,
|
||||
pub midi_ccs: Vec<HxTimedEvent>,
|
||||
}
|
||||
|
||||
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<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]
|
||||
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,
|
||||
|
|
|
@ -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<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)]
|
||||
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<HxTimedEvent> {
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue