/// A collection of simultaneous MIDI events. /// See [MIDIPacket](https://developer.apple.com/reference/coremidi/midipacket). /// #[repr(C)] pubstruct Packet { // NOTE: At runtime this type must only be used behind immutable references // that point to valid instances of MIDIPacket (mutable references would allow mem::swap). // This type must NOT implement `Copy`! // On ARM, this must be 4-byte aligned.
inner: PacketInner,
_alignment_marker: alignment::Marker,
}
#[repr(packed)] struct PacketInner {
timestamp: MIDITimeStamp,
length: u16,
data: [u8; 0], // zero-length, because we cannot make this type bigger without knowing how much data there actually is
}
impl Packet { /// Get the packet timestamp. /// pubfn timestamp(&self) -> Timestamp { self.inner.timestamp as Timestamp
}
/// Get the packet data. This method just gives raw MIDI bytes. You would need another /// library to decode them and work with higher level events. /// /// /// The following example: /// /// ``` /// let packet_list = &coremidi::PacketBuffer::new(0, &[0x90, 0x40, 0x7f]); /// for packet in packet_list.iter() { /// for byte in packet.data() { /// print!(" {:x}", byte); /// } /// } /// ``` /// /// will print: /// /// ```text /// 90 40 7f /// ``` pubfn data(&self) -> &[u8] { let data_ptr = self.inner.data.as_ptr(); let data_len = self.inner.length as usize; unsafe { slice::from_raw_parts(data_ptr, data_len) }
}
}
impl fmt::Debug for Packet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let result = write!(
f, "Packet(ptr={:x}, ts={:016x}, data=[", selfas *const _ as usize, self.timestamp() as u64
); let result = self
.data()
.iter()
.enumerate()
.fold(result, |prev_result, (i, b)| match prev_result {
Err(err) => Err(err),
Ok(()) => { let sep = if i > 0 { ", " } else { "" };
write!(f, "{}{:02x}", sep, b)
}
});
result.and_then(|_| write!(f, "])"))
}
}
impl fmt::Display for Packet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let result = write!(f, "{:016x}:", self.timestamp()); self.data()
.iter()
.fold(result, |prev_result, b| match prev_result {
Err(err) => Err(err),
Ok(()) => write!(f, " {:02x}", b),
})
}
}
/// A [list of MIDI events](https://developer.apple.com/reference/coremidi/midipacketlist) being received from, or being sent to, one endpoint. /// #[repr(C)] pubstruct PacketList { // NOTE: This type must only exist in the form of immutable references // pointing to valid instances of MIDIPacketList. // This type must NOT implement `Copy`!
inner: PacketListInner,
_do_not_construct: alignment::Marker,
}
impl PacketList { /// For internal usage only. /// Requires this instance to actually point to a valid MIDIPacketList pub(crate) unsafefn as_ptr(&self) -> *mut MIDIPacketList { selfas *const PacketList as *mut PacketList as *mut MIDIPacketList
}
}
impl PacketList { /// Check if the packet list is empty. /// pubfn is_empty(&self) -> bool { self.len() == 0
}
/// Get the number of packets in the list. /// pubfn len(&self) -> usize { self.inner.num_packets as usize
}
/// Get an iterator for the packets in the list. /// pubfn iter(&self) -> PacketListIterator {
PacketListIterator {
count: self.len(),
packet_ptr: std::ptr::addr_of!(self.inner.data) as *const MIDIPacket,
_phantom: ::std::marker::PhantomData::default(),
}
}
}
impl fmt::Debug for PacketList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let result = write!(f, "PacketList(ptr={:x}, packets=[", unsafe { self.as_ptr() as usize
}); self.iter()
.enumerate()
.fold(result, |prev_result, (i, packet)| match prev_result {
Err(err) => Err(err),
Ok(()) => { let sep = if i != 0 { ", " } else { "" };
write!(f, "{}{:?}", sep, packet)
}
})
.and_then(|_| write!(f, "])"))
}
}
impl fmt::Display for PacketList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let num_packets = self.inner.num_packets; let result = write!(f, "PacketList(len={})", num_packets); self.iter()
.fold(result, |prev_result, packet| match prev_result {
Err(err) => Err(err),
Ok(()) => write!(f, "\n {}", packet),
})
}
}
const INLINE_PACKET_BUFFER_SIZE: usize = 28; // must be divisible by 4
enum PacketBufferStorage { /// Inline stores the data directy on the stack, if it is small enough. /// NOTE: using u32 ensures correct alignment (required on ARM)
Inline([u32; INLINE_PACKET_BUFFER_SIZE / 4]), /// External is used whenever the size of the data exceeds INLINE_PACKET_BUFFER_SIZE. /// This means that the size of the contained vector is always greater than INLINE_PACKET_BUFFER_SIZE.
External(Vec<u32>),
}
unsafefn assign_packet(&mutself, packet_offset: usize, time: MIDITimeStamp, data: &[u8]) {
assert!(data.len() <= MAX_PACKET_DATA_LENGTH, "packet data too long"); // cannot store longer size in u16
if alignment::NEEDS_ALIGNMENT {
debug_assert!(packet_offset & 0b11 == 0);
}
let slice = self.get_slice_mut(); let ptr = slice[packet_offset..].as_mut_ptr() as *mut Packet;
(*ptr).inner.timestamp = time;
(*ptr).inner.length = data.len() as u16; let packet_data_start = packet_offset + PACKET_HEADER_SIZE;
slice[packet_data_start..(packet_data_start + data.len())].copy_from_slice(data);
}
/// Requires that there is a valid Packet at `offset`, which has enough space for `data` unsafefn extend_packet(&mutself, packet_offset: usize, data: &[u8]) { let slice = self.get_slice_mut(); let ptr = slice[packet_offset..].as_mut_ptr() as *mut Packet; let packet_data_start = packet_offset + PACKET_HEADER_SIZE + (*ptr).inner.length as usize;
(*ptr).inner.length += data.len() as u16;
slice[packet_data_start..(packet_data_start + data.len())].copy_from_slice(data);
}
/// Call this only with larger length values (won't make the buffer smaller) unsafefn ensure_capacity(&mutself, capacity: usize) { if capacity < INLINE_PACKET_BUFFER_SIZE || capacity < self.get_slice().len() { return;
}
let vec_capacity = ((capacity - 1) / 4) + 1; let vec: Option<Vec<u32>> = match *self {
PacketBufferStorage::Inline(ref inline) => { letmut v = Vec::with_capacity(vec_capacity);
v.extend_from_slice(inline);
v.set_len(vec_capacity);
Some(v)
}
PacketBufferStorage::External(refmut vec) => { let current_len = vec.len();
vec.reserve(vec_capacity - current_len);
vec.set_len(vec_capacity);
None
}
};
// to prevent borrowcheck errors, this must come after the `match` iflet Some(v) = vec {
*self = PacketBufferStorage::External(v);
}
}
}
impl Deref for PacketBufferStorage { type Target = PacketList;
impl DerefMut for PacketBufferStorage { // NOTE: Mutable references `&mut PacketList` must not be exposed in the public API! // The user could use mem::swap to modify the header without modifying the packets that follow. #[inline] fn deref_mut(&mutself) -> &mut PacketList { unsafe { &mut *(self.get_slice_mut().as_mut_ptr() as *mut PacketList) }
}
}
/// A mutable `PacketList` builder. /// /// A `PacketList` is an inmmutable reference to a [MIDIPacketList](https://developer.apple.com/reference/coremidi/midipacketlist) structure, /// while a `PacketBuffer` is a mutable structure that allows to build a `PacketList` by adding packets. /// It dereferences to a `PacketList`, so it can be used whenever a `PacketList` is needed. /// pubstruct PacketBuffer {
storage: PacketBufferStorage,
last_packet_offset: usize,
}
impl Deref for PacketBuffer { type Target = PacketList;
impl PacketBuffer { /// Create a `PacketBuffer` with a single packet containing the provided timestamp and data. /// /// According to the official documentation for CoreMIDI, the timestamp represents /// the time at which the events are to be played, where zero means "now". /// The timestamp applies to the first MIDI byte in the packet. /// /// Example on how to create a `PacketBuffer` with a single packet for a MIDI note on for C-5: /// /// ``` /// let buffer = coremidi::PacketBuffer::new(0, &[0x90, 0x3c, 0x7f]); /// assert_eq!(buffer.len(), 1) /// ``` pubfn new(time: MIDITimeStamp, data: &[u8]) -> PacketBuffer { let capacity = data.len() + PACKET_LIST_HEADER_SIZE + PACKET_HEADER_SIZE; letmut storage = PacketBufferStorage::with_capacity(capacity);
storage.deref_mut().inner.num_packets = 1; let last_packet_offset = PACKET_LIST_HEADER_SIZE; unsafe {
storage.assign_packet(last_packet_offset, time, data);
}
PacketBuffer {
storage,
last_packet_offset,
}
}
/// Create an empty `PacketBuffer` with no packets. /// /// Example on how to create an empty `PacketBuffer` /// with a capacity for 128 bytes in total (including headers): /// /// ``` /// let buffer = coremidi::PacketBuffer::with_capacity(128); /// assert_eq!(buffer.len(), 0); /// assert_eq!(buffer.capacity(), 128); /// ``` pubfn with_capacity(capacity: usize) -> PacketBuffer { let capacity = std::cmp::max(capacity, INLINE_PACKET_BUFFER_SIZE); letmut storage = PacketBufferStorage::with_capacity(capacity);
storage.deref_mut().inner.num_packets = 0;
/// Get underlying buffer capacity in bytes pubfn capacity(&self) -> usize { self.storage.capacity()
}
/// Add a new event containing the provided timestamp and data. /// /// According to the official documentation for CoreMIDI, the timestamp represents /// the time at which the events are to be played, where zero means "now". /// The timestamp applies to the first MIDI byte in the packet. /// /// An event must not have a timestamp that is smaller than that of a previous event /// in the same `PacketList` /// /// Example: /// /// ``` /// let mut chord = coremidi::PacketBuffer::new(0, &[0x90, 0x3c, 0x7f]); /// chord.push_data(0, &[0x90, 0x40, 0x7f]); /// assert_eq!(chord.len(), 1); /// let repr = format!("{}", &chord as &coremidi::PacketList); /// assert_eq!(repr, "PacketList(len=1)\n 0000000000000000: 90 3c 7f 90 40 7f"); /// ``` pubfn push_data(&mutself, time: MIDITimeStamp, data: &[u8]) -> & style='color:red'>mutSelf { let (can_merge, previous_data_len) = self.can_merge_into_last_packet(time, data);
if can_merge { let new_packet_size = Self::packet_size(previous_data_len + data.len()); unsafe { self.storage
.ensure_capacity(self.last_packet_offset + new_packet_size); self.storage.extend_packet(self.last_packet_offset, data);
}
} else { let packet_size = Self::packet_size(data.len()); let next_offset = self.next_packet_offset(); unsafe { self.storage.ensure_capacity(next_offset + packet_size); self.storage.assign_packet(next_offset, time, data);
} self.packet_list_mut().num_packets += 1; self.last_packet_offset = next_offset;
}
self
}
/// Clears the buffer, removing all packets. /// Note that this method has no effect on the allocated capacity of the buffer. pubfn clear(&mutself) { self.packet_list_mut().num_packets = 0; self.last_packet_offset = PACKET_LIST_HEADER_SIZE;
}
/// Checks whether the given tiemstamped data can be merged into the previous packet fn can_merge_into_last_packet(&self, time: MIDITimeStamp, data: &[u8]) -> (bool, usize) { ifself.packet_list_is_empty() {
(false, 0)
} else { let previous_packet = self.last_packet(); let previous_packet_data = previous_packet.data(); let previous_data_len = previous_packet_data.len(); let can_merge = previous_packet.timestamp() == time
&& Self::not_sysex(data)
&& Self::has_status_byte(data)
&& Self::not_sysex(previous_packet_data)
&& Self::has_status_byte(previous_packet_data)
&& previous_data_len + data.len() < MAX_PACKET_DATA_LENGTH;
(can_merge, previous_data_len)
}
}
#[inline] fn last_packet(&self) -> &Packet {
assert!(self.packet_list().num_packets > 0); let packets_slice = self.storage.get_slice(); let packet_slot = &packets_slice[self.last_packet_offset..]; unsafe { &*(packet_slot.as_ptr() as *const Packet) }
}
#[inline] fn next_packet_offset(&self) -> usize { ifself.packet_list_is_empty() { self.last_packet_offset
} else { let data_len = self.last_packet().inner.length as usize; let next_offset = self.last_packet_offset + Self::packet_size(data_len); if alignment::NEEDS_ALIGNMENT {
(next_offset + 3) & !(3usize)
} else {
next_offset
}
}
}
let dummy_packet: Packet = unsafe { mem::zeroed() }; let ptr = &dummy_packet as *const _ as *const u8;
assert_eq!(
PACKET_HEADER_SIZE,
dummy_packet.inner.data.as_ptr() as usize - ptr as usize
);
let dummy_packet_list: PacketList = unsafe { mem::zeroed() }; let ptr = &dummy_packet_list as *const _ as *const u8;
assert_eq!(
PACKET_LIST_HEADER_SIZE,
std::ptr::addr_of!(dummy_packet_list.inner.data) as usize - ptr as usize
);
}
#[test] pubfn single_packet_alloc_inline() { let packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]); iflet PacketBufferStorage::External(_) = packet_buf.storage {
panic!("A single 3-byte message must not be allocated externally")
}
}
#[test] fn packet_buffer_deref() { let packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]); let packet_list: &PacketList = &packet_buf;
assert_eq!( unsafe { packet_list.as_ptr() as *const MIDIPacketList },
packet_buf.storage.get_slice().as_ptr() as *const _ as *const MIDIPacketList
);
}
/// Compares the results of building a PacketList using our PacketBuffer API /// and the native API (MIDIPacketListAdd, etc). unsafefn compare_packet_list(packets: Vec<(MIDITimeStamp, Vec<u8>)>) { use coremidi_sys::{MIDIPacketListAdd, MIDIPacketListInit};
// allocate a buffer on the stack for building the list using native methods const BUFFER_SIZE: usize = 65536; // maximum allowed size let buffer: &mut [u8] = &mut [0; BUFFER_SIZE]; let pkt_list_ptr = buffer.as_mut_ptr() as *mut MIDIPacketList;
// build the list letmut pkt_ptr = MIDIPacketListInit(pkt_list_ptr); for pkt in &packets {
pkt_ptr = MIDIPacketListAdd(
pkt_list_ptr,
BUFFER_SIZE as u64,
pkt_ptr,
pkt.0,
pkt.1.len() as u64,
pkt.1.as_ptr(),
);
assert!(!pkt_ptr.is_null());
} let list_native = &*(pkt_list_ptr as *const _ as *const PacketList);
// build the PacketBuffer, containing the same packets letmut packet_buf = PacketBuffer::new(packets[0].0, &packets[0].1); for pkt in &packets[1..] {
packet_buf.push_data(pkt.0, &pkt.1);
}
// check if the contents match
assert_eq!(
list_native.len(),
list.len(), "PacketList lengths must match"
); for (n, p) in list_native.iter().zip(list.iter()) {
assert_eq!(n.data(), p.data());
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-17)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.