Skip to content

Commit

Permalink
Port TFruityPlug->MIDIIn (#73)
Browse files Browse the repository at this point in the history
- use raw pointer to host everywhere
- add generator case for install script on macOS
  • Loading branch information
ales-tsurko authored Apr 27, 2020
1 parent 86a4e4b commit 469e7f7
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 118 deletions.
6 changes: 5 additions & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use simplelog::{ConfigBuilder, WriteLogger};

use fpsdk::host::{Event, GetName, Host, HostMessage};
use fpsdk::plugin::{Plugin, PluginTag};
use fpsdk::{create_plugin, AsRawPtr, Info, InfoBuilder, ProcessParamFlags, ValuePtr};
use fpsdk::{create_plugin, AsRawPtr, Info, InfoBuilder, MidiMessage, ProcessParamFlags, ValuePtr};

static ONCE: Once = Once::new();
const LOG_PATH: &str = "simple.log";
Expand Down Expand Up @@ -94,6 +94,10 @@ impl Plugin for Test {
Box::new(0)
}

fn midi_in(&mut self, message: MidiMessage) {
trace!("receive MIDI message {:?}", message);
}

fn render(&mut self, input: &[[f32; 2]], output: &mut [[f32; 2]]) {
input.iter().zip(output).for_each(|(inp, outp)| {
outp[0] = inp[0] * 0.25;
Expand Down
18 changes: 14 additions & 4 deletions install.mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

# installs release build into FL effects folder
# Usage:
# install.mac.sh name destination_name
# install.mac.sh name destination_name [gen]
# if [gen] is specified, the plugin will be installed in as a generator.

set -e

name=$1
dest_name=$2
PLUG_PATH="/Applications/FL Studio 20.app/Contents/Resources/FL/Plugins/Fruity"
MIDDLE_DIR="Effects"

if [ $# -eq 3 ]; then
MIDDLE_DIR="Generators"
fi

INSTALL_DIR="${PLUG_PATH}/${MIDDLE_DIR}/${dest_name}"

rm -rf "${PLUG_PATH}/Effects/${dest_name}"
mkdir "${PLUG_PATH}/Effects/${dest_name}"
mv "target/release/examples/lib${name}.dylib" "${PLUG_PATH}/Effects/${dest_name}/${dest_name}_x64.dylib"
rm -rf "${INSTALL_DIR}"
mkdir "${INSTALL_DIR}"
mv "target/release/examples/lib${name}.dylib" "${INSTALL_DIR}/${dest_name}_x64.dylib"
30 changes: 20 additions & 10 deletions src/cxx/wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ char *init_str_from_rust(rust::String &value) {
return res;
}

TFruityPlug &create_plug_instance_c(TFruityPlugHost &Host, int Tag,
rust::Box<PluginAdapter> adapter) {
Info info = plugin_info(*adapter);
void *create_plug_instance_c(void *Host, int Tag, void *adapter) {
Info info = plugin_info(*(PluginAdapter *)adapter);

char *lname = init_str_from_rust(info.long_name);
char *sname = init_str_from_rust(info.short_name);
Expand All @@ -25,11 +24,15 @@ TFruityPlug &create_plug_instance_c(TFruityPlugHost &Host, int Tag,
(int)info.def_poly,
(int)info.num_out_ctrls,
(int)info.num_out_voices};
int ver = ((TFruityPlugHost *)Host)->HostVersion;
std::string sver = std::to_string(ver);
fplog(rust::Str(sver.c_str()));
fplog(rust::Str("host version above"));

PluginWrapper *wrapper =
new PluginWrapper(&Host, Tag, adapter.into_raw(), c_info);
PluginWrapper *wrapper = new PluginWrapper(
(TFruityPlugHost *)Host, Tag, (PluginAdapter *)adapter, c_info);

return *((TFruityPlug *)wrapper);
return wrapper;
}

PluginWrapper::PluginWrapper(TFruityPlugHost *Host, int Tag,
Expand All @@ -44,6 +47,7 @@ PluginWrapper::PluginWrapper(TFruityPlugHost *Host, int Tag,
// parameter initialze
_gain = 0.25;
_params[0] = (1 << 16);
// _host->Dispatcher(HostTag, FHD_WantMIDIInput, 0, 1);
}

PluginWrapper::~PluginWrapper() {
Expand Down Expand Up @@ -120,9 +124,7 @@ int _stdcall PluginWrapper::ProcessParam(int Index, int Value, int RECFlags) {
return plugin_process_param(adapter, message);
}

void _stdcall PluginWrapper::Idle_Public() {
plugin_idle(adapter);
}
void _stdcall PluginWrapper::Idle_Public() { plugin_idle(adapter); }

void _stdcall PluginWrapper::Eff_Render(PWAV32FS SourceBuffer,
PWAV32FS DestBuffer, int Length) {
Expand Down Expand Up @@ -156,7 +158,15 @@ void _stdcall PluginWrapper::NewTick() { plugin_tick(adapter); }

void _stdcall PluginWrapper::MIDITick() { plugin_midi_tick(adapter); }

void _stdcall PluginWrapper::MIDIIn(int &Msg) {}
void _stdcall PluginWrapper::MIDIIn(int &Msg) {
MidiMessage message = {
(uint8_t)(Msg & 0xff),
(uint8_t)((Msg >> 8) & 0xff),
(uint8_t)((Msg >> 16) & 0xff),
(int)((Msg >> 24) & 0xff),
};
plugin_midi_in(adapter, message);
}

void _stdcall PluginWrapper::MsgIn(intptr_t Msg) {}

Expand Down
9 changes: 5 additions & 4 deletions src/cxx/wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
#include "fp_plugclass.h"
#include "rust/cxx.h"

struct PluginAdapter;
struct Message;
struct MidiMessage;
struct PluginAdapter;
struct TimeSignature;

class sample_editor {};
Expand Down Expand Up @@ -60,11 +61,10 @@ class PluginWrapper : public TFruityPlug {
float _gain;
};

TFruityPlug &create_plug_instance_c(TFruityPlugHost &Host, int Tag,
rust::Box<PluginAdapter> adapter);
TimeSignature time_sig_from_raw(intptr_t raw_time_sig);

// Unsafe Rust functions
// Unsafe Rust FFI
extern "C" void *create_plug_instance_c(void *Host, int Tag, void *adapter);
extern "C" intptr_t plugin_dispatcher(PluginAdapter *adapter, Message message);
extern "C" intptr_t plugin_process_event(PluginAdapter *adapter, Message event);
extern "C" intptr_t plugin_process_param(PluginAdapter *adapter, Message event);
Expand All @@ -76,3 +76,4 @@ extern "C" void plugin_eff_render(PluginAdapter *adapter,
int len);
extern "C" void plugin_gen_render(PluginAdapter *adapter, float dest[1][2],
int len);
extern "C" void plugin_midi_in(PluginAdapter *adapter, MidiMessage message);
180 changes: 98 additions & 82 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,13 @@ pub mod ffi {
extern "C" {
include!("wrapper.h");

pub type TFruityPlug;
pub type TFruityPlugHost;

pub fn create_plug_instance_c(
host: &'static mut TFruityPlugHost,
tag: i32,
adapter: Box<PluginAdapter>,
) -> &'static mut TFruityPlug;

pub fn time_sig_from_raw(raw_time_sig: isize) -> TimeSignature;
}

extern "Rust" {
type PluginAdapter;

fn fplog(message: &str);
fn plugin_info(adapter: &PluginAdapter) -> Info;
// Used for debugging
fn print_adapter(adapter: &PluginAdapter);
Expand Down Expand Up @@ -148,6 +140,36 @@ pub const WAVETABLE_SIZE: usize = 16384;
#[allow(non_camel_case_types)]
type intptr_t = isize;

fn fplog(message: &str) {
debug!("{}", message);
}

/// Exposes your plugin from DLL. Accepts type name as input. The type should implement
/// [`Plugin`](plugin/trait.Plugin.html) trait.
#[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: i32,
adapter: *mut c_void,
) -> *mut c_void;
}

#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn CreatePlugInstance(host: *mut c_void, tag: i32) -> *mut c_void {
let ho = $crate::host::Host { version: 0 };
let plugin = <$pl as $crate::plugin::Plugin>::new(ho, tag);
let adapter = $crate::PluginAdapter(Box::new(plugin));
create_plug_instance_c(host, tag, Box::into_raw(Box::new(adapter)) as *mut c_void)
}
};
}

/// As far as we can't use trait objects to share them with C++, we need a concrete type. This type
/// wraps user's plugin as a delegate and calls its methods.
///
Expand Down Expand Up @@ -301,6 +323,19 @@ pub unsafe extern "C" fn plugin_gen_render(
(*adapter).0.render(&[[0.0, 0.0]], &mut output);
}

/// [`Plugin::midi_in`](plugin/trait.Plugin.html#tymethod.render) FFI.
///
/// It supposed to be used internally. Don't use it.
///
/// # Safety
///
/// Unsafe
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn plugin_midi_in(adapter: *mut PluginAdapter, message: MidiMessage) {
(*adapter).0.midi_in(message);
}

/// Raw pointer to value.
#[derive(Debug)]
pub struct ValuePtr(intptr_t);
Expand Down Expand Up @@ -411,63 +446,6 @@ impl FromRawPtr for bool {
}
}

impl From<u64> for MidiMessage {
fn from(value: u64) -> Self {
MidiMessage {
status: (value & 0xff) as u8,
data1: ((value >> 8) & 0xff) as u8,
data2: ((value >> 16) & 0xff) as u8,
port: -1,
}
}
}

impl fmt::Debug for MidiMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MidiMessage")
.field("status", &self.status)
.field("data1", &self.data1)
.field("data2", &self.data2)
.field("port", &self.port)
.finish()
}
}

impl fmt::Debug for Info {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Info")
.field("sdk_version", &self.sdk_version)
.field("long_name", &self.long_name)
.field("short_name", &self.short_name)
.field("flags", &self.flags)
.field("num_params", &self.num_params)
.field("def_poly", &self.def_poly)
.field("num_out_ctrls", &self.num_out_ctrls)
.field("num_out_voices", &self.num_out_voices)
.finish()
}
}

impl fmt::Debug for TimeSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TimeSignature")
.field("steps_per_bar", &self.steps_per_bar)
.field("steps_per_beat", &self.steps_per_beat)
.field("ppq", &self.ppq)
.finish()
}
}

