1
0
Fork 0

Move the Params trait out of params::internals

This makes much more sense, since this trait is a cornerstone of
NIH-plug.
This commit is contained in:
Robbert van der Helm 2022-10-20 12:10:35 +02:00
parent 4f74b4b4cc
commit fb71d0fcce
12 changed files with 133 additions and 122 deletions

View file

@ -6,6 +6,13 @@ new and what's changed, this document lists all breaking changes in reverse
chronological order. If a new feature did not require any changes to existing chronological order. If a new feature did not require any changes to existing
code then it will not be listed here. code then it will not be listed here.
## [2022-10-20]
- Some items have been moved out of `nih_plug::param::internals`. The main
`Params` trait is now located under `nih_plug::param`, and the
`PersistentTrait` trait, implementations, and helper functions are now part of
a new `nih_plug::param::persist` module.
## [2022-10-13] ## [2022-10-13]
- The `#[nested]` parameter attribute has gained super powers and has its syntax - The `#[nested]` parameter attribute has gained super powers and has its syntax

View file

@ -235,7 +235,7 @@ pub trait IcedEditor: 'static + Send + Sync + Sized {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct IcedState { pub struct IcedState {
/// The window's size in logical pixels before applying `scale_factor`. /// The window's size in logical pixels before applying `scale_factor`.
#[serde(with = "nih_plug::param::internals::serialize_atomic_cell")] #[serde(with = "nih_plug::param::persist::serialize_atomic_cell")]
size: AtomicCell<(u32, u32)>, size: AtomicCell<(u32, u32)>,
/// Whether the editor's window is currently open. /// Whether the editor's window is currently open.
#[serde(skip)] #[serde(skip)]

View file

@ -5,7 +5,7 @@
use baseview::{WindowHandle, WindowScalePolicy}; use baseview::{WindowHandle, WindowScalePolicy};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use nih_plug::param::internals::PersistentField; use nih_plug::param::persist::PersistentField;
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle}; use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};

View file

