From f0ee2739f13c2a501db647a2d0d7f8d1810b0bea Mon Sep 17 00:00:00 2001 From: Fredemus Date: Sun, 20 Mar 2022 16:07:35 +0100 Subject: [PATCH 1/3] a few more formatters --- src/formatters.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/formatters.rs b/src/formatters.rs index 2d0b9073..ab6c3083 100644 --- a/src/formatters.rs +++ b/src/formatters.rs @@ -7,6 +7,28 @@ pub fn f32_rounded(digits: usize) -> Arc String + Send + Sync> { Arc::new(move |x| format!("{:.digits$}", x)) } +/// Turn an `f32` value from linear to dBFS (reference value 1) +pub fn f32_lin_to_db(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| format!("{:.digits$}", x.log10() * 20.0)) +} + +/// Turn an `f32` value from dBFS (reference value 1) to linear +pub fn f32_db_to_lin(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| format!("{:.digits$}", 10f32.powf(x / 20.0))) +} + +/// Round an `f32` value and divide it by 1000 when it gets over 1000 +pub fn f32_hertz_then_khz(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| { + if x < 1000.0 { + format!("{:.digits$}", x) + } else { + let digits = digits + 1; + format!("{:.digits$}", x / 1000.0) + } + }) +} + /// Format a `[0, 1]` number as a percentage. Does not include the percent sign, you should specify /// this as the parameter's unit. pub fn f32_percentage(digits: usize) -> Arc String + Send + Sync> { From 41db682c203bb8e82ab771888cca06259a7af35a Mon Sep 17 00:00:00 2001 From: Fredemus Date: Sun, 20 Mar 2022 21:15:17 +0100 Subject: [PATCH 2/3] i32_note_formatter + from_f32_lin_to_db --- src/formatters.rs | 85 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/src/formatters.rs b/src/formatters.rs index ab6c3083..1665929d 100644 --- a/src/formatters.rs +++ b/src/formatters.rs @@ -6,29 +6,6 @@ use std::sync::Arc; pub fn f32_rounded(digits: usize) -> Arc String + Send + Sync> { Arc::new(move |x| format!("{:.digits$}", x)) } - -/// Turn an `f32` value from linear to dBFS (reference value 1) -pub fn f32_lin_to_db(digits: usize) -> Arc String + Send + Sync> { - Arc::new(move |x| format!("{:.digits$}", x.log10() * 20.0)) -} - -/// Turn an `f32` value from dBFS (reference value 1) to linear -pub fn f32_db_to_lin(digits: usize) -> Arc String + Send + Sync> { - Arc::new(move |x| format!("{:.digits$}", 10f32.powf(x / 20.0))) -} - -/// Round an `f32` value and divide it by 1000 when it gets over 1000 -pub fn f32_hertz_then_khz(digits: usize) -> Arc String + Send + Sync> { - Arc::new(move |x| { - if x < 1000.0 { - format!("{:.digits$}", x) - } else { - let digits = digits + 1; - format!("{:.digits$}", x / 1000.0) - } - }) -} - /// Format a `[0, 1]` number as a percentage. Does not include the percent sign, you should specify /// this as the parameter's unit. pub fn f32_percentage(digits: usize) -> Arc String + Send + Sync> { @@ -47,6 +24,38 @@ pub fn from_f32_percentage() -> Arc Option + Send + Sync> { }) } +/// Turn an `f32` value from linear to dBFS (reference value 1) +pub fn f32_lin_to_db(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| format!("{:.digits$}", x.log10() * 20.0)) +} + +/// Turn an `f32` value from dBFS (reference value 1) to linear +pub fn f32_db_to_lin(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| format!("{:.digits$}", 10f32.powf(x / 20.0))) +} +/// Parse a `dBFS` value to a linear value. Handles the dB unit for you. Used in +/// conjunction with [`f32_lin_to_db`]. +pub fn from_f32_lin_to_db() -> Arc Option + Send + Sync> { + Arc::new(|string| { + string + .trim_end_matches(&[' ', 'd', 'B']) + .parse() + .ok() + .map(|x: f32| 10f32.powf(x / 20.0)) + }) +} +/// Round an `f32` value and divide it by 1000 when it gets over 1000 +pub fn f32_hz_then_khz(digits: usize) -> Arc String + Send + Sync> { + Arc::new(move |x| { + if x < 1000.0 { + format!("{:.digits$}", x) + } else { + let digits = digits + 1; + format!("{:.digits$}", x / 1000.0) + } + }) +} + /// Format an order/power of two. Useful in conjunction with [`from_power_of_two()`] to limit /// integer parameter ranges to be only powers of two. pub fn i32_power_of_two() -> Arc String + Send + Sync> { @@ -58,3 +67,33 @@ pub fn i32_power_of_two() -> Arc String + Send + Sync> { pub fn from_i32_power_of_two() -> Arc Option + Send + Sync> { Arc::new(|string| string.parse().ok().map(|n: i32| (n as f32).log2() as i32)) } + +const NOTES: [&str; 12] = [ + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", +]; +/// Turns an integer midi number (range 0-127 usually) into a note name, e.g. 69 -> A4 +pub fn i32_note_formatter() -> Arc String + Send + Sync> { + Arc::new(move |x| { + let note = x as usize; + let note_name = NOTES[note % 12].to_string(); + let octave = (note / 12) as i32 - 1; + format!("{note_name}{octave}") + }) +} +/// parses a note name into a midi number (range 0-127 usually), e.g. A#4 -> 70 +pub fn from_i32_note_formatter() -> Arc Option + Send + Sync> { + Arc::new(|string| { + // string is too short to be a note name + if string.len() < 2 { + return None; + } + let (note_name, octave) = if string.contains("#") { + string.split_at(2) + } else { + string.split_at(1) + }; + // using unwrap_or here, or else trying to parse "##" breaks it + let note = NOTES.iter().position(|&r| r == note_name).unwrap_or(0) as i32; + octave.parse().ok().map(|n: i32| (n + 1) * 12 + note) + }) +} From 8bbcb1a09306849f9f03c2e810f6205a7c446cc9 Mon Sep 17 00:00:00 2001 From: Fredemus Date: Sun, 20 Mar 2022 21:37:50 +0100 Subject: [PATCH 3/3] add panning formatter --- src/formatters.rs | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/formatters.rs b/src/formatters.rs index 1665929d..f217e8cf 100644 --- a/src/formatters.rs +++ b/src/formatters.rs @@ -28,11 +28,6 @@ pub fn from_f32_percentage() -> Arc Option + Send + Sync> { pub fn f32_lin_to_db(digits: usize) -> Arc String + Send + Sync> { Arc::new(move |x| format!("{:.digits$}", x.log10() * 20.0)) } - -/// Turn an `f32` value from dBFS (reference value 1) to linear -pub fn f32_db_to_lin(digits: usize) -> Arc String + Send + Sync> { - Arc::new(move |x| format!("{:.digits$}", 10f32.powf(x / 20.0))) -} /// Parse a `dBFS` value to a linear value. Handles the dB unit for you. Used in /// conjunction with [`f32_lin_to_db`]. pub fn from_f32_lin_to_db() -> Arc Option + Send + Sync> { @@ -44,6 +39,42 @@ pub fn from_f32_lin_to_db() -> Arc Option + Send + Sync> { .map(|x: f32| 10f32.powf(x / 20.0)) }) } +/// Turn an `f32` value `[-1, 1]` to a panning value `[100L, 100R]` Value of `0.0` becomes `"C"` +pub fn f32_panning() -> Arc String + Send + Sync> { + Arc::new(move |x| { + if x == 0. { + "C".to_string() + } else if x > 0. { + format!("{:.0}R", x * 100.) + } else { + format!("{:.0}L", x * -100.) + } + }) +} +/// Parse a pan value to a linear value, range `[-1, 1]`. Used in +/// conjunction with [`f32_panning`]. +pub fn from_f32_panning() -> Arc Option + Send + Sync> { + Arc::new(|string| { + if string.contains('L') { + string + .trim_end_matches(&[' ', 'L']) + .parse() + .ok() + .map(|x: f32| x / -100.) + } else if string.contains('R') { + string + .trim_end_matches(&[' ', 'R']) + .parse() + .ok() + .map(|x: f32| x / 100.) + } else if string == "C" { + Some(0.) + } else { + string.trim_end_matches(&[' ']).parse().ok().map(|x: f32| x) + } + }) +} + /// Round an `f32` value and divide it by 1000 when it gets over 1000 pub fn f32_hz_then_khz(digits: usize) -> Arc String + Send + Sync> { Arc::new(move |x| {