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_info, clap_plugin_params, CLAP_EXT_PARAMS, CLAP_PARAM_IS_BYPASS,
|
||||||
CLAP_PARAM_IS_STEPPED,
|
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::ext::thread_check::{clap_host_thread_check, CLAP_EXT_THREAD_CHECK};
|
||||||
use clap_sys::host::clap_host;
|
use clap_sys::host::clap_host;
|
||||||
use clap_sys::id::{clap_id, CLAP_INVALID_ID};
|
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, clap_process_status, CLAP_PROCESS_CONTINUE, CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
|
||||||
CLAP_PROCESS_ERROR,
|
CLAP_PROCESS_ERROR,
|
||||||
};
|
};
|
||||||
|
use clap_sys::stream::{clap_istream, clap_ostream};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use crossbeam::queue::ArrayQueue;
|
use crossbeam::queue::ArrayQueue;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -124,13 +126,15 @@ pub struct Wrapper<P: ClapPlugin> {
|
||||||
/// can retrieve them later for the UI if needed.
|
/// can retrieve them later for the UI if needed.
|
||||||
param_defaults_normalized: HashMap<u32, f32>,
|
param_defaults_normalized: HashMap<u32, f32>,
|
||||||
/// Mappings from string parameter indentifiers to parameter hashes. Useful for debug logging
|
/// 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>,
|
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
|
/// 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
|
/// 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).
|
/// add a setter function to the parameter (or even worse, have it be completely untyped).
|
||||||
param_ptr_to_hash: HashMap<ParamPtr, u32>,
|
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
|
/// 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
|
/// 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
|
/// 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_id_to_hash: HashMap::new(),
|
||||||
param_ptr_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),
|
tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
|
||||||
main_thread_id: thread::current().id(),
|
main_thread_id: thread::current().id(),
|
||||||
};
|
};
|
||||||
|
@ -1055,6 +1064,23 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
// TODO: Handle automation/outputs
|
// 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.
|
/// 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::internals::ParamPtr;
|
||||||
use crate::param::Param;
|
use crate::param::Param;
|
||||||
use crate::Params;
|
use crate::{BufferConfig, Params};
|
||||||
|
|
||||||
/// A plain, unnormalized value for a parameter.
|
/// A plain, unnormalized value for a parameter.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -85,3 +85,83 @@ pub(crate) unsafe fn serialize(
|
||||||
let plugin_state = State { params, fields };
|
let plugin_state = State { params, fields };
|
||||||
serde_json::to_vec(&plugin_state)
|
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::cmp;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::mem::{self, MaybeUninit};
|
use std::mem::{self, MaybeUninit};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -18,10 +17,8 @@ use widestring::U16CStr;
|
||||||
use super::inner::WrapperInner;
|
use super::inner::WrapperInner;
|
||||||
use super::util::{VstPtr, BYPASS_PARAM_HASH, BYPASS_PARAM_ID};
|
use super::util::{VstPtr, BYPASS_PARAM_HASH, BYPASS_PARAM_ID};
|
||||||
use super::view::WrapperView;
|
use super::view::WrapperView;
|
||||||
use crate::param::internals::ParamPtr;
|
|
||||||
use crate::param::Param;
|
|
||||||
use crate::plugin::{BufferConfig, BusConfig, NoteEvent, ProcessStatus, Vst3Plugin};
|
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};
|
use crate::wrapper::util::{process_wrapper, u16strlcpy};
|
||||||
|
|
||||||
// Alias needed for the VST3 attribute macro
|
// Alias needed for the VST3 attribute macro
|
||||||
|
@ -220,80 +217,20 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
return kResultFalse;
|
return kResultFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state: State = match serde_json::from_slice(&read_buffer) {
|
let success = state::deserialize(
|
||||||
Ok(s) => s,
|
&read_buffer,
|
||||||
Err(err) => {
|
self.inner.plugin.read().params(),
|
||||||
nih_debug_assert_failure!("Error while deserializing state: {}", err);
|
&self.inner.param_by_hash,
|
||||||
return kResultFalse;
|
&self.inner.param_id_to_hash,
|
||||||
}
|
self.inner.current_buffer_config.load().as_ref(),
|
||||||
};
|
BYPASS_PARAM_ID,
|
||||||
|
&self.inner.bypass_state,
|
||||||
let sample_rate = self
|
);
|
||||||
.inner
|
if !success {
|
||||||
.current_buffer_config
|
return kResultFalse;
|
||||||
.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
|
// Reinitialize the plugin after loading state so it can respond to the new parameter values
|
||||||
// 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
|
|
||||||
let bus_config = self.inner.current_bus_config.load();
|
let bus_config = self.inner.current_bus_config.load();
|
||||||
if let Some(buffer_config) = self.inner.current_buffer_config.load() {
|
if let Some(buffer_config) = self.inner.current_buffer_config.load() {
|
||||||
self.inner.plugin.write().initialize(
|
self.inner.plugin.write().initialize(
|
||||||
|
|
Loading…
Reference in a new issue