@ -220,7 +220,7 @@ pub struct Transport {
} }
/// A convenience helper for setting parameter values. Any changes made here will be broadcasted to /// 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::param::internals::Params] object. These /// the host and reflected in the plugin's [`Params`][crate::param::Params] object. These
/// functions should only be called from the main thread. /// functions should only be called from the main thread.
pub struct ParamSetter<'a> { pub struct ParamSetter<'a> {
pub raw_context: &'a dyn GuiContext, pub raw_context: &'a dyn GuiContext,

View file

@ -1,10 +1,17 @@
//! NIH-plug can handle floating point, integer, boolean, and enum parameters. Parameters are //! NIH-plug can handle floating point, integer, boolean, and enum parameters. Parameters are
//! managed by creating a struct deriving the [`Params`][internals::Params] trait containing fields //! managed by creating a struct deriving the [`Params`][Params] trait containing fields
//! for those parameter types, and then returning a reference to that object from your //! for those parameter types, and then returning a reference to that object from your
//! [`Plugin::params()`][crate::prelude::Plugin::params()] method. See the `Params` trait for more //! [`Plugin::params()`][crate::prelude::Plugin::params()] method. See the `Params` trait for more
//! information. //! information.
use std::collections::BTreeMap;
use std::fmt::Display; use std::fmt::Display;
use std::sync::Arc;
use self::internals::ParamPtr;
// The proc-macro for deriving `Params`
pub use nih_plug_derive::Params;
// Parameter types // Parameter types
mod boolean; mod boolean;
@ -158,7 +165,7 @@ pub trait Param: Display {
/// Flags to control the parameter's behavior. See [`ParamFlags`]. /// Flags to control the parameter's behavior. See [`ParamFlags`].
fn flags(&self) -> ParamFlags; fn flags(&self) -> ParamFlags;
/// Internal implementation detail for implementing [`Params`][internals::Params]. This should /// Internal implementation detail for implementing [`Params`][Params]. This should
/// not be used directly. /// not be used directly.
fn as_ptr(&self) -> internals::ParamPtr; fn as_ptr(&self) -> internals::ParamPtr;
} }
@ -194,3 +201,105 @@ pub(crate) trait ParamMut: Param {
/// reset to the current value. /// reset to the current value.
fn update_smoother(&self, sample_rate: f32, reset: bool); fn update_smoother(&self, sample_rate: f32, reset: bool);
} }
/// Describes a struct containing parameters and other persistent fields.
///
/// # Deriving `Params` and `#[id = "stable"]`
///
/// This trait can be derived on a struct containing [`FloatParam`][super::FloatParam] and other
/// parameter fields by adding `#[derive(Params)]`. When deriving this trait, any of those parameter
/// fields should have the `#[id = "stable"]` attribute, where `stable` is an up to 6 character long
/// string (to avoid collisions) that will be used to identify the parameter internally so you can
/// safely move it around and rename the field without breaking compatibility with old presets.
///
/// ## `#[persist = "key"]`
///
/// The struct can also contain other fields that should be persisted along with the rest of the
/// preset data. These fields should be [`PersistentField`]s annotated with the `#[persist = "key"]`
/// attribute containing types that can be serialized and deserialized with
/// [Serde](https://serde.rs/).
///
/// ## `#[nested]`, `#[nested(group_name = "group name")]`
///
/// Finally, the `Params` object may include parameters from other objects. Setting a group name is
/// optional, but some hosts can use this information to display the parameters in a tree structure.
/// Parameter IDs and persisting keys still need to be **unique** when using nested parameter
/// structs. This currently has the following caveats:
///
/// - Enforcing that parameter IDs and persist keys are unique does not work across nested structs.
/// - Deserializing persisted fields will give false positives about fields not existing.
///
/// Take a look at the example gain example plugin to see how this is used.
///
/// ## `#[nested(id_prefix = "foo", group_name = "Foo")]`
///
/// Adding this attribute to a `Params` sub-object works similarly to the regular `#[nested]`
/// attribute, but it also adds an ID to all parameters from the nested object. If a parameter in
/// the nested nested object normally has parameter ID `bar`, the parameter's ID will now be renamed
/// to `foo_bar`. _This makes it possible to reuse same parameter struct with different names and
/// parameter indices._
///
/// This does **not** support persistent fields.
///
/// ## `#[nested(array, group_name = "Foo")]`
///
/// This can be applied to an array-like data structure and it works similar to a `nested` attribute
/// with an `id_name`, except that it will iterate over the array and create unique indices for all
/// nested parameters. If the nested parameters object has a parameter called `bar`, then that
/// parameter will belong to the group `Foo {array_index + 1}`, and it will have the renamed
/// parameter ID `bar_{array_index + 1}`.
///
/// This does **not** support persistent fields.
///
/// # Safety
///
/// This implementation is safe when using from the wrapper because the plugin's returned `Params`
/// object lives in an `Arc`, and the wrapper also holds a reference to this `Arc`.
pub unsafe trait Params: 'static + Send + Sync {
/// Create a mapping from unique parameter IDs to parameter pointers along with the name of the
/// group/unit/module they are in, as a `(param_id, param_ptr, group)` triple. The order of the
/// `Vec` determines the display order in the (host's) generic UI. The group name is either an
/// empty string for top level parameters, or a slash/delimited `"group name 1/Group Name 2"` if
/// this `Params` object contains nested child objects. All components of a group path must
/// exist or you may encounter panics. The derive macro does this for every parameter field
/// marked with `#[id = "stable"]`, and it also inlines all fields from nested child `Params`
/// structs marked with `#[nested(...)]` while prefixing that group name before the parameter's
/// original group name. Dereferencing the pointers stored in the values is only valid as long
/// as this object is valid.
///
/// # Note
///
/// This uses `String` even though for the `Params` derive macro `&'static str` would have been
/// fine to be able to support custom reusable Params implementations.
fn param_map(&self) -> Vec<(String, ParamPtr, String)>;
/// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing
/// JSON-representations of those fields so they can be written to the plugin's state and
/// recalled later. This uses [`serialize_field()`] under the hood.
fn serialize_fields(&self) -> BTreeMap<String, String> {
BTreeMap::new()
}
/// Restore all fields marked with `#[persist = "stable_name"]` from a hashmap created by
/// [`serialize_fields()`][Self::serialize_fields()]. All of these fields should be wrapped in a
/// [`PersistentField`] with thread safe interior mutability, like an `RwLock` or a `Mutex`.
/// This gets called when the plugin's state is being restored. This uses [deserialize_field()]
/// under the hood.
#[allow(unused_variables)]
fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {}
}
/// This may be useful when building generic UIs using nested `Params` objects.
unsafe impl<P: Params> Params for Arc<P> {
fn param_map(&self) -> Vec<(String, ParamPtr, String)> {
self.as_ref().param_map()
}
fn serialize_fields(&self) -> BTreeMap<String, String> {
self.as_ref().serialize_fields()
}
fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {
self.as_ref().deserialize_fields(serialized)
}
}

