pub trait NormalizedMap: 'static + Clone {
fn normalized_to_display(&self, normalized: f32) -> String;
fn snap(&self, normalized: f32) -> f32 {
normalized
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum DisplayDecimals {
Zero,
#[default]
One,
Two,
Three,
Four,
Five,
}
impl DisplayDecimals {
pub fn display_value(&self, value: f32) -> String {
match self {
DisplayDecimals::Zero => format!("{:.0}", value),
DisplayDecimals::One => format!("{:.1}", value),
DisplayDecimals::Two => format!("{:.2}", value),
DisplayDecimals::Three => format!("{:.3}", value),
DisplayDecimals::Four => format!("{:.4}", value),
DisplayDecimals::Five => format!("{:.5}", value),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ValueScaling {
Linear,
Power(f32),
Frequency,
}
impl ValueScaling {
pub fn normalized_to_value(&self, normalized: f32, min: f32, max: f32) -> f32 {
if normalized <= 0.0 {
return min;
} else if normalized >= 1.0 {
return max;
}
let map = |x: f32| -> f32 { (x * (max - min)) + min };
match self {
ValueScaling::Linear => map(normalized),
ValueScaling::Power(exponent) => map(normalized.powf(*exponent)),
ValueScaling::Frequency => {
let minl = min.log2();
let range = max.log2() - minl;
2.0f32.powf((normalized * range) + minl)
}
}
}
pub fn value_to_normalized(&self, value: f32, min: f32, max: f32) -> f32 {
if value <= min {
return 0.0;
} else if value >= max {
return 1.0;
}
let unmap = |x: f32| -> f32 { (x - min) / (max - min) };
match self {
ValueScaling::Linear => unmap(value),
ValueScaling::Power(exponent) => unmap(value).powf(1.0 / *exponent),
ValueScaling::Frequency => {
let minl = min.log2();
let range = max.log2() - minl;
(value.log2() - minl) / range
}
}
}
}
#[derive(Debug, Clone)]
pub struct GenericMap {
min: f32,
max: f32,
_span_recip: f32, value_scaling: ValueScaling,
display_decimals: DisplayDecimals,
units: Option<String>,
}
impl GenericMap {
pub fn new(
min: f32,
max: f32,
value_scaling: ValueScaling,
display_decimals: DisplayDecimals,
units: Option<String>,
) -> Self {
assert!(min < max);
Self { min, max, _span_recip: 1.0 / (max - min), value_scaling, display_decimals, units }
}
pub fn min(&self) -> f32 {
self.min
}
pub fn max(&self) -> f32 {
self.max
}
pub fn value_scaling(&self) -> &ValueScaling {
&self.value_scaling
}
#[inline]
pub fn normalized_to_value(&self, normalized: f32) -> f32 {
self.value_scaling.normalized_to_value(normalized, self.min, self.max)
}
#[inline]
pub fn value_to_normalized(&self, value: f32) -> f32 {
self.value_scaling.value_to_normalized(value, self.min, self.max)
}
#[inline]
pub fn clamp_value(&self, value: f32) -> f32 {
value.min(self.max).max(self.min)
}
}
impl NormalizedMap for GenericMap {
fn normalized_to_display(&self, normalized: f32) -> String {
let mut s = self.display_decimals.display_value(self.normalized_to_value(normalized));
if let Some(units) = &self.units {
s += units;
}
s
}
}
#[derive(Debug, Clone)]
pub struct DecibelMap {
min: f32,
max: f32,
value_scaling: ValueScaling,
display_decimals: DisplayDecimals,
display_units: bool,
}
impl DecibelMap {
pub fn new(
min_db: f32,
max_db: f32,
value_scaling: ValueScaling,
display_decimals: DisplayDecimals,
display_units: bool,
) -> Self {
assert!(min_db < max_db);
Self { min: min_db, max: max_db, value_scaling, display_decimals, display_units }
}
pub fn min_db(&self) -> f32 {
self.min
}
pub fn max_db(&self) -> f32 {
self.max
}
pub fn value_scaling(&self) -> &ValueScaling {
&self.value_scaling
}
#[inline]
pub fn normalized_to_db(&self, normalized: f32) -> f32 {
self.value_scaling.normalized_to_value(normalized, self.min, self.max)
}
#[inline]
pub fn normalized_to_amplitude(&self, normalized: f32) -> f32 {
db_to_amplitude(self.value_scaling.normalized_to_value(normalized, self.min, self.max))
}
#[inline]
pub fn db_to_normalized(&self, db: f32) -> f32 {
self.value_scaling.value_to_normalized(db, self.min, self.max)
}
#[inline]
pub fn amplitude_to_normalized(&self, amplitude: f32) -> f32 {
let db = amplitude_to_db(amplitude);
self.db_to_normalized(db)
}
#[inline]
pub fn clamp_db(&self, db: f32) -> f32 {
db.min(self.max).max(self.min)
}
}
impl NormalizedMap for DecibelMap {
fn normalized_to_display(&self, normalized: f32) -> String {
let mut s = self.display_decimals.display_value(self.normalized_to_db(normalized));
if self.display_units {
s += " dB"
}
s
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrequencyDisplayMode {
OnlyHz(DisplayDecimals),
HzThenKHz { under_1k: DisplayDecimals, over_1k: DisplayDecimals },
}
impl Default for FrequencyDisplayMode {
fn default() -> Self {
FrequencyDisplayMode::HzThenKHz {
under_1k: DisplayDecimals::One,
over_1k: DisplayDecimals::Two,
}
}
}
#[derive(Debug, Clone)]
pub struct FrequencyMap {
min: f32,
max: f32,
value_scaling: ValueScaling,
display_mode: FrequencyDisplayMode,
display_units: bool,
}
impl FrequencyMap {
pub fn new(
min_hz: f32,
max_hz: f32,
value_scaling: ValueScaling,
display_mode: FrequencyDisplayMode,
display_units: bool,
) -> Self {
assert!(min_hz < max_hz);
Self { min: min_hz, max: max_hz, value_scaling, display_mode, display_units }
}
pub fn min_hz(&self) -> f32 {
self.min
}
pub fn max_hz(&self) -> f32 {
self.max
}
pub fn value_scaling(&self) -> &ValueScaling {
&self.value_scaling
}
#[inline]
pub fn normalized_to_hz(&self, normalized: f32) -> f32 {
self.value_scaling.normalized_to_value(normalized, self.min, self.max)
}
#[inline]
pub fn hz_to_normalized(&self, hz: f32) -> f32 {
self.value_scaling.value_to_normalized(hz, self.min, self.max)
}
#[inline]
pub fn clamp_hz(&self, hz: f32) -> f32 {
hz.min(self.max).max(self.min)
}
}
impl NormalizedMap for FrequencyMap {
fn normalized_to_display(&self, normalized: f32) -> String {
let hz = self.normalized_to_hz(normalized);
match self.display_mode {
FrequencyDisplayMode::OnlyHz(display_decimals) => {
let mut s = display_decimals.display_value(hz);
if self.display_units {
s += " Hz"
}
s
}
FrequencyDisplayMode::HzThenKHz { under_1k, over_1k } => {
if hz < 1_000.0 {
let mut s = under_1k.display_value(hz);
if self.display_units {
s += " Hz"
}
s
} else {
let mut s = over_1k.display_value(hz / 1_000.0);
if self.display_units {
s += " kHz"
}
s
}
}
}
}
}
#[derive(Clone)]
pub struct IntMap {
min: i32,
max: i32,
span: f32, display_map: Option<&'static dyn Fn(i32) -> String>,
}
impl IntMap {
pub fn new(min: i32, max: i32, display_map: Option<&'static dyn Fn(i32) -> String>) -> Self {
assert!(min <= max);
Self { min, max, span: (max - min) as f32, display_map }
}
pub fn min(&self) -> i32 {
self.min
}
pub fn max(&self) -> i32 {
self.max
}
#[inline]
pub fn normalized_to_int(&self, normalized: f32) -> i32 {
if normalized <= 0.0 {
return self.min;
} else if normalized >= 1.0 {
return self.max;
}
(normalized * self.span).round() as i32
}
#[inline]
pub fn int_to_normalized(&self, int: i32) -> f32 {
if self.min == self.max {
0.0
} else {
if int <= self.min {
return 0.0;
} else if int >= self.max {
return 1.0;
}
(int as f32 - self.min as f32) / self.span
}
}
#[inline]
pub fn clamp_int(&self, int: i32) -> i32 {
int.min(self.max).max(self.min)
}
}
impl NormalizedMap for IntMap {
fn normalized_to_display(&self, normalized: f32) -> String {
let int = self.normalized_to_int(normalized);
if let Some(display_map) = self.display_map {
(display_map)(int)
} else {
format!("{}", int)
}
}
fn snap(&self, normalized: f32) -> f32 {
let int = self.normalized_to_int(normalized);
self.int_to_normalized(int)
}
}
#[inline]
pub fn db_to_amplitude(db: f32) -> f32 {
10.0f32.powf(db * 1.0 / 20.0)
}
#[inline]
pub fn amplitude_to_db(amp: f32) -> f32 {
20.0f32 * amp.log10()
}