coremidi 0.7.0

CoreMIDI library for Rust
use std::fmt::Formatter;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ops::Deref;
use std::slice;

use coremidi_sys::{
    MIDIEventList, MIDIEventListAdd, MIDIEventListInit, MIDIEventPacket, MIDIEventPacketNext,
};

use crate::protocol::Protocol;

pub type Timestamp = u64;

/// A variable-length list of MIDI event packets
/// See [MIDIEventList](https://developer.apple.com/documentation/coremidi/midieventlist)
///
pub struct EventList(MIDIEventList);

impl EventList {
    pub fn protocol(&self) -> Protocol {
        Protocol::from(self.0.protocol)
    }

    /// Check if the packet list is empty.
    ///
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the number of packets in the list.
    ///
    pub fn len(&self) -> usize {
        self.0.numPackets as usize
    }

    /// Get an iterator for the packets in the list.
    ///
    pub fn iter(&self) -> EventListIter {
        EventListIter {
            count: self.len(),
            packet_ptr: std::ptr::addr_of!(self.0.packet) as *const MIDIEventPacket,
            _phantom: PhantomData,
        }
    }

    /// For internal usage only.
    /// Requires this instance to actually point to a valid MIDIEventList
    pub(crate) unsafe fn as_ptr(&self) -> *const MIDIEventList {
        self as *const EventList as *const MIDIEventList
    }
}

impl std::fmt::Debug for EventList {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        writeln!(
            f,
            "EventList(protocol={:?}, packets={})",
            self.protocol(),
            self.len()
        )?;
        for packet in self.iter() {
            writeln!(f, "{:?}", packet)?;
        }
        Ok(())
    }
}

pub struct EventListIter<'a> {
    count: usize,
    packet_ptr: *const MIDIEventPacket,
    _phantom: PhantomData<&'a MIDIEventPacket>,
}

impl<'a> Iterator for EventListIter<'a> {
    type Item = &'a EventPacket;

    fn next(&mut self) -> Option<&'a EventPacket> {
        if self.count > 0 {
            let packet = unsafe { &*(self.packet_ptr as *const EventPacket) };
            self.count -= 1;
            self.packet_ptr = unsafe { MIDIEventPacketNext(self.packet_ptr) };
            Some(packet)
        } else {
            None
        }
    }
}

pub struct EventPacket(MIDIEventPacket);

impl EventPacket {
    pub fn timestamp(&self) -> Timestamp {
        self.0.timeStamp as Timestamp
    }

    /// Get the packet data. This method just gives raw MIDI words. You would need another
    /// library to decode them and work with higher level events.
    ///
    pub fn data(&self) -> &[u32] {
        let data_ptr = self.0.words.as_ptr();
        let data_len = self.0.wordCount as usize;
        unsafe { slice::from_raw_parts(data_ptr, data_len) }
    }
}

impl std::fmt::Debug for EventPacket {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "  {:024}:", self.timestamp())?;
        for word in self.data().iter() {
            write!(f, " {:08x}", word)?;
        }
        Ok(())
    }
}

#[derive(Clone)]
pub struct EventBuffer {
    storage: Storage,
    current_packet_offset: usize,
}

impl EventBuffer {
    const PACKET_HEADER_SIZE: usize = 8 +     // MIDIEventPacket::timestamp: MIDITimeStamp/UInt64
                                      4; // MIDIEventPacket::wordCount: UInt32

    /// Create an empty `EventBuffer` for a given [Protocol] without allocating.
    ///
    pub fn new(protocol: Protocol) -> Self {
        Self::with_capacity(Storage::INLINE_SIZE, protocol)
    }

    /// Create an empty `EventBuffer` of a given capacity for a given [Protocol].
    ///
    pub fn with_capacity(capacity: usize, protocol: Protocol) -> Self {
        let mut storage = Storage::with_capacity(capacity);
        let event_list_ptr = unsafe { storage.as_mut_ptr::<MIDIEventList>() };
        let current_packet_ptr = unsafe { MIDIEventListInit(event_list_ptr, protocol.into()) };
        let current_packet_offset = unsafe {
            (current_packet_ptr as *const u8).offset_from(event_list_ptr as *const u8) as usize
        };
        Self {
            storage,
            current_packet_offset,
        }
    }

