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