diff --git a/examples/simple.rs b/examples/simple.rs index da04277..f82cc53 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -18,7 +18,7 @@ use fpsdk::plugin::message; use fpsdk::plugin::{self, Info, InfoBuilder, Plugin, StateReader, StateWriter}; use fpsdk::voice::{self, ReceiveVoiceHandler, SendVoiceHandler, Voice}; use fpsdk::{ - create_plugin, AsRawPtr, MessageBoxFlags, MidiMessage, Note, Notes, NotesFlags, + create_plugin, AsRawPtr, FromRawPtr, MessageBoxFlags, MidiMessage, Note, Notes, NotesFlags, ProcessParamFlags, TimeFormat, ValuePtr, }; @@ -150,6 +150,7 @@ impl Plugin for Simple { // https://forum.image-line.com/viewtopic.php?f=100&t=199371 // https://forum.image-line.com/viewtopic.php?f=100&t=199258 .with_out_voices(1) + .loop_out() .midi_out() .build() } @@ -184,7 +185,7 @@ impl Plugin for Simple { }) .unwrap_or_else(|e| error!("error reading value from state {}", e)); } - + fn on_message(&mut self, message: host::Message) -> Box { self.host.on_message( self.tag, @@ -199,17 +200,24 @@ impl Plugin for Simple { if enabled { self.show_annoying_message(); // self.host.on_message(self.tag, message::ActivateMidi); + self.host.loop_out( + self.tag, + ValuePtr::from_raw_ptr(format!("{:?}", message).as_raw_ptr()), + ); } - self.host - .on_parameter(self.tag, 0, ValuePtr::new(0.123456789_f32.as_raw_ptr())); + self.host.on_parameter( + self.tag, + 0, + ValuePtr::from_raw_ptr(0.123456789_f32.as_raw_ptr()), + ); } // self.host.midi_out(self.tag, MidiMessage { - // status: 0x90, - // data1: 60, - // data2: 100, - // port: 2, + // status: 0x90, + // data1: 60, + // data2: 100, + // port: 2, // }); Box::new(0) @@ -238,7 +246,7 @@ impl Plugin for Simple { // looks like doesn't work fn loop_in(&mut self, message: ValuePtr) { - trace!("{:?} loop_in", message); + trace!("{} loop_in", message.get::()); } fn process_param( diff --git a/install.win.bat b/install.win.bat index 6452f40..5dfce3a 100644 --- a/install.win.bat +++ b/install.win.bat @@ -1,5 +1,5 @@ :: Usage: -:: install.win.bat name destination_name [plugins_dir] +:: install.win.bat name destination_name type_of_plugin [plugins_dir] @echo off @@ -7,21 +7,37 @@ set name=%1 set dest_name=%2 set plugins_dir="C:\Program Files\Image-Line\FL Studio 20\Plugins\Fruity" set args_count=0 -set flag=false +set incorrect_args_count=false +set type_flag=false for %%x in (%*) do Set /A args_count+=1 -if %args_count% lss 2 set flag=true -if %args_count% gtr 3 set flag=true -if "%flag%"=="true" ( - echo "Usage: install.win.bat name destination_name [plugins_dir]" +if %args_count% lss 3 set incorrect_args_count=true +if %args_count% gtr 4 set incorrect_args_count=true +if "%incorrect_args_count%"=="true" ( + echo "Usage: install.win.bat name destination_name type_of_plugin [plugins_dir]" exit /B 87 ) -if %args_count%==3 ( - set plugins_dir=%3 +if "%3"=="-e" ( + set type=Effects + set type_flag=true +) + +if "%3"=="-g" ( + set type=Generators + set type_flag=true +) + +if "%type_flag%"=="false" ( + echo "please type '-e' or '-g'" + exit /B 87 +) + +if %args_count%==4 ( + set plugins_dir=%4 ) -rd /s /q %plugins_dir%\Effects\%dest_name% -md %plugins_dir%\Effects\%dest_name% -move target\release\examples\%name%.dll %plugins_dir%\Effects\%dest_name%\%dest_name%_x64.dll +rd /s /q %plugins_dir%\%type%\%dest_name% +md %plugins_dir%\%type%\%dest_name% +move target\release\examples\%name%.dll %plugins_dir%\%type%\%dest_name%\%dest_name%_x64.dll diff --git a/src/cxx/wrapper.cpp b/src/cxx/wrapper.cpp index 39c51a6..492b1a7 100644 --- a/src/cxx/wrapper.cpp +++ b/src/cxx/wrapper.cpp @@ -281,6 +281,14 @@ void host_midi_out_del(void *host, TPluginTag tag, unsigned char status, ((TFruityPlugHost *)host)->MIDIOut_Delayed(tag, (intptr_t)msg); } +void host_loop_out(void *host, TPluginTag tag, intptr_t msg) { + ((TFruityPlugHost *)host)->PlugMsg_Delayed(tag, msg); +} + +void host_loop_kill(void *host, TPluginTag tag, intptr_t msg) { + ((TFruityPlugHost *)host)->PlugMsg_Kill(tag, msg); +} + void host_release_voice(void *host, intptr_t tag) { ((TFruityPlugHost *)host)->Voice_Release(tag); } diff --git a/src/cxx/wrapper.h b/src/cxx/wrapper.h index f03e525..d8b8f3a 100644 --- a/src/cxx/wrapper.h +++ b/src/cxx/wrapper.h @@ -125,6 +125,8 @@ extern "C" void host_midi_out(void *host, TPluginTag tag, unsigned char status, unsigned char port); extern "C" void host_midi_out_del(void *host, TPluginTag tag, unsigned char status, unsigned char data1, unsigned char data2, unsigned char port); +extern "C" void host_loop_out(void *host, TPluginTag tag, intptr_t msg); +extern "C" void host_loop_kill(void *host, TPluginTag tag, intptr_t msg); extern "C" void host_release_voice(void *host, intptr_t tag); extern "C" void host_kill_voice(void *host, intptr_t tag); diff --git a/src/host.rs b/src/host.rs index 6523141..ef97e89 100644 --- a/src/host.rs +++ b/src/host.rs @@ -150,6 +150,25 @@ impl Host { }; } + /// **MAY NOT WORK** + /// + /// Ask for a message to be dispatched to itself when the current mixing tick will be played + /// (to synchronize stuff) + /// + /// (see [`Plugin::loop_in`](../plugin/trait.Plugin.html#method.loop_in)). + /// + /// The message is guaranteed to be dispatched, however it could be sent immediately if it + /// couldn't be buffered (it's only buffered when playing). + pub fn loop_out(&mut self, tag: plugin::Tag, message: ValuePtr) { + unsafe { host_loop_out(*self.host_ptr.get_mut(), tag.0, message.0) }; + } + + /// Remove the buffered message scheduled by + /// [`Host::loop_out`](struct.Host.html#method.loop_out), so that it will never be dispatched. + pub fn loop_kill(&mut self, tag: plugin::Tag, message: ValuePtr) { + unsafe { host_loop_kill(*self.host_ptr.get_mut(), tag.0, message.0) }; + } + /// Get [`Voicer`](struct.Voicer.html) pub fn voice_handler(&self) -> Arc> { Arc::clone(&self.voicer) @@ -180,6 +199,8 @@ extern "C" { data2: c_uchar, port: c_uchar, ); + fn host_loop_out(host: *mut c_void, tag: intptr_t, message: intptr_t); + fn host_loop_kill(host: *mut c_void, tag: intptr_t, message: intptr_t); } /// Use this to manually release, kill and notify voices about events. diff --git a/src/lib.rs b/src/lib.rs index 6f8d51b..ce235b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,24 +186,6 @@ extern "C" { fn alloc_real_cstr(raw_str: *mut c_char) -> *mut c_char; } -/// Raw pointer to value. -#[derive(Debug)] -pub struct ValuePtr(intptr_t); - -impl ValuePtr { - /// Constructor. - pub fn new(ptr: intptr_t) -> Self { - Self(ptr) - } - - /// Get value. - /// - /// See [`FromRawPtr`](trait.FromRawPtr.html) for implemented types. - pub fn get(&self) -> T { - T::from_raw_ptr(self.0) - } -} - /// For types, which can be represented as `intptr_t`. pub trait AsRawPtr { /// Conversion method. @@ -316,6 +298,25 @@ impl FromRawPtr for bool { } } +/// Raw pointer to value. +#[derive(Debug)] +pub struct ValuePtr(intptr_t); + +impl ValuePtr { + /// Get value. + /// + /// See [`FromRawPtr`](trait.FromRawPtr.html) for implemented types. + pub fn get(&self) -> T { + T::from_raw_ptr(self.0) + } +} + +impl FromRawPtr for ValuePtr { + fn from_raw_ptr(value: intptr_t) -> Self { + ValuePtr(value) + } +} + bitflags! { /// Parameter flags. pub struct ParameterFlags: isize { diff --git a/src/plugin.rs b/src/plugin.rs index 1105f77..5b4c9f4 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -141,7 +141,7 @@ pub trait Plugin: std::fmt::Debug + RefUnwindSafe + Send + Sync + 'static { /// Can be called from GUI or mixer threads. fn midi_in(&mut self, _message: MidiMessage) {} /// **MAY NOT WORK** - /// + /// /// This gets called with a new buffered message to the plugin itself. fn loop_in(&mut self, _message: ValuePtr) {} } @@ -646,7 +646,6 @@ pub unsafe extern "C" fn plugin_load_state(adapter: *mut PluginAdapter, stream: (*adapter).0.load_state(StateReader(stream)); } - /// [`Plugin::loop_in`](Plugin.html#method.loop_in) FFI. /// /// It supposed to be used internally. Don't use it.