View file

@ -1,11 +1,7 @@
//! Implementation details for the parameter management. //! Implementation details for the parameter management.
use std::collections::BTreeMap;
use std::sync::Arc;
use super::{Param, ParamFlags, ParamMut}; use super::{Param, ParamFlags, ParamMut};
pub use nih_plug_derive::Params;
/// Re-export for use in the [`Params`] proc-macro. /// Re-export for use in the [`Params`] proc-macro.
pub use serde_json::from_str as deserialize_field; pub use serde_json::from_str as deserialize_field;
/// Re-export for use in the [`Params`] proc-macro. /// Re-export for use in the [`Params`] proc-macro.
@ -34,108 +30,6 @@ pub mod serialize_atomic_cell {
} }
} }
/// Describes a struct containing parameters and other persistent fields.
///
/// # Deriving `Params` and `#[id = "stable"]`
///
/// This trait can be derived on a struct containing [`FloatParam`][super::FloatParam] and other
/// parameter fields by adding `#[derive(Params)]`. When deriving this trait, any of those parameter
/// fields should have the `#[id = "stable"]` attribute, where `stable` is an up to 6 character long
/// string (to avoid collisions) that will be used to identify the parameter internally so you can
/// safely move it around and rename the field without breaking compatibility with old presets.
///
/// ## `#[persist = "key"]`
///
/// The struct can also contain other fields that should be persisted along with the rest of the
/// preset data. These fields should be [`PersistentField`]s annotated with the `#[persist = "key"]`
/// attribute containing types that can be serialized and deserialized with
/// [Serde](https://serde.rs/).
///
/// ## `#[nested]`, `#[nested(group_name = "group name")]`
///
/// Finally, the `Params` object may include parameters from other objects. Setting a group name is
/// optional, but some hosts can use this information to display the parameters in a tree structure.
/// Parameter IDs and persisting keys still need to be **unique** when using nested parameter
/// structs. This currently has the following caveats:
///
/// - Enforcing that parameter IDs and persist keys are unique does not work across nested structs.
/// - Deserializing persisted fields will give false positives about fields not existing.
///
/// Take a look at the example gain example plugin to see how this is used.
///
/// ## `#[nested(id_prefix = "foo", group_name = "Foo")]`
///
/// Adding this attribute to a `Params` sub-object works similarly to the regular `#[nested]`
/// attribute, but it also adds an ID to all parameters from the nested object. If a parameter in
/// the nested nested object normally has parameter ID `bar`, the parameter's ID will now be renamed
/// to `foo_bar`. _This makes it possible to reuse same parameter struct with different names and
/// parameter indices._
///
/// This does **not** support persistent fields.
///
/// ## `#[nested(array, group_name = "Foo")]`
///
/// This can be applied to an array-like data structure and it works similar to a `nested` attribute
/// with an `id_name`, except that it will iterate over the array and create unique indices for all
/// nested parameters. If the nested parameters object has a parameter called `bar`, then that
/// parameter will belong to the group `Foo {array_index + 1}`, and it will have the renamed
/// parameter ID `bar_{array_index + 1}`.
///
/// This does **not** support persistent fields.
///
/// # Safety
///
/// This implementation is safe when using from the wrapper because the plugin's returned `Params`
/// object lives in an `Arc`, and the wrapper also holds a reference to this `Arc`.
pub unsafe trait Params: 'static + Send + Sync {
/// Create a mapping from unique parameter IDs to parameter pointers along with the name of the
/// group/unit/module they are in, as a `(param_id, param_ptr, group)` triple. The order of the
/// `Vec` determines the display order in the (host's) generic UI. The group name is either an
/// empty string for top level parameters, or a slash/delimited `"group name 1/Group Name 2"` if
/// this `Params` object contains nested child objects. All components of a group path must
/// exist or you may encounter panics. The derive macro does this for every parameter field
/// marked with `#[id = "stable"]`, and it also inlines all fields from nested child `Params`
/// structs marked with `#[nested(...)]` while prefixing that group name before the parameter's
/// original group name. Dereferencing the pointers stored in the values is only valid as long
/// as this object is valid.
///
/// # Note
///
/// This uses `String` even though for the `Params` derive macro `&'static str` would have been
/// fine to be able to support custom reusable Params implementations.
fn param_map(&self) -> Vec<(String, ParamPtr, String)>;
/// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing
/// JSON-representations of those fields so they can be written to the plugin's state and
/// recalled later. This uses [`serialize_field()`] under the hood.
fn serialize_fields(&self) -> BTreeMap<String, String> {
BTreeMap::new()
}
/// Restore all fields marked with `#[persist = "stable_name"]` from a hashmap created by
/// [`serialize_fields()`][Self::serialize_fields()]. All of these fields should be wrapped in a
/// [`PersistentField`] with thread safe interior mutability, like an `RwLock` or a `Mutex`.
/// This gets called when the plugin's state is being restored. This uses [deserialize_field()]
/// under the hood.
#[allow(unused_variables)]
fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {}
}
/// This may be useful when building generic UIs using nested `Params` objects.
unsafe impl<P: Params> Params for Arc<P> {
fn param_map(&self) -> Vec<(String, ParamPtr, String)> {
self.as_ref().param_map()
}
fn serialize_fields(&self) -> BTreeMap<String, String> {
self.as_ref().serialize_fields()
}
fn deserialize_fields(&self, serialized: &BTreeMap<String, String>) {
self.as_ref().deserialize_fields(serialized)
}
}
/// Internal pointers to parameters. This is an implementation detail used by the wrappers for type /// Internal pointers to parameters. This is an implementation detail used by the wrappers for type
/// erasure. /// erasure.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]

