pub mod message;
use std::ffi::CString;
use std::io::{self, Read, Write};
use std::os::raw::{c_char, c_int, c_void};
use std::panic::RefUnwindSafe;
use hresult::HRESULT;
use log::{debug, error};
use crate::host::{self, Event, GetName, Host};
use crate::voice::ReceiveVoiceHandler;
use crate::{
alloc_real_cstr, intptr_t, AsRawPtr, FlMessage, MidiMessage, ProcessParamFlags, ValuePtr,
CURRENT_SDK_VERSION,
};
crate::implement_tag!();
#[macro_export]
macro_rules! create_plugin {
($pl:ty) => {
use std::os::raw::c_void;
extern "C" {
fn create_plug_instance_c(
host: *mut c_void,
tag: $crate::intptr_t,
adapter: *mut c_void,
) -> *mut c_void;
}
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn CreatePlugInstance(
host: *mut c_void,
tag: $crate::intptr_t,
) -> *mut c_void {
let ho = $crate::host::Host::new(host);
let plugin = <$pl as $crate::plugin::Plugin>::new(
ho,
$crate::plugin::Tag(tag as $crate::intptr_t),
);
let adapter = $crate::plugin::PluginAdapter(Box::new(plugin));
create_plug_instance_c(host, tag, Box::into_raw(Box::new(adapter)) as *mut c_void)
}
};
}
pub trait Plugin: std::fmt::Debug + RefUnwindSafe + Send + Sync + 'static {
fn new(host: Host, tag: Tag) -> Self
where
Self: Sized;
fn info(&self) -> Info;
fn save_state(&mut self, writer: StateWriter);
fn load_state(&mut self, reader: StateReader);
fn on_message(&mut self, message: host::Message<'_>) -> Box<dyn AsRawPtr>;
fn name_of(&self, value: GetName) -> String;
fn process_event(&mut self, _event: Event) {}
fn process_param(
&mut self,
_index: usize,
_value: ValuePtr,
_flags: ProcessParamFlags,
) -> Box<dyn AsRawPtr> {
Box::new(0)
}
fn idle(&mut self) {}
fn tick(&mut self) {}
fn midi_tick(&mut self) {}
fn render(&mut self, _input: &[[f32; 2]], _output: &mut [[f32; 2]]) {}
fn voice_handler(&mut self) -> Option<&mut dyn ReceiveVoiceHandler> {
None
}
fn midi_in(&mut self, _message: MidiMessage) {}
fn loop_in(&mut self, _message: ValuePtr) {}
}
#[repr(C)]
#[derive(Debug)]
pub struct Info {
pub sdk_version: u32,
pub long_name: *mut c_char,
pub short_name: *mut c_char,
flags: u32,
pub num_params: u32,
pub def_poly: u32,
pub num_out_ctrls: u32,
pub num_out_voices: u32,
}
#[derive(Clone, Debug)]
pub struct InfoBuilder {
sdk_version: u32,
long_name: String,
short_name: String,
flags: u32,
num_params: u32,
def_poly: u32,
num_out_ctrls: u32,
num_out_voices: u32,
}
impl InfoBuilder {
pub fn new_effect(long_name: &str, short_name: &str, num_params: u32) -> Self {
Self {
sdk_version: CURRENT_SDK_VERSION,
long_name: long_name.to_string(),
short_name: short_name.to_string(),
flags: 0,
num_params,
def_poly: 0,
num_out_ctrls: 0,
num_out_voices: 0,
}
.new_voice_params()
}
pub fn new_full_gen(long_name: &str, short_name: &str, num_params: u32) -> Self {
InfoBuilder::new_effect(long_name, short_name, num_params)
.generator()
.get_note_input()
}
pub fn new_visual(long_name: &str, short_name: &str, num_params: u32) -> Self {
InfoBuilder::new_effect(long_name, short_name, num_params).no_process()
}
pub fn with_poly(mut self, poly: u32) -> Self {
self.def_poly = poly;
self
}
pub fn with_out_ctrls(mut self, out_ctrls: u32) -> Self {
self.num_out_ctrls = out_ctrls;
self
}
pub fn with_out_voices(mut self, out_voices: u32) -> Self {
self.num_out_voices = out_voices;
self
}
pub fn generator(mut self) -> Self {
self.flags |= 1;
self
}
pub fn get_chan_custom_shape(mut self) -> Self {
self.flags |= 1 << 3;
self
}
pub fn get_note_input(mut self) -> Self {
self.flags |= 1 << 4;
self
}
pub fn want_new_tick(mut self) -> Self {
self.flags |= 1 << 5;
self
}
pub fn no_process(mut self) -> Self {
self.flags |= 1 << 6;
self
}
pub fn no_window(mut self) -> Self {
self.flags |= 1 << 10;
self
}
pub fn interfaceless(mut self) -> Self {
self.flags |= 1 << 11;
self
}
pub fn time_warp(mut self) -> Self {
self.flags |= 1 << 13;
self
}
pub fn midi_out(mut self) -> Self {
self.flags |= 1 << 14;
self
}
pub fn demo_version(mut self) -> Self {
self.flags |= 1 << 15;
self
}
pub fn can_send(mut self) -> Self {
self.flags |= 1 << 16;
self
}
pub fn loop_out(mut self) -> Self {
self.flags |= 1 << 17;
self
}
pub fn get_chan_sample(mut self) -> Self {
self.flags |= 1 << 19;
self
}
pub fn want_fit_time(mut self) -> Self {
self.flags |= 1 << 20;
self
}
fn new_voice_params(mut self) -> Self {
self.flags |= 1 << 21;
self
}
pub fn cant_smart_disable(mut self) -> Self {
self.flags |= 1 << 23;
self
}
pub fn want_settings_button(mut self) -> Self {
self.flags |= 1 << 24;
self
}
pub fn build(self) -> Info {
let log_err = |e| {
error!("{}", e);
panic!();
};
let long_name = CString::new(self.long_name)
.unwrap_or_else(log_err)
.into_raw();
let short_name = CString::new(self.short_name)
.unwrap_or_else(log_err)
.into_raw();
Info {
sdk_version: self.sdk_version,
long_name: unsafe { alloc_real_cstr(long_name) },
short_name: unsafe { alloc_real_cstr(short_name) },
flags: self.flags,
num_params: self.num_params,
def_poly: self.def_poly,
num_out_ctrls: self.num_out_ctrls,
num_out_voices: self.num_out_voices,
}
}
}
pub struct StateReader(pub(crate) *mut c_void);
impl Read for StateReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut read = 0u32;
let buf_ptr = buf.as_mut_ptr();
let res = unsafe { istream_read(self.0, buf_ptr, buf.len() as u32, &mut read) };
debug!("StateReader read {} bytes", read);
check_hresult(
HRESULT::from(res),
read as usize,
"Error reading from IStream",
)
}
}
extern "C" {
fn istream_read(istream: *mut c_void, data: *mut u8, size: u32, read: *mut u32) -> i32;
}
pub struct StateWriter(pub(crate) *mut c_void);
impl Write for StateWriter {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut write = 0u32;
let buf_ptr = buf.as_ptr();
let res = unsafe { istream_write(self.0, buf_ptr, buf.len() as u32, &mut write) };
check_hresult(
HRESULT::from(res),
write as usize,
"Error writing to IStream",
)
}
}
extern "C" {
fn istream_write(istream: *mut c_void, data: *const u8, size: u32, write: *mut u32) -> i32;
}
fn check_hresult(result: HRESULT, read: usize, error_msg: &str) -> io::Result<usize> {
if !result.is_success() {
return Err(io::Error::new(io::ErrorKind::Other, error_msg));
}
Ok(read)
}
#[doc(hidden)]
#[derive(Debug)]
pub struct PluginAdapter(pub Box<dyn Plugin>);
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_info(adapter: *mut PluginAdapter) -> *mut Info {
Box::into_raw(Box::new((*adapter).0.info()))
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_dispatcher(
adapter: *mut PluginAdapter,
message: FlMessage,
) -> intptr_t {
(*adapter).0.on_message(message.into()).as_raw_ptr()
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_name_of(
adapter: *const PluginAdapter,
message: FlMessage,
) -> *mut c_char {
let name = CString::new((*adapter).0.name_of(message.into())).unwrap_or_else(|e| {
error!("{}", e);
panic!();
});
name.into_raw()
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_process_event(adapter: *mut PluginAdapter, event: FlMessage) -> c_int {
(*adapter).0.process_event(event.into());
0
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_process_param(
adapter: *mut PluginAdapter,
message: FlMessage,
) -> intptr_t {
(*adapter)
.0
.process_param(
message.id as usize,
ValuePtr(message.index),
ProcessParamFlags::from_bits_truncate(message.value),
)
.as_raw_ptr()
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_idle(adapter: *mut PluginAdapter) {
(*adapter).0.idle();
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_tick(adapter: *mut PluginAdapter) {
(*adapter).0.tick();
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_midi_tick(adapter: *mut PluginAdapter) {
(*adapter).0.midi_tick();
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_eff_render(
adapter: *mut PluginAdapter,
source: *const [f32; 2],
dest: *mut [f32; 2],
length: i32,
) {
let input = std::slice::from_raw_parts(source, length as usize);
let mut output = std::slice::from_raw_parts_mut(dest, length as usize);
(*adapter).0.render(input, &mut output);
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_gen_render(
adapter: *mut PluginAdapter,
dest: *mut [f32; 2],
length: i32,
) {
let mut output = std::slice::from_raw_parts_mut(dest, length as usize);
(*adapter).0.render(&[[0.0, 0.0]], &mut output);
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_midi_in(adapter: *mut PluginAdapter, message: &mut c_int) {
(*adapter).0.midi_in(message.into());
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_save_state(adapter: *mut PluginAdapter, stream: *mut c_void) {
(*adapter).0.save_state(StateWriter(stream));
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_load_state(adapter: *mut PluginAdapter, stream: *mut c_void) {
(*adapter).0.load_state(StateReader(stream));
}
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn plugin_loop_in(adapter: *mut PluginAdapter, message: intptr_t) {
(*adapter).0.loop_in(ValuePtr(message));
}