#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::raw;
use derive_more::Display;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr::null_mut;
use vst::api::{AEffect, HostCallbackProc};
use vst::plugin::HostCallback;
type GetFunc = unsafe extern "C" fn(name: *const c_char) -> *mut c_void;
type Register = unsafe extern "C" fn(
name: *const c_char,
infostruct: *mut c_void,
) -> c_int;
pub(crate) type GetSwellFunc =
unsafe extern "C" fn(name: *const c_char) -> *mut c_void;
/// This represents the context which is needed to access REAPER functions from
/// plug-ins.
///
/// Once obtained, it is supposed to be passed to [`Reaper::load()`].
///
/// [`Reaper::load()`]: struct.Reaper.html#method.load
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PluginContext {
type_specific: TypeSpecificPluginContext,
h_instance: raw::HINSTANCE,
get_swell_func_ptr: Option<GetSwellFunc>,
main_thread_id: std::thread::ThreadId,
}
// The raw pointers contained in the plug-in context don't do harm when sent to
// another thread.
unsafe impl Send for PluginContext {}
/// Additional stuff available in the plug-in context specific to a certain
/// plug-in type.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum TypeSpecificPluginContext {
/// This is an extension plug-in.
Extension(ExtensionPluginContext),
/// This is a VST plug-in.
Vst(VstPluginContext),
}
/// Additional data available in the context of extension plug-ins.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct ExtensionPluginContext {
caller_version: c_int,
hwnd_main: raw::HWND,
register: Register,
get_func: GetFunc,
}
/// Additional data available in the context of VST plug-ins.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct VstPluginContext {
host_callback: HostCallbackProc,
}
impl PluginContext {
/// Creates a plug-in context from an extension entry point plug-in info.
///
/// It requires a module handle and the pointer to a
/// [`reaper_plugin_info_t`] struct. REAPER provides both when calling
/// the `ReaperPluginEntry` function (the main entry point for
/// any extension plug-in).
///
/// It's recommended to use the `reaper_extension_plugin` macro in the
/// [reaper-macros](https://crates.io/crates/reaper-macros) crate instead of calling
/// this function directly.
///
/// # Errors
///
/// Returns an error if the given plug-in info is not suitable for loading
/// REAPER functions.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid pointer.
///
/// [`reaper_plugin_info_t`]: raw/struct.reaper_plugin_info_t.html
pub unsafe fn from_extension_plugin(
h_instance: raw::HINSTANCE,
rec: raw::reaper_plugin_info_t,
static_context: StaticExtensionPluginContext,
) -> Result<PluginContext, ContextFromExtensionPluginError> {
use ContextFromExtensionPluginError::*;
if rec.caller_version != raw::REAPER_PLUGIN_VERSION as c_int {
return Err(CallerVersionIncompatible);
}
let get_func = rec.GetFunc.ok_or(FunctionProviderNotAvailable)?;
let register = rec.Register.expect(
"plug-in info doesn't container Register function pointer",
);
Ok(PluginContext {
type_specific: TypeSpecificPluginContext::Extension(
ExtensionPluginContext {
caller_version: rec.caller_version,
hwnd_main: rec.hwnd_main,
register,
get_func,
},
),
h_instance,
get_swell_func_ptr: static_context.get_swell_func,
main_thread_id: std::thread::current().id(),
})
}
/// Creates a plug-in context from a VST host callback.
///
/// It requires the host callback which [vst-rs](https://crates.io/crates/vst) passes to the
/// plugin's [`new()`] function.
///
/// # Errors
///
/// Returns an error if the given host callback is not suitable for loading
/// REAPER functions.
///
/// [`new()`]: https://docs.rs/vst/0.2.0/vst/plugin/trait.Plugin.html#method.new
pub fn from_vst_plugin(
host: &HostCallback,
static_context: StaticVstPluginContext,
) -> Result<PluginContext, ContextFromVstPluginError> {
use ContextFromVstPluginError::*;
let host_callback =
host.raw_callback().ok_or(HostCallbackNotAvailable)?;
Ok(PluginContext {
type_specific: TypeSpecificPluginContext::Vst(VstPluginContext {
host_callback,
}),
h_instance: static_context.h_instance,
get_swell_func_ptr: static_context.get_swell_func,
main_thread_id: std::thread::current().id(),
})
}
/// Returns a generic API function by its name.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid pointer.
#[allow(overflowing_literals)]
pub unsafe fn GetFunc(&self, name: *const c_char) -> *mut c_void {
use TypeSpecificPluginContext::*;
match &self.type_specific {
Extension(context) => (context.get_func)(name),
Vst(context) => {
// Invoke host callback
(context.host_callback)(
null_mut(),
0xdead_beef,
0xdead_f00d,
0,
name as *mut c_void,
0.0,
) as *mut c_void
}
}
}
/// On Windows, this returns the `HINSTANCE` passed to `DllMain` (VST
/// plug-ins) or `ReaperPluginEntry` (extension plug-ins).
///
/// The returned `HINSTANCE` represents the handle of the module (DLL)
/// containing the plug-in.
///
/// On Linux, this returns `null`.
pub fn h_instance(&self) -> raw::HINSTANCE {
self.h_instance
}
/// On Linux, this returns a pointer to a function for getting a generic
/// SWELL API function by its name.
///
/// On Windows, this returns `None`.
pub fn swell_function_provider(&self) -> Option<GetSwellFunc> {
self.get_swell_func_ptr
}
/// Returns the type-specific plug-in context.
pub fn type_specific(&self) -> &TypeSpecificPluginContext {
&self.type_specific
}
/// Returns whether we are currently in the main thread.
pub fn is_in_main_thread(&self) -> bool {
std::thread::current().id() == self.main_thread_id
}
}
impl ExtensionPluginContext {
/// Returns the caller version from `reaper_plugin_info_t`.
pub fn caller_version(&self) -> c_int {
self.caller_version
}
/// Returns the main window from `reaper_plugin_info_t`.
pub fn hwnd_main(&self) -> raw::HWND {
self.hwnd_main
}
/// This is the same like [`Reaper::plugin_register()`].
///
/// # Safety
///
/// REAPER can crash if you pass an invalid pointer.
///
/// [`Reaper::plugin_register()`]: struct.Reaper.html#method.plugin_register
pub unsafe fn Register(
&self,
name: *const ::std::os::raw::c_char,
infostruct: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int {
(self.register)(name, infostruct)
}
}
impl VstPluginContext {
/// Creates a VST plug-in context based on the given host callback.
pub fn new(host_callback: HostCallbackProc) -> Self {
Self { host_callback }
}
/// Generic host callback function for communicating with REAPER from the
/// VST plug-in.
///
/// This is just a pass-through to the VST host callback.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid pointer.
pub unsafe fn host_callback(
self,
effect: *mut AEffect,
opcode: i32,
index: i32,
value: isize,
ptr: *mut c_void,
opt: f32,
) -> isize {
(self.host_callback)(effect, opcode, index, value, ptr, opt)
}
}
/// An error which can occur when attempting to create a REAPER plug-in context
/// from an extension plug-in.
#[derive(Clone, Eq, PartialEq, Debug, Display)]
pub enum ContextFromExtensionPluginError {
/// `caller_version` doesn't match `REAPER_PLUGIN_VERSION`.
#[display(fmt = "caller version incompatible")]
CallerVersionIncompatible,
/// `GetFunc` pointer is not set.
#[display(fmt = "function provider not available")]
FunctionProviderNotAvailable,
}
impl std::error::Error for ContextFromExtensionPluginError {}
/// An error which can occur when attempting to create a REAPER plug-in context
/// from a VST plug-in.
#[derive(Clone, Eq, PartialEq, Debug, Display)]
pub enum ContextFromVstPluginError {
#[display(fmt = "host callback not available")]
HostCallbackNotAvailable,
}
impl std::error::Error for ContextFromVstPluginError {}
/// Contains those parts of the REAPER VST plug-in context which must be
/// obtained in static scope.
///
/// An instance of this struct is returned by the function
/// [`static_vst_plugin_context()`] which is generated by the
/// [`reaper_vst_plugin`] macro.
///
/// [`reaper_vst_plugin`]: macro.reaper_vst_plugin.html
/// [`static_vst_plugin_context()`]: fn.static_vst_plugin_context.html
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct StaticVstPluginContext {
/// `HINSTANCE` representing the handle of the module (DLL) containing the
/// plug-in.
///
/// Windows only.
pub h_instance: raw::HINSTANCE,
/// Function which returns a SWELL function by its name.
///
/// Linux only.
pub get_swell_func: Option<GetSwellFunc>,
}
impl Default for StaticVstPluginContext {
fn default() -> Self {
StaticVstPluginContext {
h_instance: null_mut(),
get_swell_func: None,
}
}
}
/// Contains those parts of the REAPER extension plug-in context which must be
/// obtained from static variables.
///
/// This just contains the SWELL function provider at the moment.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub struct StaticExtensionPluginContext {
/// Function which returns SWELL function by its name.
///
/// Linux/macOS only.
pub get_swell_func: Option<GetSwellFunc>,
}