127 lines
3.6 KiB
Rust
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");
|