use std::mem;
use std::os::raw::{c_int, c_void};
use crate::host::{GetName, Host};
use crate::plugin;
use crate::{
intptr_t, AsRawPtr, FlMessage, MessageBoxFlags, MessageBoxResult, NameColor, Note, Notes,
ParamMenuEntry, SongTime, TNameColor, TParamMenuEntry, Tag, Time, TimeFormat, ValuePtr,
};
pub trait Message {
type Return;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return;
}
macro_rules! impl_message {
($message: ident) => {
impl Message for $message {
type Return = ();
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
unsafe {
host_on_message(*host.host_ptr.get_mut(), tag.0, self.into());
}
}
}
};
}
macro_rules! impl_message_ty {
($message: ident, $res: ty) => {
impl Message for $message {
type Return = $res;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
ValuePtr(unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, self.into()) })
.get::<$res>()
}
}
};
}
extern "C" {
fn host_on_message(host: *mut c_void, plugin_tag: Tag, message: FlMessage) -> intptr_t;
}
#[derive(Debug)]
pub struct ParamMenu(pub usize, pub usize);
impl_message!(ParamMenu);
impl From<ParamMenu> for FlMessage {
fn from(message: ParamMenu) -> Self {
FlMessage {
id: 0,
index: message.0.as_raw_ptr(),
value: message.1.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct EditorResized;
impl_message!(EditorResized);
impl From<EditorResized> for FlMessage {
fn from(_message: EditorResized) -> Self {
FlMessage {
id: 2,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct NamesChanged(pub GetName);
impl_message!(NamesChanged);
impl From<NamesChanged> for FlMessage {
fn from(message: NamesChanged) -> Self {
FlMessage {
id: 3,
index: 0,
value: Option::<FlMessage>::from(message.0)
.map(|msg| msg.id)
.unwrap_or_default(),
}
}
}
#[derive(Debug)]
pub struct ActivateMidi;
impl_message!(ActivateMidi);
impl From<ActivateMidi> for FlMessage {
fn from(_: ActivateMidi) -> Self {
FlMessage {
id: 4,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct WantMidiInput(pub bool);
impl_message!(WantMidiInput);
impl From<WantMidiInput> for FlMessage {
fn from(message: WantMidiInput) -> Self {
FlMessage {
id: 5,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct KillAutomation(pub usize, pub usize);
impl_message!(KillAutomation);
impl From<KillAutomation> for FlMessage {
fn from(message: KillAutomation) -> Self {
FlMessage {
id: 8,
index: message.0.as_raw_ptr(),
value: message.1.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetNumPresets(pub usize);
impl_message!(SetNumPresets);
impl From<SetNumPresets> for FlMessage {
fn from(message: SetNumPresets) -> Self {
FlMessage {
id: 9,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetNewName(pub String);
impl_message!(SetNewName);
impl From<SetNewName> for FlMessage {
fn from(message: SetNewName) -> Self {
FlMessage {
id: 10,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct VstiIdle;
impl_message!(VstiIdle);
impl From<VstiIdle> for FlMessage {
fn from(_message: VstiIdle) -> Self {
FlMessage {
id: 11,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub enum WantIdle {
Disabled,
EnabledVisible,
EnabledAlways,
}
impl_message!(WantIdle);
impl From<WantIdle> for FlMessage {
fn from(message: WantIdle) -> Self {
FlMessage {
id: 13,
index: 0,
value: message.into(),
}
}
}
impl From<WantIdle> for intptr_t {
fn from(message: WantIdle) -> Self {
match message {
WantIdle::Disabled => 0,
WantIdle::EnabledVisible => 1,
WantIdle::EnabledAlways => 2,
}
}
}
#[derive(Debug)]
pub struct LocateDataFile(pub String);
impl_message_ty!(LocateDataFile, String);
impl From<LocateDataFile> for FlMessage {
fn from(message: LocateDataFile) -> Self {
FlMessage {
id: 14,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct TicksToTime(pub u32);
impl Message for TicksToTime {
type Return = SongTime;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
let message = FlMessage::from(self);
let time_ptr = message.index;
unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
ValuePtr(time_ptr).get::<Self::Return>()
}
}
impl From<TicksToTime> for FlMessage {
fn from(message: TicksToTime) -> Self {
let time = SongTime::default();
FlMessage {
id: 16,
index: (Box::into_raw(Box::new(time)) as *mut c_void).as_raw_ptr(),
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct AddToPianoRoll(pub Notes);
impl_message!(AddToPianoRoll);
impl From<AddToPianoRoll> for FlMessage {
fn from(mut message: AddToPianoRoll) -> Self {
message.0.notes.shrink_to_fit();
let notes_ptr = message.0.notes.as_mut_ptr();
let len = message.0.notes.len();
let p_notes_params = unsafe {
init_p_notes_params(
1,
message.0.flags.bits() as c_int,
message.0.channel.map(|v| v as c_int).unwrap_or(-1),
message.0.pattern.map(|v| v as c_int).unwrap_or(-1),
notes_ptr,
len as c_int,
)
};
FlMessage {
id: 17,
index: 0,
value: p_notes_params,
}
}
}
extern "C" {
fn init_p_notes_params(
target: c_int,
flags: c_int,
ch_num: c_int,
pat_num: c_int,
notes: *mut Note,
count: c_int,
) -> intptr_t;
}
#[derive(Debug)]
pub struct GetParamMenuEntry(pub usize, pub usize);
impl Message for GetParamMenuEntry {
type Return = Option<ParamMenuEntry>;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
let message = FlMessage::from(self);
let result = unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
if (result as *mut c_void).is_null() {
return None;
}
Some(ParamMenuEntry::from_ffi(
result as *mut c_void as *mut TParamMenuEntry,
))
}
}
impl From<GetParamMenuEntry> for FlMessage {
fn from(message: GetParamMenuEntry) -> Self {
FlMessage {
id: 18,
index: message.0.as_raw_ptr(),
value: message.1.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct MessageBox(pub String, pub String, pub MessageBoxFlags);
impl_message_ty!(MessageBox, MessageBoxResult);
impl From<MessageBox> for FlMessage {
fn from(message: MessageBox) -> Self {
FlMessage {
id: 19,
index: format!("{}|{}", message.0, message.1).as_raw_ptr(),
value: message.2.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct NoteOn(pub u8, pub u8, pub u8);
impl_message!(NoteOn);
impl From<NoteOn> for FlMessage {
fn from(message: NoteOn) -> Self {
FlMessage {
id: 20,
index: dword_from_note_and_ch(message.0, message.1).as_raw_ptr(),
value: message.2.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct NoteOff(pub u8);
impl_message!(NoteOff);
impl From<NoteOff> for FlMessage {
fn from(message: NoteOff) -> Self {
FlMessage {
id: 21,
index: message.0.as_raw_ptr(),
value: 0,
}
}
}
#[derive(Debug)]
pub struct OnHintDirect(pub String);
impl_message!(OnHintDirect);
impl From<OnHintDirect> for FlMessage {
fn from(message: OnHintDirect) -> Self {
FlMessage {
id: 22,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetNewColor(pub u8);
impl_message!(SetNewColor);
impl From<SetNewColor> for FlMessage {
fn from(message: SetNewColor) -> Self {
FlMessage {
id: 23,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct KillIntCtrl(pub usize, pub usize);
impl_message!(KillIntCtrl);
impl From<KillIntCtrl> for FlMessage {
fn from(message: KillIntCtrl) -> Self {
FlMessage {
id: 25,
index: message.0.as_raw_ptr(),
value: message.1.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetNumParams(pub usize);
impl_message!(SetNumParams);
impl From<SetNumParams> for FlMessage {
fn from(message: SetNumParams) -> Self {
FlMessage {
id: 27,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct PackDataFile(pub String);
impl_message_ty!(PackDataFile, String);
impl From<PackDataFile> for FlMessage {
fn from(message: PackDataFile) -> Self {
FlMessage {
id: 28,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct GetProgPath;
impl_message_ty!(GetProgPath, String);
impl From<GetProgPath> for FlMessage {
fn from(_: GetProgPath) -> Self {
FlMessage {
id: 29,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct SetLatency(pub u32);
impl_message!(SetLatency);
impl From<SetLatency> for FlMessage {
fn from(message: SetLatency) -> Self {
FlMessage {
id: 30,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct CallDownloader;
impl_message!(CallDownloader);
impl From<CallDownloader> for FlMessage {
fn from(_: CallDownloader) -> Self {
FlMessage {
id: 31,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct EditSample(pub String, pub bool);
impl_message!(EditSample);
impl From<EditSample> for FlMessage {
fn from(message: EditSample) -> Self {
FlMessage {
id: 32,
index: message.1.as_raw_ptr(),
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetThreadSafe(pub bool);
impl_message!(SetThreadSafe);
impl From<SetThreadSafe> for FlMessage {
fn from(message: SetThreadSafe) -> Self {
FlMessage {
id: 33,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SmartDisable(pub bool);
impl_message!(SmartDisable);
impl From<SmartDisable> for FlMessage {
fn from(message: SmartDisable) -> Self {
FlMessage {
id: 34,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SetUid(pub String);
impl_message!(SetUid);
impl From<SetUid> for FlMessage {
fn from(message: SetUid) -> Self {
FlMessage {
id: 35,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct GetMixingTime(pub TimeFormat, pub u64);
impl Message for GetMixingTime {
type Return = Time;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
get_time_send(self, tag, host)
}
}
fn get_time_send<T: Into<FlMessage>>(msg: T, tag: plugin::Tag, host: &mut Host) -> Time {
let message: FlMessage = msg.into();
let time_ptr = message.value;
unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
ValuePtr(time_ptr).get::<Time>()
}
impl From<GetMixingTime> for FlMessage {
fn from(message: GetMixingTime) -> Self {
get_time_ffi(36, message.0, message.1)
}
}
fn get_time_ffi(id: intptr_t, format: TimeFormat, offset: u64) -> FlMessage {
let time = Time(offset as f64, offset as f64);
FlMessage {
id,
index: u8::from(format).as_raw_ptr(),
value: (Box::into_raw(Box::new(time)) as *mut c_void).as_raw_ptr(),
}
}
#[derive(Debug)]
pub struct GetPlaybackTime(pub TimeFormat, pub u64);
impl Message for GetPlaybackTime {
type Return = Time;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
get_time_send(self, tag, host)
}
}
impl From<GetPlaybackTime> for FlMessage {
fn from(message: GetPlaybackTime) -> Self {
get_time_ffi(37, message.0, message.1)
}
}
#[derive(Debug)]
pub struct GetSelTime(pub TimeFormat);
impl Message for GetSelTime {
type Return = Time;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
get_time_send(self, tag, host)
}
}
impl From<GetSelTime> for FlMessage {
fn from(message: GetSelTime) -> Self {
get_time_ffi(38, message.0, 0)
}
}
#[derive(Debug)]
pub struct GetTimeMul;
impl_message_ty!(GetTimeMul, f32);
impl From<GetTimeMul> for FlMessage {
fn from(_: GetTimeMul) -> Self {
FlMessage {
id: 39,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct Captionize(pub bool);
impl_message!(Captionize);
impl From<Captionize> for FlMessage {
fn from(message: Captionize) -> Self {
FlMessage {
id: 40,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct SendSysEx<'a>(pub usize, pub &'a [u8]);
impl Message for SendSysEx<'_> {
type Return = ();
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
unsafe {
host_on_message(*host.host_ptr.get_mut(), tag.0, self.into());
}
}
}
impl From<SendSysEx<'_>> for FlMessage {
fn from(message: SendSysEx<'_>) -> Self {
let len = message.1.len() as i32;
let len_bytes: [u8; mem::size_of::<i32>()] = unsafe { mem::transmute(len) };
let mut final_data = [&len_bytes, message.1].concat();
let data_ptr = final_data.as_mut_ptr();
mem::forget(final_data);
FlMessage {
id: 41,
index: message.0.as_raw_ptr(),
value: (data_ptr as *mut c_void).as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct LoadAudioClip(pub String);
impl_message!(LoadAudioClip);
impl From<LoadAudioClip> for FlMessage {
fn from(message: LoadAudioClip) -> Self {
FlMessage {
id: 42,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct LoadInChannel(pub String);
impl_message!(LoadInChannel);
impl From<LoadInChannel> for FlMessage {
fn from(message: LoadInChannel) -> Self {
FlMessage {
id: 43,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct ShowInBrowser(pub String);
impl_message!(ShowInBrowser);
impl From<ShowInBrowser> for FlMessage {
fn from(message: ShowInBrowser) -> Self {
FlMessage {
id: 44,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct DebugLogMsg(pub String);
impl_message!(DebugLogMsg);
impl From<DebugLogMsg> for FlMessage {
fn from(message: DebugLogMsg) -> Self {
FlMessage {
id: 45,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct GetMainFormHandle;
impl Message for GetMainFormHandle {
type Return = Option<*mut c_void>;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
let message = FlMessage::from(self);
let result_ptr = message.value;
unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
if result_ptr == 0 {
None
} else {
Some(result_ptr as *mut c_void)
}
}
}
impl From<GetMainFormHandle> for FlMessage {
fn from(_: GetMainFormHandle) -> Self {
FlMessage {
id: 46,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct GetProjDataPath;
impl_message_ty!(GetProjDataPath, String);
impl From<GetProjDataPath> for FlMessage {
fn from(_message: GetProjDataPath) -> Self {
FlMessage {
id: 47,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct SetDirty;
impl_message!(SetDirty);
impl From<SetDirty> for FlMessage {
fn from(_message: SetDirty) -> Self {
FlMessage {
id: 48,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct AddToRecent(pub String);
impl_message!(AddToRecent);
impl From<AddToRecent> for FlMessage {
fn from(message: AddToRecent) -> Self {
FlMessage {
id: 49,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub enum GetNumInOut {
Inputs,
Outputs,
}
impl_message_ty!(GetNumInOut, usize);
impl From<GetNumInOut> for FlMessage {
fn from(message: GetNumInOut) -> Self {
FlMessage {
id: 50,
index: message.into(),
value: 0,
}
}
}
impl From<GetNumInOut> for intptr_t {
fn from(message: GetNumInOut) -> Self {
match message {
GetNumInOut::Inputs => 0,
GetNumInOut::Outputs => 1,
}
}
}
#[derive(Debug)]
pub struct GetInName(pub usize);
impl Message for GetInName {
type Return = Option<NameColor>;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
get_name_dispatcher(self, tag, host)
}
}
fn get_name_dispatcher<T: Into<FlMessage>>(
msg: T,
tag: plugin::Tag,
host: &mut Host,
) -> Option<NameColor> {
let message: FlMessage = msg.into();
let result_ptr = message.value;
let result = unsafe { host_on_message(*host.host_ptr.get_mut(), tag.0, message) };
if result == 0 || (result_ptr as *mut c_void).is_null() {
return None;
}
Some(ValuePtr(result_ptr).get::<TNameColor>().into())
}
impl From<GetInName> for FlMessage {
fn from(message: GetInName) -> Self {
get_name_ffi(51, message.0)
}
}
fn get_name_ffi(id: intptr_t, index: usize) -> FlMessage {
let name_color = TNameColor {
name: [0; 256],
vis_name: [0; 256],
color: 0,
index: index as c_int,
};
FlMessage {
id,
index: index.as_raw_ptr(),
value: (Box::into_raw(Box::new(name_color)) as *mut c_void).as_raw_ptr(),
}
}
#[derive(Debug)]
pub struct GetOutName(pub usize);
impl Message for GetOutName {
type Return = Option<NameColor>;
fn send(self, tag: plugin::Tag, host: &mut Host) -> Self::Return {
get_name_dispatcher(self, tag, host)
}
}
impl From<GetOutName> for FlMessage {
fn from(message: GetOutName) -> Self {
get_name_ffi(52, message.0)
}
}
#[derive(Debug)]
pub enum ShowEditor {
Show,
Hide,
Toggle,
}
impl_message!(ShowEditor);
impl From<ShowEditor> for FlMessage {
fn from(message: ShowEditor) -> Self {
FlMessage {
id: 53,
index: 0,
value: message.into(),
}
}
}
impl From<ShowEditor> for intptr_t {
fn from(message: ShowEditor) -> Self {
match message {
ShowEditor::Show => 1,
ShowEditor::Hide => 0,
ShowEditor::Toggle => -1,
}
}
}
#[derive(Debug)]
pub struct FloatAutomation(pub usize, pub usize);
impl_message!(FloatAutomation);
impl From<FloatAutomation> for FlMessage {
fn from(message: FloatAutomation) -> Self {
FlMessage {
id: 54,
index: message.0.as_raw_ptr(),
value: message.1.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct ShowSettings(pub bool);
impl_message!(ShowSettings);
impl From<ShowSettings> for FlMessage {
fn from(message: ShowSettings) -> Self {
FlMessage {
id: 55,
index: 0,
value: message.0.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct NoteOnOff(pub u8, pub u8, pub u8);
impl_message!(NoteOnOff);
impl From<NoteOnOff> for FlMessage {
fn from(message: NoteOnOff) -> Self {
FlMessage {
id: 56,
index: dword_from_note_and_ch(message.0, message.1).as_raw_ptr(),
value: message.2.as_raw_ptr(),
}
}
}
#[derive(Debug)]
pub enum ShowPicker {
Plugins(PickerFilter),
Project(PickerFilter),
}
impl_message!(ShowPicker);
#[derive(Debug)]
pub enum PickerFilter {
Generators,
Effects,
GeneratorsEffects,
Patcher,
}
impl From<ShowPicker> for FlMessage {
fn from(message: ShowPicker) -> Self {
let (index, value): (intptr_t, intptr_t) = message.into();
FlMessage {
id: 57,
index,
value,
}
}
}
impl From<ShowPicker> for (intptr_t, intptr_t) {
fn from(message: ShowPicker) -> Self {
match message {
ShowPicker::Plugins(filter) => (0, filter.into()),
ShowPicker::Project(filter) => (1, filter.into()),
}
}
}
impl From<PickerFilter> for intptr_t {
fn from(filter: PickerFilter) -> Self {
match filter {
PickerFilter::Generators => 0,
PickerFilter::Effects => 1,
PickerFilter::GeneratorsEffects => -1,
PickerFilter::Patcher => -2,
}
}
}
#[derive(Debug)]
pub struct GetIdleOverflow;
impl_message!(GetIdleOverflow);
impl From<GetIdleOverflow> for FlMessage {
fn from(_: GetIdleOverflow) -> Self {
FlMessage {
id: 58,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct ModalIdle;
impl_message!(ModalIdle);
impl From<ModalIdle> for FlMessage {
fn from(_: ModalIdle) -> Self {
FlMessage {
id: 59,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub struct RenderProject;
impl_message!(RenderProject);
impl From<RenderProject> for FlMessage {
fn from(_: RenderProject) -> Self {
FlMessage {
id: 60,
index: 0,
value: 0,
}
}
}
#[derive(Debug)]
pub enum GetProjectInfo {
Title,
Author,
Comments,
Url,
}
impl_message_ty!(GetProjectInfo, String);
impl From<GetProjectInfo> for FlMessage {
fn from(message: GetProjectInfo) -> Self {
FlMessage {
id: 61,
index: message.into(),
value: 0,
}
}
}
impl From<GetProjectInfo> for intptr_t {
fn from(value: GetProjectInfo) -> Self {
match value {
GetProjectInfo::Title => 0,
GetProjectInfo::Author => 1,
GetProjectInfo::Comments => 2,
GetProjectInfo::Url => 3,
}
}
}
fn dword_from_note_and_ch(note: u8, channel: u8) -> u32 {
(note as u32) | ((channel as u32) << 16)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dword() {
let value = dword_from_note_and_ch(60, 15);
assert_eq!(60, value & 0xff);
assert_eq!(15, (value >> 16) & 0xff);
}
}