Skip to main content

nice_plug_core/
params.rs

1//! nice-plug can handle floating point, integer, boolean, and enum parameters. Parameters are
2//! managed by creating a struct deriving the [`Params`] trait containing fields
3//! for those parameter types, and then returning a reference to that object from your
4//! [`Plugin::params()`][crate::plugin::Plugin::params()] method. See the `Params` trait for more
5//! information.
6
7use std::collections::BTreeMap;
8use std::fmt::{Debug, Display};
9use std::sync::Arc;
10
11use self::internals::ParamPtr;
12
13// The proc-macro for deriving `Params`
14pub use nice_plug_derive::Params;
15
16// Parameter types
17mod boolean;
18pub mod enums;
19mod float;
20mod integer;
21
22pub mod internals;
23pub mod persist;
24pub mod range;
25pub mod smoothing;
26
27pub use boolean::BoolParam;
28pub use enums::EnumParam;
29pub use float::FloatParam;
30pub use integer::IntParam;
31
32/// Parameter metadata and conversion callbacks that are not accessed by the DSP hot path.
33/// Storing these out of line reduces the cache footprint of parameter value reads during audio
34/// processing.
35struct ParamInfo<T> {
36    /// Flags controlling the parameter's behavior.
37    flags: ParamFlags,
38    /// The parameter's human-readable display name.
39    name: String,
40    /// The parameter value's unit. This is appended after `value_to_string`, when set, without
41    /// automatically inserting a space.
42    unit: &'static str,
43    /// An optional custom conversion function from a plain parameter value to a string.
44    value_to_string: Option<Arc<dyn Fn(T) -> String + Send + Sync>>,
45    /// An optional custom conversion function from a string to a plain parameter value. The input
46    /// may include the unit. Returning `None` cancels the parameter update.
47    string_to_value: Option<Arc<dyn Fn(&str) -> Option<T> + Send + Sync>>,
48}
49
50impl<T> ParamInfo<T> {
51    fn new(name: impl Into<String>) -> Self {
52        Self {
53            flags: ParamFlags::default(),
54            name: name.into(),
55            unit: "",
56            value_to_string: None,
57            string_to_value: None,
58        }
59    }
60}
61
62bitflags::bitflags! {
63    /// Flags for controlling a parameter's behavior.
64    #[repr(transparent)]
65    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
66    pub struct ParamFlags: u32 {
67        /// When applied to a [`BoolParam`], this will cause the parameter to be linked to the
68        /// host's bypass control. Only a single parameter can be marked as a bypass parameter. If
69        /// you don't have a bypass parameter, then nice-plug will add one for you. You will need to
70        /// implement this yourself if your plugin introduces latency.
71        const BYPASS = 1 << 0;
72        /// The parameter cannot be changed from an automation lane. The parameter can however still
73        /// be manually changed by the user from either the plugin's own GUI or from the host's
74        /// generic UI.
75        const NON_AUTOMATABLE = 1 << 1;
76        /// Hides the parameter in the host's generic UI for this plugin. This also implies
77        /// `NON_AUTOMATABLE`. Setting this does not prevent you from changing the parameter in the
78        /// plugin's editor GUI.
79        const HIDDEN = 1 << 2;
80        /// Don't show this parameter when generating a generic UI for the plugin using one of
81        /// nice-plug's generic UI widgets.
82        const HIDE_IN_GENERIC_UI = 1 << 3;
83    }
84}
85
86// See https://rust-lang.github.io/api-guidelines/future-proofing.html for more information
87mod sealed {
88    /// Dummy trait to prevent [`Param`] from being implemented outside of nice-plug. This is not
89    /// possible because of the way `ParamPtr` works, so it's best to just make it flat out impossible.
90    pub trait Sealed {}
91}
92pub(crate) use sealed::Sealed;
93
94/// Describes a single parameter of any type. Most parameter implementations also have a field
95/// called `value` that and a field called `smoothed`. The former stores the latest unsmoothed
96/// value, and the latter can be used to access the smoother. These two fields should be used in DSP
97/// code to either get the parameter's current (smoothed) value. In UI code the getters from this
98/// trait should be used instead.
99///
100/// # Sealed
101///
102/// This trait cannot be implemented outside of nice-plug itself. If you want to create new
103/// abstractions around parameters, consider wrapping them in a struct instead. Then use the
104/// `#[nested(id_prefix = "foo")]` syntax from the [`Params`] trait to reuse that wrapper in
105/// multiple places.
106pub trait Param: Display + Debug + sealed::Sealed {
107    /// The plain parameter type.
108    type Plain: PartialEq;
109
110    /// Get the human readable name for this parameter.
111    fn name(&self) -> &str;
112
113    /// Get the unit label for this parameter, if any.
114    fn unit(&self) -> &'static str;
115
116    /// Get this parameter's polyphonic modulation ID. If this is set for a parameter in a CLAP
117    /// plugin, then polyphonic modulation will be enabled for that parameter. Polyphonic modulation
118    /// is communicated to the plugin through
119    /// [`NoteEvent::PolyModulation`][crate::midi::NoteEvent::PolyModulation] and
120    /// [`NoteEvent::MonoAutomation`][crate::midi::NoteEvent::MonoAutomation] events. See the
121    /// documentation on those events for more information.
122    ///
123    /// # Important
124    ///
125    /// After enabling polyphonic modulation, the plugin **must** start sending
126    /// [`NoteEvent::VoiceTerminated`][crate::midi::NoteEvent::VoiceTerminated] events to the
127    /// host when a voice has fully ended. This allows the host to reuse its modulation resources.
128    fn poly_modulation_id(&self) -> Option<u32>;
129
130    /// Get the unnormalized value for this parameter.
131    fn modulated_plain_value(&self) -> Self::Plain;
132
133    /// Get the normalized `[0, 1]` value for this parameter.
134    fn modulated_normalized_value(&self) -> f32;
135
136    /// Get the unnormalized value for this parameter before any (monophonic) modulation coming from
137    /// the host has been applied. If the host is not currently modulating this parameter than this
138    /// will be the same as [`modulated_plain_value()`][Self::modulated_plain_value()]. This may be
139    /// useful for displaying modulation differently in plugin GUIs. Right now only CLAP plugins in
140    /// Bitwig Studio use modulation.
141    fn unmodulated_plain_value(&self) -> Self::Plain;
142
143    /// Get the normalized `[0, 1]` value for this parameter before any (monophonic) modulation
144    /// coming from the host has been applied. If the host is not currently modulating this
145    /// parameter than this will be the same as
146    /// [`modulated_normalized_value()`][Self::modulated_normalized_value()]. This may be useful for
147    /// displaying modulation differently in plugin GUIs. Right now only CLAP plugins in Bitwig
148    /// Studio use modulation.
149    fn unmodulated_normalized_value(&self) -> f32;
150
151    /// Get the unnormalized default value for this parameter.
152    fn default_plain_value(&self) -> Self::Plain;
153
154    /// Get the normalized `[0, 1]` default value for this parameter.
155    #[inline]
156    fn default_normalized_value(&self) -> f32 {
157        self.preview_normalized(self.default_plain_value())
158    }
159
160    /// Get the number of steps for this parameter, if it is discrete. Used for the host's generic
161    /// UI.
162    fn step_count(&self) -> Option<usize>;
163
164    /// Returns the previous step from a specific value for this parameter. This can be the same as
165    /// `from` if the value is at the start of its range. This is mainly used for scroll wheel
166    /// interaction in plugin GUIs. When the parameter is not discrete then a step should cover one
167    /// hundredth of the normalized range instead.
168    ///
169    /// If `finer` is true, then the step size should be decreased if the parameter is continuous.
170    fn previous_step(&self, from: Self::Plain, finer: bool) -> Self::Plain;
171
172    /// Returns the next step from a specific value for this parameter. This can be the same as
173    /// `from` if the value is at the end of its range. This is mainly used for scroll wheel
174    /// interaction in plugin GUIs. When the parameter is not discrete then a step should cover one
175    /// hundredth of the normalized range instead.
176    ///
177    /// If `finer` is true, then the step size should be decreased if the parameter is continuous.
178    fn next_step(&self, from: Self::Plain, finer: bool) -> Self::Plain;
179
180    /// The same as [`previous_step()`][Self::previous_step()], but for normalized values. This is
181    /// mostly useful for GUI widgets.
182    fn previous_normalized_step(&self, from: f32, finer: bool) -> f32 {
183        self.preview_normalized(self.previous_step(self.preview_plain(from), finer))
184    }
185
186    /// The same as [`next_step()`][Self::next_step()], but for normalized values. This is mostly
187    /// useful for GUI widgets.
188    fn next_normalized_step(&self, from: f32, finer: bool) -> f32 {
189        self.preview_normalized(self.next_step(self.preview_plain(from), finer))
190    }
191
192    /// Get the string representation for a normalized value. Used as part of the wrappers. Most
193    /// plugin formats already have support for units, in which case it shouldn't be part of this
194    /// string or some DAWs may show duplicate units.
195    fn normalized_value_to_string(&self, normalized: f32, include_unit: bool) -> String;
196
197    /// Get the string representation for a normalized value. Used as part of the wrappers.
198    fn string_to_normalized_value(&self, string: &str) -> Option<f32>;
199
200    /// Get the normalized value for a plain, unnormalized value, as a float. Used as part of the
201    /// wrappers.
202    fn preview_normalized(&self, plain: Self::Plain) -> f32;
203
204    /// Get the plain, unnormalized value for a normalized value, as a float. Used as part of the
205    /// wrappers. This **does** snap to step sizes for continuous parameters (i.e. [`FloatParam`]).
206    fn preview_plain(&self, normalized: f32) -> Self::Plain;
207
208    /// Get the plain, unnormalized value for this parameter after polyphonic modulation has been
209    /// applied. This is a convenience method for calling [`preview_plain()`][Self::preview_plain()]
210    /// with `unmodulated_normalized_value() + normalized_offset`.
211    #[inline]
212    fn preview_modulated(&self, normalized_offset: f32) -> Self::Plain {
213        self.preview_plain(self.unmodulated_normalized_value() + normalized_offset)
214    }
215
216    /// Flags to control the parameter's behavior. See [`ParamFlags`].
217    fn flags(&self) -> ParamFlags;
218
219    /// Internal implementation detail for implementing [`Params`]. This should
220    /// not be used directly.
221    fn as_ptr(&self) -> internals::ParamPtr;
222}
223
224/// Contains the setters for parameters. Only to be used by nice-plug's internal libraries.
225/// These are exposed as unsafe methods to avoid confusion.
226pub trait InternalParamMut: Param {
227    /// Set this parameter based on a plain, unnormalized value. This does not snap to step sizes
228    /// for continuous parameters (i.e. [`FloatParam`]). If
229    /// [`modulate_value()`][Self::_internal_modulate_value()] has previously been called with a non
230    /// zero value then this offset is taken into account to form the effective value.
231    ///
232    /// Returns whether or not the value has changed. Any parameter callbacks are only run the value
233    /// has actually changed.
234    ///
235    /// This does **not** update the smoother.
236    ///
237    /// # Safety
238    /// This is only allowed to be used by nice-plug's internal libraries.
239    unsafe fn _internal_set_plain_value(&self, plain: Self::Plain) -> bool;
240
241    /// Set this parameter based on a normalized value. The normalized value will be snapped to the
242    /// step size for continuous parameters (i.e. [`FloatParam`]). If
243    /// [`modulate_value()`][Self::_internal_modulate_value()] has previously been called with a non
244    /// zero value then this offset is taken into account to form the effective value.
245    ///
246    /// Returns whether or not the value has changed. Any parameter callbacks are only run the value
247    /// has actually changed.
248    ///
249    /// This does **not** update the smoother.
250    ///
251    /// # Safety
252    /// This is only allowed to be used by nice-plug's internal libraries.
253    unsafe fn _internal_set_normalized_value(&self, normalized: f32) -> bool;
254
255    /// Add a modulation offset to the value's unmodulated value. This value sticks until this
256    /// function is called again with a 0.0 value. Out of bound values will be clamped to the
257    /// parameter's range. The normalized value will be snapped to the step size for continuous
258    /// parameters (i.e. [`FloatParam`]).
259    ///
260    /// Returns whether or not the value has changed. Any parameter callbacks are only run the
261    /// value has actually changed.
262    ///
263    /// This does **not** update the smoother.
264    ///
265    /// # Safety
266    /// This is only allowed to be used by nice-plug's internal libraries.
267    unsafe fn _internal_modulate_value(&self, modulation_offset: f32) -> bool;
268
269    /// Update the smoother state to point to the current value. Also used when initializing and
270    /// restoring a plugin so everything is in sync. In that case the smoother should completely
271    /// reset to the current value.
272    ///
273    /// # Safety
274    /// This is only allowed to be used by nice-plug's internal libraries.
275    unsafe fn _internal_update_smoother(&self, sample_rate: f32, reset: bool);
276}
277
278/// Describes a struct containing parameters and other persistent fields.
279///
280/// # Deriving `Params` and `#[id = "stable"]`
281///
282/// This trait can be derived on a struct containing [`FloatParam`] and other parameter fields by
283/// adding `#[derive(Params)]`. When deriving this trait, any of those parameter fields should have
284/// the `#[id = "stable"]` attribute, where `stable` is an up to 6 character long string (to avoid
285/// collisions) that will be used to identify the parameter internally so you can safely move it
286/// around and rename the field without breaking compatibility with old presets.
287///
288/// ## `#[persist = "key"]`
289///
290/// The struct can also contain other fields that should be persisted along with the rest of the
291/// preset data. These fields should be [`PersistentField`][persist::PersistentField]s annotated
292/// with the `#[persist = "key"]` attribute containing types that can be serialized and deserialized
293/// with [Serde](https://serde.rs/).
294///
295/// ## `#[nested]`, `#[nested(group_name = "group name")]`
296///
297/// Finally, the `Params` object may include parameters from other objects. Setting a group name is
298/// optional, but some hosts can use this information to display the parameters in a tree structure.
299/// Parameter IDs and persisting keys still need to be **unique** when using nested parameter
300/// structs.
301///
302/// Take a look at the example gain example plugin to see how this is used.
303///
304/// ## `#[nested(id_prefix = "foo", group_name = "Foo")]`
305///
306/// Adding this attribute to a `Params` sub-object works similarly to the regular `#[nested]`
307/// attribute, but it also adds an ID to all parameters from the nested object. If a parameter in
308/// the nested nested object normally has parameter ID `bar`, the parameter's ID will now be renamed
309/// to `foo_bar`. The same thing happens with persistent field keys to support multiple copies of
310/// the field. _This makes it possible to reuse the same parameter struct with different names and
311/// parameter indices._
312///
313/// ## `#[nested(array, group_name = "Foo")]`
314///
315/// This can be applied to an array-like data structure and it works similar to a `nested` attribute
316/// with an `id_name`, except that it will iterate over the array and create unique indices for all
317/// nested parameters. If the nested parameters object has a parameter called `bar`, then that
318/// parameter will belong to the group `Foo {array_index + 1}`, and it will have the renamed
319/// parameter ID `bar_{array_index + 1}`. The same thing applies to persistent field keys.
320///
321/// # Safety
322///
323/// This implementation is safe when using from the wrapper because the plugin's returned `Params`
324/// object lives in an `Arc`, and the wrapper also holds a reference to this `Arc`.
325pub unsafe trait Params: 'static + Send + Sync {
326    /// Create a mapping from unique parameter IDs to parameter pointers along with the name of the
327    /// group/unit/module they are in, as a `(param_id, param_ptr, group)` triple. The order of the
328    /// `Vec` determines the display order in the (host's) generic UI. The group name is either an
329    /// empty string for top level parameters, or a slash/delimited `"group name 1/Group Name 2"` if
330    /// this `Params` object contains nested child objects. All components of a group path must
331    /// exist or you may encounter panics. The derive macro does this for every parameter field
332    /// marked with `#[id = "stable"]`, and it also inlines all fields from nested child `Params`
333    /// structs marked with `#[nested(...)]` while prefixing that group name before the parameter's
334    /// original group name. Dereferencing the pointers stored in the values is only valid as long
335    /// as this object is valid.
336    ///
337    /// # Note
338    ///
339    /// This uses `String` even though for the `Params` derive macro `&'static str` would have been
340    /// fine to be able to support custom reusable Params implementations.
341    fn param_map(&self) -> Vec<(String, ParamPtr, String)>;
342
343    /// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing
344    /// JSON-representations of those fields so they can be written to the plugin's state and
345    /// recalled later. This uses [`persist::serialize_field()`] under the hood.
346    fn serialize_fields(&self) -> BTreeMap<String, String> {
347        BTreeMap::new()
348    }
349
350    /// Restore all fields marked with `#[persist = "stable_name"]` from a hashmap created by
351    /// [`serialize_fields()`][Self::serialize_fields()]. All of these fields should be wrapped in a
352    /// [`persist::PersistentField`] with thread safe interior mutability, like an `RwLock` or a
353    /// `Mutex`. This gets called when the plugin's state is being restored. This uses
354    /// [`persist::deserialize_field()`] under the hood.
355    #[allow(unused_variables)]
356    fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {}
357}
358
359/// This may be useful when building generic UIs using nested `Params` objects.
360unsafe impl<P: Params> Params for Arc<P> {
361    fn param_map(&self) -> Vec<(String, ParamPtr, String)> {
362        self.as_ref().param_map()
363    }
364
365    fn serialize_fields(&self) -> BTreeMap<String, String> {
366        self.as_ref().serialize_fields()
367    }
368
369    fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {
370        self.as_ref().deserialize_fields(serialized)
371    }
372}