Implement restoring parameter values
This commit is contained in:
parent
dabd2beca8
commit
47d102f2ba
2 changed files with 88 additions and 12 deletions
|
@ -29,10 +29,9 @@ pub(crate) enum ParamValue {
|
||||||
|
|
||||||
/// A plugin's state so it can be restored at a later point.
|
/// A plugin's state so it can be restored at a later point.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(bound(deserialize = "'de: 'static"))]
|
|
||||||
pub(crate) struct State {
|
pub(crate) struct State {
|
||||||
/// 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
|
||||||
/// parmaeter automation though, depending on how the host impelments that.
|
/// parmaeter automation though, depending on how the host impelments that.
|
||||||
pub params: HashMap<&'static str, ParamValue>,
|
pub params: HashMap<String, ParamValue>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ use vst3_sys::vst::{
|
||||||
use vst3_sys::{ComPtr, VST3};
|
use vst3_sys::{ComPtr, VST3};
|
||||||
use widestring::U16CStr;
|
use widestring::U16CStr;
|
||||||
|
|
||||||
use crate::params::ParamPtr;
|
use crate::params::{Param, ParamPtr};
|
||||||
use crate::plugin::{BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin};
|
use crate::plugin::{BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin};
|
||||||
use crate::wrapper::state::{ParamValue, State};
|
use crate::wrapper::state::{ParamValue, State};
|
||||||
use crate::wrapper::util::{hash_param_id, strlcpy, u16strlcpy};
|
use crate::wrapper::util::{hash_param_id, strlcpy, u16strlcpy};
|
||||||
|
@ -103,7 +103,9 @@ pub(crate) struct Wrapper<'a, P: Plugin> {
|
||||||
param_defaults_normalized: Vec<f32>,
|
param_defaults_normalized: Vec<f32>,
|
||||||
/// Mappings from parameter hashes back to string parameter indentifiers. Useful for debug
|
/// Mappings from parameter hashes back to string parameter indentifiers. Useful for debug
|
||||||
/// logging and when handling plugin state.
|
/// logging and when handling plugin state.
|
||||||
param_id_hashes: HashMap<u32, &'static str>,
|
param_id_to_hash: HashMap<u32, &'static str>,
|
||||||
|
/// The inverse mapping from `param_id_hashes`. Used only for restoring state.
|
||||||
|
param_hash_to_id: HashMap<&'static str, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Plugin> Wrapper<'_, P> {
|
impl<P: Plugin> Wrapper<'_, P> {
|
||||||
|
@ -125,7 +127,8 @@ impl<P: Plugin> Wrapper<'_, P> {
|
||||||
HashMap::new(), // param_by_hash
|
HashMap::new(), // param_by_hash
|
||||||
Vec::new(), // param_hashes
|
Vec::new(), // param_hashes
|
||||||
Vec::new(), // param_defaults_normalized
|
Vec::new(), // param_defaults_normalized
|
||||||
HashMap::new(), // param_id_hashes
|
HashMap::new(), // param_id_to_hash
|
||||||
|
HashMap::new(), // param_hash_to_id
|
||||||
);
|
);
|
||||||
|
|
||||||
// This is a mapping from the parameter IDs specified by the plugin to pointers to thsoe
|
// This is a mapping from the parameter IDs specified by the plugin to pointers to thsoe
|
||||||
|
@ -147,10 +150,15 @@ impl<P: Plugin> Wrapper<'_, P> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|hash| unsafe { wrapper.param_by_hash[hash].normalized_value() })
|
.map(|hash| unsafe { wrapper.param_by_hash[hash].normalized_value() })
|
||||||
.collect();
|
.collect();
|
||||||
wrapper.param_id_hashes = param_map
|
wrapper.param_id_to_hash = param_map
|
||||||
.into_keys()
|
.into_keys()
|
||||||
.map(|id| (hash_param_id(id), id))
|
.map(|id| (hash_param_id(id), id))
|
||||||
.collect();
|
.collect();
|
||||||
|
wrapper.param_hash_to_id = wrapper
|
||||||
|
.param_id_to_hash
|
||||||
|
.iter()
|
||||||
|
.map(|(&hash, &id)| (id, hash))
|
||||||
|
.collect();
|
||||||
|
|
||||||
wrapper
|
wrapper
|
||||||
}
|
}
|
||||||
|
@ -287,9 +295,78 @@ impl<P: Plugin> IComponent for Wrapper<'_, P> {
|
||||||
kResultOk
|
kResultOk
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn set_state(&self, _state: *mut c_void) -> tresult {
|
unsafe fn set_state(&self, state: *mut c_void) -> tresult {
|
||||||
// TODO: Implemnt state saving and restoring
|
check_null_ptr!(state);
|
||||||
kResultFalse
|
|
||||||
|
let state: ComPtr<dyn IBStream> = ComPtr::new(state as *mut _);
|
||||||
|
|
||||||
|
// We need to know how large the state is before we can read it. The current position can be
|
||||||
|
// zero, but it can also be something else. Bitwig prepends the preset header in the stream,
|
||||||
|
// while some other hosts don't expose that to the plugin.
|
||||||
|
let mut current_pos = 0;
|
||||||
|
let mut eof_pos = 0;
|
||||||
|
if state.tell(&mut current_pos) != kResultOk
|
||||||
|
|| state.seek(0, vst3_sys::base::kIBSeekEnd, &mut eof_pos) != kResultOk
|
||||||
|
|| state.seek(current_pos, vst3_sys::base::kIBSeekSet, ptr::null_mut()) != kResultOk
|
||||||
|
{
|
||||||
|
nih_debug_assert_failure!("Could not get the stream length");
|
||||||
|
return kResultFalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream_byte_size = (eof_pos - current_pos) as i32;
|
||||||
|
let mut num_bytes_read = 0;
|
||||||
|
let mut read_buffer: Vec<u8> = Vec::with_capacity(stream_byte_size as usize);
|
||||||
|
state.read(
|
||||||
|
read_buffer.as_mut_ptr() as *mut c_void,
|
||||||
|
read_buffer.capacity() as i32,
|
||||||
|
&mut num_bytes_read,
|
||||||
|
);
|
||||||
|
read_buffer.set_len(num_bytes_read as usize);
|
||||||
|
|
||||||
|
// If the size is zero, some hsots will always return `kResultFalse` even if the read was
|
||||||
|
// 'successful', so we can't check the return value but we can check the number of bytes
|
||||||
|
// read.
|
||||||
|
if read_buffer.len() != stream_byte_size as usize {
|
||||||
|
nih_debug_assert_failure!("Unexpected stream length");
|
||||||
|
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);
|
||||||
|
return kResultFalse;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (param_id_str, param_value) in state.params {
|
||||||
|
let param_ptr = match self
|
||||||
|
.param_hash_to_id
|
||||||
|
.get(param_id_str.as_str())
|
||||||
|
.and_then(|hash| self.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),
|
||||||
|
(param_ptr, param_value) => {
|
||||||
|
nih_debug_assert_failure!(
|
||||||
|
"Invalid serialized value {:?} for parameter {} ({:?})",
|
||||||
|
param_value,
|
||||||
|
param_id_str,
|
||||||
|
param_ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kResultOk
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_state(&self, state: *mut c_void) -> tresult {
|
unsafe fn get_state(&self, state: *mut c_void) -> tresult {
|
||||||
|
@ -299,15 +376,15 @@ impl<P: Plugin> IComponent for Wrapper<'_, P> {
|
||||||
|
|
||||||
// We'll serialize parmaeter values as a simple `string_param_id: display_value` map.
|
// We'll serialize parmaeter values as a simple `string_param_id: display_value` map.
|
||||||
let params = self
|
let params = self
|
||||||
.param_id_hashes
|
.param_id_to_hash
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(hash, param_id_str)| {
|
.filter_map(|(hash, param_id_str)| {
|
||||||
let param_ptr = self.param_by_hash.get(hash)?;
|
let param_ptr = self.param_by_hash.get(hash)?;
|
||||||
Some((param_id_str, param_ptr))
|
Some((param_id_str, param_ptr))
|
||||||
})
|
})
|
||||||
.map(|(¶m_id_str, ¶m_ptr)| match param_ptr {
|
.map(|(¶m_id_str, ¶m_ptr)| match param_ptr {
|
||||||
ParamPtr::FloatParam(p) => (param_id_str, ParamValue::F32((*p).value)),
|
ParamPtr::FloatParam(p) => (param_id_str.to_string(), ParamValue::F32((*p).value)),
|
||||||
ParamPtr::IntParam(p) => (param_id_str, ParamValue::I32((*p).value)),
|
ParamPtr::IntParam(p) => (param_id_str.to_string(), ParamValue::I32((*p).value)),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let plugin_state = State { params };
|
let plugin_state = State { params };
|
||||||
|
|
Loading…
Add table
Reference in a new issue