use std::collections::VecDeque;
use sim_kernel::{Error, Result};
use crate::{PcmBuffer, PcmSpec};
pub trait PcmSource {
fn spec(&self) -> &PcmSpec;
fn read_buffer(&mut self) -> Result<Option<PcmBuffer>>;
}
pub trait PcmSink {
fn spec(&self) -> &PcmSpec;
fn write_buffer(&mut self, buffer: PcmBuffer) -> Result<()>;
fn flush(&mut self) -> Result<()>;
}
#[derive(Clone, Debug)]
pub struct MemoryPcmSource {
spec: PcmSpec,
buffers: VecDeque<PcmBuffer>,
}
impl MemoryPcmSource {
pub fn new(spec: PcmSpec, buffers: Vec<PcmBuffer>) -> Result<Self> {
for buffer in &buffers {
ensure_spec("source", spec, buffer.spec())?;
}
Ok(Self {
spec,
buffers: buffers.into(),
})
}
pub fn remaining(&self) -> usize {
self.buffers.len()
}
}
impl PcmSource for MemoryPcmSource {
fn spec(&self) -> &PcmSpec {
&self.spec
}
fn read_buffer(&mut self) -> Result<Option<PcmBuffer>> {
Ok(self.buffers.pop_front())
}
}
#[derive(Clone, Debug)]
pub struct MemoryPcmSink {
spec: PcmSpec,
buffers: Vec<PcmBuffer>,
flush_count: usize,
}
impl MemoryPcmSink {
pub fn new(spec: PcmSpec) -> Self {
Self {
spec,
buffers: Vec::new(),
flush_count: 0,
}
}
pub fn buffers(&self) -> &[PcmBuffer] {
&self.buffers
}
pub fn flush_count(&self) -> usize {
self.flush_count
}
pub fn into_buffers(self) -> Vec<PcmBuffer> {
self.buffers
}
}
impl PcmSink for MemoryPcmSink {
fn spec(&self) -> &PcmSpec {
&self.spec
}
fn write_buffer(&mut self, buffer: PcmBuffer) -> Result<()> {
ensure_spec("sink", self.spec, buffer.spec())?;
self.buffers.push(buffer);
Ok(())
}
fn flush(&mut self) -> Result<()> {
self.flush_count += 1;
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PcmPumpSummary {
buffers: usize,
frames: usize,
}
impl PcmPumpSummary {
pub fn buffers(self) -> usize {
self.buffers
}
pub fn frames(self) -> usize {
self.frames
}
pub(crate) fn record(&mut self, buffer: &PcmBuffer) {
self.buffers += 1;
self.frames += buffer.frames();
}
}
pub fn pump_pcm(source: &mut impl PcmSource, sink: &mut impl PcmSink) -> Result<PcmPumpSummary> {
ensure_spec("sink", *source.spec(), *sink.spec())?;
let mut summary = PcmPumpSummary::default();
while let Some(buffer) = source.read_buffer()? {
summary.record(&buffer);
sink.write_buffer(buffer)?;
}
sink.flush()?;
Ok(summary)
}
pub(crate) fn ensure_spec(role: &str, expected: PcmSpec, actual: PcmSpec) -> Result<()> {
if expected != actual {
return Err(Error::Eval(format!(
"PCM {role} spec mismatch: expected {:?}, got {:?}",
expected, actual
)));
}
Ok(())
}