coremidi_sys/
lib.rs

1#![cfg(any(target_os = "macos", target_os = "ios"))]
2
3#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types)]
4
5extern crate core_foundation_sys;
6
7use core_foundation_sys::string::*;
8use core_foundation_sys::data::*;
9use core_foundation_sys::dictionary::*;
10use core_foundation_sys::propertylist::*;
11
12use std::{ptr, mem};
13
14include!("generated.rs");
15
16#[inline]
17pub unsafe fn MIDIPacketNext(pkt: *const MIDIPacket) -> *const MIDIPacket {
18    // Get pointer to potentially unaligned data without triggering undefined behavior
19    // addr_of does not require creating an intermediate reference to unaligned data.
20    // See also the definition of `MIDIPacketNext` in the official SDK MIDIServices.h
21    let ptr = ptr::addr_of!((*pkt).data) as *const u8;
22    let ptr_length = ptr::addr_of!((*pkt).length) as *const u16;
23    if cfg!(any(target_arch = "arm", target_arch = "aarch64")) {
24        // MIDIPacket must be 4-byte aligned on ARM, so we need to calculate an aligned offset.
25        // We do not need `read_unaligned` for the length, because the length will never
26        // be unaligned, and `read_unaligned` would lead to less efficient machine code.
27        let offset = ptr_length.read() as isize;
28        ((ptr.offset(offset + 3) as usize) & !(3usize)) as *const MIDIPacket
29    } else {
30        // MIDIPacket is unaligned on non-ARM, so reading the length requires `read_unaligned`
31        // to not trigger Rust's UB check (although unaligned reads are harmless on Intel
32        // and `read_unaligned` will generate the same machine code as `read`).
33        let offset = ptr_length.read_unaligned() as isize;
34        ptr.offset(offset) as *const MIDIPacket
35    }
36}
37
38#[inline]
39pub unsafe fn MIDIEventPacketNext(pkt: *const MIDIEventPacket) -> *const MIDIEventPacket {
40    // Each EventPacket's size is a multiple of 4 bytes, so no special care
41    // needs to be taken when reading the data (except the timeStamp, which is not 8-byte aligned).
42    // See also the definition of `MIDIEventPacketNext` in the official SDK MIDIServices.h
43    let ptr = ptr::addr_of!((*pkt).words) as *const u8;
44    let offset = (((*pkt).wordCount as usize) * mem::size_of::<u32>()) as isize;
45    ptr.offset(offset) as *const MIDIEventPacket
46}
47
48#[allow(dead_code)]
49mod static_test {
50    /// Statically assert the correct size of `MIDIPacket` and `MIDIPacketList`,
51    /// which require non-default alignment.
52    unsafe fn assert_sizes() {
53        use super::{MIDIPacket, MIDIPacketList};
54        use std::mem::{transmute, zeroed};
55
56        let p: MIDIPacket = zeroed();
57        transmute::<_, [u8; 268]>(p);
58
59        let p: MIDIPacketList = zeroed();
60        transmute::<_, [u8; 272]>(p);
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn midi_packet_next() {
70        const BUFFER_SIZE: usize = 65536;
71        let buffer: &mut [u8] = &mut [0; BUFFER_SIZE];
72        let pkt_list_ptr = buffer.as_mut_ptr() as *mut MIDIPacketList;
73
74        let packets = vec![
75            (1, vec![0x90, 0x40, 0x7f]), // tuple of (time, [midi bytes])
76            (2, vec![0x90, 0x41, 0x7f]),
77        ];
78
79        unsafe {
80            let mut pkt_ptr = MIDIPacketListInit(pkt_list_ptr);
81            for pkt in &packets {
82                pkt_ptr = MIDIPacketListAdd(
83                    pkt_list_ptr,
84                    BUFFER_SIZE as ByteCount,
85                    pkt_ptr,
86                    pkt.0,
87                    pkt.1.len() as ByteCount,
88                    pkt.1.as_ptr(),
89                );
90                assert!(!pkt_ptr.is_null());
91            }
92        }
93
94        unsafe {
95            let first_packet = &(*pkt_list_ptr).packet as *const MIDIPacket; // get pointer to first midi packet in the list
96            let len = (*first_packet).length as usize;
97            assert_eq!(
98                &(*first_packet).data[0..len],
99                &[0x90, 0x40, 0x7f]
100            );
101
102            let second_packet = MIDIPacketNext(first_packet);
103            let ptr_length = ptr::addr_of!((*second_packet).length) as *const u16;
104            let len = ptr_length.read_unaligned() as usize;
105            assert_eq!(
106                &(*second_packet).data[0..len],
107                &[0x90, 0x41, 0x7f]
108            );
109        }
110    }
111
112    #[test]
113    fn midi_event_packet_next() {
114        const BUFFER_SIZE: usize = 65536;
115        let buffer: &mut [u8] = &mut [0; BUFFER_SIZE];
116        let pkt_list_ptr = buffer.as_mut_ptr() as *mut MIDIEventList;
117
118        let packets = vec![
119            (1, vec![10u32, 20]), // tuple of (time, [midi words])
120            (2, vec![30u32, 40, 50]),
121        ];
122
123        unsafe {
124            let mut pkt_ptr = MIDIEventListInit(pkt_list_ptr, kMIDIProtocol_2_0 as MIDIProtocolID);
125            for pkt in &packets {
126                pkt_ptr = MIDIEventListAdd(
127                    pkt_list_ptr,
128                    BUFFER_SIZE as ByteCount,
129                    pkt_ptr,
130                    pkt.0,
131                    pkt.1.len() as ByteCount,
132                    pkt.1.as_ptr(),
133                );
134                assert!(!pkt_ptr.is_null());
135            }
136        }
137
138        unsafe {
139            let first_packet = &(*pkt_list_ptr).packet as *const MIDIEventPacket; // get pointer to first midi packet in the list
140            let len = (*first_packet).wordCount as usize;
141            assert_eq!(
142                &(*first_packet).words[0..len],
143                &[10, 20]
144            );
145
146            let second_packet = MIDIEventPacketNext(first_packet);
147            let len = (*second_packet).wordCount as usize;
148            assert_eq!(
149                &(*second_packet).words[0..len],
150                &[30, 40, 50]
151            );
152        }
153    }
154}