unnieversal/crates/noted/src/lib.rs

127 lines
3.6 KiB
Rust

/*!
Noted listens for incoming midi notes, and replaces them
*/
#![allow(incomplete_features)]
#![feature(generic_associated_types)]
use baseplug::{event::Data, Event, MidiReceiver, Plugin, ProcessContext};
use serde::{Deserialize, Serialize};
baseplug::model! {
#[derive(Debug, Serialize, Deserialize)]
struct NotedModel {
#[model(min = 0.001, max = 4.0)]
#[parameter(name = "tempo", gradient = "Exponential")]
tempo: f32,
}
}
impl Default for NotedModel {
fn default() -> Self {
Self { tempo: 1.0 }
}
}
struct Noted {
notes: [bool; 128],
frame_count: u64,
note_off_buffer: Vec<u8>,
}
impl Plugin for Noted {
const NAME: &'static str = "noted";
const PRODUCT: &'static str = "noted";
const VENDOR: &'static str = "unnieversal";
const INPUT_CHANNELS: usize = 2;
const OUTPUT_CHANNELS: usize = 2;
type Model = NotedModel;
#[inline]
fn new(_sample_rate: f32, _model: &NotedModel) -> Self {
Self {
notes: [false; 128],
frame_count: 0,
note_off_buffer: Vec::with_capacity(200),
}
}
#[inline]
fn process(&mut self, model: &NotedModelProcess, ctx: &mut ProcessContext<Self>) {
let output = &mut ctx.outputs[0].buffers;
let enqueue_midi = &mut ctx.enqueue_event;
let is_playing = ctx.musical_time.is_playing;
let curr_bpm = ctx.musical_time.bpm;
let beat_in_seconds = 60.0 / curr_bpm;
let smth = beat_in_seconds * ctx.sample_rate as f64;
while let Some(note) = self.note_off_buffer.pop() {
let note_off = Event::<Noted> {
frame: 0,
data: Data::Midi([0x80, note as u8, 0]),
};
enqueue_midi(note_off);
}
for i in 0..ctx.nframes {
// write silence
output[0][i] = 0.0;
output[1][i] = 0.0;
if is_playing {
let tempo_in_samples = model.tempo[i] as f64 * smth;
let tempo_in_samples = tempo_in_samples.round() as u64;
self.frame_count += 1;
// Send note ons for active notes
if self.frame_count >= tempo_in_samples {
for (note, is_on) in self.notes.iter().enumerate() {
if *is_on {
// Send note off
let note_off = Event::<Noted> {
frame: i,
data: Data::Midi([0x80, note as u8, 0]),
};
enqueue_midi(note_off);
// Send note on
let note_on = Event::<Noted> {
frame: i,
data: Data::Midi([0x90, note as u8, 120]),
};
enqueue_midi(note_on);
}
}
self.frame_count = 0;
}
} else {
self.frame_count = 0;
}
}
}
}
impl MidiReceiver for Noted {
fn midi_input(&mut self, _model: &NotedModelProcess, data: [u8; 3]) {
match data[0] {
// note on
0x90 => {
self.notes[data[1] as usize] = true;
}
// note off
0x80 => {
self.notes[data[1] as usize] = false;
self.note_off_buffer.push(data[1]);
}
_ => (),
}
}
}
baseplug::vst2!(Noted, b"NoTe");