use std::sync::{Arc, Mutex};
use std::time::Duration;
use async_trait::async_trait;
use thiserror::Error;
pub const SAMPLE_RATE: u32 = 48_000;
pub const OPUS_RATES: [u32; 5] = [8_000, 12_000, 16_000, 24_000, 48_000];
pub const FRAME: Duration = Duration::from_millis(20);
pub fn frame_samples(sample_rate: u32) -> usize {
(sample_rate / 50) as usize
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("audio: {0}")]
pub struct AudioError(pub String);
#[async_trait]
pub trait AudioSource: Send + 'static {
fn sample_rate(&self) -> u32 {
SAMPLE_RATE
}
async fn start(&mut self) -> Result<(), AudioError> {
Ok(())
}
async fn read(&mut self) -> Result<Vec<i16>, AudioError>;
async fn close(&mut self) {}
}
#[async_trait]
pub trait AudioSink: Send + 'static {
fn sample_rate(&self) -> u32 {
SAMPLE_RATE
}
async fn start(&mut self) -> Result<(), AudioError> {
Ok(())
}
async fn write(&mut self, pcm: &[i16]) -> Result<(), AudioError>;
async fn close(&mut self) {}
}
pub(crate) fn check_rate(rate: u32) -> Result<u32, AudioError> {
if OPUS_RATES.contains(&rate) {
Ok(rate)
} else {
Err(AudioError(format!("{rate} Hz is not an Opus rate ({OPUS_RATES:?})")))
}
}
#[derive(Debug, Clone)]
pub struct SilenceSource {
sample_rate: u32,
}
impl SilenceSource {
pub fn new() -> Self {
Self { sample_rate: SAMPLE_RATE }
}
}
impl Default for SilenceSource {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl AudioSource for SilenceSource {
fn sample_rate(&self) -> u32 {
self.sample_rate
}
async fn read(&mut self) -> Result<Vec<i16>, AudioError> {
Ok(vec![0; frame_samples(self.sample_rate)])
}
}
#[derive(Debug, Clone, Default)]
pub struct RecordingSink {
played: Arc<Mutex<Vec<i16>>>,
}
impl RecordingSink {
pub fn new() -> Self {
Self::default()
}
pub fn played(&self) -> Vec<i16> {
self.played.lock().map(|p| p.clone()).unwrap_or_default()
}
}
#[async_trait]
impl AudioSink for RecordingSink {
async fn write(&mut self, pcm: &[i16]) -> Result<(), AudioError> {
let mut played = self.played.lock().map_err(|_| AudioError("recording poisoned".into()))?;
played.extend_from_slice(pcm);
Ok(())
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct Drain;
#[async_trait]
impl AudioSink for Drain {
async fn write(&mut self, _pcm: &[i16]) -> Result<(), AudioError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn silence_comes_in_20_ms_frames() {
let mut source = SilenceSource::new();
assert_eq!(source.read().await.unwrap(), vec![0; 960]);
}
#[tokio::test]
async fn clones_of_a_recording_sink_share_what_was_played() {
let sink = RecordingSink::new();
let mut writer = sink.clone();
writer.write(&[1, 2]).await.unwrap();
writer.write(&[3]).await.unwrap();
assert_eq!(sink.played(), [1, 2, 3]);
}
#[test]
fn only_opus_rates_are_accepted() {
assert!(check_rate(16_000).is_ok());
assert!(check_rate(44_100).is_err());
}
}