1
0
Fork 0

Don't internally use param string IDs in wrapper

This avoids a layer of indirection for every parameter lookup using a
hash.
This commit is contained in:
Robbert van der Helm 2022-01-27 22:31:53 +01:00
parent 217b28fdca
commit 91e20f1230
2 changed files with 41 additions and 46 deletions

View file

@ -185,7 +185,7 @@ pub trait Params {
} }
/// Internal pointers to parameters. This is an implementation detail used by the wrappers. /// Internal pointers to parameters. This is an implementation detail used by the wrappers.
#[derive(Debug)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ParamPtr { pub enum ParamPtr {
FloatParam(*mut FloatParam), FloatParam(*mut FloatParam),
IntParam(*mut IntParam), IntParam(*mut IntParam),

View file

@ -55,20 +55,18 @@ pub struct Wrapper<P: Plugin> {
/// trait. /// trait.
bypass_state: Cell<bool>, bypass_state: Cell<bool>,
/// A mapping from parameter IDs to pointers to parameters belonging to the plugin. As long as /// A mapping from parameter ID hashes (obtained from the string parameter IDs) to pointers to
/// `plugin` does not get recreated, these addresses will remain stable, as they are obtained /// parameters belonging to the plugin. As long as `plugin` does not get recreated, these
/// from a pinned object. /// addresses will remain stable, as they are obtained from a pinned object.
param_map: HashMap<&'static str, ParamPtr>, param_by_hash: HashMap<u32, ParamPtr>,
/// The keys from `param_map` in a stable order. /// The keys from `param_map` in a stable order.
param_ids: Vec<&'static str>, param_hashes: Vec<u32>,
/// Mappings from parameter hashes back to string parameter indentifiers.
///
/// TODO: To avoid needing two pointer dereferences each time, maybe restructure `param_map` in
/// this wrapper to be indexed by the numerical hash.
param_id_hashes: HashMap<u32, &'static str>,
/// The default normalized parameter value for every parameter in `param_ids`. We need to store /// The default normalized parameter value for every parameter in `param_ids`. We need to store
/// this in case the host requeries the parmaeter later. /// this in case the host requeries the parmaeter later.
param_defaults_normalized: Vec<f32>, param_defaults_normalized: Vec<f32>,
/// Mappings from parameter hashes back to string parameter indentifiers. Useful for debug
/// logging and when handling plugin state.
param_id_hashes: HashMap<u32, &'static str>,
/// The current bus configuration, modified through `IAudioProcessor::setBusArrangements()`. /// The current bus configuration, modified through `IAudioProcessor::setBusArrangements()`.
current_bus_config: RefCell<BusConfig>, current_bus_config: RefCell<BusConfig>,
@ -79,10 +77,10 @@ impl<P: Plugin> Wrapper<P> {
let mut wrapper = Self::allocate( let mut wrapper = Self::allocate(
P::default(), // plugin P::default(), // plugin
Cell::new(false), // bypass_state Cell::new(false), // bypass_state
HashMap::new(), // param_map HashMap::new(), // param_by_hash
Vec::new(), // param_ids Vec::new(), // param_hashes
HashMap::new(), // param_id_hashes
Vec::new(), // param_defaults_normalized Vec::new(), // param_defaults_normalized
HashMap::new(), // param_id_hashes
// Some hosts, like the current version of Bitwig and Ardour at the time of writing, // Some hosts, like the current version of Bitwig and Ardour at the time of writing,
// will try using the plugin's default not yet initialized bus arrangement. Because of // will try using the plugin's default not yet initialized bus arrangement. Because of
// that, we'll always initialize this configuration even before the host requests a // that, we'll always initialize this configuration even before the host requests a
@ -96,24 +94,27 @@ impl<P: Plugin> Wrapper<P> {
// 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
// parameters. Since the object returned by `params()` is pinned, these pointers are safe to // parameters. Since the object returned by `params()` is pinned, these pointers are safe to
// dereference as long as `wrapper.plugin` is alive // dereference as long as `wrapper.plugin` is alive
wrapper.param_map = wrapper.plugin.params().param_map(); let param_map = wrapper.plugin.params().param_map();
wrapper.param_ids = wrapper.param_map.keys().copied().collect();
wrapper.param_defaults_normalized = wrapper
.param_ids
.iter()
.map(|id| unsafe { wrapper.param_map[id].normalized_value() })
.collect();
wrapper.param_id_hashes = wrapper
.param_ids
.iter()
.map(|id| (hash_param_id(id), *id))
.collect();
nih_debug_assert!( nih_debug_assert!(
!wrapper.param_map.contains_key(BYPASS_PARAM_ID), !param_map.contains_key(BYPASS_PARAM_ID),
"The wrapper alread yadds its own bypass parameter" "The wrapper alread yadds its own bypass parameter"
); );
wrapper.param_by_hash = param_map
.iter()
.map(|(id, p)| (hash_param_id(id), *p))
.collect();
wrapper.param_hashes = wrapper.param_by_hash.keys().copied().collect();
wrapper.param_defaults_normalized = wrapper
.param_hashes
.iter()
.map(|hash| unsafe { wrapper.param_by_hash[hash].normalized_value() })
.collect();
wrapper.param_id_hashes = param_map
.into_keys()
.map(|id| (hash_param_id(id), id))
.collect();
wrapper wrapper
} }
} }
@ -264,7 +265,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
unsafe fn get_parameter_count(&self) -> i32 { unsafe fn get_parameter_count(&self) -> i32 {
// NOTE: We add a bypass parameter ourselves on index `self.param_ids.len()`, so these // NOTE: We add a bypass parameter ourselves on index `self.param_ids.len()`, so these
// indices are all off by one // indices are all off by one
self.param_ids.len() as i32 + 1 self.param_hashes.len() as i32 + 1
} }
unsafe fn get_parameter_info( unsafe fn get_parameter_info(
@ -273,14 +274,14 @@ impl<P: Plugin> IEditController for Wrapper<P> {
info: *mut vst3_sys::vst::ParameterInfo, info: *mut vst3_sys::vst::ParameterInfo,
) -> tresult { ) -> tresult {
// Parameter index `self.param_ids.len()` is our own bypass parameter // Parameter index `self.param_ids.len()` is our own bypass parameter
if param_index < 0 || param_index > self.param_ids.len() as i32 { if param_index < 0 || param_index > self.param_hashes.len() as i32 {
return kInvalidArgument; return kInvalidArgument;
} }
*info = std::mem::zeroed(); *info = std::mem::zeroed();
let info = &mut *info; let info = &mut *info;
if param_index == self.param_ids.len() as i32 { if param_index == self.param_hashes.len() as i32 {
info.id = hash_param_id(BYPASS_PARAM_ID); info.id = hash_param_id(BYPASS_PARAM_ID);
u16strlcpy(&mut info.title, "Bypass"); u16strlcpy(&mut info.title, "Bypass");
u16strlcpy(&mut info.short_title, "Bypass"); u16strlcpy(&mut info.short_title, "Bypass");
@ -291,11 +292,11 @@ impl<P: Plugin> IEditController for Wrapper<P> {
info.flags = vst3_sys::vst::ParameterFlags::kCanAutomate as i32 info.flags = vst3_sys::vst::ParameterFlags::kCanAutomate as i32
| vst3_sys::vst::ParameterFlags::kIsBypass as i32; | vst3_sys::vst::ParameterFlags::kIsBypass as i32;
} else { } else {
let param_id = &self.param_ids[param_index as usize]; let param_hash = &self.param_hashes[param_index as usize];
let default_value = &self.param_defaults_normalized[param_index as usize]; let default_value = &self.param_defaults_normalized[param_index as usize];
let param_ptr = &self.param_map[param_id]; let param_ptr = &self.param_by_hash[param_hash];
info.id = hash_param_id(param_id); info.id = *param_hash;
u16strlcpy(&mut info.title, param_ptr.name()); u16strlcpy(&mut info.title, param_ptr.name());
u16strlcpy(&mut info.short_title, param_ptr.name()); u16strlcpy(&mut info.short_title, param_ptr.name());
u16strlcpy(&mut info.units, param_ptr.unit()); u16strlcpy(&mut info.units, param_ptr.unit());
@ -326,8 +327,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
} }
kResultOk kResultOk
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
u16strlcpy( u16strlcpy(
dest, dest,
&param_ptr.normalized_value_to_string(value_normalized as f32), &param_ptr.normalized_value_to_string(value_normalized as f32),
@ -359,8 +359,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
*value_normalized = value; *value_normalized = value;
kResultOk kResultOk
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
let value = match param_ptr.string_to_normalized_value(&string) { let value = match param_ptr.string_to_normalized_value(&string) {
Some(v) => v as f64, Some(v) => v as f64,
None => return kResultFalse, None => return kResultFalse,
@ -376,8 +375,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
unsafe fn normalized_param_to_plain(&self, id: u32, value_normalized: f64) -> f64 { unsafe fn normalized_param_to_plain(&self, id: u32, value_normalized: f64) -> f64 {
if id == *BYPASS_PARAM_HASH { if id == *BYPASS_PARAM_HASH {
value_normalized value_normalized
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
param_ptr.preview_unnormalized(value_normalized as f32) as f64 param_ptr.preview_unnormalized(value_normalized as f32) as f64
} else { } else {
0.5 0.5
@ -387,8 +385,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
unsafe fn plain_param_to_normalized(&self, id: u32, plain_value: f64) -> f64 { unsafe fn plain_param_to_normalized(&self, id: u32, plain_value: f64) -> f64 {
if id == *BYPASS_PARAM_HASH { if id == *BYPASS_PARAM_HASH {
plain_value.clamp(0.0, 1.0) plain_value.clamp(0.0, 1.0)
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
param_ptr.preview_normalized(plain_value as f32) as f64 param_ptr.preview_normalized(plain_value as f32) as f64
} else { } else {
0.5 0.5
@ -402,8 +399,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
} else { } else {
0.0 0.0
} }
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
param_ptr.normalized_value() as f64 param_ptr.normalized_value() as f64
} else { } else {
0.5 0.5
@ -415,8 +411,7 @@ impl<P: Plugin> IEditController for Wrapper<P> {
self.bypass_state.set(value >= 0.5); self.bypass_state.set(value >= 0.5);
kResultOk kResultOk
} else if let Some(param_id) = self.param_id_hashes.get(&id) { } else if let Some(param_ptr) = self.param_by_hash.get(&id) {
let param_ptr = &self.param_map[param_id];
param_ptr.set_normalized_value(value as f32); param_ptr.set_normalized_value(value as f32);
kResultOk kResultOk