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}