1use sim_kernel::{Expr, Result, Symbol};
2use sim_lib_midi_core::MidiEvent;
3use sim_lib_stream_core::{StreamItem, StreamPacket};
4
5use crate::{Channel, LaneId, LaneKind, PerformanceIntent, Pitch, Tick, tick_to_kernel_tick};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum PlayEvent {
13 Note(NoteEvent),
15 Midi(MidiPlayEvent),
17 Pitch(PitchEvent),
19 Control(ControlEvent),
21 Audio(AudioEvent),
23 Playable(PlayableEvent),
25 Performance(PerformanceEvent),
27 Diagnostic(DiagnosticEvent),
29 Trace(TraceEvent),
31}
32
33impl PlayEvent {
34 pub fn kind(&self) -> LaneKind {
36 match self {
37 Self::Note(_) => LaneKind::Note,
38 Self::Midi(_) => LaneKind::Midi,
39 Self::Pitch(_) => LaneKind::Pitch,
40 Self::Control(_) => LaneKind::Control,
41 Self::Audio(_) => LaneKind::Audio,
42 Self::Playable(_) => LaneKind::Playable,
43 Self::Performance(_) => LaneKind::Performance,
44 Self::Diagnostic(_) => LaneKind::Diagnostic,
45 Self::Trace(_) => LaneKind::Trace,
46 }
47 }
48
49 pub fn lane_id(&self) -> &LaneId {
51 match self {
52 Self::Note(event) => &event.lane_id,
53 Self::Midi(event) => &event.lane_id,
54 Self::Pitch(event) => &event.lane_id,
55 Self::Control(event) => &event.lane_id,
56 Self::Audio(event) => &event.lane_id,
57 Self::Playable(event) => &event.lane_id,
58 Self::Performance(event) => &event.lane_id,
59 Self::Diagnostic(event) => &event.lane_id,
60 Self::Trace(event) => &event.lane_id,
61 }
62 }
63
64 pub fn time(&self) -> Tick {
66 match self {
67 Self::Note(event) => event.time,
68 Self::Midi(event) => event.event.time,
69 Self::Pitch(event) => event.time,
70 Self::Control(event) => event.time,
71 Self::Audio(event) => event.time,
72 Self::Playable(event) => event.time,
73 Self::Performance(event) => event.time,
74 Self::Diagnostic(event) => event.time,
75 Self::Trace(event) => event.time,
76 }
77 }
78
79 pub fn to_stream_item(&self, clock: Symbol) -> Result<StreamItem> {
81 StreamItem::with_ticks(
82 StreamPacket::data(play_event_data_kind(), self.to_expr()),
83 vec![tick_to_kernel_tick(self.time(), clock)],
84 )
85 }
86
87 pub fn to_expr(&self) -> Expr {
89 match self {
90 Self::Note(event) => event.to_expr(),
91 Self::Midi(event) => event.to_expr(),
92 Self::Pitch(event) => event.to_expr(),
93 Self::Control(event) => event.to_expr(),
94 Self::Audio(event) => event.to_expr(),
95 Self::Playable(event) => event.to_expr(),
96 Self::Performance(event) => event.to_expr(),
97 Self::Diagnostic(event) => event.to_expr(),
98 Self::Trace(event) => event.to_expr(),
99 }
100 }
101}
102
103#[derive(Clone, Debug, PartialEq, Eq)]
105pub struct NoteEvent {
106 pub lane_id: LaneId,
108 pub time: Tick,
110 pub duration: Tick,
112 pub pitch: Pitch,
114 pub velocity: u8,
116 pub channel: Channel,
118}
119
120impl NoteEvent {
121 pub fn to_expr(&self) -> Expr {
123 map(vec![
124 ("event", Expr::Symbol(LaneKind::Note.symbol())),
125 ("lane", Expr::String(self.lane_id.0.clone())),
126 ("time", tick_expr(self.time)),
127 ("duration", tick_expr(self.duration)),
128 ("pitch", Expr::String(pitch_label(self.pitch))),
129 ("velocity", Expr::String(self.velocity.to_string())),
130 ("channel", Expr::String(self.channel.0.to_string())),
131 ])
132 }
133}
134
135#[derive(Clone, Debug, PartialEq, Eq)]
137pub struct MidiPlayEvent {
138 pub lane_id: LaneId,
140 pub event: MidiEvent,
142}
143
144impl MidiPlayEvent {
145 pub fn to_expr(&self) -> Expr {
147 map(vec![
148 ("event", Expr::Symbol(LaneKind::Midi.symbol())),
149 ("lane", Expr::String(self.lane_id.0.clone())),
150 ("time", tick_expr(self.event.time)),
151 ("payload", Expr::String(format!("{:?}", self.event.payload))),
152 ])
153 }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
158pub struct PitchEvent {
159 pub lane_id: LaneId,
161 pub time: Tick,
163 pub pitch: Pitch,
165}
166
167impl PitchEvent {
168 pub fn to_expr(&self) -> Expr {
170 map(vec![
171 ("event", Expr::Symbol(LaneKind::Pitch.symbol())),
172 ("lane", Expr::String(self.lane_id.0.clone())),
173 ("time", tick_expr(self.time)),
174 ("pitch", Expr::String(pitch_label(self.pitch))),
175 ])
176 }
177}
178
179#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct ControlEvent {
182 pub lane_id: LaneId,
184 pub time: Tick,
186 pub control: Symbol,
188 pub value: i64,
190}
191
192impl ControlEvent {
193 pub fn to_expr(&self) -> Expr {
195 map(vec![
196 ("event", Expr::Symbol(LaneKind::Control.symbol())),
197 ("lane", Expr::String(self.lane_id.0.clone())),
198 ("time", tick_expr(self.time)),
199 ("control", Expr::Symbol(self.control.clone())),
200 ("value", Expr::String(self.value.to_string())),
201 ])
202 }
203}
204
205#[derive(Clone, Debug, PartialEq, Eq)]
207pub struct AudioEvent {
208 pub lane_id: LaneId,
210 pub time: Tick,
212 pub frames: u32,
214}
215
216impl AudioEvent {
217 pub fn to_expr(&self) -> Expr {
219 timed_count_expr(
220 LaneKind::Audio,
221 &self.lane_id,
222 self.time,
223 "frames",
224 self.frames,
225 )
226 }
227}
228
229#[derive(Clone, Debug, PartialEq, Eq)]
231pub struct PlayableEvent {
232 pub lane_id: LaneId,
234 pub time: Tick,
236 pub playable: Symbol,
238}
239
240impl PlayableEvent {
241 pub fn to_expr(&self) -> Expr {
243 map(vec![
244 ("event", Expr::Symbol(LaneKind::Playable.symbol())),
245 ("lane", Expr::String(self.lane_id.0.clone())),
246 ("time", tick_expr(self.time)),
247 ("playable", Expr::Symbol(self.playable.clone())),
248 ])
249 }
250}
251
252#[derive(Clone, Debug, PartialEq, Eq)]
254pub struct PerformanceEvent {
255 pub lane_id: LaneId,
257 pub source_id: Symbol,
259 pub input_time: Tick,
261 pub time: Tick,
263 pub intent: PerformanceIntent,
265}
266
267impl PerformanceEvent {
268 pub fn to_expr(&self) -> Expr {
270 map(vec![
271 ("event", Expr::Symbol(LaneKind::Performance.symbol())),
272 ("lane", Expr::String(self.lane_id.0.clone())),
273 ("source", Expr::Symbol(self.source_id.clone())),
274 ("input-time", tick_expr(self.input_time)),
275 ("time", tick_expr(self.time)),
276 ("intent", self.intent.to_expr()),
277 ])
278 }
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
283pub struct DiagnosticEvent {
284 pub lane_id: LaneId,
286 pub time: Tick,
288 pub message: String,
290}
291
292impl DiagnosticEvent {
293 pub fn to_expr(&self) -> Expr {
295 map(vec![
296 ("event", Expr::Symbol(LaneKind::Diagnostic.symbol())),
297 ("lane", Expr::String(self.lane_id.0.clone())),
298 ("time", tick_expr(self.time)),
299 ("message", Expr::String(self.message.clone())),
300 ])
301 }
302}
303
304#[derive(Clone, Debug, PartialEq, Eq)]
306pub struct TraceEvent {
307 pub lane_id: LaneId,
309 pub time: Tick,
311 pub step: u64,
313}
314
315impl TraceEvent {
316 pub fn to_expr(&self) -> Expr {
318 timed_count_expr(LaneKind::Trace, &self.lane_id, self.time, "step", self.step)
319 }
320}
321
322pub fn play_event_data_kind() -> Symbol {
324 Symbol::qualified("music/play", "event")
325}
326
327pub fn stable_event_order(events: &mut [PlayEvent]) {
332 events.sort_by(|left, right| {
333 left.time()
334 .ticks
335 .cmp(&right.time().ticks)
336 .then_with(|| left.lane_id().cmp(right.lane_id()))
337 .then_with(|| left.kind().cmp(&right.kind()))
338 .then_with(|| format!("{:?}", left.to_expr()).cmp(&format!("{:?}", right.to_expr())))
339 });
340}
341
342fn timed_count_expr<T: ToString>(
343 kind: LaneKind,
344 lane_id: &LaneId,
345 time: Tick,
346 field: &'static str,
347 value: T,
348) -> Expr {
349 map(vec![
350 ("event", Expr::Symbol(kind.symbol())),
351 ("lane", Expr::String(lane_id.0.clone())),
352 ("time", tick_expr(time)),
353 (field, Expr::String(value.to_string())),
354 ])
355}
356
357fn tick_expr(tick: Tick) -> Expr {
358 map(vec![
359 ("ticks", Expr::String(tick.ticks.to_string())),
360 ("tpq", Expr::String(tick.tpq.to_string())),
361 ])
362}
363
364fn pitch_label(pitch: Pitch) -> String {
365 pitch
366 .to_midi()
367 .map(|midi| format!("midi:{midi}"))
368 .unwrap_or_else(|| format!("semitone:{}", pitch.semitone()))
369}
370
371fn map(entries: Vec<(&'static str, Expr)>) -> Expr {
372 Expr::Map(
373 entries
374 .into_iter()
375 .map(|(key, value)| (Expr::Symbol(Symbol::new(key)), value))
376 .collect(),
377 )
378}