1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
//! A context passed to a plugin's editor.
use std::sync::Arc;
use super::PluginApi;
use crate::prelude::{Param, ParamPtr, Plugin, PluginState};
/// Callbacks the plugin can make when the user interacts with its GUI such as updating parameter
/// values. This is passed to the plugin during [`Editor::spawn()`][crate::prelude::Editor::spawn()]. All of
/// these functions assume they're being called from the main GUI thread.
//
// # Safety
//
// The implementing wrapper can assume that everything is being called from the main thread. Since
// NIH-plug doesn't own the GUI event loop, this invariant cannot be part of the interface.
pub trait GuiContext: Send + Sync + 'static {
/// Get the current plugin API. This may be useful to display in the plugin's GUI as part of an
/// about screen.
fn plugin_api(&self) -> PluginApi;
/// Ask the host to resize the editor window to the size specified by
/// [`Editor::size()`][crate::prelude::Editor::size()]. This will return false if the host
/// somehow didn't like this and rejected the resize, in which case the window should revert to
/// its old size. You should only actually resize your embedded window once this returns `true`.
///
/// TODO: Host->Plugin resizing has not been implemented yet
fn request_resize(&self) -> bool;
/// Inform the host a parameter will be automated. Create a [`ParamSetter`] and use
/// [`ParamSetter::begin_set_parameter()`] instead for a safe, user friendly API.
///
/// # Safety
///
/// The implementing function still needs to check if `param` actually exists. This function is
/// mostly marked as unsafe for API reasons.
unsafe fn raw_begin_set_parameter(&self, param: ParamPtr);
/// Inform the host a parameter is being automated with an already normalized value. Create a
/// [`ParamSetter`] and use [`ParamSetter::set_parameter()`] instead for a safe, user friendly
/// API.
///
/// # Safety
///
/// The implementing function still needs to check if `param` actually exists. This function is
/// mostly marked as unsafe for API reasons.
unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32);
/// Inform the host a parameter has been automated. Create a [`ParamSetter`] and use
/// [`ParamSetter::end_set_parameter()`] instead for a safe, user friendly API.
///
/// # Safety
///
/// The implementing function still needs to check if `param` actually exists. This function is
/// mostly marked as unsafe for API reasons.
unsafe fn raw_end_set_parameter(&self, param: ParamPtr);
/// Serialize the plugin's current state to a serde-serializable object. Useful for implementing
/// preset handling within a plugin's GUI.
fn get_state(&self) -> PluginState;
/// Restore the state from a previously serialized state object. This will block the GUI thread
/// until the state has been restored and a parameter value rescan has been requested from the
/// host. If the plugin is currently processing audio, then the parameter values will be
/// restored at the end of the current processing cycle.
fn set_state(&self, state: PluginState);
}
/// An way to run background tasks from the plugin's GUI, equivalent to the
/// [`ProcessContext::execute_background()`][crate::prelude::ProcessContext::execute_background()]
/// and [`ProcessContext::execute_gui()`][crate::prelude::ProcessContext::execute_gui()] functions.
/// This is passed directly to [`Plugin::editor()`] so the plugin can move it into its editor and
/// use it later.
///
/// # Note
///
/// This is only intended to be used from the GUI. Use the methods on
/// [`InitContext`][crate::prelude::InitContext] and
/// [`ProcessContext`][crate::prelude::ProcessContext] to run tasks during the `initialize()` and
/// `process()` functions.
//
// NOTE: This is separate from `GuiContext` because adding a type parameter there would clutter up a
// lot of structs, and may even be incompatible with the way certain GUI libraries work.
pub struct AsyncExecutor<P: Plugin> {
pub(crate) execute_background: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
pub(crate) execute_gui: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
}
// Can't derive this since Rust then requires `P` to also be `Clone`able
impl<P: Plugin> Clone for AsyncExecutor<P> {
fn clone(&self) -> Self {
Self {
execute_background: self.execute_background.clone(),
execute_gui: self.execute_gui.clone(),
}
}
}
/// A convenience helper for setting parameter values. Any changes made here will be broadcasted to
/// the host and reflected in the plugin's [`Params`][crate::params::Params] object. These
/// functions should only be called from the main thread.
pub struct ParamSetter<'a> {
pub raw_context: &'a dyn GuiContext,
}
impl<P: Plugin> AsyncExecutor<P> {
/// Execute a task on a background thread using `[Plugin::task_executor]`. This allows you to
/// defer expensive tasks for later without blocking either the process function or the GUI
/// thread. As long as creating the `task` is realtime-safe, this operation is too.
///
/// # Note
///
/// Scheduling the same task multiple times will cause those duplicate tasks to pile up. Try to
/// either prevent this from happening, or check whether the task still needs to be completed in
/// your task executor.
pub fn execute_background(&self, task: P::BackgroundTask) {
(self.execute_background)(task);
}
/// Execute a task on a background thread using `[Plugin::task_executor]`.
///
/// # Note
///
/// Scheduling the same task multiple times will cause those duplicate tasks to pile up. Try to
/// either prevent this from happening, or check whether the task still needs to be completed in
/// your task executor.
pub fn execute_gui(&self, task: P::BackgroundTask) {
(self.execute_gui)(task);
}
}
impl<'a> ParamSetter<'a> {
pub fn new(context: &'a dyn GuiContext) -> Self {
Self {
raw_context: context,
}
}
/// Inform the host that you will start automating a parameter. This needs to be called before
/// calling [`set_parameter()`][Self::set_parameter()] for the specified parameter.
pub fn begin_set_parameter<P: Param>(&self, param: &P) {
unsafe { self.raw_context.raw_begin_set_parameter(param.as_ptr()) };
}
/// Set a parameter to the specified parameter value. You will need to call
/// [`begin_set_parameter()`][Self::begin_set_parameter()] before and
/// [`end_set_parameter()`][Self::end_set_parameter()] after calling this so the host can
/// properly record automation for the parameter. This can be called multiple times in a row
/// before calling [`end_set_parameter()`][Self::end_set_parameter()], for instance when moving
/// a slider around.
///
/// This function assumes you're already calling this from a GUI thread. Calling any of these
/// functions from any other thread may result in unexpected behavior.
pub fn set_parameter<P: Param>(&self, param: &P, value: P::Plain) {
let ptr = param.as_ptr();
let normalized = param.preview_normalized(value);
unsafe {
self.raw_context
.raw_set_parameter_normalized(ptr, normalized)
};
}
/// Set a parameter to an already normalized value. Works exactly the same as
/// [`set_parameter()`][Self::set_parameter()] and needs to follow the same rules, but this may
/// be useful when implementing a GUI.
///
/// This does not perform any snapping. Consider converting the normalized value to a plain
/// value and setting that with [`set_parameter()`][Self::set_parameter()] instead so the
/// normalized value known to the host matches `param.normalized_value()`.
pub fn set_parameter_normalized<P: Param>(&self, param: &P, normalized: f32) {
let ptr = param.as_ptr();
unsafe {
self.raw_context
.raw_set_parameter_normalized(ptr, normalized)
};
}
/// Inform the host that you are done automating a parameter. This needs to be called after one
/// or more [`set_parameter()`][Self::set_parameter()] calls for a parameter so the host knows
/// the automation gesture has finished.
pub fn end_set_parameter<P: Param>(&self, param: &P) {
unsafe { self.raw_context.raw_end_set_parameter(param.as_ptr()) };
}
}