use std::{mem, ptr};
use libc::{self, c_char};
use std::slice;
use std::cell::RefCell;
use std::collections::VecMap;
use super::get_ladspa_descriptor;
mod ladspa {
use libc::{c_void, c_char};
pub type Data = f32;
pub type Properties = i32;
pub type PortDescriptor = i32;
pub type PortRangeHintDescriptor = i32;
pub type Handle = *mut c_void;
#[repr(C)]
#[derive(Copy)]
pub struct PortRangeHint {
pub hint_descriptor: PortRangeHintDescriptor,
pub lower_bound: Data,
pub upper_bound: Data,
}
#[repr(C)]
#[allow(missing_copy_implementations)] pub struct Descriptor {
pub unique_id: u64,
pub label: *const c_char,
pub properties: Properties,
pub name: *const c_char,
pub maker: *const c_char,
pub copyright: *const c_char,
pub port_count: u64,
pub port_descriptors: *mut PortDescriptor,
pub port_names: *mut *const c_char,
pub port_range_hints: *mut PortRangeHint,
pub implementation_data: *mut c_void,
pub instantiate: extern "C" fn(descriptor: *const Descriptor, sample_rate: u64) -> Handle,
pub connect_port: extern "C" fn(instance: Handle, port: usize, data_location: *mut Data),
pub activate: extern "C" fn(instance: Handle),
pub run: extern "C" fn(instance: Handle, sample_count: u64),
pub run_adding: extern "C" fn(instance: Handle, sample_count: u64),
pub set_run_adding_gain: extern "C" fn(instance: Handle, gain: Data),
pub deactivate: extern "C" fn(instance: Handle),
pub cleanup: extern "C" fn(instance: Handle),
}
}
struct Handle<'a> {
descriptor: &'static super::PluginDescriptor,
plugin: Box<super::Plugin + 'static>,
ports: VecMap<super::PortConnection<'a>>,
}
unsafe fn alloc<T>(num: u64) -> *mut T {
let ptr: *mut T = mem::transmute(libc::malloc(num * mem::size_of::<T>() as u64));
if ptr == ptr::null_mut() {
panic!("malloc returned null!");
}
ptr
}
unsafe fn free<T>(x: *const T) {
libc::free(mem::transmute(x));
}
unsafe fn make_c_str(s: &'static str) -> *const c_char {
let c_str: *mut c_char = alloc::<c_char>(s.len() as u64 + 1);
ptr::copy_memory(c_str, mem::transmute(s.as_ptr()), s.len());
slice::from_raw_mut_buf(&c_str, s.len() + 1)[s.len()] = 0; c_str
}
static mut init_done: bool = false;
static mut num_descriptors: u64 = 0;
static mut descriptors: Option<*mut *mut ladspa::Descriptor> = None;
static MAX_DESCRIPTORS: u64 = 32;
#[allow(dead_code)]
unsafe fn _lto_workaround() {
ladspa_descriptor(0);
}
#[no_mangle]
pub unsafe extern "C" fn ladspa_descriptor(index: u64) -> *mut ladspa::Descriptor {
if !init_done {
libc::atexit(global_destruct);
descriptors = Some(alloc(MAX_DESCRIPTORS));
init_done = true;
}
if index < num_descriptors {
return *descriptors.unwrap().offset(index as isize);
}
match get_ladspa_descriptor(index) {
Some(plugin) => {
let desc: &mut ladspa::Descriptor = mem::transmute(alloc::<ladspa::Descriptor>(1));
desc.unique_id = plugin.unique_id;
desc.label = make_c_str(plugin.label);
desc.properties = plugin.properties.bits();
desc.name = make_c_str(plugin.name);
desc.maker = make_c_str(plugin.maker);
desc.copyright = make_c_str(plugin.copyright);
desc.port_count = plugin.ports.len() as u64;
desc.port_descriptors = alloc::<ladspa::PortDescriptor>(desc.port_count);
desc.port_names = alloc::<*const c_char>(desc.port_count);
desc.port_range_hints = alloc::<ladspa::PortRangeHint>(desc.port_count);
for i in 0..desc.port_count as usize {
*desc.port_descriptors.offset(i as isize)
= plugin.ports[i].desc as i32;
*desc.port_names.offset(i as isize)
= make_c_str(plugin.ports[i].name);
let port = &plugin.ports[i];
*desc.port_range_hints.offset(i as isize)
= ladspa::PortRangeHint {
hint_descriptor: port.hint.map(|x| x.bits()).unwrap_or(0) |
port.default.map(|x| x as i32).unwrap_or(0) |
port.lower_bound.map(|_| 1).unwrap_or(0) |
port.upper_bound.map(|_| 2).unwrap_or(0),
lower_bound: port.lower_bound.unwrap_or(0_f32),
upper_bound: port.upper_bound.unwrap_or(0_f32),
};
}
desc.implementation_data = mem::transmute(alloc::<super::PluginDescriptor>(1));
ptr::write(mem::transmute::<_, *mut super::PluginDescriptor>
(desc.implementation_data), plugin);
desc.instantiate = instantiate;
desc.connect_port = connect_port;
desc.run = run;
desc.cleanup = cleanup;
desc.activate = activate;
desc.deactivate = deactivate;
let ptr = mem::transmute(desc);
*descriptors.unwrap().offset(num_descriptors as isize) = ptr;
num_descriptors += 1;
if num_descriptors >= MAX_DESCRIPTORS {
panic!("The program tried to define more than the max supported number of descriptors currently supported - this usually means you forgot to return None at some point in get_ladspa_descriptor.");
}
ptr
}
None => ptr::null_mut()
}
}
extern "C" fn global_destruct() {
unsafe {
if !init_done {
return;
}
for i in 0..num_descriptors {
free_descriptor(*descriptors.unwrap().offset(i as isize));
}
free(descriptors.unwrap());
}
}
unsafe fn free_descriptor(ptr: *mut ladspa::Descriptor) {
let desc: &mut ladspa::Descriptor = mem::transmute(ptr);
free(desc.label);
free(desc.name);
free(desc.maker);
free(desc.copyright);
free(desc.port_descriptors);
for i in 0..desc.port_count {
free(*desc.port_names.offset(i as isize));
}
free(desc.port_names);
free(desc.port_range_hints);
let rust_desc: *mut super::PluginDescriptor = mem::transmute(desc.implementation_data);
drop(ptr::read(rust_desc));
free(desc.implementation_data);
free(ptr);
}
extern "C" fn instantiate(descriptor: *const ladspa::Descriptor, sample_rate: u64) -> ladspa::Handle {
unsafe {
let desc: &mut ladspa::Descriptor = mem::transmute(descriptor);
let rust_desc: &super::PluginDescriptor = mem::transmute(desc.implementation_data);
let rust_plugin = (rust_desc.new)(rust_desc, sample_rate);
let ports: Vec<super::PortConnection> = Vec::new();
let heap_handle: *mut Handle = alloc::<Handle>(1);
ptr::write(mem::transmute(&(*heap_handle).descriptor), rust_desc);
ptr::write(mem::transmute(&(*heap_handle).plugin), rust_plugin);
ptr::write(mem::transmute(&(*heap_handle).ports), ports);
mem::transmute(heap_handle)
}
}
extern "C" fn connect_port(instance: ladspa::Handle, port_num: usize, data_location: *mut ladspa::Data) {
unsafe {
let handle: &mut Handle = mem::transmute(instance);
let port = handle.descriptor.ports[port_num];
let data = match port.desc {
super::PortDescriptor::AudioInput => {
super::PortData::AudioInput( slice::from_raw_buf(mem::transmute(&data_location), 0)) },
super::PortDescriptor::AudioOutput => {
super::PortData::AudioOutput(RefCell::new( slice::from_raw_mut_buf(mem::transmute(&data_location), 0)))
},
super::PortDescriptor::ControlInput => {
super::PortData::ControlInput(mem::transmute(data_location))
},
super::PortDescriptor::ControlOutput => {
super::PortData::ControlOutput(RefCell::new(mem::transmute(data_location)))
},
};
let conn = super::PortConnection {
port: port,
data: data,
};
handle.ports.insert(port_num, conn);
}
}
extern "C" fn run(instance: ladspa::Handle, sample_count: u64) {
unsafe {
let handle: *mut Handle = mem::transmute(instance);
for (_, port) in (*handle).ports.iter_mut() {
match port.data {
super::PortData::AudioOutput(ref mut data) => {
let ptr = mem::transmute(&data.borrow().as_ptr());
*data.borrow_mut() = slice::from_raw_mut_buf(ptr, sample_count as usize);
},
super::PortData::AudioInput(ref mut data) => {
let ptr = mem::transmute(&data.as_ptr());
*data = slice::from_raw_buf(ptr, sample_count as usize);
},
_ => { }
}
}
let ports: Vec<&super::PortConnection> =
(0..(*handle).ports.len()).map(|i| &(*handle).ports[i]).collect();
(*handle).plugin.run(sample_count as usize, ports.as_slice());
}
}
extern "C" fn activate(instance: ladspa::Handle) {
unsafe{
let handle: &mut Handle = mem::transmute(instance);
handle.plugin.activate();
}
}
extern "C" fn deactivate(instance: ladspa::Handle) {
unsafe {
let handle: &mut Handle = mem::transmute(instance);
handle.plugin.deactivate();
}
}
extern "C" fn cleanup(instance: ladspa::Handle) {
unsafe {
let handle: *mut Handle = mem::transmute(instance);
drop(ptr::read(handle));
free(instance);
}
}