diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index f64fdd40..af585eec 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -1544,7 +1544,7 @@ impl Wrapper

{ /// the JSON in the relevant plugin API methods instead. pub fn get_state_object(&self) -> PluginState { unsafe { - state::serialize_object( + state::serialize_object::

( self.params.clone(), state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash), ) @@ -3067,7 +3067,7 @@ impl Wrapper

{ check_null_ptr!(false, plugin, stream); let wrapper = &*(plugin as *const Self); - let serialized = state::serialize_json( + let serialized = state::serialize_json::

( wrapper.params.clone(), state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash), ); diff --git a/src/wrapper/standalone/wrapper.rs b/src/wrapper/standalone/wrapper.rs index d68f6b62..729fb3b9 100644 --- a/src/wrapper/standalone/wrapper.rs +++ b/src/wrapper/standalone/wrapper.rs @@ -338,7 +338,7 @@ impl Wrapper { /// the JSON in the relevant plugin API methods instead. pub fn get_state_object(&self) -> PluginState { unsafe { - state::serialize_object( + state::serialize_object::

( self.params.clone(), self.param_map .iter() diff --git a/src/wrapper/state.rs b/src/wrapper/state.rs index 6419ce93..14a91a9f 100644 --- a/src/wrapper/state.rs +++ b/src/wrapper/state.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use crate::param::internals::{ParamPtr, Params}; use crate::param::{Param, ParamMut}; -use crate::plugin::BufferConfig; +use crate::plugin::{BufferConfig, Plugin}; // These state objects are also exposed directly to the plugin so it can do its own internal preset // management @@ -30,6 +30,16 @@ pub enum ParamValue { /// The fields are stored as `BTreeMap`s so the order in the serialized file is consistent. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PluginState { + /// The plugin version this state was saved with. Right now this is not used, but later versions + /// of NIH-plug may allow you to modify the plugin state object directly before it is loaded to + /// allow migrating plugin states between breaking parameter changes. + /// + /// # Notes + /// + /// If the saved state is very old, then this field may be empty. + #[serde(default)] + pub version: String, + /// The plugin's parameter values. These are stored unnormalized. This mean sthe old values will /// be recalled when when the parameter's range gets increased. Doing so may still mess with /// parameter automation though, depending on how the host impelments that. @@ -73,7 +83,7 @@ pub(crate) fn make_params_getter<'a>( /// allow passing the raw object directly to the plugin. The parameters are not pulled directly from /// `plugin_params` by default to avoid unnecessary allocations in the `.param_map()` method, as the /// plugin wrappers will already have a list of parameters handy. See [`make_params_iter()`]. -pub(crate) unsafe fn serialize_object<'a>( +pub(crate) unsafe fn serialize_object<'a, P: Plugin>( plugin_params: Arc, params_iter: impl IntoIterator, ) -> PluginState { @@ -112,17 +122,21 @@ pub(crate) unsafe fn serialize_object<'a>( // storing things like sample data. let fields = plugin_params.serialize_fields(); - PluginState { params, fields } + PluginState { + version: String::from(P::VERSION), + params, + fields, + } } /// Serialize a plugin's state to a vector containing JSON data. This can (and should) be shared /// across plugin formats. If the `zstd` feature is enabled, then the state will be compressed using /// Zstandard. -pub(crate) unsafe fn serialize_json<'a>( +pub(crate) unsafe fn serialize_json<'a, P: Plugin>( plugin_params: Arc, params_iter: impl IntoIterator, ) -> Result> { - let plugin_state = serialize_object(plugin_params, params_iter); + let plugin_state = serialize_object::

(plugin_params, params_iter); let json = serde_json::to_vec(&plugin_state).context("Could not format as JSON")?; #[cfg(feature = "zstd")] diff --git a/src/wrapper/vst3/inner.rs b/src/wrapper/vst3/inner.rs index 4a5a79d6..9f6d4984 100644 --- a/src/wrapper/vst3/inner.rs +++ b/src/wrapper/vst3/inner.rs @@ -415,7 +415,7 @@ impl WrapperInner

{ /// the JSON in the relevant plugin API methods instead. pub fn get_state_object(&self) -> PluginState { unsafe { - state::serialize_object( + state::serialize_object::

( self.params.clone(), state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash), ) diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index c2c89514..fe81ef8e 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -537,7 +537,7 @@ impl IComponent for Wrapper

{ let state = state.upgrade().unwrap(); - let serialized = state::serialize_json( + let serialized = state::serialize_json::

( self.inner.params.clone(), state::make_params_iter(&self.inner.param_by_hash, &self.inner.param_id_to_hash), ); @@ -913,7 +913,8 @@ impl IAudioProcessor for Wrapper

{ return kInvalidArgument; } } else if dir == vst3_sys::vst::BusDirections::kOutput as i32 { - let aux_outputs_only = P::DEFAULT_OUTPUT_CHANNELS == 0 && P::DEFAULT_AUX_OUTPUTS.is_some(); + let aux_outputs_only = + P::DEFAULT_OUTPUT_CHANNELS == 0 && P::DEFAULT_AUX_OUTPUTS.is_some(); let aux_output_start_idx = if aux_outputs_only { 0 } else { 1 }; if (!aux_outputs_only || no_main_audio_io) && index == 0 { bus_config.num_output_channels