Implement step snapping for parameters
This commit is contained in:
parent
4481ef0ae9
commit
19d2dc0a67
|
@ -3,8 +3,8 @@ extern crate nih_plug;
|
||||||
|
|
||||||
use atomic_float::AtomicF32;
|
use atomic_float::AtomicF32;
|
||||||
use nih_plug::{
|
use nih_plug::{
|
||||||
formatters, util, Buffer, BufferConfig, BusConfig, Editor, IntParam, Plugin, ProcessContext,
|
util, Buffer, BufferConfig, BusConfig, Editor, IntParam, Plugin, ProcessContext, ProcessStatus,
|
||||||
ProcessStatus, Vst3Plugin,
|
Vst3Plugin,
|
||||||
};
|
};
|
||||||
use nih_plug::{FloatParam, Params, Range, SmoothingStyle};
|
use nih_plug::{FloatParam, Params, Range, SmoothingStyle};
|
||||||
use nih_plug_egui::{create_egui_editor, egui, widgets, EguiState};
|
use nih_plug_egui::{create_egui_editor, egui, widgets, EguiState};
|
||||||
|
|
19
src/param.rs
19
src/param.rs
|
@ -34,18 +34,17 @@ pub trait Param: Display {
|
||||||
/// Get the unnormalized value for this parameter.
|
/// Get the unnormalized value for this parameter.
|
||||||
fn plain_value(&self) -> Self::Plain;
|
fn plain_value(&self) -> Self::Plain;
|
||||||
|
|
||||||
/// Set this parameter based on a plain, unnormalized value.
|
/// Set this parameter based on a plain, unnormalized value. This does **not** snap to step
|
||||||
|
/// sizes for continuous parameters (i.e. [FloatParam]).
|
||||||
///
|
///
|
||||||
/// This does **not** update the smoother.
|
/// This does **not** update the smoother.
|
||||||
///
|
|
||||||
/// TDOO: Decide on whether this should update the smoother or not. That wouldn't be compatible
|
|
||||||
/// with sample accurate automation when we add that.
|
|
||||||
fn set_plain_value(&mut self, plain: Self::Plain);
|
fn set_plain_value(&mut self, plain: Self::Plain);
|
||||||
|
|
||||||
/// 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;
|
||||||
|
|
||||||
/// Set this parameter based on a normalized value.
|
/// Set this parameter based on a normalized value. This **does** snap to step sizes for
|
||||||
|
/// continuous parameters (i.e. [FloatParam]).
|
||||||
///
|
///
|
||||||
/// This does **not** update the smoother.
|
/// This does **not** update the smoother.
|
||||||
fn set_normalized_value(&mut self, normalized: f32);
|
fn set_normalized_value(&mut self, normalized: f32);
|
||||||
|
@ -63,7 +62,7 @@ pub trait Param: Display {
|
||||||
fn preview_normalized(&self, plain: Self::Plain) -> f32;
|
fn preview_normalized(&self, plain: Self::Plain) -> f32;
|
||||||
|
|
||||||
/// Get the plain, unnormalized value for a normalized value, as a float. Used as part of the
|
/// Get the plain, unnormalized value for a normalized value, as a float. Used as part of the
|
||||||
/// wrappers.
|
/// wrappers. This **does** snap to step sizes for continuous parameters (i.e. [FloatParam]).
|
||||||
fn preview_plain(&self, normalized: f32) -> Self::Plain;
|
fn preview_plain(&self, normalized: f32) -> Self::Plain;
|
||||||
|
|
||||||
/// Internal implementation detail for implementing [internals::Params]. This should not be used
|
/// Internal implementation detail for implementing [internals::Params]. This should not be used
|
||||||
|
@ -251,7 +250,13 @@ macro_rules! impl_plainparam {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preview_plain(&self, normalized: f32) -> Self::Plain {
|
fn preview_plain(&self, normalized: f32) -> Self::Plain {
|
||||||
self.range.unnormalize(normalized)
|
let value = self.range.unnormalize(normalized);
|
||||||
|
match &self.step_size {
|
||||||
|
// Step size snapping is not defined for [IntParam], so this cast is here just
|
||||||
|
// so we can keep everything in this macro
|
||||||
|
Some(step_size) => self.range.snap_to_step(value, *step_size as Self::Plain),
|
||||||
|
None => value,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_ptr(&self) -> internals::ParamPtr {
|
fn as_ptr(&self) -> internals::ParamPtr {
|
||||||
|
|
|
@ -31,6 +31,9 @@ impl Range<()> {
|
||||||
|
|
||||||
/// A normalizable range for type `T`, where `self` is expected to be a type `R<T>`. Higher kinded
|
/// A normalizable range for type `T`, where `self` is expected to be a type `R<T>`. Higher kinded
|
||||||
/// types would have made this trait definition a lot clearer.
|
/// types would have made this trait definition a lot clearer.
|
||||||
|
///
|
||||||
|
/// Floating point rounding to a step size is always done in the conversion from normalized to
|
||||||
|
/// plain, inside [super::PlainParam::preview_plain].
|
||||||
pub(crate) trait NormalizebleRange<T> {
|
pub(crate) trait NormalizebleRange<T> {
|
||||||
/// Normalize a plain, unnormalized value. Will be clamped to the bounds of the range if the
|
/// Normalize a plain, unnormalized value. Will be clamped to the bounds of the range if the
|
||||||
/// normalized value exceeds `[0, 1]`.
|
/// normalized value exceeds `[0, 1]`.
|
||||||
|
@ -39,6 +42,9 @@ pub(crate) trait NormalizebleRange<T> {
|
||||||
/// Unnormalize a normalized value. Will be clamped to `[0, 1]` if the plain, unnormalized value
|
/// Unnormalize a normalized value. Will be clamped to `[0, 1]` if the plain, unnormalized value
|
||||||
/// would exceed that range.
|
/// would exceed that range.
|
||||||
fn unnormalize(&self, normalized: f32) -> T;
|
fn unnormalize(&self, normalized: f32) -> T;
|
||||||
|
|
||||||
|
/// Snap a vlue to a step size, clamping to the minimum and maximum value of the range.
|
||||||
|
fn snap_to_step(&self, value: T, step_size: T) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Range<f32> {
|
impl Default for Range<f32> {
|
||||||
|
@ -116,6 +122,16 @@ impl NormalizebleRange<f32> for Range<f32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn snap_to_step(&self, value: f32, step_size: f32) -> f32 {
|
||||||
|
let (min, max) = match &self {
|
||||||
|
Range::Linear { min, max } => (min, max),
|
||||||
|
Range::Skewed { min, max, .. } => (min, max),
|
||||||
|
Range::SymmetricalSkewed { min, max, .. } => (min, max),
|
||||||
|
};
|
||||||
|
|
||||||
|
((value / step_size).round() * step_size).clamp(*min, *max)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NormalizebleRange<i32> for Range<i32> {
|
impl NormalizebleRange<i32> for Range<i32> {
|
||||||
|
@ -175,6 +191,12 @@ impl NormalizebleRange<i32> for Range<i32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn snap_to_step(&self, value: i32, _step_size: i32) -> i32 {
|
||||||
|
// Integers are already discrete, and we don't allow setting step sizes on them through the
|
||||||
|
// builder interface
|
||||||
|
value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -226,6 +248,20 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_size() {
|
||||||
|
// These are weird step sizes, but if it works here then it will work for anything
|
||||||
|
let range = make_linear_float_range();
|
||||||
|
assert_eq!(range.snap_to_step(13.0, 4.73), 14.49);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_size_clamping() {
|
||||||
|
let range = make_linear_float_range();
|
||||||
|
assert_eq!(range.snap_to_step(10.0, 4.73), 10.0);
|
||||||
|
assert_eq!(range.snap_to_step(20.0, 6.73), 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
mod linear {
|
mod linear {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in a new issue