use anyhow::Result;
#[derive(Debug, Clone)]
pub struct AudioBuffer {
pub samples: Vec<f32>,
pub channels: u16,
pub sample_rate: u32,
}
pub type AudioCallback = Box<dyn FnMut(&mut [f32]) + Send>;
pub trait AudioBackend: Send {
fn start(&mut self, callback: AudioCallback) -> Result<()>;
fn stop(&mut self) -> Result<()>;
fn sample_rate(&self) -> u32;
fn buffer_size(&self) -> u32;
fn channels(&self) -> u16;
}
pub struct TestBackend {
sample_rate: u32,
buffer_size: u32,
channels: u16,
captured: Vec<f32>,
}
impl TestBackend {
pub fn new(sample_rate: u32, buffer_size: u32, channels: u16) -> Self {
Self {
sample_rate,
buffer_size,
channels,
captured: Vec::new(),
}
}
pub fn process_blocks(
&mut self,
num_buffers: usize,
mut callback: impl FnMut(&mut [f32]),
) -> AudioBuffer {
let block_size = self.buffer_size as usize * self.channels as usize;
self.captured.clear();
self.captured.reserve(block_size * num_buffers);
for _ in 0..num_buffers {
let start = self.captured.len();
self.captured.resize(start + block_size, 0.0);
callback(&mut self.captured[start..]);
}
AudioBuffer {
samples: self.captured.clone(),
channels: self.channels,
sample_rate: self.sample_rate,
}
}
}
impl AudioBackend for TestBackend {
fn start(&mut self, _callback: Box<dyn FnMut(&mut [f32]) + Send>) -> Result<()> {
Ok(())
}
fn stop(&mut self) -> Result<()> {
Ok(())
}
fn sample_rate(&self) -> u32 {
self.sample_rate
}
fn buffer_size(&self) -> u32 {
self.buffer_size
}
fn channels(&self) -> u16 {
self.channels
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backend_captures_silence() {
let mut backend = TestBackend::new(44100, 64, 2);
let result = backend.process_blocks(10, |_buf| {
});
assert_eq!(result.samples.len(), 64 * 2 * 10);
assert!(result.samples.iter().all(|&s| s == 0.0));
}
#[test]
fn test_backend_captures_signal() {
let mut backend = TestBackend::new(44100, 64, 1);
let mut phase = 0.0f32;
let result = backend.process_blocks(10, |buf| {
for sample in buf.iter_mut() {
*sample = phase.sin();
phase += 440.0 * std::f32::consts::TAU / 44100.0;
}
});
assert_eq!(result.samples.len(), 64 * 10);
assert!(result.samples.iter().any(|&s| s.abs() > 0.1));
assert!(result.samples.iter().all(|s| s.is_finite()));
}
#[test]
fn test_backend_correct_metadata() {
let backend = TestBackend::new(48000, 128, 2);
assert_eq!(backend.sample_rate(), 48000);
assert_eq!(backend.buffer_size(), 128);
assert_eq!(backend.channels(), 2);
}
}