use std::os::raw::c_void;
use std::sync::Arc;
use self::consts::*;
use crate::{
editor::Editor,
plugin::{Info, Plugin, PluginParameters},
};
#[allow(missing_docs)] pub mod consts {
pub const MAX_PRESET_NAME_LEN: usize = 24;
pub const MAX_PARAM_STR_LEN: usize = 32;
pub const MAX_LABEL: usize = 64;
pub const MAX_SHORT_LABEL: usize = 8;
pub const MAX_PRODUCT_STR_LEN: usize = 64;
pub const MAX_VENDOR_STR_LEN: usize = 64;
pub const VST_MAGIC: i32 = ('V' as i32) << 24 | ('s' as i32) << 16 | ('t' as i32) << 8 | ('P' as i32);
}
pub type PluginMain = fn(callback: HostCallbackProc) -> *mut AEffect;
pub type HostCallbackProc =
extern "C" fn(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize;
pub type DispatcherProc =
extern "C" fn(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize;
pub type ProcessProc =
extern "C" fn(effect: *mut AEffect, inputs: *const *const f32, outputs: *mut *mut f32, sample_frames: i32);
pub type ProcessProcF64 =
extern "C" fn(effect: *mut AEffect, inputs: *const *const f64, outputs: *mut *mut f64, sample_frames: i32);
pub type SetParameterProc = extern "C" fn(effect: *mut AEffect, index: i32, parameter: f32);
pub type GetParameterProc = extern "C" fn(effect: *mut AEffect, index: i32) -> f32;
#[allow(non_snake_case)]
#[repr(C)]
pub struct AEffect {
pub magic: i32,
pub dispatcher: DispatcherProc,
pub _process: ProcessProc,
pub setParameter: SetParameterProc,
pub getParameter: GetParameterProc,
pub numPrograms: i32,
pub numParams: i32,
pub numInputs: i32,
pub numOutputs: i32,
pub flags: i32,
pub reserved1: isize,
pub reserved2: isize,
pub initialDelay: i32,
pub _realQualities: i32,
pub _offQualities: i32,
pub _ioRatio: f32,
pub object: *mut c_void,
pub user: *mut c_void,
pub uniqueId: i32,
pub version: i32,
pub processReplacing: ProcessProc,
pub processReplacingF64: ProcessProcF64,
pub future: [u8; 56],
}
impl AEffect {
#[allow(clippy::borrowed_box)]
pub unsafe fn get_plugin(&self) -> &mut Box<dyn Plugin> {
&mut *(self.object as *mut Box<dyn Plugin>)
}
pub unsafe fn get_info(&self) -> &Info {
&(*(self.user as *mut super::PluginCache)).info
}
pub unsafe fn get_params(&self) -> &Arc<dyn PluginParameters> {
&(*(self.user as *mut super::PluginCache)).params
}
pub unsafe fn get_editor(&self) -> &mut Option<Box<dyn Editor>> {
&mut (*(self.user as *mut super::PluginCache)).editor
}
pub unsafe fn drop_plugin(&mut self) {
drop(Box::from_raw(self.object as *mut Box<dyn Plugin>));
drop(Box::from_raw(self.user as *mut super::PluginCache));
}
}
#[repr(C)]
pub struct ChannelProperties {
pub name: [u8; MAX_LABEL as usize],
pub flags: i32,
pub arrangement_type: SpeakerArrangementType,
pub short_name: [u8; MAX_SHORT_LABEL as usize],
pub future: [u8; 48],
}
#[repr(i32)]
#[derive(Clone, Copy)]
pub enum SpeakerArrangementType {
Custom = -2,
Empty = -1,
Mono = 0,
Stereo,
StereoSurround,
StereoCenter,
StereoSide,
StereoCLfe,
Cinema30,
Music30,
Cinema31,
Music31,
Cinema40,
Music40,
Cinema41,
Music41,
Surround50,
Surround51,
Cinema60,
Music60,
Cinema61,
Music61,
Cinema70,
Music70,
Cinema71,
Music71,
Cinema80,
Music80,
Cinema81,
Music81,
Surround102,
}
#[allow(missing_docs)]
#[derive(PartialEq, Eq)]
pub enum Supported {
Yes,
Maybe,
No,
Custom(isize),
}
impl Supported {
pub fn from(val: isize) -> Option<Supported> {
use self::Supported::*;
match val {
1 => Some(Yes),
0 => Some(Maybe),
-1 => Some(No),
_ => None,
}
}
}
impl Into<isize> for Supported {
fn into(self) -> isize {
use self::Supported::*;
match self {
Yes => 1,
Maybe => 0,
No => -1,
Custom(i) => i,
}
}
}
#[repr(i32)]
pub enum ProcessLevel {
Unknown = 0,
User,
Realtime,
Prefetch,
Offline,
}
#[repr(i32)]
#[allow(missing_docs)]
pub enum HostLanguage {
English = 1,
German,
French,
Italian,
Spanish,
Japanese,
}
#[repr(i32)]
pub enum FileSelectCommand {
Load = 0,
Save,
LoadMultipleFiles,
SelectDirectory,
}
pub enum FileSelectType {
Regular,
}
#[repr(C)]
pub struct FileType {
pub name: [u8; 128],
pub osx_type: [u8; 8],
pub win_type: [u8; 8],
pub nix_type: [u8; 8],
pub mime_type_1: [u8; 128],
pub mime_type_2: [u8; 128],
}
#[repr(C)]
pub struct FileSelect {
pub command: FileSelectCommand,
pub select_type: FileSelectType,
pub mac_creator: i32,
pub num_types: i32,
pub file_types: *mut FileType,
pub title: [u8; 1024],
pub initial_path: *mut u8,
pub return_path: *mut u8,
pub size_return_path: i32,
pub return_multiple_paths: *mut *mut u8,
pub num_paths: i32,
pub reserved: isize,
pub future: [u8; 116],
}
#[repr(C)]
pub struct Events {
pub num_events: i32,
pub _reserved: isize,
pub events: [*mut Event; 2],
}
impl Events {
#[inline]
pub(crate) fn events_raw(&self) -> &[*const Event] {
use std::slice;
unsafe {
slice::from_raw_parts(
&self.events[0] as *const *mut _ as *const *const _,
self.num_events as usize,
)
}
}
#[inline]
pub(crate) fn events_raw_mut(&mut self) -> &mut [*const SysExEvent] {
use std::slice;
unsafe {
slice::from_raw_parts_mut(
&mut self.events[0] as *mut *mut _ as *mut *const _,
self.num_events as usize,
)
}
}
#[inline]
#[allow(clippy::needless_lifetimes)]
pub fn events<'a>(&'a self) -> impl Iterator<Item = crate::event::Event<'a>> {
self.events_raw()
.iter()
.map(|ptr| unsafe { crate::event::Event::from_raw_event(*ptr) })
}
}
#[repr(i32)]
#[derive(Copy, Clone, Debug)]
pub enum EventType {
_Placeholder = 0,
Midi = 1,
_Audio,
_Video,
_Parameter,
_Trigger,
SysEx,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Event {
pub event_type: EventType,
pub byte_size: i32,
pub delta_frames: i32,
pub _flags: i32,
pub _reserved: [u8; 16],
}
#[repr(C)]
pub struct MidiEvent {
pub event_type: EventType,
pub byte_size: i32,
pub delta_frames: i32,
pub flags: i32,
pub note_length: i32,
pub note_offset: i32,
pub midi_data: [u8; 3],
pub _midi_reserved: u8,
pub detune: i8,
pub note_off_velocity: u8,
pub _reserved1: u8,
pub _reserved2: u8,
}
#[repr(C)]
#[derive(Clone)]
pub struct SysExEvent {
pub event_type: EventType,
pub byte_size: i32,
pub delta_frames: i32,
pub _flags: i32,
pub data_size: i32,
pub _reserved1: isize,
pub system_data: *mut u8,
pub _reserved2: isize,
}
unsafe impl Send for SysExEvent {}
#[repr(C)]
#[derive(Clone, Default, Copy)]
pub struct TimeInfo {
pub sample_pos: f64,
pub sample_rate: f64,
pub nanoseconds: f64,
pub ppq_pos: f64,
pub tempo: f64,
pub bar_start_pos: f64,
pub cycle_start_pos: f64,
pub cycle_end_pos: f64,
pub time_sig_numerator: i32,
pub time_sig_denominator: i32,
pub smpte_offset: i32,
pub smpte_frame_rate: SmpteFrameRate,
pub samples_to_next_clock: i32,
pub flags: i32,
}
#[repr(i32)]
#[derive(Copy, Clone, Debug)]
pub enum SmpteFrameRate {
Smpte24fps = 0,
Smpte25fps = 1,
Smpte2997fps = 2,
Smpte30fps = 3,
Smpte2997dfps = 4,
Smpte30dfps = 5,
SmpteFilm16mm = 6,
SmpteFilm35mm = 7,
Smpte239fps = 10,
Smpte249fps = 11,
Smpte599fps = 12,
Smpte60fps = 13,
}
impl Default for SmpteFrameRate {
fn default() -> Self {
SmpteFrameRate::Smpte24fps
}
}
bitflags! {
pub struct ChannelFlags: i32 {
const ACTIVE = 1;
const STEREO = 1 << 1;
const SPEAKER = 1 << 2;
}
}
bitflags! {
pub struct PluginFlags: i32 {
const HAS_EDITOR = 1;
const CAN_REPLACING = 1 << 4;
const PROGRAM_CHUNKS = 1 << 5;
const IS_SYNTH = 1 << 8;
const NO_SOUND_IN_STOP = 1 << 9;
const CAN_DOUBLE_REPLACING = 1 << 12;
}
}
bitflags! {
pub struct ModifierKey: u8 {
const SHIFT = 1;
const ALT = 1 << 1;
const COMMAND = 1 << 2;
const CONTROL = 1 << 3; }
}
bitflags! {
pub struct MidiEventFlags: i32 {
const REALTIME_EVENT = 1;
}
}
bitflags! {
pub struct TimeInfoFlags : i32 {
const TRANSPORT_CHANGED = 1;
const TRANSPORT_PLAYING = 1 << 1;
const TRANSPORT_CYCLE_ACTIVE = 1 << 2;
const TRANSPORT_RECORDING = 1 << 3;
const AUTOMATION_WRITING = 1 << 6;
const AUTOMATION_READING = 1 << 7;
const NANOSECONDS_VALID = 1 << 8;
const PPQ_POS_VALID = 1 << 9;
const TEMPO_VALID = 1 << 10;
const BARS_VALID = 1 << 11;
const CYCLE_POS_VALID = 1 << 12;
const TIME_SIG_VALID = 1 << 13;
const SMPTE_VALID = 1 << 14;
const VST_CLOCK_VALID = 1 << 15;
}
}
#[cfg(test)]
mod tests {
use super::super::event;
use super::*;
use std::mem;
pub struct EventContainer {
stored_event: Box<Event>,
pub events: Events,
}
fn encode_midi_message_as_events(message: [u8; 3]) -> EventContainer {
let midi_event: MidiEvent = MidiEvent {
event_type: EventType::Midi,
byte_size: mem::size_of::<MidiEvent>() as i32,
delta_frames: 0,
flags: 0,
note_length: 0,
note_offset: 0,
midi_data: [message[0], message[1], message[2]],
_midi_reserved: 0,
detune: 0,
note_off_velocity: 0,
_reserved1: 0,
_reserved2: 0,
};
let mut event: Event = unsafe { std::mem::transmute(midi_event) };
event.event_type = EventType::Midi;
let events = Events {
num_events: 1,
_reserved: 0,
events: [&mut event, &mut event], };
let mut ec = EventContainer {
stored_event: Box::new(event),
events,
};
ec.events.events[0] = &mut *(ec.stored_event); ec
}
#[test]
fn encode_and_decode_gives_back_original_message() {
let message: [u8; 3] = [35, 16, 22];
let encoded = encode_midi_message_as_events(message);
assert_eq!(encoded.events.num_events, 1);
assert_eq!(encoded.events.events.len(), 2);
let e_vec: Vec<event::Event> = encoded.events.events().collect();
assert_eq!(e_vec.len(), 1);
match e_vec[0] {
event::Event::Midi(event::MidiEvent { data, .. }) => {
assert_eq!(data, message);
}
_ => {
panic!("Not a midi event!");
}
};
}
#[test]
fn message_survives_calling_events() {
let message: [u8; 3] = [35, 16, 22];
let encoded = encode_midi_message_as_events(message);
for e in encoded.events.events() {
match e {
event::Event::Midi(event::MidiEvent { data, .. }) => {
assert_eq!(data, message);
}
_ => {
panic!("Not a midi event!");
}
}
}
for e in encoded.events.events() {
match e {
event::Event::Midi(event::MidiEvent { data, .. }) => {
assert_eq!(data, message);
}
_ => {
panic!("Not a midi event!"); }
}
}
}
}