Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  events.rs

  Sprache: 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(crateunsafe 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]) -> &le='color:red'>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(crateenum 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(crateconst INLINE_SIZE: usize = 8 // MIDIEventList header
        + 12 // MIDIEventPacket header
        + 4 * 4// 4 words

    #[inline]
    #[allow(clippy::uninit_vec)]
    pub(cratefn with_capacity(capacity: usize) -> Self {
        if capacity <= Self::INLINE_SIZE {
            Self::Inline([0Self::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(cratefn capacity(&self) -> usize {
        match *self {
            Storage::Inline(ref inline) => inline.len() * 4,
            Storage::External(ref vec) => vec.len() * 4,
        }
    }

    #[inline]
    pub(cratefn 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(crateunsafe 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(crateunsafe 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(crateunsafe 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,
                [12].as_ptr(),
            )
        };
        let _ = unsafe {
            MIDIEventListAdd(
                event_list_ptr,
                BUFFER_SIZE as ByteCount,
                event_packet_ptr,
                20,
                3,
                [345].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![12]), (20, vec![345]),]
        );
    }

    #[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, &[12])
            .with_packet(20, &[345]);

        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![12]), (20, vec![345]),]
        );
    }

    #[test]
    fn event_buffer_push_within_capacity() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20);
        event_buffer.push(10, &[12]).push(20, &[345]);

        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![12]), (20, vec![345]),]
        );
    }

    #[test]
    fn event_buffer_push_over_capacity() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20);
        event_buffer
            .push(10, &[12])
            .push(20, &[345678910]);

        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![12]), (20, vec![345678910])]
        );
    }

    #[test]
    fn event_buffer_clear() {
        let mut event_buffer = EventBuffer::new(Protocol::Midi20).with_packet(10, &['color: green'>12]);

        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![12])]
        );

        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![]
        );
    }
}

Messung V0.5 in Prozent
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-08-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=661743