View file

@ -7,7 +7,7 @@ use std::sync::Arc;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::context::{GuiContext, InitContext, ProcessContext}; use crate::context::{GuiContext, InitContext, ProcessContext};
use crate::midi::MidiConfig; use crate::midi::MidiConfig;
use crate::param::internals::Params; use crate::param::Params;
use crate::wrapper::clap::features::ClapFeature; use crate::wrapper::clap::features::ClapFeature;
/// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to /// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to

View file

@ -15,9 +15,10 @@ pub use crate::context::{GuiContext, InitContext, ParamSetter, PluginApi, Proces
// This also includes the derive macro // This also includes the derive macro
pub use crate::midi::{control_change, MidiConfig, NoteEvent}; pub use crate::midi::{control_change, MidiConfig, NoteEvent};
pub use crate::param::enums::{Enum, EnumParam}; pub use crate::param::enums::{Enum, EnumParam};
pub use crate::param::internals::{ParamPtr, Params}; pub use crate::param::internals::ParamPtr;
pub use crate::param::range::{FloatRange, IntRange}; pub use crate::param::range::{FloatRange, IntRange};
pub use crate::param::smoothing::{Smoothable, Smoother, SmoothingStyle}; pub use crate::param::smoothing::{Smoothable, Smoother, SmoothingStyle};
pub use crate::param::Params;
pub use crate::param::{BoolParam, FloatParam, IntParam, Param, ParamFlags}; pub use crate::param::{BoolParam, FloatParam, IntParam, Param, ParamFlags};
pub use crate::plugin::{ pub use crate::plugin::{
AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Editor, AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, ClapPlugin, Editor,

View file

@ -80,8 +80,8 @@ use crate::buffer::Buffer;
use crate::context::Transport; use crate::context::Transport;
use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY}; use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
use crate::midi::{MidiConfig, NoteEvent}; use crate::midi::{MidiConfig, NoteEvent};
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::ParamPtr;
use crate::param::ParamFlags; use crate::param::{ParamFlags, Params};
use crate::plugin::{ use crate::plugin::{
AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, Editor, ParentWindowHandle, ProcessMode, AuxiliaryBuffers, BufferConfig, BusConfig, ClapPlugin, Editor, ParentWindowHandle, ProcessMode,
ProcessStatus, ProcessStatus,

View file

@ -15,8 +15,8 @@ use super::config::WrapperConfig;
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext}; use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
use crate::context::Transport; use crate::context::Transport;
use crate::midi::NoteEvent; use crate::midi::NoteEvent;
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::ParamPtr;
use crate::param::ParamFlags; use crate::param::{ParamFlags, Params};
use crate::plugin::{ use crate::plugin::{
AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Editor, ParentWindowHandle, AuxiliaryBuffers, AuxiliaryIOConfig, BufferConfig, BusConfig, Editor, ParentWindowHandle,
Plugin, ProcessMode, ProcessStatus, Plugin, ProcessMode, ProcessStatus,

View file

@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::sync::Arc; use std::sync::Arc;
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::ParamPtr;
use crate::param::{Param, ParamMut}; use crate::param::{Param, ParamMut, Params};
use crate::plugin::{BufferConfig, Plugin}; use crate::plugin::{BufferConfig, Plugin};
// These state objects are also exposed directly to the plugin so it can do its own internal preset // These state objects are also exposed directly to the plugin so it can do its own internal preset
@ -45,7 +45,7 @@ pub struct PluginState {
/// parameter automation though, depending on how the host implements that. /// parameter automation though, depending on how the host implements that.
pub params: BTreeMap<String, ParamValue>, pub params: BTreeMap<String, ParamValue>,
/// Arbitrary fields that should be persisted together with the plugin's parameters. Any field /// Arbitrary fields that should be persisted together with the plugin's parameters. Any field
/// on the [`Params`][crate::param::internals::Params] struct that's annotated with `#[persist = /// on the [`Params`][crate::param::Params] struct that's annotated with `#[persist =
/// "stable_name"]` will be persisted this way. /// "stable_name"]` will be persisted this way.
/// ///
/// The individual fields are also serialized as JSON so they can safely be restored /// The individual fields are also serialized as JSON so they can safely be restored

View file

@ -18,8 +18,8 @@ use crate::buffer::Buffer;
use crate::context::Transport; use crate::context::Transport;
use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop}; use crate::event_loop::{EventLoop, MainThreadExecutor, OsEventLoop};
use crate::midi::{MidiConfig, NoteEvent}; use crate::midi::{MidiConfig, NoteEvent};
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::ParamPtr;
use crate::param::ParamFlags; use crate::param::{ParamFlags, Params};
use crate::plugin::{BufferConfig, BusConfig, Editor, ProcessMode, ProcessStatus, Vst3Plugin}; use crate::plugin::{BufferConfig, BusConfig, Editor, ProcessMode, ProcessStatus, Vst3Plugin};
use crate::wrapper::state::{self, PluginState}; use crate::wrapper::state::{self, PluginState};
use crate::wrapper::util::{hash_param_id, process_wrapper}; use crate::wrapper::util::{hash_param_id, process_wrapper};