1use std::fmt::Display;
14use std::str::FromStr;
15
16use sim_kernel::{Cx, Datum, DatumStore, Error, Expr, Ref, Result, Symbol};
17use sim_value::access;
18
19use crate::buffer::{expr_kind, field, string_field, symbol_field};
20use crate::metadata::StreamMedia;
21
22#[path = "packet/pcm.rs"]
23mod pcm;
24
25pub use pcm::{PcmPacket, PcmSampleFormat};
26
27#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct MidiPacketEvent {
33 ticks: i64,
34 tpq: u16,
35 bytes: Vec<u8>,
36}
37
38impl MidiPacketEvent {
39 pub fn new(ticks: i64, tpq: u16, bytes: Vec<u8>) -> Result<Self> {
44 if tpq == 0 {
45 return Err(Error::Eval(
46 "MIDI packet TPQ must be greater than zero".to_owned(),
47 ));
48 }
49 Ok(Self { ticks, tpq, bytes })
50 }
51
52 pub fn ticks(&self) -> i64 {
54 self.ticks
55 }
56
57 pub fn tpq(&self) -> u16 {
59 self.tpq
60 }
61
62 pub fn bytes(&self) -> &[u8] {
64 &self.bytes
65 }
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
71pub struct MidiPacket {
72 tpq: u16,
73 events: Vec<MidiPacketEvent>,
74}
75
76impl MidiPacket {
77 pub fn new(events: Vec<MidiPacketEvent>) -> Result<Self> {
82 let Some(first) = events.first() else {
83 return Err(Error::Eval(
84 "MIDI packet must contain at least one event".to_owned(),
85 ));
86 };
87 let tpq = first.tpq;
88 if events.iter().any(|event| event.tpq != tpq) {
89 return Err(Error::Eval(
90 "MIDI packet events must use one shared TPQ".to_owned(),
91 ));
92 }
93 Ok(Self { tpq, events })
94 }
95
96 pub fn tpq(&self) -> u16 {
98 self.tpq
99 }
100
101 pub fn events(&self) -> &[MidiPacketEvent] {
103 &self.events
104 }
105
106 pub fn to_expr(&self) -> Expr {
108 Expr::Map(vec![
109 (
110 Expr::Symbol(Symbol::new("packet")),
111 Expr::Symbol(Symbol::qualified("stream/packet", "midi")),
112 ),
113 (
114 Expr::Symbol(Symbol::new("tpq")),
115 Expr::String(self.tpq.to_string()),
116 ),
117 (
118 Expr::Symbol(Symbol::new("events")),
119 Expr::List(self.events.iter().map(midi_event_expr).collect()),
120 ),
121 ])
122 }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq)]
128pub struct StreamDiagnostic {
129 kind: Symbol,
130 message: String,
131}
132
133impl StreamDiagnostic {
134 pub fn new(kind: Symbol, message: impl Into<String>) -> Self {
136 Self {
137 kind,
138 message: message.into(),
139 }
140 }
141
142 pub fn kind(&self) -> &Symbol {
144 &self.kind
145 }
146
147 pub fn message(&self) -> &str {
149 &self.message
150 }
151
152 pub fn to_expr(&self) -> Expr {
154 Expr::Map(vec![
155 (
156 Expr::Symbol(Symbol::new("packet")),
157 Expr::Symbol(Symbol::qualified("stream/packet", "diagnostic")),
158 ),
159 (
160 Expr::Symbol(Symbol::new("kind")),
161 Expr::Symbol(self.kind.clone()),
162 ),
163 (
164 Expr::Symbol(Symbol::new("message")),
165 Expr::String(self.message.clone()),
166 ),
167 ])
168 }
169}
170
171#[derive(Clone, Debug, PartialEq, Eq)]
177pub struct DataPacket {
178 pub kind: Symbol,
180 pub payload: Expr,
182}
183
184impl DataPacket {
185 pub fn new(kind: Symbol, payload: Expr) -> Self {
187 Self { kind, payload }
188 }
189
190 pub fn to_expr(&self) -> Expr {
192 Expr::Map(vec![
193 (
194 Expr::Symbol(Symbol::new("packet")),
195 Expr::Symbol(Symbol::qualified("stream/packet", "data")),
196 ),
197 (
198 Expr::Symbol(Symbol::new("kind")),
199 Expr::Symbol(self.kind.clone()),
200 ),
201 (Expr::Symbol(Symbol::new("payload")), self.payload.clone()),
202 ])
203 }
204}
205
206#[derive(Clone, Debug, PartialEq, Eq)]
212pub enum StreamPacket {
213 Pcm(PcmPacket),
215 Midi(MidiPacket),
217 Diagnostic(StreamDiagnostic),
219 Data(DataPacket),
221}
222
223impl StreamPacket {
224 pub fn media(&self) -> StreamMedia {
226 match self {
227 Self::Pcm(_) => StreamMedia::Pcm,
228 Self::Midi(_) => StreamMedia::Midi,
229 Self::Diagnostic(_) => StreamMedia::Diagnostic,
230 Self::Data(_) => StreamMedia::Data,
231 }
232 }
233
234 pub fn data(kind: Symbol, payload: Expr) -> Self {
237 Self::Data(DataPacket::new(kind, payload))
238 }
239
240 pub fn model_event(payload: Expr) -> Self {
242 Self::data(Symbol::qualified("stream/data", "model-event"), payload)
243 }
244
245 pub fn rank_frontier(payload: Expr) -> Self {
247 Self::data(Symbol::qualified("stream/data", "rank-frontier"), payload)
248 }
249
250 pub fn to_expr(&self) -> Expr {
252 match self {
253 Self::Pcm(packet) => packet.to_expr(),
254 Self::Midi(packet) => packet.to_expr(),
255 Self::Diagnostic(packet) => packet.to_expr(),
256 Self::Data(packet) => packet.to_expr(),
257 }
258 }
259
260 pub fn intern_ref(&self, cx: &mut Cx) -> Result<Ref> {
266 let datum = Datum::try_from(self.to_expr())?;
267 cx.datum_store_mut().intern(datum).map(Ref::Content)
268 }
269}
270
271impl TryFrom<Expr> for StreamPacket {
272 type Error = Error;
273
274 fn try_from(expr: Expr) -> Result<Self> {
275 let Expr::Map(entries) = &expr else {
276 return Err(Error::TypeMismatch {
277 expected: "stream packet map",
278 found: expr_kind(&expr),
279 });
280 };
281 let packet = packet_symbol(entries)?;
282 match packet.as_qualified_str().as_str() {
283 "stream/packet/pcm" => PcmPacket::from_entries(entries).map(Self::Pcm),
284 "stream/packet/midi" => MidiPacket::from_entries(entries).map(Self::Midi),
285 "stream/packet/diagnostic" => {
286 StreamDiagnostic::from_entries(entries).map(Self::Diagnostic)
287 }
288 "stream/packet/data" => DataPacket::from_entries(entries).map(Self::Data),
289 other => Err(Error::Eval(format!("unknown stream packet kind {other}"))),
290 }
291 }
292}
293
294impl MidiPacket {
295 fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
296 let tpq = parse_string_field::<u16>(entries, "tpq")?;
297 let events = list_field(entries, "events")?
298 .iter()
299 .enumerate()
300 .map(|(index, expr)| {
301 let event = MidiPacketEvent::from_expr(expr)?;
302 if event.tpq() != tpq {
303 return Err(Error::Eval(format!(
304 "MIDI packet event {index} TPQ {} does not match packet TPQ {tpq}",
305 event.tpq()
306 )));
307 }
308 Ok(event)
309 })
310 .collect::<Result<Vec<_>>>()?;
311 Self::new(events)
312 }
313}
314
315impl MidiPacketEvent {
316 fn from_expr(expr: &Expr) -> Result<Self> {
317 let Expr::Map(entries) = expr else {
318 return Err(Error::TypeMismatch {
319 expected: "MIDI packet event map",
320 found: expr_kind(expr),
321 });
322 };
323 let ticks = parse_string_field::<i64>(entries, "ticks")?;
324 let tpq = parse_string_field::<u16>(entries, "tpq")?;
325 let bytes = bytes_field(entries, "bytes")?.to_vec();
326 Self::new(ticks, tpq, bytes)
327 }
328}
329
330impl StreamDiagnostic {
331 fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
332 Ok(Self::new(
333 symbol_field(entries, "kind")?.clone(),
334 string_field(entries, "message")?.to_owned(),
335 ))
336 }
337}
338
339impl DataPacket {
340 fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
341 ensure_data_fields_closed(entries)?;
342 Ok(Self::new(
343 symbol_field(entries, "kind")?.clone(),
344 field(entries, "payload")?.clone(),
345 ))
346 }
347}
348
349fn packet_symbol(entries: &[(Expr, Expr)]) -> Result<&Symbol> {
350 entries
351 .iter()
352 .find_map(|(key, value)| match (key, value) {
353 (Expr::Symbol(key), Expr::Symbol(value)) if key.name.as_ref() == "packet" => {
354 Some(value)
355 }
356 _ => None,
357 })
358 .ok_or_else(|| Error::Eval("stream packet missing packet symbol".to_owned()))
359}
360
361fn ensure_data_fields_closed(entries: &[(Expr, Expr)]) -> Result<()> {
362 for (key, _) in entries {
363 let Expr::Symbol(symbol) = key else {
364 return Err(Error::TypeMismatch {
365 expected: "symbol data packet field",
366 found: expr_kind(key),
367 });
368 };
369 if symbol.namespace.is_none()
370 && matches!(symbol.name.as_ref(), "packet" | "kind" | "payload")
371 {
372 continue;
373 }
374 return Err(Error::Eval(format!(
375 "unknown data packet field {}",
376 symbol.as_qualified_str()
377 )));
378 }
379 Ok(())
380}
381
382pub(super) fn parse_string_field<T>(entries: &[(Expr, Expr)], name: &str) -> Result<T>
383where
384 T: FromStr,
385 T::Err: Display,
386{
387 string_field(entries, name)?
388 .parse::<T>()
389 .map_err(|err| Error::Eval(format!("invalid stream packet {name}: {err}")))
390}
391
392pub(super) fn parse_string_expr<T>(expr: &Expr, expected: &'static str) -> Result<T>
393where
394 T: FromStr,
395 T::Err: Display,
396{
397 match expr {
398 Expr::String(value) => value
399 .parse::<T>()
400 .map_err(|err| Error::Eval(format!("{expected} parse failed: {err}"))),
401 other => Err(Error::TypeMismatch {
402 expected,
403 found: expr_kind(other),
404 }),
405 }
406}
407
408pub(super) fn list_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a [Expr]> {
409 access::entry_required_list(entries, name, "list field")
410}
411
412fn bytes_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a [u8]> {
413 match field(entries, name)? {
414 Expr::Bytes(bytes) => Ok(bytes),
415 other => Err(Error::TypeMismatch {
416 expected: "bytes field",
417 found: expr_kind(other),
418 }),
419 }
420}
421
422fn midi_event_expr(event: &MidiPacketEvent) -> Expr {
423 Expr::Map(vec![
424 (
425 Expr::Symbol(Symbol::new("ticks")),
426 Expr::String(event.ticks.to_string()),
427 ),
428 (
429 Expr::Symbol(Symbol::new("tpq")),
430 Expr::String(event.tpq.to_string()),
431 ),
432 (
433 Expr::Symbol(Symbol::new("bytes")),
434 Expr::Bytes(event.bytes.clone()),
435 ),
436 ])
437}