1
0
Fork 0

Add parameter getters for unmodulated values

This is needed to be able to support modulation events in CLAP. There an
offset gets applied to the parameter's actual current value. That way
GUIs don't have to move around when parameters are being modulated and
save states cannot get influenced by modulation.
This commit is contained in:
Robbert van der Helm 2022-05-01 18:30:30 +02:00
parent 6451555f85
commit a17c63bf71
7 changed files with 150 additions and 16 deletions

View file

@ -62,6 +62,20 @@ pub trait Param: Display {
/// Get the normalized `[0, 1]` value for this parameter. /// Get the normalized `[0, 1]` value for this parameter.
fn normalized_value(&self) -> f32; fn normalized_value(&self) -> f32;
/// Get the unnormalized value for this parameter before any (monophonic) modulation coming from
/// the host has been applied. If the host is not currently modulating this parameter than this
/// will be the same as [`plain_value()`][Self::plain_value()]. This may be useful for
/// displaying modulation differently in plugin GUIs. Right now only CLAP plugins in Bitwig
/// Studio use modulation.
fn unmodulated_plain_value(&self) -> Self::Plain;
/// Get the normalized `[0, 1]` value for this parameter before any (monophonic) modulation
/// coming from the host has been applied. If the host is not currently modulating this
/// parameter than this will be the same as [`plain_value()`][Self::plain_value()]. This may be
/// useful for displaying modulation differently in plugin GUIs. Right now only CLAP plugins in
/// Bitwig Studio use modulation.
fn unmodulated_normalized_value(&self) -> f32;
/// Get the unnormalized default value for this parameter. /// Get the unnormalized default value for this parameter.
fn default_plain_value(&self) -> Self::Plain; fn default_plain_value(&self) -> Self::Plain;

View file

@ -13,6 +13,12 @@ pub struct BoolParam {
pub value: bool, pub value: bool,
/// The field's current value normalized to the `[0, 1]` range. /// The field's current value normalized to the `[0, 1]` range.
normalized_value: f32, normalized_value: f32,
/// The field's value before any monophonic automation coming from the host has been applied.
/// This will always be the same as `value` for VST3 plugins.
unmodulated_value: bool,
/// The field's value normalized to the `[0, 1]` range before any monophonic automation coming
/// from the host has been applied. This will always be the same as `value` for VST3 plugins.
unmodulated_normalized_value: f32,
/// The field's default value. /// The field's default value.
default: bool, default: bool,
@ -65,6 +71,16 @@ impl Param for BoolParam {
self.normalized_value self.normalized_value
} }
#[inline]
fn unmodulated_plain_value(&self) -> Self::Plain {
self.unmodulated_value
}
#[inline]
fn unmodulated_normalized_value(&self) -> f32 {
self.unmodulated_normalized_value
}
#[inline] #[inline]
fn default_plain_value(&self) -> Self::Plain { fn default_plain_value(&self) -> Self::Plain {
self.default self.default
@ -83,16 +99,20 @@ impl Param for BoolParam {
} }
fn set_plain_value(&mut self, plain: Self::Plain) { fn set_plain_value(&mut self, plain: Self::Plain) {
self.value = plain; self.unmodulated_value = plain;
self.normalized_value = self.preview_normalized(plain); self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
} }
fn set_normalized_value(&mut self, normalized: f32) { fn set_normalized_value(&mut self, normalized: f32) {
self.value = self.preview_plain(normalized); self.unmodulated_value = self.preview_plain(normalized);
self.normalized_value = normalized; self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
@ -151,6 +171,8 @@ impl BoolParam {
Self { Self {
value: default, value: default,
normalized_value: if default { 1.0 } else { 0.0 }, normalized_value: if default { 1.0 } else { 0.0 },
unmodulated_value: default,
unmodulated_normalized_value: if default { 1.0 } else { 0.0 },
default, default,
flags: ParamFlags::default(), flags: ParamFlags::default(),

View file

@ -98,6 +98,16 @@ impl<T: Enum + PartialEq> Param for EnumParam<T> {
self.inner.normalized_value() self.inner.normalized_value()
} }
#[inline]
fn unmodulated_plain_value(&self) -> Self::Plain {
T::from_index(self.inner.unmodulated_plain_value() as usize)
}
#[inline]
fn unmodulated_normalized_value(&self) -> f32 {
self.inner.unmodulated_normalized_value()
}
#[inline] #[inline]
fn default_plain_value(&self) -> Self::Plain { fn default_plain_value(&self) -> Self::Plain {
T::from_index(self.inner.default_plain_value() as usize) T::from_index(self.inner.default_plain_value() as usize)
@ -183,6 +193,16 @@ impl Param for EnumParamInner {
self.inner.default_plain_value() self.inner.default_plain_value()
} }
#[inline]
fn unmodulated_plain_value(&self) -> Self::Plain {
self.inner.unmodulated_plain_value()
}
#[inline]
fn unmodulated_normalized_value(&self) -> f32 {
self.inner.unmodulated_normalized_value()
}
fn step_count(&self) -> Option<usize> { fn step_count(&self) -> Option<usize> {
Some(self.len() - 1) Some(self.len() - 1)
} }

View file

@ -25,6 +25,12 @@ pub struct FloatParam {
pub value: f32, pub value: f32,
/// The field's current value normalized to the `[0, 1]` range. /// The field's current value normalized to the `[0, 1]` range.
normalized_value: f32, normalized_value: f32,
/// The field's plain, unnormalized value before any monophonic automation coming from the host
/// has been applied. This will always be the same as `value` for VST3 plugins.
unmodulated_value: f32,
/// The field's value normalized to the `[0, 1]` range before any monophonic automation coming
/// from the host has been applied. This will always be the same as `value` for VST3 plugins.
unmodulated_normalized_value: f32,
/// The field's default plain, unnormalized value. /// The field's default plain, unnormalized value.
default: f32, default: f32,
/// An optional smoother that will automatically interpolate between the new automation values /// An optional smoother that will automatically interpolate between the new automation values
@ -99,6 +105,16 @@ impl Param for FloatParam {
self.normalized_value self.normalized_value
} }
#[inline]
fn unmodulated_plain_value(&self) -> Self::Plain {
self.unmodulated_value
}
#[inline]
fn unmodulated_normalized_value(&self) -> f32 {
self.unmodulated_normalized_value
}
#[inline] #[inline]
fn default_plain_value(&self) -> Self::Plain { fn default_plain_value(&self) -> Self::Plain {
self.default self.default
@ -132,16 +148,20 @@ impl Param for FloatParam {
} }
fn set_plain_value(&mut self, plain: Self::Plain) { fn set_plain_value(&mut self, plain: Self::Plain) {
self.value = plain; self.unmodulated_value = plain;
self.normalized_value = self.preview_normalized(plain); self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
} }
fn set_normalized_value(&mut self, normalized: f32) { fn set_normalized_value(&mut self, normalized: f32) {
self.value = self.preview_plain(normalized); self.unmodulated_value = self.preview_plain(normalized);
self.normalized_value = normalized; self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
@ -215,6 +235,8 @@ impl FloatParam {
Self { Self {
value: default, value: default,
normalized_value: range.normalize(default), normalized_value: range.normalize(default),
unmodulated_value: default,
unmodulated_normalized_value: range.normalize(default),
default, default,
smoothed: Smoother::none(), smoothed: Smoother::none(),

View file

@ -25,6 +25,12 @@ pub struct IntParam {
pub value: i32, pub value: i32,
/// The field's current value normalized to the `[0, 1]` range. /// The field's current value normalized to the `[0, 1]` range.
normalized_value: f32, normalized_value: f32,
/// The field's plain, unnormalized value before any monophonic automation coming from the host
/// has been applied. This will always be the same as `value` for VST3 plugins.
unmodulated_value: i32,
/// The field's value normalized to the `[0, 1]` range before any monophonic automation coming
/// from the host has been applied. This will always be the same as `value` for VST3 plugins.
unmodulated_normalized_value: f32,
/// The field's default plain, unnormalized value. /// The field's default plain, unnormalized value.
default: i32, default: i32,
/// An optional smoother that will automatically interpolate between the new automation values /// An optional smoother that will automatically interpolate between the new automation values
@ -91,6 +97,16 @@ impl Param for IntParam {
self.normalized_value self.normalized_value
} }
#[inline]
fn unmodulated_plain_value(&self) -> Self::Plain {
self.unmodulated_value
}
#[inline]
fn unmodulated_normalized_value(&self) -> f32 {
self.unmodulated_normalized_value
}
#[inline] #[inline]
fn default_plain_value(&self) -> Self::Plain { fn default_plain_value(&self) -> Self::Plain {
self.default self.default
@ -109,16 +125,20 @@ impl Param for IntParam {
} }
fn set_plain_value(&mut self, plain: Self::Plain) { fn set_plain_value(&mut self, plain: Self::Plain) {
self.value = plain; self.unmodulated_value = plain;
self.normalized_value = self.preview_normalized(plain); self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
} }
fn set_normalized_value(&mut self, normalized: f32) { fn set_normalized_value(&mut self, normalized: f32) {
self.value = self.preview_plain(normalized); self.unmodulated_value = self.preview_plain(normalized);
self.normalized_value = normalized; self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if let Some(f) = &self.value_changed { if let Some(f) = &self.value_changed {
f(self.value); f(self.value);
} }
@ -180,6 +200,8 @@ impl IntParam {
Self { Self {
value: default, value: default,
normalized_value: range.normalize(default), normalized_value: range.normalize(default),
unmodulated_value: default,
unmodulated_normalized_value: range.normalize(default),
default, default,
smoothed: Smoother::none(), smoothed: Smoother::none(),

View file

@ -149,6 +149,7 @@ impl ParamPtr {
param_ptr_forward!(pub unsafe fn name(&self) -> &str); param_ptr_forward!(pub unsafe fn name(&self) -> &str);
param_ptr_forward!(pub unsafe fn unit(&self) -> &'static str); param_ptr_forward!(pub unsafe fn unit(&self) -> &'static str);
param_ptr_forward!(pub unsafe fn normalized_value(&self) -> f32); param_ptr_forward!(pub unsafe fn normalized_value(&self) -> f32);
param_ptr_forward!(pub unsafe fn unmodulated_normalized_value(&self) -> f32);
param_ptr_forward!(pub unsafe fn default_normalized_value(&self) -> f32); param_ptr_forward!(pub unsafe fn default_normalized_value(&self) -> f32);
param_ptr_forward!(pub unsafe fn step_count(&self) -> Option<usize>); param_ptr_forward!(pub unsafe fn step_count(&self) -> Option<usize>);
param_ptr_forward!(pub unsafe fn previous_normalized_step(&self, from: f32) -> f32); param_ptr_forward!(pub unsafe fn previous_normalized_step(&self, from: f32) -> f32);
@ -181,6 +182,28 @@ impl ParamPtr {
} }
} }
/// Get the parameter's plain, unnormalized value, converted to a float, before any monophonic
/// host modulation has been applied. This is useful for handling modulated parameters for CLAP
/// plugins in Bitwig in a way where the actual parameter does not move in the GUI while the
/// parameter is being modulated. You can also use this to show the difference between the
/// unmodulated value and the current value. Useful in conjunction with
/// [`preview_plain()`][Self::preview_plain()] to compare a snapped discrete value to a
/// parameter's current snapped value without having to do a back and forth conversion using
/// normalized values.
///
/// # Safety
///
/// Calling this function is only safe as long as the object this `ParamPtr` was created for is
/// still alive.
pub unsafe fn unmodulated_plain_value(&self) -> f32 {
match &self {
ParamPtr::FloatParam(p) => (**p).unmodulated_plain_value(),
ParamPtr::IntParam(p) => (**p).unmodulated_plain_value() as f32,
ParamPtr::BoolParam(p) => (**p).unmodulated_normalized_value(),
ParamPtr::EnumParam(p) => (**p).unmodulated_plain_value() as f32,
}
}
/// Get the parameter's default value as a plain, unnormalized value, converted to a float. /// Get the parameter's default value as a plain, unnormalized value, converted to a float.
/// ///
/// # Safety /// # Safety

View file

@ -73,17 +73,28 @@ pub(crate) unsafe fn serialize_object<'a>(
params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>, params_iter: impl IntoIterator<Item = (&'a String, ParamPtr)>,
) -> PluginState { ) -> PluginState {
// We'll serialize parameter values as a simple `string_param_id: display_value` map. // We'll serialize parameter values as a simple `string_param_id: display_value` map.
// NOTE: If the plugin is being modulated (and the plugin is a CLAP plugin in Bitwig Studio),
// then this should save the values without any modulation applied to it
let params: HashMap<_, _> = params_iter let params: HashMap<_, _> = params_iter
.into_iter() .into_iter()
.map(|(param_id_str, param_ptr)| match param_ptr { .map(|(param_id_str, param_ptr)| match param_ptr {
ParamPtr::FloatParam(p) => (param_id_str.clone(), ParamValue::F32((*p).plain_value())), ParamPtr::FloatParam(p) => (
ParamPtr::IntParam(p) => (param_id_str.clone(), ParamValue::I32((*p).plain_value())), param_id_str.clone(),
ParamPtr::BoolParam(p) => (param_id_str.clone(), ParamValue::Bool((*p).plain_value())), ParamValue::F32((*p).unmodulated_plain_value()),
),
ParamPtr::IntParam(p) => (
param_id_str.clone(),
ParamValue::I32((*p).unmodulated_plain_value()),
),
ParamPtr::BoolParam(p) => (
param_id_str.clone(),
ParamValue::Bool((*p).unmodulated_plain_value()),
),
ParamPtr::EnumParam(p) => ( ParamPtr::EnumParam(p) => (
// Enums are serialized based on the active variant's index (which may not be // Enums are serialized based on the active variant's index (which may not be
// the same as the discriminator) // the same as the discriminator)
param_id_str.clone(), param_id_str.clone(),
ParamValue::I32((*p).plain_value()), ParamValue::I32((*p).unmodulated_plain_value()),
), ),
}) })
.collect(); .collect();