Skip to content

Commit

Permalink
Merge pull request #50 from 223230/feature-peak-buffer-decay
Browse files Browse the repository at this point in the history
Peak Buffer decay
  • Loading branch information
exa04 authored Apr 20, 2024
2 parents db5b385 + 9916a3e commit 250c5e9
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 31 deletions.
2 changes: 1 addition & 1 deletion examples/visualizers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
72 changes: 42 additions & 30 deletions src/utils/buffers/peak_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<f32>::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;
}
}

Expand All @@ -51,7 +72,17 @@ impl VisualizerBuffer<f32> 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.;
}
Expand Down Expand Up @@ -95,7 +126,7 @@ impl VisualizerBuffer<f32> for PeakBuffer {
return;
};
self.buffer.grow(size);
self.sample_delta = Self::sample_delta(size, self.sample_rate, self.duration);
self.update();
self.buffer.clear();
}

Expand All @@ -105,7 +136,7 @@ impl VisualizerBuffer<f32> for PeakBuffer {
return;
};
self.buffer.shrink(size);
self.sample_delta = Self::sample_delta(size, self.sample_rate, self.duration);
self.update();
self.buffer.clear();
}
}
Expand All @@ -122,22 +153,3 @@ impl IndexMut<usize> 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.);
}
}
20 changes: 20 additions & 0 deletions src/utils/buffers/ring_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ impl<T: Default + Copy> RingBuffer<T> {
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());
Expand Down Expand Up @@ -271,4 +275,20 @@ mod tests {

rb[4];
}

#[test]
fn peek() {
let mut rb = RingBuffer::<i32>::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);
}
}

0 comments on commit 250c5e9

Please sign in to comment.