    /// Get underlying buffer capacity in bytes
    ///
    pub fn capacity(&self) -> usize {
        self.storage.capacity()
    }

    /// Add a new packet containing the provided timestamp and data.
    /// It consumes the instance and returns it modified with the new packet.
    ///
    /// See [EventBuffer::push] for further details.
    ///
    /// Example:
    ///
    /// ```
    /// use coremidi::{Protocol, Timestamp, EventBuffer};
    ///
    /// let buffer = EventBuffer::new(Protocol::Midi20)
    ///     .with_packet(0, &[0x40903c00, 0xffff0000]); // Note On for Middle C
    ///
    /// assert_eq!(buffer.len(), 1);
    /// assert_eq!(
    ///     buffer.iter()
    ///         .map(|packet| (packet.timestamp(), packet.data().to_vec()))
    ///         .collect::<Vec<(Timestamp, Vec<u32>)>>(),
    ///     vec![(0, vec![0x40903c00, 0xffff0000])],
    /// )
    /// ```
    pub fn with_packet(mut self, timestamp: Timestamp, data: &[u32]) -> Self {
        self.push(timestamp, data);
        self
    }

    /// 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 word in the packet.
    ///
    /// An event must not have a timestamp that is smaller than that of a previous event
    /// in the same `EventBuffer`
    ///
    /// Example:
    ///
    /// ```
    /// use coremidi::{EventBuffer, Protocol, Timestamp};
    ///
    /// let mut buffer = EventBuffer::new(Protocol::Midi20);
    /// buffer.push(0, &[0x40903c00, 0xffff0000]); // Note On for Middle C
    ///
    /// assert_eq!(buffer.len(), 1);
    /// assert_eq!(
    ///     buffer.iter()
    ///         .map(|packet| (packet.timestamp(), packet.data().to_vec()))
    ///         .collect::<Vec<(Timestamp, Vec<u32>)>>(),
    ///     vec![(0, vec![0x40903c00, 0xffff0000])],
    /// )
    /// ```
    pub fn push(&mut self, timestamp: Timestamp, data: &[u32]) -> &mut Self {
        self.ensure_capacity(data.len());

        let packet_list_ptr = unsafe { self.storage.as_mut_ptr::<MIDIEventList>() };
        let current_packet_ptr = unsafe {
            self.storage.as_ptr::<u8>().add(self.current_packet_offset) as *mut MIDIEventPacket
        };
        let current_packet_ptr = unsafe {
            MIDIEventListAdd(
                packet_list_ptr,
                self.storage.capacity() as u64,
                current_packet_ptr,
                timestamp,
                data.len() as u64,
                data.as_ptr(),
            )
        };

        self.current_packet_offset = unsafe {
            (current_packet_ptr as *const u8).offset_from(packet_list_ptr as *const u8) as usize
        };

        self
    }

    /// Clears the buffer, removing all packets.
    /// Note that this method has no effect on the allocated capacity of the buffer.
    pub fn clear(&mut self) {
        let event_list_ptr = unsafe { self.storage.as_mut_ptr::<MIDIEventList>() };
        let protocol = unsafe { (*event_list_ptr).protocol };
        let current_packet_ptr = unsafe { MIDIEventListInit(event_list_ptr, protocol) };
        self.current_packet_offset = unsafe {
            (current_packet_ptr as *const u8).offset_from(event_list_ptr as *const u8) as usize
        };
    }

    fn ensure_capacity(&mut self, data_len: usize) {
        let next_capacity =
            self.aligned_bytes_len() + Self::PACKET_HEADER_SIZE + data_len * size_of::<u32>();

        unsafe {
            // We ensure capacity for the worst case as if there was no merge with the current packet
            self.storage.ensure_capacity(next_capacity);
        }
    }

