use core::future::Future;
use crate::AudioFrame;
pub trait AudioSource: Send {
fn next_frame(&mut self) -> impl Future<Output = Option<AudioFrame<'static>>> + Send;
}
pub trait AudioSink: Send {
fn write_frame(&mut self, frame: AudioFrame<'_>) -> impl Future<Output = ()> + Send;
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct VecSink {
frames: Vec<AudioFrame<'static>>,
}
impl AudioSink for VecSink {
async fn write_frame(&mut self, frame: AudioFrame<'_>) {
self.frames.push(frame.into_owned());
}
}
struct OnceSource {
frame: Option<AudioFrame<'static>>,
}
impl AudioSource for OnceSource {
async fn next_frame(&mut self) -> Option<AudioFrame<'static>> {
self.frame.take()
}
}
struct QueueSource {
frames: std::collections::VecDeque<AudioFrame<'static>>,
}
impl AudioSource for QueueSource {
async fn next_frame(&mut self) -> Option<AudioFrame<'static>> {
self.frames.pop_front()
}
}
struct CapSink {
cap: usize,
frames: Vec<AudioFrame<'static>>,
dropped: usize,
}
impl AudioSink for CapSink {
async fn write_frame(&mut self, frame: AudioFrame<'_>) {
if self.frames.len() >= self.cap {
self.dropped += 1;
return;
}
self.frames.push(frame.into_owned());
}
}
#[tokio::test]
async fn traits_compose_end_to_end() {
let mut source = OnceSource {
frame: Some(AudioFrame::from_vec(vec![0.5, -0.5], 8000)),
};
let mut sink = VecSink::default();
let frame = source.next_frame().await.expect("frame");
sink.write_frame(frame).await;
assert!(source.next_frame().await.is_none());
assert_eq!(sink.frames.len(), 1);
assert_eq!(sink.frames[0].samples(), &[0.5, -0.5]);
assert_eq!(sink.frames[0].sample_rate(), 8000);
}
#[tokio::test]
async fn source_drains_in_order_then_returns_none() {
let mut source = QueueSource {
frames: vec![
AudioFrame::from_vec(vec![0.1], 8000),
AudioFrame::from_vec(vec![0.2], 8000),
AudioFrame::from_vec(vec![0.3], 8000),
]
.into(),
};
let mut sink = VecSink::default();
while let Some(f) = source.next_frame().await {
sink.write_frame(f).await;
}
assert_eq!(sink.frames.len(), 3);
assert_eq!(sink.frames[0].samples(), &[0.1]);
assert_eq!(sink.frames[1].samples(), &[0.2]);
assert_eq!(sink.frames[2].samples(), &[0.3]);
assert!(source.next_frame().await.is_none());
assert!(source.next_frame().await.is_none());
}
#[tokio::test]
async fn sink_with_capacity_drops_overflow() {
let mut sink = CapSink {
cap: 2,
frames: Vec::new(),
dropped: 0,
};
for i in 0..5 {
sink.write_frame(AudioFrame::from_vec(vec![i as f32], 8000))
.await;
}
assert_eq!(sink.frames.len(), 2);
assert_eq!(sink.dropped, 3);
assert_eq!(sink.frames[0].samples(), &[0.0]);
assert_eq!(sink.frames[1].samples(), &[1.0]);
}
#[tokio::test]
async fn frame_sample_rate_round_trips_through_sink() {
let mut sink = VecSink::default();
sink.write_frame(AudioFrame::from_vec(vec![0.0], 8000))
.await;
sink.write_frame(AudioFrame::from_vec(vec![0.0], 16_000))
.await;
sink.write_frame(AudioFrame::from_vec(vec![0.0], 48_000))
.await;
let rates: Vec<u32> = sink.frames.iter().map(|f| f.sample_rate()).collect();
assert_eq!(rates, vec![8000, 16_000, 48_000]);
}
#[test]
fn impls_are_send() {
fn assert_send<T: Send>(_: &T) {}
let source = OnceSource { frame: None };
let sink = VecSink::default();
assert_send(&source);
assert_send(&sink);
}
}