Also move state deserialization to a function
This commit is contained in:
parent
7a3d3b8c8e
commit
ec452bd41d
|
@ -19,6 +19,7 @@ use clap_sys::ext::params::{
|
|||
clap_param_info, clap_plugin_params, CLAP_EXT_PARAMS, CLAP_PARAM_IS_BYPASS,
|
||||
CLAP_PARAM_IS_STEPPED,
|
||||
};
|
||||
use clap_sys::ext::state::clap_plugin_state;
|
||||
use clap_sys::ext::thread_check::{clap_host_thread_check, CLAP_EXT_THREAD_CHECK};
|
||||
use clap_sys::host::clap_host;
|
||||
use clap_sys::id::{clap_id, CLAP_INVALID_ID};
|
||||
|
@ -27,6 +28,7 @@ use clap_sys::process::{
|
|||
clap_process, clap_process_status, CLAP_PROCESS_CONTINUE, CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
|
||||
CLAP_PROCESS_ERROR,
|
||||
};
|
||||
use clap_sys::stream::{clap_istream, clap_ostream};
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use crossbeam::queue::ArrayQueue;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -124,13 +126,15 @@ pub struct Wrapper<P: ClapPlugin> {
|
|||
/// can retrieve them later for the UI if needed.
|
||||
param_defaults_normalized: HashMap<u32, f32>,
|
||||
/// Mappings from string parameter indentifiers to parameter hashes. Useful for debug logging
|
||||
/// and when storing and restorign plugin state.
|
||||
/// and when storing and restoring plugin state.
|
||||
param_id_to_hash: HashMap<&'static str, u32>,
|
||||
/// The inverse mapping from [Self::param_by_hash]. This is needed to be able to have an
|
||||
/// ergonomic parameter setting API that uses references to the parameters instead of having to
|
||||
/// add a setter function to the parameter (or even worse, have it be completely untyped).
|
||||
param_ptr_to_hash: HashMap<ParamPtr, u32>,
|
||||
|
||||
clap_plugin_state: clap_plugin_state,
|
||||
|
||||
/// A queue of tasks that still need to be performed. Because CLAP lets the plugin request a
|
||||
/// host callback directly, we don't need to use the OsEventLoop we use in our other plugin
|
||||
/// implementations. Instead, we'll post tasks to this queue, ask the host to call
|
||||
|
@ -273,6 +277,11 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
param_id_to_hash: HashMap::new(),
|
||||
param_ptr_to_hash: HashMap::new(),
|
||||
|
||||
clap_plugin_state: clap_plugin_state {
|
||||
save: Self::ext_state_save,
|
||||
load: Self::ext_state_load,
|
||||
},
|
||||
|
||||
tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
|
||||
main_thread_id: thread::current().id(),
|
||||
};
|
||||
|
@ -1055,6 +1064,23 @@ impl<P: ClapPlugin> Wrapper<P> {
|
|||
|
||||
// TODO: Handle automation/outputs
|
||||
}
|
||||
|
||||
unsafe extern "C" fn ext_state_save(
|
||||
plugin: *const clap_plugin,
|
||||
stream: *mut clap_ostream,
|
||||
) -> bool {
|
||||
check_null_ptr!(false, plugin, stream);
|
||||
let wrapper = &*(plugin as *const Self);
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn ext_state_load(
|
||||
plugin: *const clap_plugin,
|
||||
stream: *mut clap_istream,
|
||||
) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function to query an extennsion from the host.
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
|
||||
use crate::param::internals::ParamPtr;
|
||||
use crate::param::Param;
|
||||
use crate::Params;
|
||||
use crate::{BufferConfig, Params};
|
||||
|
||||
/// A plain, unnormalized value for a parameter.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -85,3 +85,83 @@ pub(crate) unsafe fn serialize(
|
|||
let plugin_state = State { params, fields };
|
||||
serde_json::to_vec(&plugin_state)
|
||||
}
|
||||
|
||||
/// Serialize a plugin's state to a vector containing JSON data. This can (and should) be shared
|
||||
/// across plugin formats. Returns `false` and logs an error if the state could not be deserialized.
|
||||
///
|
||||
/// Make sure to reinitialize plugin after deserializing the state so it can react to the new
|
||||
/// parameter values. The smoothers have already been reset by this function.
|
||||
pub(crate) unsafe fn deserialize(
|
||||
state: &[u8],
|
||||
plugin_params: Pin<&dyn Params>,
|
||||
param_by_hash: &HashMap<u32, ParamPtr>,
|
||||
param_id_to_hash: &HashMap<&'static str, u32>,
|
||||
current_buffer_config: Option<&BufferConfig>,
|
||||
bypass_param_id: &str,
|
||||
bypass_state: &AtomicBool,
|
||||
) -> bool {
|
||||
let state: State = match serde_json::from_slice(state) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
nih_debug_assert_failure!("Error while deserializing state: {}", err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let sample_rate = current_buffer_config.map(|c| c.sample_rate);
|
||||
for (param_id_str, param_value) in state.params {
|
||||
// Handle the bypass parameter separately
|
||||
if param_id_str == bypass_param_id {
|
||||
match param_value {
|
||||
ParamValue::Bool(b) => bypass_state.store(b, Ordering::SeqCst),
|
||||
_ => nih_debug_assert_failure!(
|
||||
"Invalid serialized value {:?} for parameter \"{}\"",
|
||||
param_value,
|
||||
param_id_str,
|
||||
),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
let param_ptr = match param_id_to_hash
|
||||
.get(param_id_str.as_str())
|
||||
.and_then(|hash| param_by_hash.get(hash))
|
||||
{
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
nih_debug_assert_failure!("Unknown parameter: {}", param_id_str);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
match (param_ptr, param_value) {
|
||||
(ParamPtr::FloatParam(p), ParamValue::F32(v)) => (**p).set_plain_value(v),
|
||||
(ParamPtr::IntParam(p), ParamValue::I32(v)) => (**p).set_plain_value(v),
|
||||
(ParamPtr::BoolParam(p), ParamValue::Bool(v)) => (**p).set_plain_value(v),
|
||||
// Enums are serialized based on the active variant's index (which may not be the
|
||||
// same as the discriminator)
|
||||
(ParamPtr::EnumParam(p), ParamValue::I32(variant_idx)) => {
|
||||
(**p).set_plain_value(variant_idx)
|
||||
}
|
||||
(param_ptr, param_value) => {
|
||||
nih_debug_assert_failure!(
|
||||
"Invalid serialized value {:?} for parameter \"{}\" ({:?})",
|
||||
param_value,
|
||||
param_id_str,
|
||||
param_ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure everything starts out in sync
|
||||
if let Some(sample_rate) = sample_rate {
|
||||
param_ptr.update_smoother(sample_rate, true);
|
||||
}
|
||||
}
|
||||
|
||||
// The plugin can also persist arbitrary fields alongside its parameters. This is useful for
|
||||
// storing things like sample data.
|
||||
plugin_params.deserialize_fields(&state.fields);
|
||||
|
||||
true
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ptr;
|
||||
|
@ -18,10 +17,8 @@ use widestring::U16CStr;
|
|||
use super::inner::WrapperInner;
|
||||
use super::util::{VstPtr, BYPASS_PARAM_HASH, BYPASS_PARAM_ID};
|
||||
use super::view::WrapperView;
|
||||
use crate::param::internals::ParamPtr;
|
||||
use crate::param::Param;
|
||||
use crate::plugin::{BufferConfig, BusConfig, NoteEvent, ProcessStatus, Vst3Plugin};
|
||||
use crate::wrapper::state::{self, ParamValue, State};
|
||||
use crate::wrapper::state;
|
||||
use crate::wrapper::util::{process_wrapper, u16strlcpy};
|
||||
|
||||
// Alias needed for the VST3 attribute macro
|
||||
|
@ -220,80 +217,20 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
|||
return kResultFalse;
|
||||
}
|
||||
|
||||
let state: State = match serde_json::from_slice(&read_buffer) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
nih_debug_assert_failure!("Error while deserializing state: {}", err);
|
||||
let success = state::deserialize(
|
||||
&read_buffer,
|
||||
self.inner.plugin.read().params(),
|
||||
&self.inner.param_by_hash,
|
||||
&self.inner.param_id_to_hash,
|
||||
self.inner.current_buffer_config.load().as_ref(),
|
||||
BYPASS_PARAM_ID,
|
||||
&self.inner.bypass_state,
|
||||
);
|
||||
if !success {
|
||||
return kResultFalse;
|
||||
}
|
||||
};
|
||||
|
||||
let sample_rate = self
|
||||
.inner
|
||||
.current_buffer_config
|
||||
.load()
|
||||
.map(|c| c.sample_rate);
|
||||
for (param_id_str, param_value) in state.params {
|
||||
// Handle the bypass parameter separately
|
||||
if param_id_str == BYPASS_PARAM_ID {
|
||||
match param_value {
|
||||
ParamValue::Bool(b) => self.inner.bypass_state.store(b, Ordering::SeqCst),
|
||||
_ => nih_debug_assert_failure!(
|
||||
"Invalid serialized value {:?} for parameter \"{}\"",
|
||||
param_value,
|
||||
param_id_str,
|
||||
),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
let param_ptr = match self
|
||||
.inner
|
||||
.param_id_to_hash
|
||||
.get(param_id_str.as_str())
|
||||
.and_then(|hash| self.inner.param_by_hash.get(hash))
|
||||
{
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
nih_debug_assert_failure!("Unknown parameter: {}", param_id_str);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
match (param_ptr, param_value) {
|
||||
(ParamPtr::FloatParam(p), ParamValue::F32(v)) => (**p).set_plain_value(v),
|
||||
(ParamPtr::IntParam(p), ParamValue::I32(v)) => (**p).set_plain_value(v),
|
||||
(ParamPtr::BoolParam(p), ParamValue::Bool(v)) => (**p).set_plain_value(v),
|
||||
// Enums are serialized based on the active variant's index (which may not be the
|
||||
// same as the discriminator)
|
||||
(ParamPtr::EnumParam(p), ParamValue::I32(variant_idx)) => {
|
||||
(**p).set_plain_value(variant_idx)
|
||||
}
|
||||
(param_ptr, param_value) => {
|
||||
nih_debug_assert_failure!(
|
||||
"Invalid serialized value {:?} for parameter \"{}\" ({:?})",
|
||||
param_value,
|
||||
param_id_str,
|
||||
param_ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure everything starts out in sync
|
||||
if let Some(sample_rate) = sample_rate {
|
||||
param_ptr.update_smoother(sample_rate, true);
|
||||
}
|
||||
}
|
||||
|
||||
// The plugin can also persist arbitrary fields alongside its parameters. This is useful for
|
||||
// storing things like sample data.
|
||||
self.inner
|
||||
.plugin
|
||||
.read()
|
||||
.params()
|
||||
.deserialize_fields(&state.fields);
|
||||
|
||||
// Reinitialize the plugin after loading state so it can respond to the new parmaeters
|
||||
// Reinitialize the plugin after loading state so it can respond to the new parameter values
|
||||
let bus_config = self.inner.current_bus_config.load();
|
||||
if let Some(buffer_config) = self.inner.current_buffer_config.load() {
|
||||
self.inner.plugin.write().initialize(
|
||||
|
|
Loading…
Reference in a new issue