    #[inline]
    fn aligned_bytes_len(&self) -> usize {
        let storage_start_ptr = unsafe { self.storage.as_ptr::<u8>() };
        if self.as_ref().is_empty() {
            self.current_packet_offset
        } else {
            let current_packet = unsafe {
                &*(self.storage.as_ptr::<u8>().add(self.current_packet_offset)
                    as *const MIDIEventPacket)
            };
            let current_packet_data_ptr = current_packet.words.as_ptr() as *const u8;
            let data_offset = current_packet_data_ptr as *const u8 as usize
                - storage_start_ptr as *const u8 as usize;
            let data_len = current_packet.wordCount as usize * size_of::<u32>();
            (data_offset + data_len + 3) & !3
        }
    }
}

impl AsRef<EventList> for EventBuffer {
    #[inline]
    fn as_ref(&self) -> &EventList {
        unsafe { &*self.storage.as_ptr::<EventList>() }
    }
}

impl Deref for EventBuffer {
    type Target = EventList;

    #[inline]
    fn deref(&self) -> &EventList {
        self.as_ref()
    }
}

#[derive(Clone)]
pub(crate) enum Storage {
    /// Inline stores the data directly on the stack, if it is small enough.
    /// NOTE: using u32 ensures correct alignment (required on ARM)
    Inline([u32; Storage::INLINE_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>),
}

impl Storage {
    pub(crate) const INLINE_SIZE: usize = 8 // MIDIEventList header
        + 12 // MIDIEventPacket header
        + 4 * 4; // 4 words

    #[inline]
    #[allow(clippy::uninit_vec)]
    pub(crate) fn with_capacity(capacity: usize) -> Self {
        if capacity <= Self::INLINE_SIZE {
            Self::Inline([0; Self::INLINE_SIZE / 4])
        } else {
            let u32_len = ((capacity - 1) / 4) + 1;
            let mut buffer = Vec::with_capacity(u32_len);
            unsafe {
                buffer.set_len(u32_len);
            }
            Storage::External(buffer)
        }
    }

    #[inline]
    pub(crate) fn capacity(&self) -> usize {
        match *self {
            Storage::Inline(ref inline) => inline.len() * 4,
            Storage::External(ref vec) => vec.len() * 4,
        }
    }

    #[inline]
    pub(crate) fn get_slice<T>(&self) -> &[T] {
        unsafe {
            match *self {
                Storage::Inline(ref inline) => slice::from_raw_parts(
                    inline.as_ptr() as *const T,
                    inline.len() * size_of::<u32>() / size_of::<T>(),
                ),
                Storage::External(ref vec) => {
                    slice::from_raw_parts(vec.as_ptr() as *const T, vec.len() * 4 / size_of::<T>())
                }
            }
        }
    }

    /// Call this only with larger length values (won't make the buffer smaller)
    #[allow(clippy::uninit_vec)]
    pub(crate) unsafe fn ensure_capacity(&mut self, capacity: usize) {
        if capacity < Self::INLINE_SIZE || capacity < self.get_slice::<u8>().len() {
            return;
        }

        let vec_capacity = ((capacity - 1) / 4) + 1;
        let vec: Option<Vec<u32>> = match *self {
            Storage::Inline(ref inline) => {
                let mut v = Vec::with_capacity(vec_capacity);
                v.extend_from_slice(inline);
                v.set_len(vec_capacity);
                Some(v)
            }
            Storage::External(ref mut vec) => {
                let current_len = vec.len();
                vec.reserve(vec_capacity - current_len);
                vec.set_len(vec_capacity);
                None
            }
        };

        // to prevent borrow-check errors, this must come after the `match`
        if let Some(v) = vec {
            *self = Storage::External(v);
        }
    }

    #[inline]
    pub(crate) unsafe fn as_ptr<T>(&self) -> *const T {
        match *self {
            Storage::Inline(ref inline) => inline.as_ptr() as *const T,
            Storage::External(ref vec) => vec.as_ptr() as *const T,
        }
    }

    #[inline]
    pub(crate) unsafe fn as_mut_ptr<T>(&mut self) -> *mut T {
        self.as_ptr::<T>() as *mut T
    }
}

impl std::fmt::Debug for Storage {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        for b in self.get_slice::<u8>() {
            write!(f, " {:02x}", *b)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::events::{Storage, Timestamp};
    use crate::protocol::Protocol;
    use crate::{EventBuffer, EventList};
    use coremidi_sys::{
        kMIDIProtocol_2_0, ByteCount, MIDIEventList, MIDIEventListAdd, MIDIEventListInit,
        MIDIProtocolID,
    };

