unnieversal/crates/baseplug/src/api/vst2/mod.rs

459 lines
13 KiB
Rust

use std::ffi::CStr;
use std::os::raw::c_void;
use std::ptr;
use std::{io, os::raw::c_char};
use std::{mem, slice};
pub use vst2_sys;
use vst2_sys::*;
use crate::wrapper::*;
use crate::*;
mod ui;
use ui::*;
mod abi;
pub use abi::plugin_main;
const MAX_PARAM_STR_LEN: usize = 32;
const MAX_EFFECT_NAME_LEN: usize = 32;
const MAX_VENDOR_STR_LEN: usize = 64;
const MAX_PRODUCT_STR_LEN: usize = 64;
const TRANSPORT_PLAYING: i32 = 2;
// output events buffer size
const OUTPUT_BUFFER_SIZE: usize = 256;
#[inline]
fn cstr_as_slice<'a>(ptr: *mut c_void, len: usize) -> &'a mut [u8] {
unsafe {
slice::from_raw_parts_mut(ptr as *mut u8, len)
}
}
fn cstrcpy(ptr: *mut c_void, src: &str, max_len: usize) {
let dest = cstr_as_slice(ptr, max_len);
let src_bytes = src.as_bytes();
let len = src_bytes.len().min(max_len - 1);
dest[..len].copy_from_slice(&src_bytes[..len]);
dest[len] = 0;
}
#[inline]
fn param_for_vst2_id<P, M>(id: i32) -> Option<&'static Param<P, M::Smooth>>
where
P: Plugin,
M: Model<P>,
{
M::Smooth::PARAMS.get(id as usize).copied()
}
macro_rules! param_for_idx {
($id:ident) => {
match param_for_vst2_id::<P, P::Model>($id) {
Some(p) => p,
None => return 0,
}
}
}
// represents an output buffer to send events to host
#[repr(C)]
pub struct OutgoingEvents {
num_events: i32,
_reserved: isize,
event_ptrs: [*mut MidiEvent; OUTPUT_BUFFER_SIZE],
events: [MidiEvent; OUTPUT_BUFFER_SIZE],
}
impl OutgoingEvents {
pub fn new() -> Self {
// create placeholders, ownership stays here
let blnk_evts = [vst2_sys::MidiEvent {
event_type: MIDI_TYPE,
byte_size: std::mem::size_of::<MidiEvent>() as i32,
delta_frames: 0,
flags: 0,
..unsafe { std::mem::zeroed() }
}; OUTPUT_BUFFER_SIZE];
// init ptrs to null
let evts_ptrs: [*mut MidiEvent; OUTPUT_BUFFER_SIZE] = [ptr::null_mut(); OUTPUT_BUFFER_SIZE];
OutgoingEvents {
num_events: 0,
_reserved: 0,
events: blnk_evts,
event_ptrs: evts_ptrs,
}
}
}
struct VST2Adapter<P: Plugin> {
effect: AEffect,
host_cb: HostCallbackProc,
wrapped: WrappedPlugin<P>,
editor_rect: Rect,
// when the VST2 host asks us for the chunk/data/state, the lifetime for that data extends
// until the *next* time that the host asks us for state. this means we have to just hold this
// around in memory indefinitely.
//
// allow(dead_code) here because we don't read from it after assignment, we only hold onto it
// here so that the host has access to it. compiler warns about "never read" without the allow.
#[allow(dead_code)]
state: Option<Vec<u8>>,
// output events buffer
output_events_buffer: OutgoingEvents,
}
impl<P: Plugin> VST2Adapter<P> {
#[inline]
fn dispatch(&mut self, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize {
match opcode {
////
// lifecycle
////
effect_opcodes::CLOSE => {
unsafe {
drop(Box::from_raw(self))
};
},
effect_opcodes::SET_SAMPLE_RATE => self.wrapped.set_sample_rate(opt),
effect_opcodes::MAINS_CHANGED => {
if value == 1 {
self.wrapped.reset();
}
},
////
// parameters
////
effect_opcodes::GET_PARAM_NAME => {
let param = param_for_idx!(index);
cstrcpy(ptr, param.get_name(), MAX_PARAM_STR_LEN);
return 0;
},
effect_opcodes::GET_PARAM_LABEL => {
let param = param_for_idx!(index);
cstrcpy(ptr, param.get_label(), MAX_PARAM_STR_LEN);
return 0;
},
effect_opcodes::GET_PARAM_DISPLAY => {
let param = param_for_idx!(index);
let dest = cstr_as_slice(ptr, MAX_PARAM_STR_LEN);
let mut cursor = io::Cursor::new(
&mut dest[..MAX_PARAM_STR_LEN - 1]);
match param.get_display(&self.wrapped.smoothed_model, &mut cursor) {
Ok(_) => {
let len = cursor.position();
dest[len as usize] = 0;
return len as isize;
},
Err(_) => {
dest[0] = 0;
return 0;
}
}
},
effect_opcodes::CAN_BE_AUTOMATED => return 1,
////
// plugin metadata
////
effect_opcodes::GET_EFFECT_NAME => {
cstrcpy(ptr, P::NAME, MAX_EFFECT_NAME_LEN);
return 1;
},
effect_opcodes::GET_PRODUCT_STRING => {
cstrcpy(ptr, P::PRODUCT, MAX_PRODUCT_STR_LEN);
return 1;
},
effect_opcodes::GET_VENDOR_STRING => {
cstrcpy(ptr, P::VENDOR, MAX_VENDOR_STR_LEN);
return 1;
},
////
// events
////
effect_opcodes::PROCESS_EVENTS => unsafe {
let vst_events = &*(ptr as *const Events);
let ev_slice = slice::from_raw_parts(
vst_events.events.as_ptr() as *const *const MidiEvent,
vst_events.num_events as usize
);
for ev in ev_slice {
if (**ev).event_type == MIDI_TYPE {
let ev = *ev as *const MidiEvent;
self.wrapped.midi_input(
(*ev).delta_frames as usize,
[(*ev).midi_data[0], (*ev).midi_data[1], (*ev).midi_data[2]]
);
}
}
return 0;
},
////
// state
////
effect_opcodes::GET_CHUNK => {
let new_state = match self.wrapped.serialise() {
None => return 0,
Some(s) => s
};
unsafe {
*(ptr as *mut *const c_void) =
new_state.as_ptr() as *const c_void;
}
let len = new_state.len() as isize;
self.state = Some(new_state);
return len;
},
effect_opcodes::SET_CHUNK => {
let state = unsafe {
slice::from_raw_parts(ptr as *mut u8, value as usize)
};
self.wrapped.deserialise(state);
return 0;
},
////
// editor
////
effect_opcodes::EDIT_GET_RECT => {
let ptr = ptr as *mut *mut c_void;
let (width, height) = match self.ui_get_rect() {
Some((w, h)) => (w, h),
None => unsafe {
*ptr = ptr::null_mut();
return 0;
}
};
self.editor_rect = Rect {
top: 0,
left: 0,
bottom: height,
right: width,
};
unsafe {
// we never read from editor_rect, just set it.
*ptr = (&self.editor_rect as *const _) as *mut c_void;
return 1;
}
},
effect_opcodes::EDIT_OPEN => {
return match self.ui_open(ptr) {
Ok(_) => 1,
Err(_) => 0,
};
},
effect_opcodes::EDIT_IDLE => {},
effect_opcodes::EDIT_CLOSE => {
self.ui_close();
},
effect_opcodes::CAN_DO => {
// get the property
let can_do = String::from_utf8_lossy(unsafe {
CStr::from_ptr(ptr as *mut c_char).to_bytes()
})
.into_owned();
let can_do = match can_do.as_str() {
"sendVstEvents" => 1,
"sendVstMidiEvent" => 1,
"receiveVstTimeInfo" => 1,
_otherwise => 0,
};
return can_do;
},
////
// ~who knows~
////
o => {
eprintln!("unhandled opcode {:?}", o);
},
}
0
}
#[inline]
fn get_parameter(&self, index: i32) -> f32 {
let param = match param_for_vst2_id::<P, P::Model>(index) {
Some(p) => p,
None => return 0.0
};
self.wrapped.get_parameter(param)
}
#[inline]
fn set_parameter(&mut self, index: i32, val: f32) {
let param = match param_for_vst2_id::<P, P::Model>(index) {
Some(p) => p,
None => return
};
self.wrapped.set_parameter(param, val);
}
fn get_musical_time(&mut self) -> MusicalTime {
let mut mtime = MusicalTime {
bpm: 0.0,
beat: 0.0,
is_playing: false
};
let time_info = {
let flags = time_info_flags::TEMPO_VALID | time_info_flags::PPQ_POS_VALID;
let vti = (self.host_cb)(&mut self.effect,
host_opcodes::GET_TIME, 0,
flags as isize,
ptr::null_mut(), 0.0);
match vti {
0 => return mtime,
ptr => unsafe { &*(ptr as *const TimeInfo) }
}
};
if (time_info.flags | time_info_flags::TEMPO_VALID) != 0 {
mtime.bpm = time_info.tempo;
}
if (time_info.flags | time_info_flags::PPQ_POS_VALID) != 0 {
mtime.beat = time_info.ppq_pos;
}
if (time_info.flags | TRANSPORT_PLAYING) != 0 {
mtime.is_playing = true;
}
mtime
}
#[inline]
fn process_replacing(
&mut self,
in_buffers: *const *const f32,
out_buffers: *mut *mut f32,
nframes: i32,
) {
let input = unsafe {
let b = slice::from_raw_parts(in_buffers, P::INPUT_CHANNELS);
let mut a: [&[f32]; 16] = Default::default();
for i in 0..P::INPUT_CHANNELS {
a[i] = slice::from_raw_parts(b[i], nframes as usize);
}
a
};
let output = unsafe {
let b = slice::from_raw_parts(out_buffers, P::OUTPUT_CHANNELS);
let mut a: [&mut [f32]; 16] = Default::default();
for i in 0..P::OUTPUT_CHANNELS {
a[i] = slice::from_raw_parts_mut(b[i], nframes as usize);
}
a
};
let musical_time = self.get_musical_time();
self.wrapped
.process(musical_time, input, output, nframes as usize);
// write output_events in the buffer
self.send_output_events();
// clear
self.wrapped.output_events.clear();
}
#[inline]
fn send_output_events(&mut self) {
self.output_events_buffer.num_events = 0;
// write into output buffer
for (bevt, ev) in self
.wrapped
.output_events
.iter()
.zip(self.output_events_buffer.events.iter_mut())
{
match bevt.data {
event::Data::Midi(midi_data) => {
let midi_event: MidiEvent = MidiEvent {
event_type: MIDI_TYPE,
byte_size: mem::size_of::<MidiEvent>() as i32,
delta_frames: bevt.frame as i32,
flags: 1,
note_length: 0,
note_offset: 0,
midi_data: [midi_data[0], midi_data[1], midi_data[2], 0],
detune: 0,
note_off_velocity: 0,
reserved_1: 0,
reserved_2: 0,
};
*ev = midi_event;
self.output_events_buffer.num_events += 1;
}
_ => {}
}
}
if self.output_events_buffer.num_events > 0 {
// update pointers
for (evt, evt_ptr) in self
.output_events_buffer
.events
.iter_mut()
.zip(self.output_events_buffer.event_ptrs.iter_mut())
{
*evt_ptr = evt as *mut MidiEvent;
}
// send to host
(self.host_cb)(&mut self.effect as *mut AEffect,
host_opcodes::PROCESS_EVENTS,
0, 0, &self.output_events_buffer as *const _ as *mut _, 0.0);
}
}
}