mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-10 13:46:42 +01:00
wip: refactor into fewer crates
This commit is contained in:
parent
c367a0444e
commit
77703d83a5
105 changed files with 64 additions and 131 deletions
370
deps/vst/src/interfaces.rs
vendored
Normal file
370
deps/vst/src/interfaces.rs
vendored
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
//! 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, ¶ms.get_preset_name(num), MAX_PRESET_NAME_LEN);
|
||||
}
|
||||
|
||||
Ok(OpCode::GetParameterLabel) => {
|
||||
return copy_string(ptr, ¶ms.get_parameter_label(index), MAX_PARAM_STR_LEN)
|
||||
}
|
||||
Ok(OpCode::GetParameterDisplay) => {
|
||||
return copy_string(ptr, ¶ms.get_parameter_text(index), MAX_PARAM_STR_LEN)
|
||||
}
|
||||
Ok(OpCode::GetParameterName) => return copy_string(ptr, ¶ms.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, ¶ms.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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue