use std::sync::Arc;
use parking_lot::{Mutex, RwLock};
const DEFAULT_BUFFER_SIZE: usize = 4096;
pub const NUM_BARS: usize = 48;
#[derive(Clone)]
pub struct AnalysisOutput {
pub spectrum: [f32; NUM_BARS],
pub peaks: [f32; NUM_BARS],
pub vu_levels: [f32; 2],
}
impl Default for AnalysisOutput {
fn default() -> Self {
Self {
spectrum: [0.0; NUM_BARS],
peaks: [0.0; NUM_BARS],
vu_levels: [0.0; 2],
}
}
}
pub type SharedAnalysisOutput = Arc<Mutex<AnalysisOutput>>;
#[derive(Clone)]
pub struct VizFrame {
pub spectrum: [f32; NUM_BARS],
pub vu_levels: [f32; 2],
pub timestamp: std::time::Instant,
}
impl Default for VizFrame {
fn default() -> Self {
Self {
spectrum: [0.0; NUM_BARS],
vu_levels: [0.0; 2],
timestamp: std::time::Instant::now(),
}
}
}
pub struct VizSnapshot {
inner: RwLock<VizFrame>,
}
impl VizSnapshot {
pub fn new() -> Arc<Self> {
Arc::new(Self {
inner: RwLock::new(VizFrame::default()),
})
}
pub fn read(&self) -> VizFrame {
self.inner.read().clone()
}
pub fn write(&self, frame: VizFrame) {
*self.inner.write() = frame;
}
}
impl Default for VizSnapshot {
fn default() -> Self {
Self {
inner: RwLock::new(VizFrame::default()),
}
}
}
pub struct RawVizSnapshot {
pub samples: Vec<f32>,
pub channels: u16,
pub sample_rate: u32,
}
struct VizSamples {
buf: Vec<f32>,
write_pos: usize,
channels: u16,
sample_rate: u32,
}
pub struct VizBuffer {
samples: Mutex<VizSamples>,
}
impl VizBuffer {
pub fn new() -> Arc<Self> {
Arc::new(Self {
samples: Mutex::new(VizSamples {
buf: vec![0.0; DEFAULT_BUFFER_SIZE],
write_pos: 0,
channels: 2,
sample_rate: 44100,
}),
})
}
pub fn push_samples(&self, samples: &[f32], channels: u16, sample_rate: u32) {
let mut inner = self.samples.lock();
inner.channels = channels;
inner.sample_rate = sample_rate;
let buf_len = inner.buf.len();
if samples.len() >= buf_len {
let start = samples.len() - buf_len;
inner.buf.copy_from_slice(&samples[start..]);
inner.write_pos = 0;
} else {
let pos = inner.write_pos;
let first = buf_len - pos;
if samples.len() <= first {
inner.buf[pos..pos + samples.len()].copy_from_slice(samples);
inner.write_pos = (pos + samples.len()) % buf_len;
} else {
inner.buf[pos..].copy_from_slice(&samples[..first]);
let remaining = samples.len() - first;
inner.buf[..remaining].copy_from_slice(&samples[first..]);
inner.write_pos = remaining;
}
}
}
pub fn snapshot(&self) -> Vec<f32> {
let mut out = Vec::new();
self.snapshot_into(&mut out);
out
}
pub fn snapshot_into(&self, out: &mut Vec<f32>) {
let inner = self.samples.lock();
let buf_len = inner.buf.len();
let pos = inner.write_pos;
out.clear();
out.reserve(buf_len);
out.extend_from_slice(&inner.buf[pos..]);
out.extend_from_slice(&inner.buf[..pos]);
}
pub fn snapshot_with_meta(&self) -> RawVizSnapshot {
let inner = self.samples.lock();
let buf_len = inner.buf.len();
let pos = inner.write_pos;
let mut samples = Vec::with_capacity(buf_len);
samples.extend_from_slice(&inner.buf[pos..]);
samples.extend_from_slice(&inner.buf[..pos]);
RawVizSnapshot {
samples,
channels: inner.channels,
sample_rate: inner.sample_rate,
}
}
pub fn channels(&self) -> u16 {
self.samples.lock().channels
}
pub fn sample_rate(&self) -> u32 {
self.samples.lock().sample_rate
}
}
impl Default for VizBuffer {
fn default() -> Self {
Self {
samples: Mutex::new(VizSamples {
buf: vec![0.0; DEFAULT_BUFFER_SIZE],
write_pos: 0,
channels: 2,
sample_rate: 44100,
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_and_snapshot_basic() {
let buf = VizBuffer::new();
let samples: Vec<f32> = (0..100).map(|i| i as f32).collect();
buf.push_samples(&samples, 2, 44100);
let snap = buf.snapshot();
assert_eq!(snap.len(), DEFAULT_BUFFER_SIZE);
let tail = &snap[DEFAULT_BUFFER_SIZE - 100..];
for (i, &val) in tail.iter().enumerate() {
assert_eq!(val, i as f32);
}
}
#[test]
fn push_wraps_around() {
let buf = VizBuffer::new();
let samples: Vec<f32> = (0..DEFAULT_BUFFER_SIZE as u32).map(|i| i as f32).collect();
buf.push_samples(&samples, 2, 44100);
let extra: Vec<f32> = (0..10).map(|i| (i + 1000) as f32).collect();
buf.push_samples(&extra, 2, 44100);
let snap = buf.snapshot();
let tail = &snap[DEFAULT_BUFFER_SIZE - 10..];
for (i, &val) in tail.iter().enumerate() {
assert_eq!(val, (i + 1000) as f32);
}
}
#[test]
fn push_larger_than_buffer() {
let buf = VizBuffer::new();
let big: Vec<f32> = (0..(DEFAULT_BUFFER_SIZE + 500) as u32)
.map(|i| i as f32)
.collect();
buf.push_samples(&big, 2, 48000);
let snap = buf.snapshot();
assert_eq!(snap.len(), DEFAULT_BUFFER_SIZE);
for (i, &val) in snap.iter().enumerate() {
assert_eq!(val, (i + 500) as f32);
}
assert_eq!(buf.sample_rate(), 48000);
}
#[test]
fn channels_and_sample_rate() {
let buf = VizBuffer::new();
assert_eq!(buf.channels(), 2);
assert_eq!(buf.sample_rate(), 44100);
buf.push_samples(&[1.0, 2.0], 1, 96000);
assert_eq!(buf.channels(), 1);
assert_eq!(buf.sample_rate(), 96000);
}
#[test]
fn viz_snapshot_read_write() {
let snap = VizSnapshot::new();
let frame = snap.read();
assert_eq!(frame.spectrum.len(), NUM_BARS);
assert_eq!(frame.vu_levels, [0.0, 0.0]);
let mut new_spectrum = [0.0f32; NUM_BARS];
new_spectrum[5] = 0.9;
snap.write(VizFrame {
spectrum: new_spectrum,
vu_levels: [0.5, 0.5],
timestamp: std::time::Instant::now(),
});
let frame2 = snap.read();
assert!((frame2.spectrum[5] - 0.9).abs() < 0.001);
assert!((frame2.vu_levels[0] - 0.5).abs() < 0.001);
}
}