use std::fmt::Display;
use std::str::FromStr;
use sim_kernel::{Cx, Datum, DatumStore, Error, Expr, Ref, Result, Symbol};
use crate::buffer::{expr_kind, field, string_field, symbol_field};
use crate::metadata::StreamMedia;
#[path = "packet/pcm.rs"]
mod pcm;
pub use pcm::{PcmPacket, PcmSampleFormat};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MidiPacketEvent {
ticks: i64,
tpq: u16,
bytes: Vec<u8>,
}
impl MidiPacketEvent {
pub fn new(ticks: i64, tpq: u16, bytes: Vec<u8>) -> Result<Self> {
if tpq == 0 {
return Err(Error::Eval(
"MIDI packet TPQ must be greater than zero".to_owned(),
));
}
Ok(Self { ticks, tpq, bytes })
}
pub fn ticks(&self) -> i64 {
self.ticks
}
pub fn tpq(&self) -> u16 {
self.tpq
}
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MidiPacket {
tpq: u16,
events: Vec<MidiPacketEvent>,
}
impl MidiPacket {
pub fn new(events: Vec<MidiPacketEvent>) -> Result<Self> {
let Some(first) = events.first() else {
return Err(Error::Eval(
"MIDI packet must contain at least one event".to_owned(),
));
};
let tpq = first.tpq;
if events.iter().any(|event| event.tpq != tpq) {
return Err(Error::Eval(
"MIDI packet events must use one shared TPQ".to_owned(),
));
}
Ok(Self { tpq, events })
}
pub fn tpq(&self) -> u16 {
self.tpq
}
pub fn events(&self) -> &[MidiPacketEvent] {
&self.events
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("packet")),
Expr::Symbol(Symbol::qualified("stream/packet", "midi")),
),
(
Expr::Symbol(Symbol::new("tpq")),
Expr::String(self.tpq.to_string()),
),
(
Expr::Symbol(Symbol::new("events")),
Expr::List(self.events.iter().map(midi_event_expr).collect()),
),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StreamDiagnostic {
kind: Symbol,
message: String,
}
impl StreamDiagnostic {
pub fn new(kind: Symbol, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
}
}
pub fn kind(&self) -> &Symbol {
&self.kind
}
pub fn message(&self) -> &str {
&self.message
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("packet")),
Expr::Symbol(Symbol::qualified("stream/packet", "diagnostic")),
),
(
Expr::Symbol(Symbol::new("kind")),
Expr::Symbol(self.kind.clone()),
),
(
Expr::Symbol(Symbol::new("message")),
Expr::String(self.message.clone()),
),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DataPacket {
pub kind: Symbol,
pub payload: Expr,
}
impl DataPacket {
pub fn new(kind: Symbol, payload: Expr) -> Self {
Self { kind, payload }
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("packet")),
Expr::Symbol(Symbol::qualified("stream/packet", "data")),
),
(
Expr::Symbol(Symbol::new("kind")),
Expr::Symbol(self.kind.clone()),
),
(Expr::Symbol(Symbol::new("payload")), self.payload.clone()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StreamPacket {
Pcm(PcmPacket),
Midi(MidiPacket),
Diagnostic(StreamDiagnostic),
Data(DataPacket),
}
impl StreamPacket {
pub fn media(&self) -> StreamMedia {
match self {
Self::Pcm(_) => StreamMedia::Pcm,
Self::Midi(_) => StreamMedia::Midi,
Self::Diagnostic(_) => StreamMedia::Diagnostic,
Self::Data(_) => StreamMedia::Data,
}
}
pub fn data(kind: Symbol, payload: Expr) -> Self {
Self::Data(DataPacket::new(kind, payload))
}
pub fn model_event(payload: Expr) -> Self {
Self::data(Symbol::qualified("stream/data", "model-event"), payload)
}
pub fn rank_frontier(payload: Expr) -> Self {
Self::data(Symbol::qualified("stream/data", "rank-frontier"), payload)
}
pub fn to_expr(&self) -> Expr {
match self {
Self::Pcm(packet) => packet.to_expr(),
Self::Midi(packet) => packet.to_expr(),
Self::Diagnostic(packet) => packet.to_expr(),
Self::Data(packet) => packet.to_expr(),
}
}
pub fn intern_ref(&self, cx: &mut Cx) -> Result<Ref> {
let datum = Datum::try_from(self.to_expr())?;
cx.datum_store_mut().intern(datum).map(Ref::Content)
}
}
impl TryFrom<Expr> for StreamPacket {
type Error = Error;
fn try_from(expr: Expr) -> Result<Self> {
let Expr::Map(entries) = &expr else {
return Err(Error::TypeMismatch {
expected: "stream packet map",
found: expr_kind(&expr),
});
};
let packet = packet_symbol(entries)?;
match packet.as_qualified_str().as_str() {
"stream/packet/pcm" => PcmPacket::from_entries(entries).map(Self::Pcm),
"stream/packet/midi" => MidiPacket::from_entries(entries).map(Self::Midi),
"stream/packet/diagnostic" => {
StreamDiagnostic::from_entries(entries).map(Self::Diagnostic)
}
"stream/packet/data" => DataPacket::from_entries(entries).map(Self::Data),
other => Err(Error::Eval(format!("unknown stream packet kind {other}"))),
}
}
}
impl MidiPacket {
fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
let tpq = parse_string_field::<u16>(entries, "tpq")?;
let events = list_field(entries, "events")?
.iter()
.enumerate()
.map(|(index, expr)| {
let event = MidiPacketEvent::from_expr(expr)?;
if event.tpq() != tpq {
return Err(Error::Eval(format!(
"MIDI packet event {index} TPQ {} does not match packet TPQ {tpq}",
event.tpq()
)));
}
Ok(event)
})
.collect::<Result<Vec<_>>>()?;
Self::new(events)
}
}
impl MidiPacketEvent {
fn from_expr(expr: &Expr) -> Result<Self> {
let Expr::Map(entries) = expr else {
return Err(Error::TypeMismatch {
expected: "MIDI packet event map",
found: expr_kind(expr),
});
};
let ticks = parse_string_field::<i64>(entries, "ticks")?;
let tpq = parse_string_field::<u16>(entries, "tpq")?;
let bytes = bytes_field(entries, "bytes")?.to_vec();
Self::new(ticks, tpq, bytes)
}
}
impl StreamDiagnostic {
fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
Ok(Self::new(
symbol_field(entries, "kind")?.clone(),
string_field(entries, "message")?.to_owned(),
))
}
}
impl DataPacket {
fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
ensure_data_fields_closed(entries)?;
Ok(Self::new(
symbol_field(entries, "kind")?.clone(),
field(entries, "payload")?.clone(),
))
}
}
fn packet_symbol(entries: &[(Expr, Expr)]) -> Result<&Symbol> {
entries
.iter()
.find_map(|(key, value)| match (key, value) {
(Expr::Symbol(key), Expr::Symbol(value)) if key.name.as_ref() == "packet" => {
Some(value)
}
_ => None,
})
.ok_or_else(|| Error::Eval("stream packet missing packet symbol".to_owned()))
}
fn ensure_data_fields_closed(entries: &[(Expr, Expr)]) -> Result<()> {
for (key, _) in entries {
let Expr::Symbol(symbol) = key else {
return Err(Error::TypeMismatch {
expected: "symbol data packet field",
found: expr_kind(key),
});
};
if symbol.namespace.is_none()
&& matches!(symbol.name.as_ref(), "packet" | "kind" | "payload")
{
continue;
}
return Err(Error::Eval(format!(
"unknown data packet field {}",
symbol.as_qualified_str()
)));
}
Ok(())
}
pub(super) fn parse_string_field<T>(entries: &[(Expr, Expr)], name: &str) -> Result<T>
where
T: FromStr,
T::Err: Display,
{
string_field(entries, name)?
.parse::<T>()
.map_err(|err| Error::Eval(format!("invalid stream packet {name}: {err}")))
}
pub(super) fn parse_string_expr<T>(expr: &Expr, expected: &'static str) -> Result<T>
where
T: FromStr,
T::Err: Display,
{
match expr {
Expr::String(value) => value
.parse::<T>()
.map_err(|err| Error::Eval(format!("{expected} parse failed: {err}"))),
other => Err(Error::TypeMismatch {
expected,
found: expr_kind(other),
}),
}
}
pub(super) 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 bytes_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a [u8]> {
match field(entries, name)? {
Expr::Bytes(bytes) => Ok(bytes),
other => Err(Error::TypeMismatch {
expected: "bytes field",
found: expr_kind(other),
}),
}
}
fn midi_event_expr(event: &MidiPacketEvent) -> Expr {
Expr::Map(vec![
(
Expr::Symbol(Symbol::new("ticks")),
Expr::String(event.ticks.to_string()),
),
(
Expr::Symbol(Symbol::new("tpq")),
Expr::String(event.tpq.to_string()),
),
(
Expr::Symbol(Symbol::new("bytes")),
Expr::Bytes(event.bytes.clone()),
),
])
}