use std::sync::{Arc, Mutex}; use crate::errors::*; use crate::{Ignore, MidiMessage}; use coremidi::*; mod external { #[link(name = "CoreAudio", kind = "framework")] extern "C" { pub fn AudioConvertHostTimeToNanos(inHostTime: u64) -> u64; pub fn AudioGetCurrentHostTime() -> u64; } } pub struct MidiInput { client: Client, ignore_flags: Ignore, } #[derive(Clone)] pub struct MidiInputPort { source: Arc, } impl MidiInputPort { pub fn id(&self) -> String { self.source .unique_id() // According to macos docs "The system assigns unique IDs to all objects.", so I think we can ignore this case .unwrap_or(0) .to_string() } } impl PartialEq for MidiInputPort { fn eq(&self, other: &Self) -> bool { if let (Some(id1), Some(id2)) = (self.source.unique_id(), other.source.unique_id()) { id1 == id2 } else { // According to macos docs "The system assigns unique IDs to all objects.", so I think we can ignore this case false } } } impl MidiInput { pub fn new(client_name: &str) -> Result { match Client::new(client_name) { Ok(cl) => Ok(MidiInput { client: cl, ignore_flags: Ignore::None, }), Err(_) => Err(InitError), } } pub(crate) fn ports_internal(&self) -> Vec { Sources .into_iter() .map(|s| crate::common::MidiInputPort { imp: MidiInputPort { source: Arc::new(s), }, }) .collect() } pub fn ignore(&mut self, flags: Ignore) { self.ignore_flags = flags; } pub fn port_count(&self) -> usize { Sources::count() } pub fn port_name(&self, port: &MidiInputPort) -> Result { match port.source.display_name() { Some(name) => Ok(name), None => Err(PortInfoError::CannotRetrievePortName), } } fn handle_input(packets: &PacketList, handler_data: &mut HandlerData) { let continue_sysex = &mut handler_data.continue_sysex; let ignore = handler_data.ignore_flags; let message = &mut handler_data.message; let data = &mut handler_data.user_data.as_mut().unwrap(); for p in packets.iter() { let pdata = p.data(); if pdata.len() == 0 { continue; } let mut timestamp = p.timestamp(); if cfg!(not(target_os = "ios")) { if timestamp == 0 { // this might happen for asnychronous sysex messages (?) timestamp = unsafe { external::AudioGetCurrentHostTime() }; } if !*continue_sysex { message.timestamp = unsafe { external::AudioConvertHostTimeToNanos(timestamp) } as u64 / 1000; } } let mut cur_byte = 0; if *continue_sysex { // We have a continuing, segmented sysex message. if !ignore.contains(Ignore::Sysex) { // If we're not ignoring sysex messages, copy the entire packet. message.bytes.extend_from_slice(pdata); } *continue_sysex = pdata[pdata.len() - 1] != 0xF7; if !ignore.contains(Ignore::Sysex) && !*continue_sysex { // If we reached the end of the sysex, invoke the user callback (handler_data.callback)(message.timestamp, &message.bytes, data); message.bytes.clear(); } } else { while cur_byte < pdata.len() { // We are expecting that the next byte in the packet is a status byte. let status = pdata[cur_byte]; if status & 0x80 == 0 { break; } // Determine the number of bytes in the MIDI message. let size; if status < 0xC0 { size = 3; } else if status < 0xE0 { size = 2; } else if status < 0xF0 { size = 3; } else if status == 0xF0 { // A MIDI sysex if ignore.contains(Ignore::Sysex) { size = 0; cur_byte = pdata.len(); } else { size = pdata.len() - cur_byte; } *continue_sysex = pdata[pdata.len() - 1] != 0xF7; } else if status == 0xF1 { // A MIDI time code message if ignore.contains(Ignore::Time) { size = 0; cur_byte += 2; } else { size = 2; } } else if status == 0xF2 { size = 3; } else if status == 0xF3 { size = 2; } else if status == 0xF8 && ignore.contains(Ignore::Time) { // A MIDI timing tick message and we're ignoring it. size = 0; cur_byte += 1; } else if status == 0xFE && ignore.contains(Ignore::ActiveSense) { // A MIDI active sensing message and we're ignoring it. size = 0; cur_byte += 1; } else { size = 1; } // Copy the MIDI data to our vector. if size > 0 { let message_bytes = &pdata[cur_byte..(cur_byte + size)]; if !*continue_sysex { // This is either a non-sysex message or a non-segmented sysex message (handler_data.callback)(message.timestamp, message_bytes, data); message.bytes.clear(); } else { // This is the beginning of a segmented sysex message message.bytes.extend_from_slice(message_bytes); } cur_byte += size; } } } } } pub fn connect( self, port: &MidiInputPort, port_name: &str, callback: F, data: T, ) -> Result, ConnectError> where F: FnMut(u64, &[u8], &mut T) + Send + 'static, { let handler_data = Arc::new(Mutex::new(HandlerData { message: MidiMessage::new(), ignore_flags: self.ignore_flags, continue_sysex: false, callback: Box::new(callback), user_data: Some(data), })); let handler_data2 = handler_data.clone(); let iport = match self.client.input_port(port_name, move |packets| { MidiInput::handle_input(packets, &mut *handler_data2.lock().unwrap()) }) { Ok(p) => p, Err(_) => return Err(ConnectError::other("error creating MIDI input port", self)), }; if let Err(_) = iport.connect_source(&port.source) { return Err(ConnectError::other( "error connecting MIDI input port", self, )); } Ok(MidiInputConnection { client: self.client, details: InputConnectionDetails::Explicit(iport), handler_data: handler_data, }) } pub fn create_virtual( self, port_name: &str, callback: F, data: T, ) -> Result, ConnectError> where F: FnMut(u64, &[u8], &mut T) + Send + 'static, { let handler_data = Arc::new(Mutex::new(HandlerData { message: MidiMessage::new(), ignore_flags: self.ignore_flags, continue_sysex: false, callback: Box::new(callback), user_data: Some(data), })); let handler_data2 = handler_data.clone(); let vrt = match self.client.virtual_destination(port_name, move |packets| { MidiInput::handle_input(packets, &mut *handler_data2.lock().unwrap()) }) { Ok(p) => p, Err(_) => return Err(ConnectError::other("error creating MIDI input port", self)), }; Ok(MidiInputConnection { client: self.client, details: InputConnectionDetails::Virtual(vrt), handler_data: handler_data, }) } } enum InputConnectionDetails { Explicit(InputPort), Virtual(VirtualDestination), } pub struct MidiInputConnection { client: Client, #[allow(dead_code)] details: InputConnectionDetails, // TODO: get rid of Arc & Mutex? // synchronization is required because the borrow checker does not // know that the callback we're in here is never called concurrently // (always in sequence) handler_data: Arc>>, } impl MidiInputConnection { pub fn close(self) -> (MidiInput, T) { let mut handler_data_locked = self.handler_data.lock().unwrap(); ( MidiInput { client: self.client, ignore_flags: handler_data_locked.ignore_flags, }, handler_data_locked.user_data.take().unwrap(), ) } } /// This is all the data that is stored on the heap as long as a connection /// is opened and passed to the callback handler. /// /// It is important that `user_data` is the last field to not influence /// offsets after monomorphization. struct HandlerData { message: MidiMessage, ignore_flags: Ignore, continue_sysex: bool, callback: Box, user_data: Option, } pub struct MidiOutput { client: Client, } #[derive(Clone)] pub struct MidiOutputPort { dest: Arc, } impl MidiOutputPort { pub fn id(&self) -> String { self.dest .unique_id() // According to macos docs "The system assigns unique IDs to all objects.", so I think we can ignore this case .unwrap_or(0) .to_string() } } impl PartialEq for MidiOutputPort { fn eq(&self, other: &Self) -> bool { if let (Some(id1), Some(id2)) = (self.dest.unique_id(), other.dest.unique_id()) { id1 == id2 } else { // Acording to macos docs "The system assigns unique IDs to all objects.", so I think we can ignore this case false } } } impl MidiOutput { pub fn new(client_name: &str) -> Result { match Client::new(client_name) { Ok(cl) => Ok(MidiOutput { client: cl }), Err(_) => Err(InitError), } } pub(crate) fn ports_internal(&self) -> Vec { Destinations .into_iter() .map(|d| crate::common::MidiOutputPort { imp: MidiOutputPort { dest: Arc::new(d) }, }) .collect() } pub fn port_count(&self) -> usize { Destinations::count() } pub fn port_name(&self, port: &MidiOutputPort) -> Result { match port.dest.display_name() { Some(name) => Ok(name), None => Err(PortInfoError::CannotRetrievePortName), } } pub fn connect( self, port: &MidiOutputPort, port_name: &str, ) -> Result> { let oport = match self.client.output_port(port_name) { Ok(p) => p, Err(_) => return Err(ConnectError::other("error creating MIDI output port", self)), }; Ok(MidiOutputConnection { client: self.client, details: OutputConnectionDetails::Explicit(oport, port.dest.clone()), }) } pub fn create_virtual( self, port_name: &str, ) -> Result> { let vrt = match self.client.virtual_source(port_name) { Ok(p) => p, Err(_) => { return Err(ConnectError::other( "error creating virtual MIDI source", self, )) } }; Ok(MidiOutputConnection { client: self.client, details: OutputConnectionDetails::Virtual(vrt), }) } } enum OutputConnectionDetails { Explicit(OutputPort, Arc), Virtual(VirtualSource), } pub struct MidiOutputConnection { client: Client, details: OutputConnectionDetails, } impl MidiOutputConnection { pub fn close(self) -> MidiOutput { MidiOutput { client: self.client, } } pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { let send_time = if cfg!(feature = "coremidi_send_timestamped") && cfg!(not(target_os = "ios")) { unsafe { external::AudioGetCurrentHostTime() } } else { 0 }; let packets = PacketBuffer::new(send_time, message); match self.details { OutputConnectionDetails::Explicit(ref port, ref dest) => port .send(&dest, &packets) .map_err(|_| SendError::Other("error sending MIDI message to port")), OutputConnectionDetails::Virtual(ref vrt) => vrt .received(&packets) .map_err(|_| SendError::Other("error sending MIDI to virtual destinations")), } } }