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 nih_plug::{
|
||||
formatters, util, Buffer, BufferConfig, BusConfig, Editor, IntParam, Plugin, ProcessContext,
|
||||
ProcessStatus, Vst3Plugin,
|
||||
util, Buffer, BufferConfig, BusConfig, Editor, IntParam, Plugin, ProcessContext, ProcessStatus,
|
||||
Vst3Plugin,
|
||||
};
|
||||
use nih_plug::{FloatParam, Params, Range, SmoothingStyle};
|
||||
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.
|
||||
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.
|
||||
///
|
||||
/// 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);
|
||||
|
||||
/// Get the normalized `[0, 1]` value for this parameter.
|
||||
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.
|
||||
fn set_normalized_value(&mut self, normalized: f32);
|
||||
|
@ -63,7 +62,7 @@ pub trait Param: Display {
|
|||
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
|
||||
/// wrappers.
|
||||
/// wrappers. This **does** snap to step sizes for continuous parameters (i.e. [FloatParam]).
|
||||
fn preview_plain(&self, normalized: f32) -> Self::Plain;
|
||||
|
||||
/// 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 {
|
||||
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 {
|
||||
|
|
|
@ -31,6 +31,9 @@ impl Range<()> {
|
|||
|
||||
/// 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.
|
||||
///
|
||||
/// 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> {
|
||||
/// Normalize a plain, unnormalized value. Will be clamped to the bounds of the range if the
|
||||
/// 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
|
||||
/// would exceed that range.
|
||||
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> {
|
||||
|
@ -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> {
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
use super::super::*;
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in a new issue