1
0
Fork 0

Store plugin version in the state

This may later be used to allow migrations between breaking plugin
versions.
This commit is contained in:
Robbert van der Helm 2022-08-20 15:09:54 +02:00
parent ee62a45d0d
commit 8a9c98943e
5 changed files with 26 additions and 11 deletions

View file

@ -1544,7 +1544,7 @@ impl<P: ClapPlugin> Wrapper<P> {
/// the JSON in the relevant plugin API methods instead. /// the JSON in the relevant plugin API methods instead.
pub fn get_state_object(&self) -> PluginState { pub fn get_state_object(&self) -> PluginState {
unsafe { unsafe {
state::serialize_object( state::serialize_object::<P>(
self.params.clone(), self.params.clone(),
state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash), state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash),
) )
@ -3067,7 +3067,7 @@ impl<P: ClapPlugin> Wrapper<P> {
check_null_ptr!(false, plugin, stream); check_null_ptr!(false, plugin, stream);
let wrapper = &*(plugin as *const Self); let wrapper = &*(plugin as *const Self);
let serialized = state::serialize_json( let serialized = state::serialize_json::<P>(
wrapper.params.clone(), wrapper.params.clone(),
state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash), state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash),
); );

View file

@ -338,7 +338,7 @@ impl<P: Plugin, B: Backend> Wrapper<P, B> {
/// the JSON in the relevant plugin API methods instead. /// the JSON in the relevant plugin API methods instead.
pub fn get_state_object(&self) -> PluginState { pub fn get_state_object(&self) -> PluginState {
unsafe { unsafe {
state::serialize_object( state::serialize_object::<P>(
self.params.clone(), self.params.clone(),
self.param_map self.param_map
.iter() .iter()

View file

@ -8,7 +8,7 @@ use std::sync::Arc;
use crate::param::internals::{ParamPtr, Params}; use crate::param::internals::{ParamPtr, Params};
use crate::param::{Param, ParamMut}; 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 // These state objects are also exposed directly to the plugin so it can do its own internal preset
// management // management
@ -30,6 +30,16 @@ pub enum ParamValue {
/// The fields are stored as `BTreeMap`s so the order in the serialized file is consistent. /// The fields are stored as `BTreeMap`s so the order in the serialized file is consistent.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginState { 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 /// 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 /// 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. /// 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 /// 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_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()`]. /// 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<dyn Params>, plugin_params: Arc<dyn Params>,
params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>, params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>,
) -> PluginState { ) -> PluginState {
@ -112,17 +122,21 @@ pub(crate) unsafe fn serialize_object<'a>(
// storing things like sample data. // storing things like sample data.
let fields = plugin_params.serialize_fields(); 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 /// 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 /// across plugin formats. If the `zstd` feature is enabled, then the state will be compressed using
/// Zstandard. /// Zstandard.
pub(crate) unsafe fn serialize_json<'a>( pub(crate) unsafe fn serialize_json<'a, P: Plugin>(
plugin_params: Arc<dyn Params>, plugin_params: Arc<dyn Params>,
params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>, params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>,
) -> Result<Vec<u8>> { ) -> Result<Vec<u8>> {
let plugin_state = serialize_object(plugin_params, params_iter); let plugin_state = serialize_object::<P>(plugin_params, params_iter);
let json = serde_json::to_vec(&plugin_state).context("Could not format as JSON")?; let json = serde_json::to_vec(&plugin_state).context("Could not format as JSON")?;
#[cfg(feature = "zstd")] #[cfg(feature = "zstd")]

View file

@ -415,7 +415,7 @@ impl<P: Vst3Plugin> WrapperInner<P> {
/// the JSON in the relevant plugin API methods instead. /// the JSON in the relevant plugin API methods instead.
pub fn get_state_object(&self) -> PluginState { pub fn get_state_object(&self) -> PluginState {
unsafe { unsafe {
state::serialize_object( state::serialize_object::<P>(
self.params.clone(), self.params.clone(),
state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash), state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash),
) )

View file

@ -537,7 +537,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
let state = state.upgrade().unwrap(); let state = state.upgrade().unwrap();
let serialized = state::serialize_json( let serialized = state::serialize_json::<P>(
self.inner.params.clone(), self.inner.params.clone(),
state::make_params_iter(&self.inner.param_by_hash, &self.inner.param_id_to_hash), state::make_params_iter(&self.inner.param_by_hash, &self.inner.param_id_to_hash),
); );
@ -913,7 +913,8 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
return kInvalidArgument; return kInvalidArgument;
} }
} else if dir == vst3_sys::vst::BusDirections::kOutput as i32 { } 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 }; let aux_output_start_idx = if aux_outputs_only { 0 } else { 1 };
if (!aux_outputs_only || no_main_audio_io) && index == 0 { if (!aux_outputs_only || no_main_audio_io) && index == 0 {
bus_config.num_output_channels bus_config.num_output_channels