From 992fcfe9699dc606ffe5184d8210736175715263 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 5 Jul 2022 19:39:18 +0200 Subject: [PATCH] Add polyphonic modulation IDs to parameters In a bit we should be able to use this with a new `PolyModulation` note event to allow polyphonic modulation. --- src/param.rs | 15 +++++++++++++++ src/param/boolean.rs | 26 ++++++++++++++++++++++++++ src/param/enums.rs | 23 +++++++++++++++++++++++ src/param/float.rs | 25 +++++++++++++++++++++++++ src/param/integer.rs | 26 ++++++++++++++++++++++++++ src/param/internals.rs | 1 + 6 files changed, 116 insertions(+) diff --git a/src/param.rs b/src/param.rs index ca1d6f93..b1e989f1 100644 --- a/src/param.rs +++ b/src/param.rs @@ -60,6 +60,21 @@ pub trait Param: Display { /// Get the unit label for this parameter, if any. fn unit(&self) -> &'static str; + /// Get this parameter's polyphonic modulation ID. If this is set for a parameter in a CLAP + /// plugin, then polyphonic modulation will be enabled for that parameter. Polyphonic modulation + /// is sent through [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] + /// events containing a **normalized** value for this parameter. This value must be converted to + /// a plain value using [`preview_plain()`][Self::preview_plain()] before it can be used. The + /// plugin should use this value in place of the parameter's normal (smoothed) value for the + /// affected note, and it should apply smooth to these values as necessary. + /// + /// # Important + /// + /// After enabling polyphonic modulation, the plugin **must** start sending + /// [`NoteEvent::VoiceTerminated`][crate::prelude::NoteEvent::VoiceEnd] events to the host when a voice + /// has fully ended. This allows the host to reuse its modulation resources. + fn poly_modulation_id(&self) -> Option; + /// Get the unnormalized value for this parameter. fn plain_value(&self) -> Self::Plain; diff --git a/src/param/boolean.rs b/src/param/boolean.rs index 4075e5f9..489f2ea9 100644 --- a/src/param/boolean.rs +++ b/src/param/boolean.rs @@ -36,6 +36,12 @@ pub struct BoolParam { /// The parameter's human readable display name. name: String, + /// If this parameter has been marked as polyphonically modulatable, then this will be a unique + /// integer identifying the parameter. Because this value is determined by the plugin itself, + /// the plugin can easily map + /// [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] events to the + /// correct parameter by pattern matching on a constant. + poly_modulation_id: Option, /// Optional custom conversion function from a boolean value to a string. value_to_string: Option String + Send + Sync>>, /// Optional custom conversion function from a string to a boolean value. If the string cannot @@ -65,6 +71,10 @@ impl Param for BoolParam { "" } + fn poly_modulation_id(&self) -> Option { + self.poly_modulation_id + } + #[inline] fn plain_value(&self) -> Self::Plain { self.value @@ -207,11 +217,27 @@ impl BoolParam { value_changed: None, name: name.into(), + poly_modulation_id: None, value_to_string: None, string_to_value: None, } } + /// Enable polyphonic modulation for this parameter. The ID is used to uniquely identify this + /// parameter in [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] + /// events, and must thus be unique between _all_ polyphonically modulatable parameters. See the + /// event's documentation for more information on how to use this. + /// + /// # Important + /// + /// After enabling polyphonic modulation, the plugin **must** start sending + /// [`NoteEvent::VoiceTerminated`][crate::prelude::NoteEvent::VoiceEnd] events to the host when + /// a voice has fully ended. This allows the host to reuse its modulation resources. + pub fn with_poly_modulation_id(mut self, id: u32) -> Self { + self.poly_modulation_id = Some(id); + self + } + /// Run a callback whenever this parameter's value changes. The argument passed to this function /// is the parameter's new value. This should not do anything expensive as it may be called /// multiple times in rapid succession, and it can be run from both the GUI and the audio diff --git a/src/param/enums.rs b/src/param/enums.rs index ec5000d4..f92d1a6a 100644 --- a/src/param/enums.rs +++ b/src/param/enums.rs @@ -117,6 +117,10 @@ impl Param for EnumParam { self.inner.unit() } + fn poly_modulation_id(&self) -> Option { + self.inner.poly_modulation_id() + } + #[inline] fn plain_value(&self) -> Self::Plain { T::from_index(self.inner.plain_value() as usize) @@ -195,6 +199,10 @@ impl Param for EnumParamInner { "" } + fn poly_modulation_id(&self) -> Option { + self.inner.poly_modulation_id() + } + #[inline] fn plain_value(&self) -> Self::Plain { self.inner.plain_value() @@ -326,6 +334,21 @@ impl EnumParam { } } + /// Enable polyphonic modulation for this parameter. The ID is used to uniquely identify this + /// parameter in [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] + /// events, and must thus be unique between _all_ polyphonically modulatable parameters. See the + /// event's documentation for more information on how to use this. + /// + /// # Important + /// + /// After enabling polyphonic modulation, the plugin **must** start sending + /// [`NoteEvent::VoiceTerminated`][crate::prelude::NoteEvent::VoiceEnd] events to the host when + /// a voice has fully ended. This allows the host to reuse its modulation resources. + pub fn with_poly_modulation_id(mut self, id: u32) -> Self { + self.inner.inner = self.inner.inner.with_poly_modulation_id(id); + self + } + /// Run a callback whenever this parameter's value changes. The argument passed to this function /// is the parameter's new value. This should not do anything expensive as it may be called /// multiple times in rapid succession, and it can be run from both the GUI and the audio diff --git a/src/param/float.rs b/src/param/float.rs index 2bc85b63..8da312e0 100644 --- a/src/param/float.rs +++ b/src/param/float.rs @@ -64,6 +64,12 @@ pub struct FloatParam { /// The parameter value's unit, added after [`value_to_string`][Self::value_to_string] if that /// is set. NIH-plug will not automatically add a space before the unit. unit: &'static str, + /// If this parameter has been marked as polyphonically modulatable, then this will be a unique + /// integer identifying the parameter. Because this value is determined by the plugin itself, + /// the plugin can easily map + /// [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] events to the + /// correct parameter by pattern matching on a constant. + poly_modulation_id: Option, /// Optional custom conversion function from a plain **unnormalized** value to a string. value_to_string: Option String + Send + Sync>>, /// Optional custom conversion function from a string to a plain **unnormalized** value. If the @@ -99,6 +105,10 @@ impl Param for FloatParam { self.unit } + fn poly_modulation_id(&self) -> Option { + self.poly_modulation_id + } + #[inline] fn plain_value(&self) -> Self::Plain { self.value @@ -275,11 +285,26 @@ impl FloatParam { step_size: None, name: name.into(), unit: "", + poly_modulation_id: None, value_to_string: None, string_to_value: None, } } + /// Enable polyphonic modulation for this parameter. The ID is used to uniquely identify this + /// parameter in [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] + /// events, and must thus be unique between _all_ polyphonically modulatable parameters. + /// + /// # Important + /// + /// After enabling polyphonic modulation, the plugin **must** start sending + /// [`NoteEvent::VoiceTerminated`][crate::prelude::NoteEvent::VoiceEnd] events to the host when + /// a voice has fully ended. This allows the host to reuse its modulation resources. + pub fn with_poly_modulation_id(mut self, id: u32) -> Self { + self.poly_modulation_id = Some(id); + self + } + /// Set up a smoother that can gradually interpolate changes made to this parameter, preventing /// clicks and zipper noises. pub fn with_smoother(mut self, style: SmoothingStyle) -> Self { diff --git a/src/param/integer.rs b/src/param/integer.rs index 70413f4f..e854fbec 100644 --- a/src/param/integer.rs +++ b/src/param/integer.rs @@ -60,6 +60,12 @@ pub struct IntParam { /// The parameter value's unit, added after `value_to_string` if that is set. NIH-plug will not /// automatically add a space before the unit. unit: &'static str, + /// If this parameter has been marked as polyphonically modulatable, then this will be a unique + /// integer identifying the parameter. Because this value is determined by the plugin itself, + /// the plugin can easily map + /// [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] events to the + /// correct parameter by pattern matching on a constant. + poly_modulation_id: Option, /// Optional custom conversion function from a plain **unnormalized** value to a string. value_to_string: Option String + Send + Sync>>, /// Optional custom conversion function from a string to a plain **unnormalized** value. If the @@ -91,6 +97,10 @@ impl Param for IntParam { self.unit } + fn poly_modulation_id(&self) -> Option { + self.poly_modulation_id + } + #[inline] fn plain_value(&self) -> Self::Plain { self.value @@ -239,11 +249,27 @@ impl IntParam { range, name: name.into(), unit: "", + poly_modulation_id: None, value_to_string: None, string_to_value: None, } } + /// Enable polyphonic modulation for this parameter. The ID is used to uniquely identify this + /// parameter in [`NoteEvent::PolyModulation][crate::prelude::NoteEvent::PolyModulation`] + /// events, and must thus be unique between _all_ polyphonically modulatable parameters. See the + /// event's documentation for more information on how to use this. + /// + /// # Important + /// + /// After enabling polyphonic modulation, the plugin **must** start sending + /// [`NoteEvent::VoiceTerminated`][crate::prelude::NoteEvent::VoiceEnd] events to the host when + /// a voice has fully ended. This allows the host to reuse its modulation resources. + pub fn with_poly_modulation_id(mut self, id: u32) -> Self { + self.poly_modulation_id = Some(id); + self + } + /// Set up a smoother that can gradually interpolate changes made to this parameter, preventing /// clicks and zipper noises. pub fn with_smoother(mut self, style: SmoothingStyle) -> Self { diff --git a/src/param/internals.rs b/src/param/internals.rs index e403d1f5..6ec84f0a 100644 --- a/src/param/internals.rs +++ b/src/param/internals.rs @@ -154,6 +154,7 @@ macro_rules! param_ptr_forward( impl ParamPtr { 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 poly_modulation_id(&self) -> Option); 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);