#![deny(
nonstandard_style,
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts
)]
#![warn(
deprecated_in_future,
missing_docs,
unused_import_braces,
unused_labels,
unused_lifetimes,
unused_qualifications,
unreachable_pub
)]
pub mod host;
pub mod plugin;
pub mod voice;
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use bitflags::bitflags;
use log::{debug, error};
pub const CURRENT_SDK_VERSION: u32 = 1;
pub const WAVETABLE_SIZE: usize = 16384;
#[allow(non_camel_case_types)]
#[doc(hidden)]
pub type intptr_t = isize;
pub(crate) type Tag = intptr_t;
#[doc(hidden)]
#[macro_export]
macro_rules! implement_tag {
() => {
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Tag(pub crate::Tag);
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
};
}
#[derive(Debug, Clone)]
#[repr(C)]
struct FlMessage {
id: intptr_t,
index: intptr_t,
value: intptr_t,
}
pub trait AsRawPtr {
fn as_raw_ptr(&self) -> intptr_t;
}
macro_rules! primitive_as_raw_ptr {
($type:ty) => {
impl AsRawPtr for $type {
fn as_raw_ptr(&self) -> intptr_t {
(*self) as intptr_t
}
}
};
}
primitive_as_raw_ptr!(i8);
primitive_as_raw_ptr!(u8);
primitive_as_raw_ptr!(i16);
primitive_as_raw_ptr!(u16);
primitive_as_raw_ptr!(i32);
primitive_as_raw_ptr!(u32);
primitive_as_raw_ptr!(i64);
primitive_as_raw_ptr!(u64);
primitive_as_raw_ptr!(usize);
primitive_as_raw_ptr!(*mut c_void);
primitive_as_raw_ptr!(*const c_void);
impl AsRawPtr for bool {
fn as_raw_ptr(&self) -> intptr_t {
(self.to_owned() as u8).into()
}
}
impl AsRawPtr for f32 {
fn as_raw_ptr(&self) -> intptr_t {
self.to_bits() as intptr_t
}
}
impl AsRawPtr for f64 {
fn as_raw_ptr(&self) -> intptr_t {
self.to_bits() as intptr_t
}
}
impl AsRawPtr for String {
fn as_raw_ptr(&self) -> intptr_t {
let value = CString::new(self.clone()).unwrap_or_else(|e| {
error!("{}", e);
panic!();
});
unsafe { alloc_real_cstr(value.into_raw()) as intptr_t }
}
}
#[no_mangle]
extern "C" {
fn alloc_real_cstr(raw_str: *mut c_char) -> *mut c_char;
}
pub trait FromRawPtr {
fn from_raw_ptr(value: intptr_t) -> Self
where
Self: Sized;
}
macro_rules! primitive_from_raw_ptr {
($type:ty) => {
impl FromRawPtr for $type {
fn from_raw_ptr(value: intptr_t) -> Self {
value as Self
}
}
};
}
primitive_from_raw_ptr!(i8);
primitive_from_raw_ptr!(u8);
primitive_from_raw_ptr!(i16);
primitive_from_raw_ptr!(u16);
primitive_from_raw_ptr!(i32);
primitive_from_raw_ptr!(u32);
primitive_from_raw_ptr!(i64);
primitive_from_raw_ptr!(u64);
primitive_from_raw_ptr!(usize);
primitive_from_raw_ptr!(*mut c_void);
primitive_from_raw_ptr!(*const c_void);
impl FromRawPtr for f32 {
fn from_raw_ptr(value: intptr_t) -> Self {
f32::from_bits(value as i32 as u32)
}
}
impl FromRawPtr for f64 {
fn from_raw_ptr(value: intptr_t) -> Self {
f64::from_bits(value as i64 as u64)
}
}
impl FromRawPtr for String {
fn from_raw_ptr(value: intptr_t) -> Self {
let cstr = unsafe { CStr::from_ptr(value as *const c_char) };
cstr.to_string_lossy().to_string()
}
}
impl FromRawPtr for bool {
fn from_raw_ptr(value: intptr_t) -> Self {
value != 0
}
}
#[derive(Debug)]
pub struct ValuePtr(intptr_t);
impl ValuePtr {
pub fn get<T: FromRawPtr>(&self) -> T {
T::from_raw_ptr(self.0)
}
}
impl FromRawPtr for ValuePtr {
fn from_raw_ptr(value: intptr_t) -> Self {
ValuePtr(value)
}
}
#[derive(Debug, Clone)]
pub struct TimeSignature {
pub steps_per_bar: u32,
pub steps_per_beat: u32,
pub ppq: u32,
}
impl From<TTimeSigInfo> for TimeSignature {
fn from(value: TTimeSigInfo) -> Self {
Self {
steps_per_bar: value.steps_per_bar as u32,
steps_per_beat: value.steps_per_beat as u32,
ppq: value.ppq as u32,
}
}
}
#[repr(C)]
struct TTimeSigInfo {
steps_per_bar: c_int,
steps_per_beat: c_int,
ppq: c_int,
}
impl FromRawPtr for TTimeSigInfo {
fn from_raw_ptr(raw_ptr: intptr_t) -> Self {
let sig_ptr = raw_ptr as *mut c_void as *mut TTimeSigInfo;
unsafe {
Self {
steps_per_beat: (*sig_ptr).steps_per_beat,
steps_per_bar: (*sig_ptr).steps_per_bar,
ppq: (*sig_ptr).ppq,
}
}
}
}
#[derive(Debug)]
pub enum TimeFormat {
Beats,
AbsoluteMs,
RunningMs,
RestartMs,
}
impl From<TimeFormat> for u8 {
fn from(format: TimeFormat) -> Self {
match format {
TimeFormat::Beats => 0,
TimeFormat::AbsoluteMs => 1,
TimeFormat::RunningMs => 2,
TimeFormat::RestartMs => 3,
}
}
}
#[derive(Debug, Default)]
#[repr(C)]
pub struct Time(pub f64, pub f64);
impl FromRawPtr for Time {
fn from_raw_ptr(value: intptr_t) -> Self {
unsafe { *Box::from_raw(value as *mut c_void as *mut Time) }
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Default)]
#[repr(C)]
pub struct SongTime {
pub bar: i32,
pub step: i32,
pub tick: i32,
}
impl FromRawPtr for SongTime {
fn from_raw_ptr(value: intptr_t) -> Self {
unsafe { *Box::from_raw(value as *mut c_void as *mut Self) }
}
}
#[derive(Debug)]
pub struct NameColor {
pub name: String,
pub vis_name: String,
pub color: u8,
pub index: usize,
}
#[repr(C)]
struct TNameColor {
name: [u8; 256],
vis_name: [u8; 256],
color: c_int,
index: c_int,
}
impl From<TNameColor> for NameColor {
fn from(name_color: TNameColor) -> Self {
Self {
name: String::from_utf8_lossy(&name_color.name[..]).to_string(),
vis_name: String::from_utf8_lossy(&name_color.vis_name[..]).to_string(),
color: name_color.color as u8,
index: name_color.index as usize,
}
}
}
impl From<NameColor> for TNameColor {
fn from(name_color: NameColor) -> Self {
let mut name = [0_u8; 256];
name.copy_from_slice(name_color.name.as_bytes());
let mut vis_name = [0_u8; 256];
vis_name.copy_from_slice(name_color.vis_name.as_bytes());
Self {
name,
vis_name,
color: name_color.color as c_int,
index: name_color.index as c_int,
}
}
}
impl FromRawPtr for TNameColor {
fn from_raw_ptr(value: intptr_t) -> Self {
unsafe { *Box::from_raw(value as *mut Self) }
}
}
#[derive(Debug)]
pub struct MidiMessage {
pub status: u8,
pub data1: u8,
pub data2: u8,
pub port: u8,
}
impl From<&mut c_int> for MidiMessage {
fn from(value: &mut c_int) -> Self {
Self {
status: (*value & 0xff) as u8,
data1: ((*value >> 8) & 0xff) as u8,
data2: ((*value >> 16) & 0xff) as u8,
port: ((*value >> 24) & 0xff) as u8,
}
}
}
impl From<c_int> for MidiMessage {
fn from(value: c_int) -> Self {
Self {
status: (value & 0xff) as u8,
data1: ((value >> 8) & 0xff) as u8,
data2: ((value >> 16) & 0xff) as u8,
port: ((value >> 24) & 0xff) as u8,
}
}
}
#[derive(Debug)]
pub struct Notes {
pub notes: Vec<Note>,
pub flags: NotesFlags,
pub pattern: Option<u32>,
pub channel: Option<u32>,
}
#[derive(Debug)]
#[repr(C)]
pub struct Note {
pub position: i32,
pub length: i32,
pub pan: i32,
pub vol: i32,
pub note: i16,
pub color: i16,
pub pitch: i32,
pub mod_x: f32,
pub mod_y: f32,
}
bitflags! {
pub struct NotesFlags: isize {
const EMPTY_FIRST = 1;
const USE_SELECTION = 2;
}
}
#[repr(C)]
struct TNotesParams {
target: c_int,
flags: c_int,
pat_num: c_int,
chan_num: c_int,
count: c_int,
notes: *mut Note,
}
impl From<Notes> for TNotesParams {
fn from(mut notes: Notes) -> Self {
notes.notes.shrink_to_fit();
let notes_ptr = notes.notes.as_mut_ptr();
let len = notes.notes.len();
mem::forget(notes.notes);
Self {
target: 1,
flags: notes.flags.bits() as c_int,
pat_num: notes.pattern.map(|v| v as c_int).unwrap_or(-1),
chan_num: notes.channel.map(|v| v as c_int).unwrap_or(-1),
count: len as c_int,
notes: notes_ptr,
}
}
}
#[derive(Debug)]
pub struct ParamMenuEntry {
pub name: String,
pub flags: ParamMenuItemFlags,
}
bitflags! {
pub struct ParamMenuItemFlags: i32 {
const DISABLED = 1;
const CHECKED = 2;
}
}
impl ParamMenuEntry {
fn from_ffi(ffi_t: *mut TParamMenuEntry) -> Self {
Self {
name: unsafe { CString::from_raw((*ffi_t).name) }
.to_string_lossy()
.to_string(),
flags: ParamMenuItemFlags::from_bits(unsafe { (*ffi_t).flags })
.unwrap_or_else(ParamMenuItemFlags::empty),
}
}
}
#[derive(Debug)]
#[repr(C)]
struct TParamMenuEntry {
name: *mut c_char,
flags: c_int,
}
bitflags! {
pub struct ParameterFlags: isize {
const CANT_INTERPOLATE = 1;
const FLOAT = 2;
const CENTERED = 4;
}
}
impl AsRawPtr for ParameterFlags {
fn as_raw_ptr(&self) -> intptr_t {
self.bits()
}
}
bitflags! {
pub struct ProcessModeFlags: isize {
const NORMAL = 0;
const HQ_REALTIME = 1;
const HQ_NON_REALTIME = 2;
const IS_RENDERING = 16;
const IP_MASK = 0xFFFF << 8;
}
}
bitflags! {
pub struct ProcessParamFlags: isize {
const UPDATE_VALUE = 1;
const GET_VALUE = 2;
const SHOW_HINT = 4;
const UPDATE_CONTROL = 16;
const FROM_MIDI = 32;
const NO_LINK = 1024;
const INTERNAL_CTRL = 2048;
const PLUG_RESERVED = 4096;
}
}
bitflags! {
pub struct SampleLoadFlags: isize {
const SHOW_DIALOG = 1;
const FORCE_RELOAD = 2;
const GET_NAME = 4;
const NO_RESAMPLING = 5;
}
}
#[allow(missing_docs)]
#[derive(Debug)]
pub enum Transport {
Jog(Jog),
Jog2(Jog),
Strip(Jog),
StripJog(Jog),
StripHold(Jog),
Previous(Button),
Next(Button),
PreviousNext(Jog),
MoveJog(Jog),
Play(Button),
Stop(Button),
Record(Button),
Rewind(Hold),
FastForward(Hold),
Loop(Button),
Mute(Button),
Mode(Button),
Undo(Button),
UndoUp(Button),
UndoJog(Jog),
Punch(Hold),
PunchIn(Button),
PunchOut(Button),
AddMarker(Button),
AddAltMarker(Button),
MarkerJumpJog(Jog),
MarkerSelJog(Jog),
Up(Button),
Down(Button),
Left(Button),
Right(Button),
HZoomJog(Jog),
VZoomJog(Jog),
Snap(Button),
SnapMode(Jog),
Cut(Button),
Copy(Button),
Paste(Button),
Insert(Button),
Delete(Button),
NextWindow(Button),
WindowJog(Jog),
F1(Button),
F2(Button),
F3(Button),
F4(Button),
F5(Button),
F6(Button),
F7(Button),
F8(Button),
F9(Button),
F10(Button),
Enter(Button),
Escape(Button),
Yes(Button),
No(Button),
Menu(Button),
ItemMenu(Button),
Save(Button),
SaveNew(Button),
Unknown,
}
impl From<FlMessage> for Transport {
fn from(message: FlMessage) -> Self {
match message.index {
0 => Transport::Jog(Jog(message.value as i64)),
1 => Transport::Jog2(Jog(message.value as i64)),
2 => Transport::Strip(Jog(message.value as i64)),
3 => Transport::StripJog(Jog(message.value as i64)),
4 => Transport::StripHold(Jog(message.value as i64)),
5 => Transport::Previous(Button(message.value as u8)),
6 => Transport::Next(Button(message.value as u8)),
7 => Transport::PreviousNext(Jog(message.value as i64)),
8 => Transport::MoveJog(Jog(message.value as i64)),
10 => Transport::Play(Button(message.value as u8)),
11 => Transport::Stop(Button(message.value as u8)),
12 => Transport::Record(Button(message.value as u8)),
13 => Transport::Rewind(Hold(message.value != 0)),
14 => Transport::FastForward(Hold(message.value != 0)),
15 => Transport::Loop(Button(message.value as u8)),
16 => Transport::Mute(Button(message.value as u8)),
17 => Transport::Mode(Button(message.value as u8)),
20 => Transport::Undo(Button(message.value as u8)),
21 => Transport::UndoUp(Button(message.value as u8)),
22 => Transport::UndoJog(Jog(message.value as i64)),
30 => Transport::Punch(Hold(message.value != 0)),
31 => Transport::PunchIn(Button(message.value as u8)),
32 => Transport::PunchOut(Button(message.value as u8)),
33 => Transport::AddMarker(Button(message.value as u8)),
34 => Transport::AddAltMarker(Button(message.value as u8)),
35 => Transport::MarkerJumpJog(Jog(message.value as i64)),
36 => Transport::MarkerSelJog(Jog(message.value as i64)),
40 => Transport::Up(Button(message.value as u8)),
41 => Transport::Down(Button(message.value as u8)),
42 => Transport::Left(Button(message.value as u8)),
43 => Transport::Right(Button(message.value as u8)),
44 => Transport::HZoomJog(Jog(message.value as i64)),
45 => Transport::VZoomJog(Jog(message.value as i64)),
48 => Transport::Snap(Button(message.value as u8)),
49 => Transport::SnapMode(Jog(message.value as i64)),
50 => Transport::Cut(Button(message.value as u8)),
51 => Transport::Copy(Button(message.value as u8)),
52 => Transport::Paste(Button(message.value as u8)),
53 => Transport::Insert(Button(message.value as u8)),
54 => Transport::Delete(Button(message.value as u8)),
58 => Transport::NextWindow(Button(message.value as u8)),
59 => Transport::WindowJog(Jog(message.value as i64)),
60 => Transport::F1(Button(message.value as u8)),
61 => Transport::F2(Button(message.value as u8)),
62 => Transport::F3(Button(message.value as u8)),
63 => Transport::F4(Button(message.value as u8)),
64 => Transport::F5(Button(message.value as u8)),
65 => Transport::F6(Button(message.value as u8)),
66 => Transport::F7(Button(message.value as u8)),
67 => Transport::F8(Button(message.value as u8)),
68 => Transport::F9(Button(message.value as u8)),
69 => Transport::F10(Button(message.value as u8)),
80 => Transport::Enter(Button(message.value as u8)),
81 => Transport::Escape(Button(message.value as u8)),
82 => Transport::Yes(Button(message.value as u8)),
83 => Transport::No(Button(message.value as u8)),
90 => Transport::Menu(Button(message.value as u8)),
91 => Transport::ItemMenu(Button(message.value as u8)),
92 => Transport::Save(Button(message.value as u8)),
93 => Transport::SaveNew(Button(message.value as u8)),
_ => Transport::Unknown,
}
}
}
#[derive(Debug)]
pub struct Button(pub u8);
#[derive(Debug)]
pub struct Hold(pub bool);
#[derive(Debug)]
pub struct Jog(pub i64);
bitflags! {
pub struct MessageBoxFlags: isize {
const ABORTRETRYIGNORE = 0x0000_0002;
const CANCELTRYCONTINUE = 0x0000_0006;
const HELP = 0x0000_4000;
const OK = 0x0000_0000;
const OKCANCEL = 0x0000_0001;
const RETRYCANCEL = 0x0000_0005;
const YESNO = 0x0000_0004;
const YESNOCANCEL = 0x0000_0003;
const ICONEXCLAMATION = 0x0000_0030;
const ICONWARNING = 0x0000_0030;
const ICONINFORMATION = 0x0000_0040;
const ICONASTERISK = 0x0000_0040;
const ICONQUESTION = 0x0000_0020;
const ICONSTOP = 0x0000_0010;
const ICONERROR = 0x0000_0010;
const ICONHAND = 0x0000_0010;
const DEFBUTTON1 = 0x0000_0000;
const DEFBUTTON2 = 0x0000_0100;
const DEFBUTTON3 = 0x0000_0200;
const DEFBUTTON4 = 0x0000_0300;
const APPLMODAL = 0x0000_0000;
const SYSTEMMODAL = 0x0000_1000;
const TASKMODAL = 0x0000_2000;
const DEFAULT_DESKTOP_ONLY = 0x0002_0000;
const RIGHT = 0x0008_0000;
const RTLREADING = 0x0010_0000;
const SETFOREGROUND = 0x0001_0000;
const TOPMOST = 0x0004_0000;
const SERVICE_NOTIFICATION = 0x0020_0000;
}
}
impl AsRawPtr for MessageBoxFlags {
fn as_raw_ptr(&self) -> intptr_t {
self.bits()
}
}
#[derive(Debug)]
pub enum MessageBoxResult {
Ok,
Cancel,
Abort,
Retry,
Ignore,
Yes,
No,
TryAgain,
Continue,
Unknown,
}
impl FromRawPtr for MessageBoxResult {
fn from_raw_ptr(value: intptr_t) -> Self {
match value {
1 => MessageBoxResult::Ok,
2 => MessageBoxResult::Cancel,
3 => MessageBoxResult::Abort,
4 => MessageBoxResult::Retry,
5 => MessageBoxResult::Ignore,
6 => MessageBoxResult::Yes,
7 => MessageBoxResult::No,
10 => MessageBoxResult::TryAgain,
11 => MessageBoxResult::Continue,
_ => MessageBoxResult::Unknown,
}
}
}
#[no_mangle]
unsafe extern "C" fn fplog(message: *const c_char) {
debug!("{}", CStr::from_ptr(message).to_string_lossy());
}
#[no_mangle]
unsafe extern "C" fn free_rbox_raw(raw_ptr: *mut c_void) {
let _ = Box::from_raw(raw_ptr);
}
#[no_mangle]
unsafe extern "C" fn free_rstring(raw_str: *mut c_char) {
let _ = CString::from_raw(raw_str);
}