use sim_kernel::{Error, Expr, Result, Symbol};
use crate::buffer::{expr_kind, field, symbol_field};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LatencyClass {
OfflineRender,
BlockLocal,
Interactive,
SampleExact,
BufferedPreview,
CollabBarDelay,
RemoteCollaboration,
}
impl LatencyClass {
pub fn wire_label(self) -> &'static str {
match self {
Self::OfflineRender => "offline-render",
Self::BlockLocal => "block-local",
Self::Interactive => "interactive",
Self::SampleExact => "sample-exact",
Self::BufferedPreview => "buffered-preview",
Self::CollabBarDelay => "collab-bardelay",
Self::RemoteCollaboration => "remote-collaboration",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("stream/latency", self.wire_label())
}
pub fn from_symbol(symbol: &Symbol) -> Result<Self> {
match symbol.as_qualified_str().as_str() {
"offline-render" | "stream/latency/offline-render" => Ok(Self::OfflineRender),
"block-local" | "stream/latency/block-local" => Ok(Self::BlockLocal),
"interactive" | "stream/latency/interactive" => Ok(Self::Interactive),
"sample-exact" | "stream/latency/sample-exact" => Ok(Self::SampleExact),
"buffered-preview" | "stream/latency/buffered-preview" => Ok(Self::BufferedPreview),
"collab-bardelay" | "stream/latency/collab-bardelay" => Ok(Self::CollabBarDelay),
"remote-collaboration" | "stream/latency/remote-collaboration" => {
Ok(Self::RemoteCollaboration)
}
other => Err(Error::Eval(format!("unknown stream latency class {other}"))),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StreamCapability {
Exact,
Deterministic,
Realtime,
Bounded,
Remote,
Replayable,
Preview,
Persistent,
Resumable,
Lossy,
}
impl StreamCapability {
pub fn wire_label(self) -> &'static str {
match self {
Self::Exact => "exact",
Self::Deterministic => "deterministic",
Self::Realtime => "realtime",
Self::Bounded => "bounded",
Self::Remote => "remote",
Self::Replayable => "replayable",
Self::Preview => "preview",
Self::Persistent => "persistent",
Self::Resumable => "resumable",
Self::Lossy => "lossy",
}
}
pub fn symbol(self) -> Symbol {
Symbol::qualified("stream/capability", self.wire_label())
}
pub fn from_symbol(symbol: &Symbol) -> Result<Self> {
match symbol.as_qualified_str().as_str() {
"exact" | "stream/capability/exact" => Ok(Self::Exact),
"deterministic" | "stream/capability/deterministic" => Ok(Self::Deterministic),
"realtime" | "stream/capability/realtime" => Ok(Self::Realtime),
"bounded" | "stream/capability/bounded" => Ok(Self::Bounded),
"remote" | "stream/capability/remote" => Ok(Self::Remote),
"replayable" | "stream/capability/replayable" => Ok(Self::Replayable),
"preview" | "stream/capability/preview" => Ok(Self::Preview),
"persistent" | "stream/capability/persistent" => Ok(Self::Persistent),
"resumable" | "stream/capability/resumable" => Ok(Self::Resumable),
"lossy" | "stream/capability/lossy" => Ok(Self::Lossy),
other => Err(Error::Eval(format!("unknown stream capability {other}"))),
}
}
pub fn latency_class(self) -> LatencyClass {
match self {
Self::Exact => LatencyClass::SampleExact,
Self::Deterministic => LatencyClass::OfflineRender,
Self::Realtime => LatencyClass::SampleExact,
Self::Bounded => LatencyClass::BlockLocal,
Self::Remote => LatencyClass::RemoteCollaboration,
Self::Replayable => LatencyClass::OfflineRender,
Self::Preview => LatencyClass::BufferedPreview,
Self::Persistent => LatencyClass::RemoteCollaboration,
Self::Resumable => LatencyClass::RemoteCollaboration,
Self::Lossy => LatencyClass::BufferedPreview,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransportProfile {
name: Symbol,
latency_class: LatencyClass,
capabilities: Vec<StreamCapability>,
}
impl TransportProfile {
pub fn new(
name: Symbol,
latency_class: LatencyClass,
capabilities: Vec<StreamCapability>,
) -> Result<Self> {
validate_capabilities(latency_class, &capabilities)?;
Ok(Self {
name,
latency_class,
capabilities,
})
}
pub fn memory_local() -> Self {
Self::new(
Symbol::qualified("stream/profile", "memory-local"),
LatencyClass::BlockLocal,
vec![
StreamCapability::Exact,
StreamCapability::Deterministic,
StreamCapability::Bounded,
StreamCapability::Replayable,
],
)
.expect("memory-local stream profile is valid")
}
pub fn realtime_local_audio() -> Self {
Self::new(
Symbol::qualified("stream/profile", "realtime-local-audio"),
LatencyClass::SampleExact,
vec![
StreamCapability::Exact,
StreamCapability::Realtime,
StreamCapability::Bounded,
],
)
.expect("realtime-local-audio stream profile is valid")
}
pub fn buffered_pcm_preview() -> Self {
Self::new(
Symbol::qualified("stream/profile", "buffered-pcm-preview"),
LatencyClass::BufferedPreview,
vec![
StreamCapability::Bounded,
StreamCapability::Preview,
StreamCapability::Lossy,
],
)
.expect("buffered-pcm-preview stream profile is valid")
}
pub fn remote_stream_fabric() -> Self {
Self::new(
Symbol::qualified("stream/profile", "remote-stream-fabric"),
LatencyClass::RemoteCollaboration,
vec![
StreamCapability::Remote,
StreamCapability::Bounded,
StreamCapability::Replayable,
StreamCapability::Resumable,
],
)
.expect("remote-stream-fabric stream profile is valid")
}
pub fn lan_midi_control() -> Self {
Self::new(
Symbol::qualified("stream/profile", "lan-midi-control"),
LatencyClass::Interactive,
vec![
StreamCapability::Remote,
StreamCapability::Bounded,
StreamCapability::Replayable,
],
)
.expect("lan-midi-control stream profile is valid")
}
pub fn lan_buffered_audio_preview() -> Self {
Self::new(
Symbol::qualified("stream/profile", "lan-buffered-audio-preview"),
LatencyClass::BufferedPreview,
vec![
StreamCapability::Remote,
StreamCapability::Bounded,
StreamCapability::Preview,
StreamCapability::Lossy,
],
)
.expect("lan-buffered-audio-preview stream profile is valid")
}
pub fn lan_render_return() -> Self {
Self::new(
Symbol::qualified("stream/profile", "lan-render-return"),
LatencyClass::OfflineRender,
vec![
StreamCapability::Remote,
StreamCapability::Bounded,
StreamCapability::Deterministic,
StreamCapability::Replayable,
StreamCapability::Resumable,
],
)
.expect("lan-render-return stream profile is valid")
}
pub fn name(&self) -> &Symbol {
&self.name
}
pub fn latency_class(&self) -> LatencyClass {
self.latency_class
}
pub fn capabilities(&self) -> &[StreamCapability] {
&self.capabilities
}
pub fn has_capability(&self, capability: StreamCapability) -> bool {
self.capabilities.contains(&capability)
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("name")),
Expr::Symbol(self.name.clone()),
),
(
Expr::Symbol(Symbol::new("latency-class")),
Expr::Symbol(self.latency_class.symbol()),
),
(
Expr::Symbol(Symbol::new("capabilities")),
Expr::List(
self.capabilities
.iter()
.map(|capability| Expr::Symbol(capability.symbol()))
.collect(),
),
),
])
}
pub fn from_expr(expr: &Expr) -> Result<Self> {
let Expr::Map(entries) = expr else {
return Err(Error::TypeMismatch {
expected: "stream transport profile map",
found: expr_kind(expr),
});
};
ensure_fields(entries, &["name", "latency-class", "capabilities"])?;
Self::new(
symbol_field(entries, "name")?.clone(),
LatencyClass::from_symbol(symbol_field(entries, "latency-class")?)?,
symbol_list(entries, "capabilities")?
.iter()
.map(StreamCapability::from_symbol)
.collect::<Result<Vec<_>>>()?,
)
}
}
fn validate_capabilities(
latency_class: LatencyClass,
capabilities: &[StreamCapability],
) -> Result<()> {
let has = |needle| capabilities.contains(&needle);
if has(StreamCapability::Exact) && has(StreamCapability::Lossy) {
return Err(Error::Eval(
"stream capabilities exact and lossy cannot be combined".to_owned(),
));
}
if latency_class == LatencyClass::SampleExact && has(StreamCapability::Remote) {
return Err(Error::Eval(
"remote streams cannot claim sample-exact latency".to_owned(),
));
}
if latency_class == LatencyClass::RemoteCollaboration && has(StreamCapability::Realtime) {
return Err(Error::Eval(
"remote-collaboration streams cannot claim realtime capability".to_owned(),
));
}
if latency_class == LatencyClass::OfflineRender && has(StreamCapability::Realtime) {
return Err(Error::Eval(
"offline-render streams cannot claim realtime capability".to_owned(),
));
}
Ok(())
}
fn symbol_list(entries: &[(Expr, Expr)], name: &str) -> Result<Vec<Symbol>> {
list_field(entries, name)?
.iter()
.map(|expr| match expr {
Expr::Symbol(symbol) => Ok(symbol.clone()),
other => Err(Error::TypeMismatch {
expected: "symbol list item",
found: expr_kind(other),
}),
})
.collect()
}
fn list_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a [Expr]> {
match field(entries, name)? {
Expr::List(items) => Ok(items),
other => Err(Error::TypeMismatch {
expected: "list field",
found: expr_kind(other),
}),
}
}
fn ensure_fields(entries: &[(Expr, Expr)], allowed: &[&str]) -> Result<()> {
for (key, _) in entries {
let Expr::Symbol(symbol) = key else {
return Err(Error::TypeMismatch {
expected: "symbol stream profile field",
found: expr_kind(key),
});
};
if symbol.namespace.is_none() && allowed.contains(&symbol.name.as_ref()) {
continue;
}
return Err(Error::Eval(format!(
"unknown stream profile field {}",
symbol.as_qualified_str()
)));
}
Ok(())
}