7ecc718f5d
app -> app-desktop-macos, presets -> app-presets, server -> remote-server, daw-config-reaper -> osc-config-daw, plugin-reaper-realearn -> plugin-reaper-relearn. Updated run.py and presets.py path references accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
271 lines
9.2 KiB
Rust
271 lines
9.2 KiB
Rust
use crate::{Interval, UnitIncrement, UnitValue};
|
|
use derive_more::Display;
|
|
use helgoboss_midi::U7;
|
|
use std::cmp;
|
|
use std::convert::TryFrom;
|
|
use std::fmt::{Display, Formatter};
|
|
use std::ops::Sub;
|
|
|
|
/// A positive discrete number most likely representing a step count.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Display)]
|
|
pub struct DiscreteValue(u32);
|
|
|
|
impl DiscreteValue {
|
|
/// Creates the discrete value.
|
|
pub const fn new(value: u32) -> DiscreteValue {
|
|
DiscreteValue(value)
|
|
}
|
|
|
|
/// Returns the underlying number.
|
|
pub fn get(&self) -> u32 {
|
|
self.0
|
|
}
|
|
|
|
/// Converts this discrete value to a discrete increment, either negative or positive depending
|
|
/// on the given signum. Returns `None` if this value is zero.
|
|
pub fn to_increment(self, signum: i32) -> Option<DiscreteIncrement> {
|
|
if self.is_zero() {
|
|
return None;
|
|
}
|
|
Some(unsafe { DiscreteIncrement::new_unchecked(signum * self.0 as i32) })
|
|
}
|
|
|
|
/// Returns whether this is 0.
|
|
pub fn is_zero(&self) -> bool {
|
|
self.0 == 0
|
|
}
|
|
|
|
/// Clamps this value to the given interval bounds.
|
|
pub fn clamp_to_interval(&self, interval: &Interval<DiscreteValue>) -> DiscreteValue {
|
|
DiscreteValue::new(num::clamp(
|
|
self.0,
|
|
interval.min_val().0,
|
|
interval.max_val().0,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for DiscreteValue {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
|
let primitive = u32::from_str(source).map_err(|_| "not a valid positive integer")?;
|
|
Ok(DiscreteValue(primitive))
|
|
}
|
|
}
|
|
|
|
impl Sub for DiscreteValue {
|
|
type Output = u32;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
self.0 - rhs.0
|
|
}
|
|
}
|
|
|
|
/// A discrete number representing a positive or negative increment, never 0 (otherwise it wouldn't
|
|
/// be an increment after all).
|
|
#[derive(
|
|
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
|
|
)]
|
|
#[serde(try_from = "i32")]
|
|
pub struct DiscreteIncrement(i32);
|
|
|
|
impl Display for DiscreteIncrement {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{:+}", self.0)
|
|
}
|
|
}
|
|
|
|
impl DiscreteIncrement {
|
|
pub const NEGATIVE_MIN: Self = Self(-1);
|
|
pub const POSITIVE_MIN: Self = Self(1);
|
|
|
|
/// Creates the discrete increment. Panics if the given number is 0.
|
|
pub fn new(increment: i32) -> DiscreteIncrement {
|
|
assert_ne!(increment, 0);
|
|
DiscreteIncrement(increment)
|
|
}
|
|
|
|
/// Creates the discrete increment. Panics if the given number is 0.
|
|
pub fn new_checked(increment: i32) -> Option<DiscreteIncrement> {
|
|
if increment == 0 {
|
|
return None;
|
|
}
|
|
Some(DiscreteIncrement(increment))
|
|
}
|
|
|
|
/// Checks preconditions only in debug build. Should only be used if you want to squeeze out
|
|
/// every last bit of performance and you are super sure that the number meets the
|
|
/// preconditions. This constructor is offered because it's not unlikely that a lot of those
|
|
/// values will be constructed in audio thread.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Make sure the given increment is not zero.
|
|
pub unsafe fn new_unchecked(increment: i32) -> DiscreteIncrement {
|
|
debug_assert_ne!(increment, 0);
|
|
DiscreteIncrement(increment)
|
|
}
|
|
|
|
/// Creates an increment from the given MIDI control-change value assuming that the device
|
|
/// emitting the control-change messages uses a protocol which is called "Relative 1" in REAPER.
|
|
///
|
|
/// - 127 = decrement; 0 = none; 1 = increment
|
|
/// - 127 > value > 63 results in higher decrement step sizes (64 possible decrement step sizes)
|
|
/// - 1 < value <= 63 results in higher increment step sizes (63 possible increment step sizes)
|
|
pub fn from_encoder_1_value(value: U7) -> Result<DiscreteIncrement, &'static str> {
|
|
let value = value.get();
|
|
if value == 0 {
|
|
return Err("increment must not be zero");
|
|
}
|
|
let increment = if value <= 63 {
|
|
// Zero and increment
|
|
value as i32
|
|
} else {
|
|
// Decrement
|
|
-((128 - value) as i32)
|
|
};
|
|
Ok(unsafe { DiscreteIncrement::new_unchecked(increment) })
|
|
}
|
|
|
|
/// Creates an increment from the given MIDI control-change value assuming that the device
|
|
/// emitting the control-change messages uses a protocol which is called "Relative 2" in REAPER.
|
|
///
|
|
/// - 63 = decrement; 64 = none; 65 = increment
|
|
/// - 63 > value >= 0 results in higher decrement step sizes (64 possible decrement step sizes)
|
|
/// - 65 < value <= 127 results in higher increment step sizes (63 possible increment step
|
|
/// sizes)
|
|
pub fn from_encoder_2_value(value: U7) -> Result<DiscreteIncrement, &'static str> {
|
|
let value = value.get();
|
|
if value == 64 {
|
|
return Err("increment must not be zero");
|
|
}
|
|
let increment = if value > 64 {
|
|
// Zero and increment
|
|
(value - 64) as i32
|
|
} else {
|
|
// Decrement
|
|
-((64 - value) as i32)
|
|
};
|
|
Ok(unsafe { DiscreteIncrement::new_unchecked(increment) })
|
|
}
|
|
|
|
/// Creates an increment from the given MIDI control-change value assuming that the device
|
|
/// emitting the control-change messages uses a protocol which is called "Relative 3" in REAPER.
|
|
///
|
|
/// - 65 = decrement; 0 = none; 1 = increment
|
|
/// - 65 < value <= 127 results in higher decrement step sizes (63 possible decrement step
|
|
/// sizes)
|
|
/// - 1 < value <= 64 results in higher increment step sizes (64 possible increment step sizes)
|
|
pub fn from_encoder_3_value(value: U7) -> Result<DiscreteIncrement, &'static str> {
|
|
let value = value.get();
|
|
if value == 0 {
|
|
return Err("increment must not be zero");
|
|
}
|
|
let increment = if value <= 64 {
|
|
// Zero and increment
|
|
value as i32
|
|
} else {
|
|
// Decrement
|
|
-((value - 64) as i32)
|
|
};
|
|
Ok(unsafe { DiscreteIncrement::new_unchecked(increment) })
|
|
}
|
|
|
|
/// Clamps this increment to the given interval bounds.
|
|
pub fn clamp_to_interval(&self, interval: &Interval<DiscreteIncrement>) -> DiscreteIncrement {
|
|
// Step count interval: (-3, 4) = -3, -2, -1, 1, 2, 3, 4
|
|
// 1 => -3
|
|
// 2 => -2
|
|
// 7 => 4
|
|
// 8 => 4
|
|
// Step count interval: (4, 10) = 4, 5, 6, 7, 8, 9, 10
|
|
// 1 => 4
|
|
// 2 => 5
|
|
// 7 => 10
|
|
// 8 => 10
|
|
let positive_increment = self.0.unsigned_abs();
|
|
let min: i32 = interval.min_val().get();
|
|
let max: i32 = interval.max_val().get();
|
|
let count: u32 = if min < 0 && max > 0 {
|
|
(max - min) as u32
|
|
} else {
|
|
(max - min) as u32 + 1
|
|
};
|
|
let addend: u32 = cmp::min(positive_increment - 1, count - 1);
|
|
let sum = min + addend as i32;
|
|
let skip_zero_sum = if min < 0 && sum >= 0 { sum + 1 } else { sum };
|
|
let clamped = cmp::min(skip_zero_sum, max);
|
|
DiscreteIncrement::new(clamped)
|
|
}
|
|
|
|
/// Converts this discrete increment into a discrete value thereby "losing" its direction.
|
|
pub fn to_value(self) -> DiscreteValue {
|
|
DiscreteValue::new(self.0.unsigned_abs())
|
|
}
|
|
|
|
/// Switches the direction of this increment (makes a positive one negative and vice versa).
|
|
pub fn inverse(&self) -> DiscreteIncrement {
|
|
unsafe { DiscreteIncrement::new_unchecked(-self.0) }
|
|
}
|
|
|
|
pub fn with_direction(&self, signum: i32) -> DiscreteIncrement {
|
|
let abs = self.0.abs();
|
|
let inner = if signum >= 0 { abs } else { -abs };
|
|
DiscreteIncrement::new(inner)
|
|
}
|
|
|
|
/// Returns the underlying number.
|
|
pub fn get(&self) -> i32 {
|
|
self.0
|
|
}
|
|
|
|
/// Returns if this increment is positive.
|
|
pub fn is_positive(&self) -> bool {
|
|
self.0 >= 0
|
|
}
|
|
|
|
/// Returns the signum (-1 if it's a negative increment, otherwise +1).
|
|
pub fn signum(&self) -> i32 {
|
|
if self.is_positive() {
|
|
1
|
|
} else {
|
|
-1
|
|
}
|
|
}
|
|
|
|
/// Returns a unit increment or None in case of 0.0.
|
|
///
|
|
/// The unit increment is built by creating a multiple of the given atomic unit value (= minimum
|
|
/// step size) and clamping the result if it exceeds the unit interval.
|
|
pub fn to_unit_increment(self, atomic_unit_value: UnitValue) -> Option<UnitIncrement> {
|
|
let positive_large = self.to_value().get() as f64 * atomic_unit_value.get();
|
|
let unit_value = UnitValue::new(num::clamp(positive_large, 0.0, 1.0));
|
|
unit_value.to_increment(self.signum())
|
|
}
|
|
}
|
|
|
|
impl Sub for DiscreteIncrement {
|
|
type Output = i32;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
self.0 - rhs.0
|
|
}
|
|
}
|
|
|
|
impl TryFrom<i32> for DiscreteIncrement {
|
|
type Error = &'static str;
|
|
|
|
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
|
if value == 0 {
|
|
return Err("zero is not an increment");
|
|
}
|
|
Ok(DiscreteIncrement::new(value))
|
|
}
|
|
}
|
|
|
|
/// Convenience method for creating an interval of discrete increments.
|
|
pub fn create_discrete_increment_interval(min: i32, max: i32) -> Interval<DiscreteIncrement> {
|
|
Interval::new(DiscreteIncrement::new(min), DiscreteIncrement::new(max))
|
|
}
|