impl fmt::Debug for ffi::Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Message")
.field("id", &self.id)
.field("index", &self.index)
.field("value", &self.value)
.finish()
}
}

bitflags! {
/// Parameter flags.
pub struct ParameterFlags: isize {
Expand Down Expand Up @@ -964,23 +942,61 @@ impl InfoBuilder {
}
}

/// Exposes your plugin from DLL. Accepts type name as input. The type should implement
/// [`Plugin`](plugin/trait.Plugin.html) trait.
#[macro_export]
macro_rules! create_plugin {
($pl:ty) => {
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn CreatePlugInstance(
host: *mut $crate::ffi::TFruityPlugHost,
tag: i32,
) -> *mut $crate::ffi::TFruityPlug {
let ho = $crate::host::Host { version: 0 };
let plugin = <$pl as $crate::plugin::Plugin>::new(ho, tag);
let adapter = $crate::PluginAdapter(Box::new(plugin));
$crate::ffi::create_plug_instance_c(&mut *host, tag, Box::new(adapter))
impl From<u64> for MidiMessage {
fn from(value: u64) -> Self {
MidiMessage {
status: (value & 0xff) as u8,
data1: ((value >> 8) & 0xff) as u8,
data2: ((value >> 16) & 0xff) as u8,
port: -1,
}
};
}
}

impl fmt::Debug for MidiMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MidiMessage")
.field("status", &self.status)
.field("data1", &self.data1)
.field("data2", &self.data2)
.field("port", &self.port)
.finish()
}
}

impl fmt::Debug for Info {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Info")
.field("sdk_version", &self.sdk_version)
.field("long_name", &self.long_name)
.field("short_name", &self.short_name)
.field("flags", &self.flags)
.field("num_params", &self.num_params)
.field("def_poly", &self.def_poly)
.field("num_out_ctrls", &self.num_out_ctrls)
.field("num_out_voices", &self.num_out_voices)
.finish()
}
}

impl fmt::Debug for TimeSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TimeSignature")
.field("steps_per_bar", &self.steps_per_bar)
.field("steps_per_beat", &self.steps_per_beat)
.field("ppq", &self.ppq)
.finish()
}
}

impl fmt::Debug for ffi::Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Message")
.field("id", &self.id)
.field("index", &self.index)
.field("value", &self.value)
.finish()
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 469e7f7

Please sign in to comment.