    #[test]
    fn event_list_accessors() {
        const BUFFER_SIZE: usize = 256;
        let buffer = [0u8; BUFFER_SIZE];
        let event_list_ptr = buffer.as_ptr() as *const MIDIEventList as *mut MIDIEventList;
        let event_packet_ptr =
            unsafe { MIDIEventListInit(event_list_ptr, kMIDIProtocol_2_0 as MIDIProtocolID) };
        let event_packet_ptr = unsafe {
            MIDIEventListAdd(
                event_list_ptr,
                BUFFER_SIZE as ByteCount,
                event_packet_ptr,
                10,
                2,
                [1, 2].as_ptr(),
            )
        };
        let _ = unsafe {
            MIDIEventListAdd(
                event_list_ptr,
                BUFFER_SIZE as ByteCount,
                event_packet_ptr,
                20,
                3,
                [3, 4, 5].as_ptr(),
            )
        };
        let event_list = unsafe { &*(event_list_ptr as *const EventList) };

        assert_eq!(event_list.protocol(), Protocol::Midi20);
        assert!(!event_list.is_empty());
        assert_eq!(event_list.len(), 2);

        assert_eq!(
            event_list
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![(10, vec![1, 2]), (20, vec![3, 4, 5]),]
        );
    }

    #[test]
    fn event_buffer_new() {
        let event_buffer = EventBuffer::new(Protocol::Midi20);

        assert_eq!(event_buffer.capacity(), Storage::INLINE_SIZE);
        assert_eq!(event_buffer.protocol(), Protocol::Midi20);
        assert_eq!(event_buffer.len(), 0);
    }

    #[test]
    fn event_buffer_with_capacity_inline() {
        let event_buffer = EventBuffer::with_capacity(2, Protocol::Midi20);

        assert_eq!(event_buffer.capacity(), Storage::INLINE_SIZE);
        assert_eq!(event_buffer.protocol(), Protocol::Midi20);
        assert_eq!(event_buffer.len(), 0);
    }

    #[test]
    fn event_buffer_with_capacity_external() {
        let event_buffer = EventBuffer::with_capacity(Storage::INLINE_SIZE * 2, Protocol::Midi20);

        assert_eq!(event_buffer.capacity(), Storage::INLINE_SIZE * 2);
    }

    #[test]
    fn event_buffer_with_packet() {
        let event_buffer = EventBuffer::new(Protocol::Midi20)
            .with_packet(10, &[1, 2])
            .with_packet(20, &[3, 4, 5]);

        assert_eq!(event_buffer.len(), 2);
        assert_eq!(
            event_buffer
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![(10, vec![1, 2]), (20, vec![3, 4, 5]),]
        );
    }

    #[test]
    fn event_buffer_push_within_capacity() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20);
        event_buffer.push(10, &[1, 2]).push(20, &[3, 4, 5]);

        assert_eq!(event_buffer.len(), 2);
        assert_eq!(
            event_buffer
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![(10, vec![1, 2]), (20, vec![3, 4, 5]),]
        );
    }

    #[test]
    fn event_buffer_push_over_capacity() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20);
        event_buffer
            .push(10, &[1, 2])
            .push(20, &[3, 4, 5, 6, 7, 8, 9, 10]);

        assert_eq!(event_buffer.len(), 2);
        assert_eq!(
            event_buffer
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![(10, vec![1, 2]), (20, vec![3, 4, 5, 6, 7, 8, 9, 10])]
        );
    }

    #[test]
    fn event_buffer_clear() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20).with_packet(10, &[1, 2]);

        assert_eq!(event_buffer.len(), 1);
        assert_eq!(
            event_buffer
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![(10, vec![1, 2])]
        );

        event_buffer.clear();

        assert_eq!(event_buffer.len(), 0);
        assert_eq!(event_buffer.capacity(), Storage::INLINE_SIZE);
        assert_eq!(
            event_buffer
                .iter()
                .map(|packet| (packet.timestamp(), packet.data().to_vec()))
                .collect::<Vec<(Timestamp, Vec<u32>)>>(),
            vec![]
        );
    }
}