use crate::error::Result;
use crate::latent::{LatentBlock, LatentKind};
use crate::seams::AudioDecoder;
use crate::signal::PcmBlock;
use crate::time::{Frames, TimeBase};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct StreamSpec {
block: Frames,
lookahead: Frames,
time_base: TimeBase,
}
impl StreamSpec {
pub fn new(block: Frames, lookahead: Frames, time_base: TimeBase) -> Result<Self> {
if block.get() == 0 {
return Err(crate::error::Error::validation(
"stream block size must be nonzero",
));
}
Ok(Self {
block,
lookahead,
time_base,
})
}
pub fn block(&self) -> Frames {
self.block
}
pub fn lookahead(&self) -> Frames {
self.lookahead
}
pub fn time_base(&self) -> TimeBase {
self.time_base
}
pub fn latency_seconds(&self) -> f64 {
self.time_base.seconds(Frames(
self.block.get().saturating_add(self.lookahead.get()),
))
}
}
pub trait StreamingDecoder<K: LatentKind>: AudioDecoder<K> {
type Session: StreamSession<K>;
fn spec(&self) -> StreamSpec;
fn open(&self) -> Self::Session;
}
pub trait StreamSession<K: LatentKind> {
fn push(&mut self, block: &LatentBlock<K>) -> Result<PcmBlock>;
fn finish(self) -> Result<PcmBlock>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::time::SampleRate;
use std::num::NonZeroU32;
fn tb() -> TimeBase {
TimeBase::new(
SampleRate::try_from(48_000).unwrap(),
NonZeroU32::new(480).unwrap(),
)
}
#[test]
fn spec_rejects_zero_block() {
assert!(StreamSpec::new(Frames(0), Frames(0), tb()).is_err());
assert!(StreamSpec::new(Frames(1), Frames(0), tb()).is_ok());
}
#[test]
fn latency_is_block_plus_lookahead_on_the_clock() {
let spec = StreamSpec::new(Frames(4), Frames(2), tb()).unwrap();
assert!((spec.latency_seconds() - 0.060).abs() < 1e-12);
let causal = StreamSpec::new(Frames(3), Frames(0), tb()).unwrap();
assert!((causal.latency_seconds() - 0.030).abs() < 1e-12);
}
}