use sim_kernel::{Expr, Result, Symbol};
use sim_lib_midi_core::MidiEvent;
use sim_lib_stream_core::{StreamItem, StreamPacket};
use crate::{Channel, LaneId, LaneKind, PerformanceIntent, Pitch, Tick, tick_to_kernel_tick};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PlayEvent {
Note(NoteEvent),
Midi(MidiPlayEvent),
Pitch(PitchEvent),
Control(ControlEvent),
Audio(AudioEvent),
Playable(PlayableEvent),
Performance(PerformanceEvent),
Diagnostic(DiagnosticEvent),
Trace(TraceEvent),
}
impl PlayEvent {
pub fn kind(&self) -> LaneKind {
match self {
Self::Note(_) => LaneKind::Note,
Self::Midi(_) => LaneKind::Midi,
Self::Pitch(_) => LaneKind::Pitch,
Self::Control(_) => LaneKind::Control,
Self::Audio(_) => LaneKind::Audio,
Self::Playable(_) => LaneKind::Playable,
Self::Performance(_) => LaneKind::Performance,
Self::Diagnostic(_) => LaneKind::Diagnostic,
Self::Trace(_) => LaneKind::Trace,
}
}
pub fn lane_id(&self) -> &LaneId {
match self {
Self::Note(event) => &event.lane_id,
Self::Midi(event) => &event.lane_id,
Self::Pitch(event) => &event.lane_id,
Self::Control(event) => &event.lane_id,
Self::Audio(event) => &event.lane_id,
Self::Playable(event) => &event.lane_id,
Self::Performance(event) => &event.lane_id,
Self::Diagnostic(event) => &event.lane_id,
Self::Trace(event) => &event.lane_id,
}
}
pub fn time(&self) -> Tick {
match self {
Self::Note(event) => event.time,
Self::Midi(event) => event.event.time,
Self::Pitch(event) => event.time,
Self::Control(event) => event.time,
Self::Audio(event) => event.time,
Self::Playable(event) => event.time,
Self::Performance(event) => event.time,
Self::Diagnostic(event) => event.time,
Self::Trace(event) => event.time,
}
}
pub fn to_stream_item(&self, clock: Symbol) -> Result<StreamItem> {
StreamItem::with_ticks(
StreamPacket::data(play_event_data_kind(), self.to_expr()),
vec![tick_to_kernel_tick(self.time(), clock)],
)
}
pub fn to_expr(&self) -> Expr {
match self {
Self::Note(event) => event.to_expr(),
Self::Midi(event) => event.to_expr(),
Self::Pitch(event) => event.to_expr(),
Self::Control(event) => event.to_expr(),
Self::Audio(event) => event.to_expr(),
Self::Playable(event) => event.to_expr(),
Self::Performance(event) => event.to_expr(),
Self::Diagnostic(event) => event.to_expr(),
Self::Trace(event) => event.to_expr(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NoteEvent {
pub lane_id: LaneId,
pub time: Tick,
pub duration: Tick,
pub pitch: Pitch,
pub velocity: u8,
pub channel: Channel,
}
impl NoteEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Note.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.time)),
("duration", tick_expr(self.duration)),
("pitch", Expr::String(pitch_label(self.pitch))),
("velocity", Expr::String(self.velocity.to_string())),
("channel", Expr::String(self.channel.0.to_string())),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MidiPlayEvent {
pub lane_id: LaneId,
pub event: MidiEvent,
}
impl MidiPlayEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Midi.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.event.time)),
("payload", Expr::String(format!("{:?}", self.event.payload))),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PitchEvent {
pub lane_id: LaneId,
pub time: Tick,
pub pitch: Pitch,
}
impl PitchEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Pitch.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.time)),
("pitch", Expr::String(pitch_label(self.pitch))),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ControlEvent {
pub lane_id: LaneId,
pub time: Tick,
pub control: Symbol,
pub value: i64,
}
impl ControlEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Control.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.time)),
("control", Expr::Symbol(self.control.clone())),
("value", Expr::String(self.value.to_string())),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AudioEvent {
pub lane_id: LaneId,
pub time: Tick,
pub frames: u32,
}
impl AudioEvent {
pub fn to_expr(&self) -> Expr {
timed_count_expr(
LaneKind::Audio,
&self.lane_id,
self.time,
"frames",
self.frames,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PlayableEvent {
pub lane_id: LaneId,
pub time: Tick,
pub playable: Symbol,
}
impl PlayableEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Playable.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.time)),
("playable", Expr::Symbol(self.playable.clone())),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PerformanceEvent {
pub lane_id: LaneId,
pub source_id: Symbol,
pub input_time: Tick,
pub time: Tick,
pub intent: PerformanceIntent,
}
impl PerformanceEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Performance.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("source", Expr::Symbol(self.source_id.clone())),
("input-time", tick_expr(self.input_time)),
("time", tick_expr(self.time)),
("intent", self.intent.to_expr()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DiagnosticEvent {
pub lane_id: LaneId,
pub time: Tick,
pub message: String,
}
impl DiagnosticEvent {
pub fn to_expr(&self) -> Expr {
map(vec![
("event", Expr::Symbol(LaneKind::Diagnostic.symbol())),
("lane", Expr::String(self.lane_id.0.clone())),
("time", tick_expr(self.time)),
("message", Expr::String(self.message.clone())),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TraceEvent {
pub lane_id: LaneId,
pub time: Tick,
pub step: u64,
}
impl TraceEvent {
pub fn to_expr(&self) -> Expr {
timed_count_expr(LaneKind::Trace, &self.lane_id, self.time, "step", self.step)
}
}
pub fn play_event_data_kind() -> Symbol {
Symbol::qualified("music/play", "event")
}
pub fn stable_event_order(events: &mut [PlayEvent]) {
events.sort_by(|left, right| {
left.time()
.ticks
.cmp(&right.time().ticks)
.then_with(|| left.lane_id().cmp(right.lane_id()))
.then_with(|| left.kind().cmp(&right.kind()))
.then_with(|| format!("{:?}", left.to_expr()).cmp(&format!("{:?}", right.to_expr())))
});
}
fn timed_count_expr<T: ToString>(
kind: LaneKind,
lane_id: &LaneId,
time: Tick,
field: &'static str,
value: T,
) -> Expr {
map(vec![
("event", Expr::Symbol(kind.symbol())),
("lane", Expr::String(lane_id.0.clone())),
("time", tick_expr(time)),
(field, Expr::String(value.to_string())),
])
}
fn tick_expr(tick: Tick) -> Expr {
map(vec![
("ticks", Expr::String(tick.ticks.to_string())),
("tpq", Expr::String(tick.tpq.to_string())),
])
}
fn pitch_label(pitch: Pitch) -> String {
pitch
.to_midi()
.map(|midi| format!("midi:{midi}"))
.unwrap_or_else(|| format!("semitone:{}", pitch.semitone()))
}
fn map(entries: Vec<(&'static str, Expr)>) -> Expr {
Expr::Map(
entries
.into_iter()
.map(|(key, value)| (Expr::Symbol(Symbol::new(key)), value))
.collect(),
)
}