diff --git a/examples/visualizers/src/lib.rs b/examples/visualizers/src/lib.rs index 07a6ba5..2a86708 100644 --- a/examples/visualizers/src/lib.rs +++ b/examples/visualizers/src/lib.rs @@ -36,7 +36,7 @@ impl Default for VisualizersDemo { Self { params: Arc::new(DemoParams::default()), oscilloscope_buffer: Arc::new(Mutex::new(WaveformBuffer::new(800, 44100.0, 5.0))), - peak_buffer: Arc::new(Mutex::new(PeakBuffer::new(800, 44100.0, 10.0))), + peak_buffer: Arc::new(Mutex::new(PeakBuffer::new(800, 10.0, 50.))), lissajous_buffer: Arc::new(Mutex::new(RingBuffer::new(2048))), spectrum_input, diff --git a/src/utils/buffers/peak_buffer.rs b/src/utils/buffers/peak_buffer.rs index 3bae02a..290f545 100644 --- a/src/utils/buffers/peak_buffer.rs +++ b/src/utils/buffers/peak_buffer.rs @@ -14,35 +14,56 @@ pub struct PeakBuffer { duration: f32, // The current time, counts down from sample_delta to 0 t: f32, + /// The decay time for the peak amplitude to halve. + decay: f32, + // This is set `set_sample_rate()` based on the sample_delta + decay_weight: f32, } impl PeakBuffer { - pub fn new(size: usize, sample_rate: f32, duration: f32) -> Self { - let sample_delta = Self::sample_delta(size, sample_rate as f32, duration as f32); + pub fn new(size: usize, duration: f32, decay: f32) -> Self { + let decay_weight = Self::decay_weight(decay, size, duration); Self { buffer: RingBuffer::::new(size), max_acc: 0., - sample_delta, - sample_rate, + sample_delta: 0., + sample_rate: 0., duration, - t: sample_delta, + t: 0., + decay, + decay_weight, } } + pub fn set_decay(self: &mut Self, decay: f32) { + self.decay = decay; + self.update(); + } + pub fn set_sample_rate(self: &mut Self, sample_rate: f32) { self.sample_rate = sample_rate; - self.sample_delta = Self::sample_delta(self.buffer.len(), sample_rate, self.duration); + self.update(); self.buffer.clear(); } pub fn set_duration(self: &mut Self, duration: f32) { self.duration = duration; - self.sample_delta = Self::sample_delta(self.buffer.len(), self.sample_rate, duration); + self.update(); self.buffer.clear(); } fn sample_delta(size: usize, sample_rate: f32, duration: f32) -> f32 { - (sample_rate * duration) / size as f32 + ((sample_rate as f64 * duration as f64) / size as f64) as f32 + } + + fn decay_weight(decay: f32, size: usize, duration: f32) -> f32 { + 0.25f64.powf((decay as f64 / 1000. * (size as f64 / duration as f64)).recip()) as f32 + } + + fn update(self: &mut Self) { + self.decay_weight = Self::decay_weight(self.decay, self.buffer.len(), self.duration); + self.sample_delta = Self::sample_delta(self.buffer.len(), self.sample_rate, self.duration); + self.t = self.sample_delta; } } @@ -51,7 +72,17 @@ impl VisualizerBuffer for PeakBuffer { let value = value.abs(); self.t -= 1.0; if self.t < 0.0 { - self.buffer.enqueue(self.max_acc); + let last_peak = self.buffer.peek(); + let mut peak = self.max_acc; + + // If the current peak is greater than the last one, we immediately enqueue it. If it's less than + // the last one, we weigh the previous into the current one, analogous to how peak meters work. + self.buffer.enqueue(if peak >= last_peak { + peak + } else { + (last_peak * self.decay_weight) + (peak * (1.0 - self.decay_weight)) + }); + self.t += self.sample_delta; self.max_acc = 0.; } @@ -95,7 +126,7 @@ impl VisualizerBuffer for PeakBuffer { return; }; self.buffer.grow(size); - self.sample_delta = Self::sample_delta(size, self.sample_rate, self.duration); + self.update(); self.buffer.clear(); } @@ -105,7 +136,7 @@ impl VisualizerBuffer for PeakBuffer { return; }; self.buffer.shrink(size); - self.sample_delta = Self::sample_delta(size, self.sample_rate, self.duration); + self.update(); self.buffer.clear(); } } @@ -122,22 +153,3 @@ impl IndexMut for PeakBuffer { self.buffer.index_mut(index) } } - -#[cfg(test)] -mod tests { - use crate::utils::VisualizerBuffer; - - use super::PeakBuffer; - - #[test] - fn enqueue() { - let mut rb = PeakBuffer::new(16, 4.0, 8.0); - - rb.enqueue(2.); - rb.enqueue(9.); - rb.enqueue(19.); - rb.enqueue(-10.); - rb.enqueue(4.); - rb.enqueue(6.); - } -} diff --git a/src/utils/buffers/ring_buffer.rs b/src/utils/buffers/ring_buffer.rs index 270706c..0263ffd 100644 --- a/src/utils/buffers/ring_buffer.rs +++ b/src/utils/buffers/ring_buffer.rs @@ -96,6 +96,10 @@ impl RingBuffer { self.head = (self.head + 1) % self.size; } + pub fn peek(self: &Self) -> T { + self.data[(self.size + self.head - 1) % self.size] + } + /// Clears the entire buffer, filling it with default values (usually 0) pub fn clear(self: &mut Self) { self.data.iter_mut().for_each(|x| *x = T::default()); @@ -271,4 +275,20 @@ mod tests { rb[4]; } + + #[test] + fn peek() { + let mut rb = RingBuffer::::new(4); + + rb.enqueue(1); + assert_eq!(rb.peek(), 1); + rb.enqueue(2); + rb.enqueue(3); + assert_eq!(rb.peek(), 3); + rb.enqueue(4); + rb.enqueue(5); + rb.enqueue(6); + rb.enqueue(7); + assert_eq!(rb.peek(), 7); + } }