tek/deps/vst/src/interfaces.rs

370 lines
13 KiB
Rust

//! Function interfaces for VST 2.4 API.
#![doc(hidden)]
use std::cell::Cell;
use std::os::raw::{c_char, c_void};
use std::{mem, slice};
use crate::{
api::{self, consts::*, AEffect, TimeInfo},
buffer::AudioBuffer,
editor::{Key, KeyCode, KnobMode, Rect},
host::Host,
};
/// Deprecated process function.
pub extern "C" fn process_deprecated(
_effect: *mut AEffect,
_raw_inputs: *const *const f32,
_raw_outputs: *mut *mut f32,
_samples: i32,
) {
}
/// VST2.4 replacing function.
pub extern "C" fn process_replacing(
effect: *mut AEffect,
raw_inputs: *const *const f32,
raw_outputs: *mut *mut f32,
samples: i32,
) {
// Handle to the VST
let plugin = unsafe { (*effect).get_plugin() };
let info = unsafe { (*effect).get_info() };
let (input_count, output_count) = (info.inputs as usize, info.outputs as usize);
let mut buffer =
unsafe { AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize) };
plugin.process(&mut buffer);
}
/// VST2.4 replacing function with `f64` values.
pub extern "C" fn process_replacing_f64(
effect: *mut AEffect,
raw_inputs: *const *const f64,
raw_outputs: *mut *mut f64,
samples: i32,
) {
let plugin = unsafe { (*effect).get_plugin() };
let info = unsafe { (*effect).get_info() };
let (input_count, output_count) = (info.inputs as usize, info.outputs as usize);
let mut buffer =
unsafe { AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize) };
plugin.process_f64(&mut buffer);
}
/// VST2.4 set parameter function.
pub extern "C" fn set_parameter(effect: *mut AEffect, index: i32, value: f32) {
unsafe { (*effect).get_params() }.set_parameter(index, value);
}
/// VST2.4 get parameter function.
pub extern "C" fn get_parameter(effect: *mut AEffect, index: i32) -> f32 {
unsafe { (*effect).get_params() }.get_parameter(index)
}
/// Copy a string into a destination buffer.
///
/// String will be cut at `max` characters.
fn copy_string(dst: *mut c_void, src: &str, max: usize) -> isize {
unsafe {
use libc::{memcpy, memset};
use std::cmp::min;
let dst = dst as *mut c_void;
memset(dst, 0, max);
memcpy(dst, src.as_ptr() as *const c_void, min(max, src.as_bytes().len()));
}
1 // Success
}
/// VST2.4 dispatch function. This function handles dispatching all opcodes to the VST plugin.
pub extern "C" fn dispatch(
effect: *mut AEffect,
opcode: i32,
index: i32,
value: isize,
ptr: *mut c_void,
opt: f32,
) -> isize {
use crate::plugin::{CanDo, OpCode};
// Convert passed in opcode to enum
let opcode = OpCode::try_from(opcode);
// Only query plugin or editor when needed to avoid creating multiple
// concurrent mutable references to the same object.
let get_plugin = || unsafe { (*effect).get_plugin() };
let get_editor = || unsafe { (*effect).get_editor() };
let params = unsafe { (*effect).get_params() };
match opcode {
Ok(OpCode::Initialize) => get_plugin().init(),
Ok(OpCode::Shutdown) => unsafe {
(*effect).drop_plugin();
drop(Box::from_raw(effect))
},
Ok(OpCode::ChangePreset) => params.change_preset(value as i32),
Ok(OpCode::GetCurrentPresetNum) => return params.get_preset_num() as isize,
Ok(OpCode::SetCurrentPresetName) => params.set_preset_name(read_string(ptr)),
Ok(OpCode::GetCurrentPresetName) => {
let num = params.get_preset_num();
return copy_string(ptr, &params.get_preset_name(num), MAX_PRESET_NAME_LEN);
}
Ok(OpCode::GetParameterLabel) => {
return copy_string(ptr, &params.get_parameter_label(index), MAX_PARAM_STR_LEN)
}
Ok(OpCode::GetParameterDisplay) => {
return copy_string(ptr, &params.get_parameter_text(index), MAX_PARAM_STR_LEN)
}
Ok(OpCode::GetParameterName) => return copy_string(ptr, &params.get_parameter_name(index), MAX_PARAM_STR_LEN),
Ok(OpCode::SetSampleRate) => get_plugin().set_sample_rate(opt),
Ok(OpCode::SetBlockSize) => get_plugin().set_block_size(value as i64),
Ok(OpCode::StateChanged) => {
if value == 1 {
get_plugin().resume();
} else {
get_plugin().suspend();
}
}
Ok(OpCode::EditorGetRect) => {
if let Some(ref mut editor) = get_editor() {
let size = editor.size();
let pos = editor.position();
unsafe {
// Given a Rect** structure
// TODO: Investigate whether we are given a valid Rect** pointer already
*(ptr as *mut *mut c_void) = Box::into_raw(Box::new(Rect {
left: pos.0 as i16, // x coord of position
top: pos.1 as i16, // y coord of position
right: (pos.0 + size.0) as i16, // x coord of pos + x coord of size
bottom: (pos.1 + size.1) as i16, // y coord of pos + y coord of size
})) as *mut _; // TODO: free memory
}
return 1;
}
}
Ok(OpCode::EditorOpen) => {
if let Some(ref mut editor) = get_editor() {
// `ptr` is a window handle to the parent window.
// See the documentation for `Editor::open` for details.
if editor.open(ptr) {
return 1;
}
}
}
Ok(OpCode::EditorClose) => {
if let Some(ref mut editor) = get_editor() {
editor.close();
}
}
Ok(OpCode::EditorIdle) => {
if let Some(ref mut editor) = get_editor() {
editor.idle();
}
}
Ok(OpCode::GetData) => {
let mut chunks = if index == 0 {
params.get_bank_data()
} else {
params.get_preset_data()
};
chunks.shrink_to_fit();
let len = chunks.len() as isize; // eventually we should be using ffi::size_t
unsafe {
*(ptr as *mut *mut c_void) = chunks.as_ptr() as *mut c_void;
}
mem::forget(chunks);
return len;
}
Ok(OpCode::SetData) => {
let chunks = unsafe { slice::from_raw_parts(ptr as *mut u8, value as usize) };
if index == 0 {
params.load_bank_data(chunks);
} else {
params.load_preset_data(chunks);
}
}
Ok(OpCode::ProcessEvents) => {
get_plugin().process_events(unsafe { &*(ptr as *const api::Events) });
}
Ok(OpCode::CanBeAutomated) => return params.can_be_automated(index) as isize,
Ok(OpCode::StringToParameter) => return params.string_to_parameter(index, read_string(ptr)) as isize,
Ok(OpCode::GetPresetName) => return copy_string(ptr, &params.get_preset_name(index), MAX_PRESET_NAME_LEN),
Ok(OpCode::GetInputInfo) => {
if index >= 0 && index < get_plugin().get_info().inputs {
unsafe {
let ptr = ptr as *mut api::ChannelProperties;
*ptr = get_plugin().get_input_info(index).into();
}
}
}
Ok(OpCode::GetOutputInfo) => {
if index >= 0 && index < get_plugin().get_info().outputs {
unsafe {
let ptr = ptr as *mut api::ChannelProperties;
*ptr = get_plugin().get_output_info(index).into();
}
}
}
Ok(OpCode::GetCategory) => {
return get_plugin().get_info().category.into();
}
Ok(OpCode::GetEffectName) => return copy_string(ptr, &get_plugin().get_info().name, MAX_VENDOR_STR_LEN),
Ok(OpCode::GetVendorName) => return copy_string(ptr, &get_plugin().get_info().vendor, MAX_VENDOR_STR_LEN),
Ok(OpCode::GetProductName) => return copy_string(ptr, &get_plugin().get_info().name, MAX_PRODUCT_STR_LEN),
Ok(OpCode::GetVendorVersion) => return get_plugin().get_info().version as isize,
Ok(OpCode::VendorSpecific) => return get_plugin().vendor_specific(index, value, ptr, opt),
Ok(OpCode::CanDo) => {
let can_do = CanDo::from_str(&read_string(ptr));
return get_plugin().can_do(can_do).into();
}
Ok(OpCode::GetTailSize) => {
if get_plugin().get_tail_size() == 0 {
return 1;
} else {
return get_plugin().get_tail_size();
}
}
//OpCode::GetParamInfo => { /*TODO*/ }
Ok(OpCode::GetApiVersion) => return 2400,
Ok(OpCode::EditorKeyDown) => {
if let Some(ref mut editor) = get_editor() {
if let Ok(key) = Key::try_from(value) {
editor.key_down(KeyCode {
character: index as u8 as char,
key,
modifier: opt.to_bits() as u8,
});
}
}
}
Ok(OpCode::EditorKeyUp) => {
if let Some(ref mut editor) = get_editor() {
if let Ok(key) = Key::try_from(value) {
editor.key_up(KeyCode {
character: index as u8 as char,
key,
modifier: opt.to_bits() as u8,
});
}
}
}
Ok(OpCode::EditorSetKnobMode) => {
if let Some(ref mut editor) = get_editor() {
if let Ok(knob_mode) = KnobMode::try_from(value) {
editor.set_knob_mode(knob_mode);
}
}
}
Ok(OpCode::StartProcess) => get_plugin().start_process(),
Ok(OpCode::StopProcess) => get_plugin().stop_process(),
Ok(OpCode::GetNumMidiInputs) => return unsafe { (*effect).get_info() }.midi_inputs as isize,
Ok(OpCode::GetNumMidiOutputs) => return unsafe { (*effect).get_info() }.midi_outputs as isize,
_ => {
debug!("Unimplemented opcode ({:?})", opcode);
trace!(
"Arguments; index: {}, value: {}, ptr: {:?}, opt: {}",
index,
value,
ptr,
opt
);
}
}
0
}
pub fn host_dispatch(
host: &mut dyn Host,
effect: *mut AEffect,
opcode: i32,
index: i32,
value: isize,
ptr: *mut c_void,
opt: f32,
) -> isize {
use crate::host::OpCode;
let opcode = OpCode::try_from(opcode);
match opcode {
Ok(OpCode::Version) => return 2400,
Ok(OpCode::Automate) => host.automate(index, opt),
Ok(OpCode::BeginEdit) => host.begin_edit(index),
Ok(OpCode::EndEdit) => host.end_edit(index),
Ok(OpCode::Idle) => host.idle(),
// ...
Ok(OpCode::CanDo) => {
info!("Plugin is asking if host can: {}.", read_string(ptr));
}
Ok(OpCode::GetVendorVersion) => return host.get_info().0,
Ok(OpCode::GetVendorString) => return copy_string(ptr, &host.get_info().1, MAX_VENDOR_STR_LEN),
Ok(OpCode::GetProductString) => return copy_string(ptr, &host.get_info().2, MAX_PRODUCT_STR_LEN),
Ok(OpCode::ProcessEvents) => {
host.process_events(unsafe { &*(ptr as *const api::Events) });
}
Ok(OpCode::GetTime) => {
return match host.get_time_info(value as i32) {
None => 0,
Some(result) => {
thread_local! {
static TIME_INFO: Cell<TimeInfo> =
Cell::new(TimeInfo::default());
}
TIME_INFO.with(|time_info| {
(*time_info).set(result);
time_info.as_ptr() as isize
})
}
};
}
Ok(OpCode::GetBlockSize) => return host.get_block_size(),
_ => {
trace!("VST: Got unimplemented host opcode ({:?})", opcode);
trace!(
"Arguments; effect: {:?}, index: {}, value: {}, ptr: {:?}, opt: {}",
effect,
index,
value,
ptr,
opt
);
}
}
0
}
// Read a string from the `ptr` buffer
fn read_string(ptr: *mut c_void) -> String {
use std::ffi::CStr;
String::from_utf8_lossy(unsafe { CStr::from_ptr(ptr as *mut c_char).to_bytes() }).into_owned()
}