1
0
Fork 0

Make parameter modulation stick after automation

This is how it's supposed to be implemented in CLAP.
This commit is contained in:
Robbert van der Helm 2022-05-02 15:46:57 +02:00
parent 10ae8e1d05
commit 06c5e4b04a
5 changed files with 82 additions and 38 deletions

View file

@ -145,20 +145,25 @@ pub trait Param: Display {
/// Contains the setters for parameters. These should not be exposed to plugins to avoid confusion.
pub(crate) trait ParamMut: Param {
/// Set this parameter based on a plain, unnormalized value. This does not snap to step sizes
/// for continuous parameters (i.e. [`FloatParam`]).
/// for continuous parameters (i.e. [`FloatParam`]). If
/// [`modulate_value()`][Self::modulate_value()] has previously been called with a non zero
/// value then this offset is taken into account to form the effective value.
///
/// This does **not** update the smoother.
fn set_plain_value(&mut self, plain: Self::Plain);
/// Set this parameter based on a normalized value. The normalized value will be snapped to the
/// step size for continuous parameters (i.e. [`FloatParam`]).
/// step size for continuous parameters (i.e. [`FloatParam`]). If
/// [`modulate_value()`][Self::modulate_value()] has previously been called with a non zero
/// value then this offset is taken into account to form the effective value.
///
/// This does **not** update the smoother.
fn set_normalized_value(&mut self, normalized: f32);
/// Add a modulation offset to the value's unmodulated value. Out of bound values will be
/// clamped to the parameter's range. The normalized value will be snapped to the step size for
/// continuous parameters (i.e. [`FloatParam`]).
/// Add a modulation offset to the value's unmodulated value. This value sticks until this
/// function is called again with a 0.0 value. Out of bound values will be clamped to the
/// parameter's range. The normalized value will be snapped to the step size for continuous
/// parameters (i.e. [`FloatParam`]).
///
/// This does **not** update the smoother.
fn modulate_value(&mut self, modulation_offset: f32);

View file

@ -19,6 +19,10 @@ pub struct BoolParam {
/// 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,
/// A value in `[-1, 1]` indicating the amount of modulation applied to
/// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are
/// clamped, and this value persists after new automation events.
modulation_offset: f32,
/// The field's default value.
default: bool,
@ -144,8 +148,14 @@ impl ParamMut for BoolParam {
fn set_plain_value(&mut self, plain: Self::Plain) {
self.unmodulated_value = plain;
self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
@ -154,20 +164,22 @@ impl ParamMut for BoolParam {
fn set_normalized_value(&mut self, normalized: f32) {
self.unmodulated_value = self.preview_plain(normalized);
self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
}
fn modulate_value(&mut self, modulation_offset: f32) {
self.normalized_value =
(self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
if let Some(f) = &self.value_changed {
f(self.value);
}
self.modulation_offset = modulation_offset;
self.set_normalized_value(self.unmodulated_normalized_value);
}
fn update_smoother(&mut self, _sample_rate: f32, _init: bool) {
@ -184,6 +196,7 @@ impl BoolParam {
normalized_value: if default { 1.0 } else { 0.0 },
unmodulated_value: default,
unmodulated_normalized_value: if default { 1.0 } else { 0.0 },
modulation_offset: 0.0,
default,
flags: ParamFlags::default(),

View file

@ -31,6 +31,10 @@ pub struct FloatParam {
/// 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,
/// A value in `[-1, 1]` indicating the amount of modulation applied to
/// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are
/// clamped, and this value persists after new automation events.
modulation_offset: f32,
/// The field's default plain, unnormalized value.
default: f32,
/// An optional smoother that will automatically interpolate between the new automation values
@ -204,8 +208,14 @@ impl ParamMut for FloatParam {
fn set_plain_value(&mut self, plain: Self::Plain) {
self.unmodulated_value = plain;
self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
@ -214,20 +224,22 @@ impl ParamMut for FloatParam {
fn set_normalized_value(&mut self, normalized: f32) {
self.unmodulated_value = self.preview_plain(normalized);
self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
}
fn modulate_value(&mut self, modulation_offset: f32) {
self.normalized_value =
(self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
if let Some(f) = &self.value_changed {
f(self.value);
}
self.modulation_offset = modulation_offset;
self.set_normalized_value(self.unmodulated_normalized_value);
}
fn update_smoother(&mut self, sample_rate: f32, reset: bool) {
@ -248,6 +260,7 @@ impl FloatParam {
normalized_value: range.normalize(default),
unmodulated_value: default,
unmodulated_normalized_value: range.normalize(default),
modulation_offset: 0.0,
default,
smoothed: Smoother::none(),

View file

@ -31,6 +31,10 @@ pub struct IntParam {
/// 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,
/// A value in `[-1, 1]` indicating the amount of modulation applied to
/// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are
/// clamped, and this value persists after new automation events.
modulation_offset: f32,
/// The field's default plain, unnormalized value.
default: i32,
/// An optional smoother that will automatically interpolate between the new automation values
@ -169,8 +173,14 @@ impl ParamMut for IntParam {
fn set_plain_value(&mut self, plain: Self::Plain) {
self.unmodulated_value = plain;
self.unmodulated_normalized_value = self.preview_normalized(plain);
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
@ -179,20 +189,22 @@ impl ParamMut for IntParam {
fn set_normalized_value(&mut self, normalized: f32) {
self.unmodulated_value = self.preview_plain(normalized);
self.unmodulated_normalized_value = normalized;
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
if self.modulation_offset == 0.0 {
self.value = self.unmodulated_value;
self.normalized_value = self.unmodulated_normalized_value;
} else {
self.normalized_value =
(self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
}
if let Some(f) = &self.value_changed {
f(self.value);
}
}
fn modulate_value(&mut self, modulation_offset: f32) {
self.normalized_value =
(self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0);
self.value = self.preview_plain(self.normalized_value);
if let Some(f) = &self.value_changed {
f(self.value);
}
self.modulation_offset = modulation_offset;
self.set_normalized_value(self.unmodulated_normalized_value);
}
fn update_smoother(&mut self, sample_rate: f32, reset: bool) {
@ -213,6 +225,7 @@ impl IntParam {
normalized_value: range.normalize(default),
unmodulated_value: default,
unmodulated_normalized_value: range.normalize(default),
modulation_offset: 0.0,
default,
smoothed: Smoother::none(),

View file

@ -249,9 +249,9 @@ pub enum ClapParamUpdate {
/// Set the parameter to this plain value. In our wrapper the plain values are the normalized
/// values multiplied by the step count for discrete parameters.
PlainValueSet(f64),
/// Set a normalized offset for the parameter's plain value. A `PlainValueSet` clears out any
/// modulation, and subsequent modulation events replace the previous one. In other words, this
/// is not additive. These values should also be divided by the step size.
/// Set a normalized offset for the parameter's plain value. Subsequent modulation events
/// override the previous one, but `PlainValueSet`s do not override the existing modulation.
/// These values should also be divided by the step size.
PlainValueMod(f64),
}