use sim_kernel::{Error, Result, Symbol};
use crate::{
BufferPolicy, ClockDomain, LatencyClass, RateContract, StreamDirection, StreamEdge,
StreamMedia, StreamMetadata,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DomainBridgeKind {
Resampler,
JitterBuffer,
LatencyCompDelay,
EventRateGate,
}
impl DomainBridgeKind {
pub fn name(self) -> &'static str {
match self {
Self::Resampler => "resampler",
Self::JitterBuffer => "jitter-buffer",
Self::LatencyCompDelay => "latency-comp-delay",
Self::EventRateGate => "event-rate-gate",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("stream/bridge", self.name())
}
pub fn diagnostic_symbol(self) -> Symbol {
Symbol::qualified("stream/bridge-diagnostic", self.name())
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct BridgeLatency {
frames: u64,
packets: u32,
}
impl BridgeLatency {
pub fn zero() -> Self {
Self {
frames: 0,
packets: 0,
}
}
pub fn frames(frames: u64) -> Self {
Self { frames, packets: 0 }
}
pub fn packets(packets: u32) -> Self {
Self { frames: 0, packets }
}
pub fn frames_and_packets(frames: u64, packets: u32) -> Self {
Self { frames, packets }
}
pub fn frame_count(self) -> u64 {
self.frames
}
pub fn packet_count(self) -> u32 {
self.packets
}
pub fn plus(self, other: Self) -> Self {
Self {
frames: self.frames.saturating_add(other.frames),
packets: self.packets.saturating_add(other.packets),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DomainBridgeDescriptor {
kind: DomainBridgeKind,
input_rate: RateContract,
output_rate: RateContract,
latency: BridgeLatency,
diagnostics: Vec<Symbol>,
}
impl DomainBridgeDescriptor {
pub fn new(
kind: DomainBridgeKind,
input_rate: RateContract,
output_rate: RateContract,
latency: BridgeLatency,
diagnostics: Vec<Symbol>,
) -> Self {
Self {
kind,
input_rate,
output_rate,
latency,
diagnostics,
}
}
pub fn resampler(input_hz: u32, output_hz: u32) -> Result<Self> {
if input_hz == 0 || output_hz == 0 {
return Err(Error::Eval(
"resampler rates must be greater than zero".to_owned(),
));
}
Ok(Self::new(
DomainBridgeKind::Resampler,
RateContract::sample_exact(Some(input_hz)),
RateContract::sample_exact(Some(output_hz)),
BridgeLatency::frames(32),
vec![DomainBridgeKind::Resampler.diagnostic_symbol()],
))
}
pub fn jitter_buffer(max_late_packets: u32) -> Self {
Self::new(
DomainBridgeKind::JitterBuffer,
RateContract::new(ClockDomain::Wall, LatencyClass::BufferedPreview, None),
RateContract::new(ClockDomain::Wall, LatencyClass::BufferedPreview, None),
BridgeLatency::packets(max_late_packets),
vec![DomainBridgeKind::JitterBuffer.diagnostic_symbol()],
)
}
pub fn latency_comp_delay(frames: u64) -> Self {
Self::new(
DomainBridgeKind::LatencyCompDelay,
RateContract::block_local(),
RateContract::block_local(),
BridgeLatency::frames(frames),
vec![DomainBridgeKind::LatencyCompDelay.diagnostic_symbol()],
)
}
pub fn event_rate_gate(input_domain: ClockDomain) -> Result<Self> {
let input_rate = match input_domain {
ClockDomain::Control => RateContract::control(),
ClockDomain::MidiTick => RateContract::midi_tick(),
other => {
return Err(Error::Eval(format!(
"event-rate-gate cannot accept {} input",
other.wire_label()
)));
}
};
Ok(Self::new(
DomainBridgeKind::EventRateGate,
input_rate,
RateContract::block_local(),
BridgeLatency::zero(),
vec![DomainBridgeKind::EventRateGate.diagnostic_symbol()],
))
}
pub fn kind(&self) -> DomainBridgeKind {
self.kind
}
pub fn name(&self) -> &'static str {
self.kind.name()
}
pub fn input_rate(&self) -> RateContract {
self.input_rate
}
pub fn output_rate(&self) -> RateContract {
self.output_rate
}
pub fn latency(&self) -> BridgeLatency {
self.latency
}
pub fn diagnostics(&self) -> &[Symbol] {
&self.diagnostics
}
pub fn input_edge(&self, media: StreamMedia) -> StreamEdge {
StreamEdge::new(
Symbol::new("in"),
self.input_rate,
bridge_metadata(
self.kind,
"in",
media,
StreamDirection::Sink,
self.input_rate,
),
)
}
pub fn output_edge(&self, media: StreamMedia) -> StreamEdge {
StreamEdge::new(
Symbol::new("out"),
self.output_rate,
bridge_metadata(
self.kind,
"out",
media,
StreamDirection::Source,
self.output_rate,
),
)
}
}
fn bridge_metadata(
kind: DomainBridgeKind,
port: &str,
media: StreamMedia,
direction: StreamDirection,
rate: RateContract,
) -> StreamMetadata {
StreamMetadata::new(
Symbol::qualified("stream/bridge-edge", format!("{}-{port}", kind.name())),
media,
direction,
rate.clock_domain().symbol(),
BufferPolicy::bounded(1).expect("bridge metadata uses a nonzero buffer"),
)
}