Skip to main content

nice_plug_core/context/
gui.rs

1//! A context passed to a plugin's editor.
2
3use std::sync::Arc;
4
5use crate::{
6    params::{Param, internals::ParamPtr},
7    plugin::{Plugin, PluginState},
8};
9
10use super::PluginApi;
11
12/// Callbacks the plugin can make when the user interacts with its GUI such as updating parameter
13/// values. This is passed to the plugin during [`Editor::spawn()`][crate::editor::Editor::spawn()].
14/// All of these functions assume they're being called from the main GUI thread.
15#[derive(Clone)]
16pub struct GuiContext {
17    inner: Arc<dyn GuiContextInner>,
18}
19
20impl GuiContext {
21    pub fn new(inner: Arc<dyn GuiContextInner>) -> Self {
22        Self { inner }
23    }
24
25    /// Get the current plugin API. This may be useful to display in the plugin's GUI as part of an
26    /// about screen.
27    pub fn plugin_api(&self) -> PluginApi {
28        self.inner.plugin_api()
29    }
30
31    pub fn param_setter<'a>(&'a self) -> ParamSetter<'a> {
32        ParamSetter {
33            raw_context: &*self.inner,
34        }
35    }
36
37    /// Inform the host a parameter will be automated. Create a [`ParamSetter`] and use
38    /// [`ParamSetter::begin_set_parameter()`] instead for a safe, user friendly API.
39    ///
40    /// # Safety
41    ///
42    /// The implementing function still needs to check if `param` actually exists. This function is
43    /// mostly marked as unsafe for API reasons.
44    pub unsafe fn raw_begin_set_parameter(&self, param: ParamPtr) {
45        unsafe {
46            self.inner.raw_begin_set_parameter(param);
47        }
48    }
49
50    /// Inform the host a parameter is being automated with an already normalized value. Create a
51    /// [`ParamSetter`] and use [`ParamSetter::set_parameter()`] instead for a safe, user friendly
52    /// API.
53    ///
54    /// # Safety
55    ///
56    /// The implementing function still needs to check if `param` actually exists. This function is
57    /// mostly marked as unsafe for API reasons.
58    pub unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32) {
59        unsafe {
60            self.inner.raw_set_parameter_normalized(param, normalized);
61        }
62    }
63
64    /// Inform the host a parameter has been automated. Create a [`ParamSetter`] and use
65    /// [`ParamSetter::end_set_parameter()`] instead for a safe, user friendly API.
66    ///
67    /// # Safety
68    ///
69    /// The implementing function still needs to check if `param` actually exists. This function is
70    /// mostly marked as unsafe for API reasons.
71    pub unsafe fn raw_end_set_parameter(&self, param: ParamPtr) {
72        unsafe {
73            self.inner.raw_end_set_parameter(param);
74        }
75    }
76
77    /// Serialize the plugin's current state to a serde-serializable object. Useful for implementing
78    /// preset handling within a plugin's GUI.
79    pub fn get_state(&self) -> PluginState {
80        self.inner.get_state()
81    }
82
83    /// Restore the state from a previously serialized state object. This will block the GUI thread
84    /// until the state has been restored and a parameter value rescan has been requested from the
85    /// host. If the plugin is currently processing audio, then the parameter values will be
86    /// restored at the end of the current processing cycle.
87    pub fn set_state(&self, state: PluginState) {
88        self.inner.set_state(state);
89    }
90
91    /// Request the plugin to be restarted.
92    pub fn request_restart(&self) {
93        self.inner.request_restart();
94    }
95}
96
97/// Callbacks the plugin can make when the user interacts with its GUI such as updating parameter
98/// values. This is passed to the plugin during [`Editor::spawn()`][crate::editor::Editor::spawn()].
99/// All of these functions assume they're being called from the main GUI thread.
100//
101// # Safety
102//
103// The implementing wrapper can assume that everything is being called from the main thread. Since
104// nice-plug doesn't own the GUI event loop, this invariant cannot be part of the interface.
105pub trait GuiContextInner: Send + Sync + 'static {
106    /// Get the current plugin API. This may be useful to display in the plugin's GUI as part of an
107    /// about screen.
108    fn plugin_api(&self) -> PluginApi;
109
110    /// Inform the host a parameter will be automated. Create a [`ParamSetter`] and use
111    /// [`ParamSetter::begin_set_parameter()`] instead for a safe, user friendly API.
112    ///
113    /// # Safety
114    ///
115    /// The implementing function still needs to check if `param` actually exists. This function is
116    /// mostly marked as unsafe for API reasons.
117    unsafe fn raw_begin_set_parameter(&self, param: ParamPtr);
118
119    /// Inform the host a parameter is being automated with an already normalized value. Create a
120    /// [`ParamSetter`] and use [`ParamSetter::set_parameter()`] instead for a safe, user friendly
121    /// API.
122    ///
123    /// # Safety
124    ///
125    /// The implementing function still needs to check if `param` actually exists. This function is
126    /// mostly marked as unsafe for API reasons.
127    unsafe fn raw_set_parameter_normalized(&self, param: ParamPtr, normalized: f32);
128
129    /// Inform the host a parameter has been automated. Create a [`ParamSetter`] and use
130    /// [`ParamSetter::end_set_parameter()`] instead for a safe, user friendly API.
131    ///
132    /// # Safety
133    ///
134    /// The implementing function still needs to check if `param` actually exists. This function is
135    /// mostly marked as unsafe for API reasons.
136    unsafe fn raw_end_set_parameter(&self, param: ParamPtr);
137
138    /// Serialize the plugin's current state to a serde-serializable object. Useful for implementing
139    /// preset handling within a plugin's GUI.
140    fn get_state(&self) -> PluginState;
141
142    /// Restore the state from a previously serialized state object. This will block the GUI thread
143    /// until the state has been restored and a parameter value rescan has been requested from the
144    /// host. If the plugin is currently processing audio, then the parameter values will be
145    /// restored at the end of the current processing cycle.
146    fn set_state(&self, state: PluginState);
147
148    /// Request the plugin to be restarted.
149    fn request_restart(&self);
150}
151
152/// An way to run background tasks from the plugin's GUI, equivalent to the
153/// [`ProcessContext::execute_background()`][crate::context::process::ProcessContext::execute_background()]
154/// and [`ProcessContext::execute_gui()`][crate::context::process::ProcessContext::execute_gui()]
155/// functions. This is passed directly to [`Plugin::editor()`] so the plugin can move it into its
156/// editor and use it later.
157///
158/// # Note
159///
160/// This is only intended to be used from the GUI. Use the methods on
161/// [`ActivateContext`][crate::context::activate::ActivateContext] and
162/// [`ProcessContext`][crate::context::process::ProcessContext] to run tasks during the `activate()`
163/// and `process()` functions.
164//
165// NOTE: This is separate from `GuiContext` because adding a type parameter there would clutter up a
166//       lot of structs, and may even be incompatible with the way certain GUI libraries work.
167pub struct AsyncExecutor<P: Plugin> {
168    pub(crate) execute_background: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
169    pub(crate) execute_gui: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
170}
171
172impl<P: Plugin> AsyncExecutor<P> {
173    pub fn new(
174        execute_background: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
175        execute_gui: Arc<dyn Fn(P::BackgroundTask) + Send + Sync>,
176    ) -> Self {
177        Self {
178            execute_background,
179            execute_gui,
180        }
181    }
182}
183
184// Can't derive this since Rust then requires `P` to also be `Clone`able
185impl<P: Plugin> Clone for AsyncExecutor<P> {
186    fn clone(&self) -> Self {
187        Self {
188            execute_background: self.execute_background.clone(),
189            execute_gui: self.execute_gui.clone(),
190        }
191    }
192}
193
194/// A convenience helper for setting parameter values. Any changes made here will be broadcasted to
195/// the host and reflected in the plugin's [`Params`][crate::params::Params] object. These
196/// functions should only be called from the main thread.
197pub struct ParamSetter<'a> {
198    pub raw_context: &'a dyn GuiContextInner,
199}
200
201impl<P: Plugin> AsyncExecutor<P> {
202    /// Execute a task on a background thread using `[Plugin::task_executor]`. This allows you to
203    /// defer expensive tasks for later without blocking either the process function or the GUI
204    /// thread. As long as creating the `task` is realtime-safe, this operation is too.
205    ///
206    /// # Note
207    ///
208    /// Scheduling the same task multiple times will cause those duplicate tasks to pile up. Try to
209    /// either prevent this from happening, or check whether the task still needs to be completed in
210    /// your task executor.
211    pub fn execute_background(&self, task: P::BackgroundTask) {
212        (self.execute_background)(task);
213    }
214
215    /// Execute a task on a background thread using `[Plugin::task_executor]`.
216    ///
217    /// # Note
218    ///
219    /// Scheduling the same task multiple times will cause those duplicate tasks to pile up. Try to
220    /// either prevent this from happening, or check whether the task still needs to be completed in
221    /// your task executor.
222    pub fn execute_gui(&self, task: P::BackgroundTask) {
223        (self.execute_gui)(task);
224    }
225}
226
227impl<'a> ParamSetter<'a> {
228    pub fn new(context: &'a dyn GuiContextInner) -> Self {
229        Self {
230            raw_context: context,
231        }
232    }
233
234    /// Inform the host that you will start automating a parameter. This needs to be called before
235    /// calling [`set_parameter()`][Self::set_parameter()] for the specified parameter.
236    pub fn begin_set_parameter<P: Param>(&self, param: &P) {
237        unsafe { self.raw_context.raw_begin_set_parameter(param.as_ptr()) };
238    }
239
240    /// Set a parameter to the specified parameter value. You will need to call
241    /// [`begin_set_parameter()`][Self::begin_set_parameter()] before and
242    /// [`end_set_parameter()`][Self::end_set_parameter()] after calling this so the host can
243    /// properly record automation for the parameter. This can be called multiple times in a row
244    /// before calling [`end_set_parameter()`][Self::end_set_parameter()], for instance when moving
245    /// a slider around.
246    ///
247    /// This function assumes you're already calling this from a GUI thread. Calling any of these
248    /// functions from any other thread may result in unexpected behavior.
249    pub fn set_parameter<P: Param>(&self, param: &P, value: P::Plain) {
250        let ptr = param.as_ptr();
251        let normalized = param.preview_normalized(value);
252        unsafe {
253            self.raw_context
254                .raw_set_parameter_normalized(ptr, normalized)
255        };
256    }
257
258    /// Set a parameter to an already normalized value. Works exactly the same as
259    /// [`set_parameter()`][Self::set_parameter()] and needs to follow the same rules, but this may
260    /// be useful when implementing a GUI.
261    ///
262    /// This does not perform any snapping. Consider converting the normalized value to a plain
263    /// value and setting that with [`set_parameter()`][Self::set_parameter()] instead so the
264    /// normalized value known to the host matches `param.normalized_value()`.
265    pub fn set_parameter_normalized<P: Param>(&self, param: &P, normalized: f32) {
266        let ptr = param.as_ptr();
267        unsafe {
268            self.raw_context
269                .raw_set_parameter_normalized(ptr, normalized)
270        };
271    }
272
273    /// Inform the host that you are done automating a parameter. This needs to be called after one
274    /// or more [`set_parameter()`][Self::set_parameter()] calls for a parameter so the host knows
275    /// the automation gesture has finished.
276    pub fn end_set_parameter<P: Param>(&self, param: &P) {
277        unsafe { self.raw_context.raw_end_set_parameter(param.as_ptr()) };
278    }
279}