#[cfg(not(feature = "std"))]
use num_traits::Float;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DbMeterNormalizer {
min_db: f32,
range_recip: f32,
factor: f32,
}
impl DbMeterNormalizer {
pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
assert!(max_db > min_db);
assert!(center_db > min_db && center_db < max_db);
let range_recip = (max_db - min_db).recip();
let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
Self {
min_db,
range_recip,
factor: 0.5_f32.log(center_normalized),
}
}
#[inline]
pub fn normalize(&self, db: f32) -> f32 {
((db - self.min_db) * self.range_recip)
.clamp(0.0, 1.0)
.powf(self.factor)
}
}
impl Default for DbMeterNormalizer {
fn default() -> Self {
Self::new(-100.0, 0.0, -22.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PeakMeterSmootherConfig {
pub decay_rate: f32,
pub refresh_rate: f32,
pub clip_hold_frames: usize,
}
impl Default for PeakMeterSmootherConfig {
fn default() -> Self {
Self {
decay_rate: 0.3,
refresh_rate: 60.0,
clip_hold_frames: 60,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PeakMeterSmoother<const NUM_CHANNELS: usize> {
smoothed_peaks: [f32; NUM_CHANNELS],
clipped_frames_left: [usize; NUM_CHANNELS],
level_decay: f32,
frame_interval: f32,
frame_counter: f32,
clip_hold_frames: usize,
}
impl<const NUM_CHANNELS: usize> PeakMeterSmoother<NUM_CHANNELS> {
pub fn new(config: PeakMeterSmootherConfig) -> Self {
assert!(config.decay_rate > 0.0);
assert!(config.refresh_rate > 0.0);
assert!(config.clip_hold_frames > 0);
Self {
smoothed_peaks: [-100.0; NUM_CHANNELS],
clipped_frames_left: [0; NUM_CHANNELS],
level_decay: 1.0 - (-1.0 / (config.refresh_rate * config.decay_rate)).exp(),
frame_interval: config.refresh_rate.recip(),
frame_counter: 0.0,
clip_hold_frames: config.clip_hold_frames,
}
}
pub fn reset(&mut self) {
self.smoothed_peaks = [-100.0; NUM_CHANNELS];
self.clipped_frames_left = [0; NUM_CHANNELS];
}
pub fn update(&mut self, peaks_db: [f32; NUM_CHANNELS], delta_seconds: f32) {
for ((smoothed_peak, &in_peak), clipped_frames_left) in self
.smoothed_peaks
.iter_mut()
.zip(peaks_db.iter())
.zip(self.clipped_frames_left.iter_mut())
{
if in_peak >= *smoothed_peak {
*smoothed_peak = in_peak;
if in_peak > 0.0 {
*clipped_frames_left = self.clip_hold_frames;
}
}
}
self.frame_counter += delta_seconds;
if (self.frame_counter - self.frame_interval).abs() < 0.0001 {
self.frame_counter = self.frame_interval;
}
while self.frame_counter >= self.frame_interval {
self.frame_counter -= self.frame_interval;
if (self.frame_counter - self.frame_interval).abs() < 0.0001 {
self.frame_counter = self.frame_interval;
}
for ((smoothed_peak, &in_peak), clipped_frames_left) in self
.smoothed_peaks
.iter_mut()
.zip(peaks_db.iter())
.zip(self.clipped_frames_left.iter_mut())
{
if in_peak + 0.001 < *smoothed_peak {
*smoothed_peak += ((in_peak - *smoothed_peak) * self.level_decay).max(-100.0);
}
if *smoothed_peak > 0.0 {
*clipped_frames_left = self.clip_hold_frames;
} else if *clipped_frames_left > 0 {
*clipped_frames_left -= 1;
}
}
}
}
pub fn has_clipped(&self) -> [bool; NUM_CHANNELS] {
core::array::from_fn(|i| self.clipped_frames_left[i] > 0)
}
pub fn smoothed_peaks_db(&self) -> &[f32; NUM_CHANNELS] {
&self.smoothed_peaks
}
pub fn smoothed_peak_db_mono(&self) -> f32 {
let mut max_value: f32 = -100.0;
for ch in self.smoothed_peaks {
max_value = max_value.max(ch);
}
max_value
}
pub fn smoothed_peaks_normalized(&self, normalizer: &DbMeterNormalizer) -> [f32; NUM_CHANNELS] {
core::array::from_fn(|i| normalizer.normalize(self.smoothed_peaks[i]))
}
pub fn smoothed_peaks_normalized_mono(&self, normalizer: &DbMeterNormalizer) -> f32 {
normalizer.normalize(self.smoothed_peak_db_mono())
}
}