midi-harbor/crates/core/src/rtevent.rs
2026-09-28 13:59:10 -05:00

52 lines
2 KiB
Rust

//! The fixed-size event that crosses the real-time boundary.
//!
//! A MIDI read callback runs in a real-time context: it may not allocate, lock, or block. So the
//! only thing it does is stamp what arrived and push it into a ring buffer for an ordinary task
//! to deal with. Everything here is `Copy` and fixed-size for that reason.
//!
//! System-exclusive data is unbounded and cannot travel inline. It goes through a separate byte
//! ring alongside this one: the event records how many bytes to read, and because both rings are
//! written by the same thread, ordering between them is preserved without any synchronisation.
use crate::ids::EndpointId;
use crate::midi::MidiMessage;
use crate::stream::SysExEnd;
/// What a real-time event carries.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RtPayload {
/// A channel or system message, small enough to travel inline.
Message(MidiMessage),
/// A run of system-exclusive bytes waiting in the byte ring.
SysEx {
/// How many bytes to read from the byte ring.
len: u16,
/// Whether the message is finished, and whether it finished properly.
end: SysExEnd,
},
}
/// One thing that happened on an endpoint, as seen from the real-time path.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RtEvent {
/// Where it arrived, so the router knows what to do with it without a lookup by name.
pub source: EndpointId,
/// When it arrived, in the platform's own units.
///
/// Taken in the callback rather than when the event is drained, because the delay before
/// draining is exactly the jitter this timestamp exists to remove.
pub timestamp: u64,
/// What it carries.
pub payload: RtPayload,
}
impl RtEvent {
/// Creates an event carrying a single message.
pub fn message(source: EndpointId, timestamp: u64, message: MidiMessage) -> Self {
Self {
source,
timestamp,
payload: RtPayload::Message(message),
}
}
}