diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/midir/src/backend | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/midir/src/backend')
-rwxr-xr-x | third_party/rust/midir/src/backend/alsa/mod.rs | 733 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/coremidi/mod.rs | 351 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/jack/mod.rs | 364 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/jack/wrappers.rs | 250 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/mod.rs | 22 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/webmidi/mod.rs | 250 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/winmm/handler.rs | 85 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/winmm/mod.rs | 536 | ||||
-rw-r--r-- | third_party/rust/midir/src/backend/winrt/mod.rs | 299 |
9 files changed, 2890 insertions, 0 deletions
diff --git a/third_party/rust/midir/src/backend/alsa/mod.rs b/third_party/rust/midir/src/backend/alsa/mod.rs new file mode 100755 index 0000000000..f57ffb798e --- /dev/null +++ b/third_party/rust/midir/src/backend/alsa/mod.rs @@ -0,0 +1,733 @@ +extern crate libc; +extern crate alsa; + +use std::mem; +use std::thread::{Builder, JoinHandle}; +use std::io::{stderr, Write}; +use std::ffi::{CString, CStr}; + +use self::alsa::{Seq, Direction}; +use self::alsa::seq::{PortInfo, PortSubscribe, Addr, QueueTempo, EventType, PortCap, PortType}; + +use ::{MidiMessage, Ignore}; +use ::errors::*; + +mod helpers { + use super::alsa::seq::{Seq, Addr, ClientIter, PortIter, PortInfo, PortCap, MidiEvent, PortType}; + use ::errors::PortInfoError; + + pub fn poll(fds: &mut [super::libc::pollfd], timeout: i32) -> i32 { + unsafe { super::libc::poll(fds.as_mut_ptr(), fds.len() as super::libc::nfds_t, timeout) } + } + + #[inline] + pub fn get_ports<F, T>(s: &Seq, capability: PortCap, f: F) -> Vec<T> where F: Fn(PortInfo) -> T { + ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client())) + .filter(|p| p.get_type().intersects(PortType::MIDI_GENERIC | PortType::SYNTH | PortType::APPLICATION)) + .filter(|p| p.get_capability().intersects(capability)) + .map(f) + .collect() + } + + #[inline] + pub fn get_port_count(s: &Seq, capability: PortCap) -> usize { + ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client())) + .filter(|p| p.get_type().intersects(PortType::MIDI_GENERIC | PortType::SYNTH | PortType::APPLICATION)) + .filter(|p| p.get_capability().intersects(capability)) + .count() + } + + #[inline] + pub fn get_port_name(s: &Seq, addr: Addr) -> Result<String, PortInfoError> { + use std::fmt::Write; + + let pinfo = match s.get_any_port_info(addr) { + Ok(p) => p, + Err(_) => return Err(PortInfoError::InvalidPort) + }; + + let cinfo = s.get_any_client_info(pinfo.get_client()).map_err(|_| PortInfoError::CannotRetrievePortName)?; + let mut output = String::new(); + write!(&mut output, "{}:{} {}:{}", + cinfo.get_name().map_err(|_| PortInfoError::CannotRetrievePortName)?, + pinfo.get_name().map_err(|_| PortInfoError::CannotRetrievePortName)?, + pinfo.get_client(), // These lines added to make sure devices are listed + pinfo.get_port() // with full portnames added to ensure individual device names + ).unwrap(); + Ok(output) + } + + pub struct EventDecoder { + ev: MidiEvent + } + + impl EventDecoder { + pub fn new(merge_commands: bool) -> EventDecoder { + let coder = MidiEvent::new(0).unwrap(); + coder.enable_running_status(merge_commands); + EventDecoder { ev: coder } + } + + #[inline] + pub fn get_wrapped(&mut self) -> &mut MidiEvent { + &mut self.ev + } + } + + pub struct EventEncoder { + ev: MidiEvent, + buffer_size: u32 + } + + unsafe impl Send for EventEncoder {} + + impl EventEncoder { + #[inline] + pub fn new(buffer_size: u32) -> EventEncoder { + EventEncoder { + ev: MidiEvent::new(buffer_size).unwrap(), + buffer_size: buffer_size + } + } + + #[inline] + pub fn get_buffer_size(&self) -> u32 { + self.buffer_size + } + + #[inline] + pub fn resize_buffer(&mut self, bufsize: u32) -> Result<(), ()> { + match self.ev.resize_buffer(bufsize) { + Ok(_) => { + self.buffer_size = bufsize; + Ok(()) + }, + Err(_) => Err(()) + } + } + + #[inline] + pub fn get_wrapped(&mut self) -> &mut MidiEvent { + &mut self.ev + } + } +} + +const INITIAL_CODER_BUFFER_SIZE: usize = 32; + +pub struct MidiInput { + ignore_flags: Ignore, + seq: Option<Seq>, +} + +#[derive(Clone, PartialEq)] +pub struct MidiInputPort { + addr: Addr +} + +pub struct MidiInputConnection<T: 'static> { + subscription: Option<PortSubscribe>, + thread: Option<JoinHandle<(HandlerData<T>, T)>>, + vport: i32, // TODO: probably port numbers are only u8, therefore could use Option<u8> + trigger_send_fd: i32, +} + +struct HandlerData<T: 'static> { + ignore_flags: Ignore, + seq: Seq, + trigger_rcv_fd: i32, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>, + queue_id: i32, // an input queue is needed to get timestamped events +} + +impl MidiInput { + pub fn new(client_name: &str) -> Result<Self, InitError> { + let seq = match Seq::open(None, None, true) { + Ok(s) => s, + Err(_) => { return Err(InitError); } + }; + + let c_client_name = CString::new(client_name).map_err(|_| InitError)?; + seq.set_client_name(&c_client_name).map_err(|_| InitError)?; + + Ok(MidiInput { + ignore_flags: Ignore::None, + seq: Some(seq), + }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + helpers::get_ports(self.seq.as_ref().unwrap(), PortCap::READ | PortCap::SUBS_READ, |p| ::common::MidiInputPort { + imp: MidiInputPort { + addr: p.addr() + } + }) + } + + pub fn port_count(&self) -> usize { + helpers::get_port_count(self.seq.as_ref().unwrap(), PortCap::READ | PortCap::SUBS_READ) + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + helpers::get_port_name(self.seq.as_ref().unwrap(), port.addr) + } + + fn init_queue(&mut self) -> i32 { + let seq = self.seq.as_mut().unwrap(); + let mut queue_id = 0; + // Create the input queue + if !cfg!(feature = "avoid_timestamping") { + queue_id = seq.alloc_named_queue(unsafe { CStr::from_bytes_with_nul_unchecked(b"midir queue\0") }).unwrap(); + // Set arbitrary tempo (mm=100) and resolution (240) + let qtempo = QueueTempo::empty().unwrap(); + qtempo.set_tempo(600_000); + qtempo.set_ppq(240); + seq.set_queue_tempo(queue_id, &qtempo).unwrap(); + let _ = seq.drain_output(); + } + + queue_id + } + + fn init_trigger(&mut self) -> Result<[i32; 2], ()> { + let mut trigger_fds = [-1, -1]; + + if unsafe { self::libc::pipe(trigger_fds.as_mut_ptr()) } == -1 { + Err(()) + } else { + Ok(trigger_fds) + } + } + + fn create_port(&mut self, port_name: &CStr, queue_id: i32) -> Result<i32, ()> { + let mut pinfo = PortInfo::empty().unwrap(); + // these functions are private, and the values are zeroed already by `empty()` + //pinfo.set_client(0); + //pinfo.set_port(0); + pinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE); + pinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION); + pinfo.set_midi_channels(16); + + if !cfg!(feature = "avoid_timestamping") { + pinfo.set_timestamping(true); + pinfo.set_timestamp_real(true); + pinfo.set_timestamp_queue(queue_id); + } + + pinfo.set_name(port_name); + match self.seq.as_mut().unwrap().create_port(&mut pinfo) { + Ok(_) => Ok(pinfo.get_port()), + Err(_) => Err(()) + } + } + + fn start_input_queue(&mut self, queue_id: i32) { + if !cfg!(feature = "avoid_timestamping") { + let seq = self.seq.as_mut().unwrap(); + let _ = seq.control_queue(queue_id, EventType::Start, 0, None); + let _ = seq.drain_output(); + } + } + + pub fn connect<F, T: Send>( + mut self, port: &MidiInputPort, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<Self>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let trigger_fds = match self.init_trigger() { + Ok(fds) => fds, + Err(()) => { return Err(ConnectError::other("could not create communication pipe for ALSA handler", self)); } + }; + + let queue_id = self.init_queue(); + + let src_pinfo = match self.seq.as_ref().unwrap().get_any_port_info(port.addr) { + Ok(p) => p, + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + + let c_port_name = match CString::new(port_name) { + Ok(c_port_name) => c_port_name, + Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self)) + }; + + let vport = match self.create_port(&c_port_name, queue_id) { + Ok(vp) => vp, + Err(_) => { + return Err(ConnectError::other("could not create ALSA input port", self)); + } + }; + + // Make subscription + let sub = PortSubscribe::empty().unwrap(); + sub.set_sender(src_pinfo.addr()); + sub.set_dest(Addr { client: self.seq.as_ref().unwrap().client_id().unwrap(), port: vport}); + if self.seq.as_ref().unwrap().subscribe_port(&sub).is_err() { + return Err(ConnectError::other("could not create ALSA input subscription", self)); + } + let subscription = sub; + + // Start the input queue + self.start_input_queue(queue_id); + + // Start our MIDI input thread. + let handler_data = HandlerData { + ignore_flags: self.ignore_flags, + seq: self.seq.take().unwrap(), + trigger_rcv_fd: trigger_fds[0], + callback: Box::new(callback), + queue_id: queue_id + }; + + let threadbuilder = Builder::new(); + let name = format!("midir ALSA input handler (port '{}')", port_name); + let threadbuilder = threadbuilder.name(name); + let thread = match threadbuilder.spawn(move || { + let mut d = data; + let h = handle_input(handler_data, &mut d); + (h, d) // return both the handler data and the user data + }) { + Ok(handle) => handle, + Err(_) => { + //unsafe { snd_seq_unsubscribe_port(self.seq.as_mut_ptr(), sub.as_ptr()) }; + return Err(ConnectError::other("could not start ALSA input handler thread", self)); + } + }; + + Ok(MidiInputConnection { + subscription: Some(subscription), + thread: Some(thread), + vport: vport, + trigger_send_fd: trigger_fds[1] + }) + } + + pub fn create_virtual<F, T: Send>( + mut self, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<Self>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + let trigger_fds = match self.init_trigger() { + Ok(fds) => fds, + Err(()) => { return Err(ConnectError::other("could not create communication pipe for ALSA handler", self)); } + }; + + let queue_id = self.init_queue(); + + let c_port_name = match CString::new(port_name) { + Ok(c_port_name) => c_port_name, + Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self)) + }; + + let vport = match self.create_port(&c_port_name, queue_id) { + Ok(vp) => vp, + Err(_) => { + return Err(ConnectError::other("could not create ALSA input port", self)); + } + }; + + // Start the input queue + self.start_input_queue(queue_id); + + // Start our MIDI input thread. + let handler_data = HandlerData { + ignore_flags: self.ignore_flags, + seq: self.seq.take().unwrap(), + trigger_rcv_fd: trigger_fds[0], + callback: Box::new(callback), + queue_id: queue_id + }; + + let threadbuilder = Builder::new(); + let thread = match threadbuilder.spawn(move || { + let mut d = data; + let h = handle_input(handler_data, &mut d); + (h, d) // return both the handler data and the user data + }) { + Ok(handle) => handle, + Err(_) => { + //unsafe { snd_seq_unsubscribe_port(self.seq.as_mut_ptr(), sub.as_ptr()) }; + return Err(ConnectError::other("could not start ALSA input handler thread", self)); + } + }; + + Ok(MidiInputConnection { + subscription: None, + thread: Some(thread), + vport: vport, + trigger_send_fd: trigger_fds[1] + }) + } +} + +impl<T> MidiInputConnection<T> { + pub fn close(mut self) -> (MidiInput, T) { + let (handler_data, user_data) = self.close_internal(); + + (MidiInput { + ignore_flags: handler_data.ignore_flags, + seq: Some(handler_data.seq), + }, user_data) + } + + /// This must only be called if the handler thread has not yet been shut down + fn close_internal(&mut self) -> (HandlerData<T>, T) { + // Request the thread to stop. + let _res = unsafe { self::libc::write(self.trigger_send_fd, &false as *const bool as *const _, mem::size_of::<bool>() as self::libc::size_t) }; + + let thread = self.thread.take().unwrap(); + // Join the thread to get the handler_data back + let (handler_data, user_data) = match thread.join() { + Ok(data) => data, + // TODO: handle this more gracefully? + Err(e) => { + if let Some(e) = e.downcast_ref::<&'static str>() { + panic!("Error when joining ALSA thread: {}", e); + } else { + panic!("Unknown error when joining ALSA thread: {:?}", e); + } + } + }; + + // TODO: find out why snd_seq_unsubscribe_port takes a long time if there was not yet any input message + if let Some(ref subscription) = self.subscription { + let _ = handler_data.seq.unsubscribe_port(subscription.get_sender(), subscription.get_dest()); + } + + // Close the trigger fds (TODO: make sure that these are closed even in the presence of panic in thread) + unsafe { + self::libc::close(handler_data.trigger_rcv_fd); + self::libc::close(self.trigger_send_fd); + } + + // Stop and free the input queue + if !cfg!(feature = "avoid_timestamping") { + let _ = handler_data.seq.control_queue(handler_data.queue_id, EventType::Stop, 0, None); + let _ = handler_data.seq.drain_output(); + let _ = handler_data.seq.free_queue(handler_data.queue_id); + } + + // Delete the port + let _ = handler_data.seq.delete_port(self.vport); + + (handler_data, user_data) + } +} + + +impl<T> Drop for MidiInputConnection<T> { + fn drop(&mut self) { + // Use `self.thread` as a flag whether the connection has already been dropped + if self.thread.is_some() { + self.close_internal(); + } + } +} + +pub struct MidiOutput { + seq: Option<Seq>, // TODO: if `Seq` is marked as non-zero, this should just be pointer-sized +} + +#[derive(Clone, PartialEq)] +pub struct MidiOutputPort { + addr: Addr +} + +pub struct MidiOutputConnection { + seq: Option<Seq>, + vport: i32, + coder: helpers::EventEncoder, + subscription: Option<PortSubscribe> +} + +impl MidiOutput { + pub fn new(client_name: &str) -> Result<Self, InitError> { + let seq = match Seq::open(None, Some(Direction::Playback), true) { + Ok(s) => s, + Err(_) => { return Err(InitError); } + }; + + let c_client_name = CString::new(client_name).map_err(|_| InitError)?; + seq.set_client_name(&c_client_name).map_err(|_| InitError)?; + + Ok(MidiOutput { + seq: Some(seq), + }) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + helpers::get_ports(self.seq.as_ref().unwrap(), PortCap::WRITE | PortCap::SUBS_WRITE, |p| ::common::MidiOutputPort { + imp: MidiOutputPort { + addr: p.addr() + } + }) + } + + pub fn port_count(&self) -> usize { + helpers::get_port_count(self.seq.as_ref().unwrap(), PortCap::WRITE | PortCap::SUBS_WRITE) + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + helpers::get_port_name(self.seq.as_ref().unwrap(), port.addr) + } + + pub fn connect(mut self, port: &MidiOutputPort, port_name: &str) -> Result<MidiOutputConnection, ConnectError<Self>> { + let pinfo = match self.seq.as_ref().unwrap().get_any_port_info(port.addr) { + Ok(p) => p, + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + + let c_port_name = match CString::new(port_name) { + Ok(c_port_name) => c_port_name, + Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self)) + }; + + let vport = match self.seq.as_ref().unwrap().create_simple_port(&c_port_name, PortCap::READ | PortCap::SUBS_READ, PortType::MIDI_GENERIC | PortType::APPLICATION) { + Ok(vport) => vport, + Err(_) => return Err(ConnectError::other("could not create ALSA output port", self)) + }; + + // Make subscription + let sub = PortSubscribe::empty().unwrap(); + sub.set_sender(Addr { client: self.seq.as_ref().unwrap().client_id().unwrap(), port: vport }); + sub.set_dest(pinfo.addr()); + sub.set_time_update(true); + sub.set_time_real(true); + if self.seq.as_ref().unwrap().subscribe_port(&sub).is_err() { + return Err(ConnectError::other("could not create ALSA output subscription", self)); + } + + Ok(MidiOutputConnection { + seq: self.seq.take(), + vport: vport, + coder: helpers::EventEncoder::new(INITIAL_CODER_BUFFER_SIZE as u32), + subscription: Some(sub) + }) + } + + pub fn create_virtual( + mut self, port_name: &str + ) -> Result<MidiOutputConnection, ConnectError<Self>> { + let c_port_name = match CString::new(port_name) { + Ok(c_port_name) => c_port_name, + Err(_) => return Err(ConnectError::other("port_name must not contain null bytes", self)) + }; + + let vport = match self.seq.as_ref().unwrap().create_simple_port(&c_port_name, PortCap::READ | PortCap::SUBS_READ, PortType::MIDI_GENERIC | PortType::APPLICATION) { + Ok(vport) => vport, + Err(_) => return Err(ConnectError::other("could not create ALSA output port", self)) + }; + + Ok(MidiOutputConnection { + seq: self.seq.take(), + vport: vport, + coder: helpers::EventEncoder::new(INITIAL_CODER_BUFFER_SIZE as u32), + subscription: None + }) + } +} + + +impl MidiOutputConnection { + pub fn close(mut self) -> MidiOutput { + self.close_internal(); + + MidiOutput { + seq: self.seq.take(), + } + } + + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + let nbytes = message.len(); + assert!(nbytes <= u32::max_value() as usize); + + if nbytes > self.coder.get_buffer_size() as usize { + if self.coder.resize_buffer(nbytes as u32).is_err() { + return Err(SendError::Other("could not resize ALSA encoding buffer")); + } + } + + let mut ev = match self.coder.get_wrapped().encode(message) { + Ok((_, Some(ev))) => ev, + _ => return Err(SendError::InvalidData("ALSA encoder reported invalid data")) + }; + + ev.set_source(self.vport); + ev.set_subs(); + ev.set_direct(); + + // Send the event. + if self.seq.as_ref().unwrap().event_output(&mut ev).is_err() { + return Err(SendError::Other("could not send encoded ALSA message")); + } + + let _ = self.seq.as_mut().unwrap().drain_output(); + Ok(()) + } + + fn close_internal(&mut self) { + let seq = self.seq.as_mut().unwrap(); + if let Some(ref subscription) = self.subscription { + let _ = seq.unsubscribe_port(subscription.get_sender(), subscription.get_dest()); + } + let _ = seq.delete_port(self.vport); + } +} + +impl Drop for MidiOutputConnection { + fn drop(&mut self) { + if self.seq.is_some() { + self.close_internal(); + } + } +} + +fn handle_input<T>(mut data: HandlerData<T>, user_data: &mut T) -> HandlerData<T> { + use self::alsa::PollDescriptors; + use self::alsa::seq::Connect; + + let mut continue_sysex: bool = false; + + // ALSA documentation says: + // The required buffer size for a sequencer event it as most 12 bytes, except for System Exclusive events (which we handle separately) + let mut buffer = [0; 12]; + + let mut coder = helpers::EventDecoder::new(false); + + let mut poll_fds: Box<[self::libc::pollfd]>; + { + let poll_desc_info = (&data.seq, Some(Direction::Capture)); + let poll_fd_count = poll_desc_info.count() + 1; + let mut vec = Vec::with_capacity(poll_fd_count); + unsafe { + vec.set_len(poll_fd_count); + poll_fds = vec.into_boxed_slice(); + } + poll_desc_info.fill(&mut poll_fds[1..]).unwrap(); + } + poll_fds[0].fd = data.trigger_rcv_fd; + poll_fds[0].events = self::libc::POLLIN; + + + let mut message = MidiMessage::new(); + + { // open scope where we can borrow data.seq + let mut seq_input = data.seq.input(); + + let mut do_input = true; + while do_input { + if let Ok(0) = seq_input.event_input_pending(true) { + // No data pending + if helpers::poll(&mut poll_fds, -1) >= 0 { + // Read from our "channel" whether we should stop the thread + if poll_fds[0].revents & self::libc::POLLIN != 0 { + let _res = unsafe { self::libc::read(poll_fds[0].fd, mem::transmute(&mut do_input), mem::size_of::<bool>() as self::libc::size_t) }; + } + } + continue; + } + + // This is a bit weird, but we now have to decode an ALSA MIDI + // event (back) into MIDI bytes. We'll ignore non-MIDI types. + + // The ALSA sequencer has a maximum buffer size for MIDI sysex + // events of 256 bytes. If a device sends sysex messages larger + // than this, they are segmented into 256 byte chunks. So, + // we'll watch for this and concatenate sysex chunks into a + // single sysex message if necessary. + // + // TODO: Figure out if this is still true (seems to not be the case) + // If not (i.e., each event represents a complete message), we can + // call the user callback with the byte buffer directly, without the + // copying to `message.bytes` first. + if !continue_sysex { message.bytes.clear() } + + let ignore_flags = data.ignore_flags; + + // If here, there should be data. + let mut ev = match seq_input.event_input() { + Ok(ev) => ev, + Err(ref e) if e.errno() == alsa::nix::errno::Errno::ENOSPC => { + let _ = writeln!(stderr(), "\nError in handle_input: ALSA MIDI input buffer overrun!\n"); + continue; + }, + Err(ref e) if e.errno() == alsa::nix::errno::Errno::EAGAIN => { + let _ = writeln!(stderr(), "\nError in handle_input: no input event from ALSA MIDI input buffer!\n"); + continue; + }, + Err(ref e) => { + let _ = writeln!(stderr(), "\nError in handle_input: unknown ALSA MIDI input error ({})!\n", e); + //perror("System reports"); + continue; + } + }; + + let do_decode = match ev.get_type() { + EventType::PortSubscribed => { + if cfg!(debug) { println!("Notice from handle_input: ALSA port connection made!") }; + false + }, + EventType::PortUnsubscribed => { + if cfg!(debug) { + let _ = writeln!(stderr(), "Notice from handle_input: ALSA port connection has closed!"); + let connect = ev.get_data::<Connect>().unwrap(); + let _ = writeln!(stderr(), "sender = {}:{}, dest = {}:{}", + connect.sender.client, + connect.sender.port, + connect.dest.client, + connect.dest.port + ); + } + false + }, + EventType::Qframe => { // MIDI time code + !ignore_flags.contains(Ignore::Time) + }, + EventType::Tick => { // 0xF9 ... MIDI timing tick + !ignore_flags.contains(Ignore::Time) + }, + EventType::Clock => { // 0xF8 ... MIDI timing (clock) tick + !ignore_flags.contains(Ignore::Time) + }, + EventType::Sensing => { // Active sensing + !ignore_flags.contains(Ignore::ActiveSense) + }, + EventType::Sysex => { + if !ignore_flags.contains(Ignore::Sysex) { + // Directly copy the data from the external buffer to our message + message.bytes.extend_from_slice(ev.get_ext().unwrap()); + continue_sysex = *message.bytes.last().unwrap() != 0xF7; + } + false // don't ever decode sysex messages (it would unnecessarily copy the message content to another buffer) + }, + _ => true + }; + + // NOTE: SysEx messages have already been "decoded" at this point! + if do_decode { + if let Ok(nbytes) = coder.get_wrapped().decode(&mut buffer, &mut ev) { + if nbytes > 0 { + message.bytes.extend_from_slice(&buffer[0..nbytes]); + } + } + } + + if message.bytes.len() == 0 || continue_sysex { continue; } + + // Calculate the time stamp: + // Use the ALSA sequencer event time data. + // (thanks to Pedro Lopez-Cabanillas!). + let alsa_time = ev.get_time().unwrap(); + let secs = alsa_time.as_secs(); + let nsecs = alsa_time.subsec_nanos(); + + message.timestamp = ( secs as u64 * 1_000_000 ) + ( nsecs as u64 / 1_000 ); + (data.callback)(message.timestamp, &message.bytes, user_data); + } + + } // close scope where data.seq is borrowed + data // return data back to thread owner +} diff --git a/third_party/rust/midir/src/backend/coremidi/mod.rs b/third_party/rust/midir/src/backend/coremidi/mod.rs new file mode 100644 index 0000000000..31fd905778 --- /dev/null +++ b/third_party/rust/midir/src/backend/coremidi/mod.rs @@ -0,0 +1,351 @@ +extern crate coremidi; + +use std::sync::{Arc, Mutex}; + +use ::errors::*; +use ::Ignore; +use ::MidiMessage; + +use self::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<Source> +} + +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 { + // Acording 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<Self, InitError> { + 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<::common::MidiInputPort> { + Sources.into_iter().map(|s| ::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<String, PortInfoError> { + match port.source.display_name() { + Some(name) => Ok(name), + None => Err(PortInfoError::CannotRetrievePortName) + } + } + + fn handle_input<T>(packets: &PacketList, handler_data: &mut HandlerData<T>) { + 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 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<F, T: Send + 'static>( + self, port: &MidiInputPort, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + 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<F, T: Send + 'static>( + self, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + 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<T> { + 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<Mutex<HandlerData<T>>> +} + +impl<T> MidiInputConnection<T> { + 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<T> { + message: MidiMessage, + ignore_flags: Ignore, + continue_sysex: bool, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>, + user_data: Option<T> +} + +pub struct MidiOutput { + client: Client +} + +#[derive(Clone)] +pub struct MidiOutputPort { + dest: Arc<Destination> +} + +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<Self, InitError> { + match Client::new(client_name) { + Ok(cl) => Ok(MidiOutput { client: cl }), + Err(_) => Err(InitError) + } + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + Destinations.into_iter().map(|d| ::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<String, PortInfoError> { + match port.dest.display_name() { + Some(name) => Ok(name), + None => Err(PortInfoError::CannotRetrievePortName) + } + } + + pub fn connect(self, port: &MidiOutputPort, port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + 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<MidiOutputConnection, ConnectError<MidiOutput>> { + 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<Destination>), + 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 packets = PacketBuffer::new(0, 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")) + } + } + + } +} diff --git a/third_party/rust/midir/src/backend/jack/mod.rs b/third_party/rust/midir/src/backend/jack/mod.rs new file mode 100644 index 0000000000..92cc81a295 --- /dev/null +++ b/third_party/rust/midir/src/backend/jack/mod.rs @@ -0,0 +1,364 @@ +extern crate jack_sys; +extern crate libc; + +use self::jack_sys::jack_nframes_t; +use self::libc::c_void; + +use std::{mem, slice}; +use std::ffi::CString; + +mod wrappers; +use self::wrappers::*; + +use ::{Ignore, MidiMessage}; +use ::errors::*; + +const OUTPUT_RINGBUFFER_SIZE: usize = 16384; + +struct InputHandlerData<T> { + port: Option<MidiPort>, + ignore_flags: Ignore, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>, + user_data: Option<T> +} + +pub struct MidiInput { + ignore_flags: Ignore, + client: Option<Client>, +} + +#[derive(Clone, PartialEq)] +pub struct MidiInputPort { + name: CString +} + +pub struct MidiInputConnection<T> { + handler_data: Box<InputHandlerData<T>>, + client: Option<Client> +} + +impl MidiInput { + pub fn new(client_name: &str) -> Result<Self, InitError> { + let client = match Client::open(client_name, JackOpenOptions::NoStartServer) { + Ok(c) => c, + Err(_) => { return Err(InitError); } // TODO: maybe add message that Jack server might not be running + }; + + Ok(MidiInput { + ignore_flags: Ignore::None, + client: Some(client), + }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + let ports = self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsOutput); + let mut result = Vec::with_capacity(ports.count()); + for i in 0..ports.count() { + result.push(::common::MidiInputPort { + imp: MidiInputPort { + name: ports.get_c_name(i).into() + } + }) + } + result + } + + pub fn port_count(&self) -> usize { + self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsOutput).count() + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + Ok(port.name.to_string_lossy().into()) + } + + fn activate_callback<F, T: Send>(&mut self, callback: F, data: T) + -> Box<InputHandlerData<T>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static + { + let handler_data = Box::new(InputHandlerData { + port: None, + ignore_flags: self.ignore_flags, + callback: Box::new(callback), + user_data: Some(data) + }); + + let data_ptr = unsafe { mem::transmute_copy::<_, *mut InputHandlerData<T>>(&handler_data) }; + + self.client.as_mut().unwrap().set_process_callback(handle_input::<T>, data_ptr as *mut c_void); + self.client.as_mut().unwrap().activate(); + handler_data + } + + pub fn connect<F, T: Send>( + mut self, port: &MidiInputPort, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let mut handler_data = self.activate_callback(callback, data); + + // Create port ... + let dest_port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsInput) { + Ok(p) => p, + Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); } + }; + + // ... and connect it to the output + if let Err(_) = self.client.as_mut().unwrap().connect(&port.name, dest_port.get_name()) { + return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)); + } + + handler_data.port = Some(dest_port); + + Ok(MidiInputConnection { + handler_data: handler_data, + client: self.client.take() + }) + } + + pub fn create_virtual<F, T: Send>( + mut self, port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<Self>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let mut handler_data = self.activate_callback(callback, data); + + // Create port + let port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsInput) { + Ok(p) => p, + Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); } + }; + + handler_data.port = Some(port); + + Ok(MidiInputConnection { + handler_data: handler_data, + client: self.client.take() + }) + } +} + +impl<T> MidiInputConnection<T> { + pub fn close(mut self) -> (MidiInput, T) { + self.close_internal(); + + (MidiInput { + client: self.client.take(), + ignore_flags: self.handler_data.ignore_flags, + }, self.handler_data.user_data.take().unwrap()) + } + + fn close_internal(&mut self) { + let port = self.handler_data.port.take().unwrap(); + self.client.as_mut().unwrap().unregister_midi_port(port); + self.client.as_mut().unwrap().deactivate(); + } +} + +impl<T> Drop for MidiInputConnection<T> { + fn drop(&mut self) { + if self.client.is_some() { + self.close_internal(); + } + } +} + +extern "C" fn handle_input<T>(nframes: jack_nframes_t, arg: *mut c_void) -> i32 { + let data: &mut InputHandlerData<T> = unsafe { &mut *(arg as *mut InputHandlerData<T>) }; + + // Is port created? + if let Some(ref port) = data.port { + let buff = port.get_midi_buffer(nframes); + + let mut message = MidiMessage::new(); // TODO: create MidiMessage once and reuse its buffer for every handle_input call + + // We have midi events in buffer + let evcount = buff.get_event_count(); + let mut event = mem::MaybeUninit::uninit(); + + for j in 0..evcount { + message.bytes.clear(); + unsafe { buff.get_event(event.as_mut_ptr(), j) }; + let event = unsafe { event.assume_init() }; + + for i in 0..event.size { + message.bytes.push(unsafe { *event.buffer.offset(i as isize) }); + } + + message.timestamp = Client::get_time(); // this is in microseconds + (data.callback)(message.timestamp, &message.bytes, data.user_data.as_mut().unwrap()); + } + } + + return 0; +} + +struct OutputHandlerData { + port: Option<MidiPort>, + buff_size: Ringbuffer, + buff_message: Ringbuffer, +} + +pub struct MidiOutput { + client: Option<Client>, +} + +#[derive(Clone, PartialEq)] +pub struct MidiOutputPort { + name: CString +} + +pub struct MidiOutputConnection { + handler_data: Box<OutputHandlerData>, + client: Option<Client> +} + +impl MidiOutput { + pub fn new(client_name: &str) -> Result<Self, InitError> { + let client = match Client::open(client_name, JackOpenOptions::NoStartServer) { + Ok(c) => c, + Err(_) => { return Err(InitError); } // TODO: maybe add message that Jack server might not be running + }; + + Ok(MidiOutput { + client: Some(client), + }) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + let ports = self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsInput); + let mut result = Vec::with_capacity(ports.count()); + for i in 0..ports.count() { + result.push(::common::MidiOutputPort { + imp: MidiOutputPort { + name: ports.get_c_name(i).into() + } + }) + } + result + } + + pub fn port_count(&self) -> usize { + self.client.as_ref().unwrap().get_midi_ports(PortFlags::PortIsInput).count() + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + Ok(port.name.to_string_lossy().into()) + } + + fn activate_callback(&mut self) -> Box<OutputHandlerData> { + let handler_data = Box::new(OutputHandlerData { + port: None, + buff_size: Ringbuffer::new(OUTPUT_RINGBUFFER_SIZE), + buff_message: Ringbuffer::new(OUTPUT_RINGBUFFER_SIZE) + }); + + let data_ptr = unsafe { mem::transmute_copy::<_, *mut OutputHandlerData>(&handler_data) }; + + self.client.as_mut().unwrap().set_process_callback(handle_output, data_ptr as *mut c_void); + self.client.as_mut().unwrap().activate(); + handler_data + } + + pub fn connect(mut self, port: &MidiOutputPort, port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + let mut handler_data = self.activate_callback(); + + // Create port ... + let source_port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsOutput) { + Ok(p) => p, + Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); } + }; + + // ... and connect it to the input + if let Err(_) = self.client.as_mut().unwrap().connect(source_port.get_name(), &port.name) { + return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)); + } + + handler_data.port = Some(source_port); + + Ok(MidiOutputConnection { + handler_data: handler_data, + client: self.client.take() + }) + } + + pub fn create_virtual( + mut self, port_name: &str + ) -> Result<MidiOutputConnection, ConnectError<Self>> { + let mut handler_data = self.activate_callback(); + + // Create port + let port = match self.client.as_mut().unwrap().register_midi_port(port_name, PortFlags::PortIsOutput) { + Ok(p) => p, + Err(()) => { return Err(ConnectError::other("could not register JACK port", self)); } + }; + + handler_data.port = Some(port); + + Ok(MidiOutputConnection { + handler_data: handler_data, + client: self.client.take() + }) + } +} + +impl MidiOutputConnection { + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + let nbytes = message.len(); + + // Write full message to buffer + let written = self.handler_data.buff_message.write(message); + debug_assert!(written == nbytes, "not enough bytes written to ALSA ringbuffer `message`"); + let nbytes_slice = unsafe { slice::from_raw_parts(&nbytes as *const usize as *const u8, mem::size_of_val(&nbytes)) }; + let written = self.handler_data.buff_size.write(nbytes_slice); + debug_assert!(written == mem::size_of_val(&nbytes), "not enough bytes written to ALSA ringbuffer `size`"); + Ok(()) + } + + pub fn close(mut self) -> MidiOutput { + self.close_internal(); + + MidiOutput { + client: self.client.take(), + } + } + + fn close_internal(&mut self) { + let port = self.handler_data.port.take().unwrap(); + self.client.as_mut().unwrap().unregister_midi_port(port); + self.client.as_mut().unwrap().deactivate(); + } +} + +impl Drop for MidiOutputConnection { + fn drop(&mut self) { + if self.client.is_some() { + self.close_internal(); + } + } +} + +extern "C" fn handle_output(nframes: jack_nframes_t, arg: *mut c_void) -> i32 { + let data: &mut OutputHandlerData = unsafe { mem::transmute(arg) }; + + // Is port created? + if let Some(ref port) = data.port { + let mut space: usize = 0; + + let mut buff = port.get_midi_buffer(nframes); + buff.clear(); + + while data.buff_size.get_read_space() > 0 { + let read = data.buff_size.read(&mut space as *mut usize as *mut u8, mem::size_of::<usize>()); + debug_assert!(read == mem::size_of::<usize>(), "not enough bytes read from `size` ringbuffer"); + let midi_data = buff.event_reserve(0, space); + let read = data.buff_message.read(midi_data, space); + debug_assert!(read == space, "not enough bytes read from `message` ringbuffer"); + } + } + + return 0; +} diff --git a/third_party/rust/midir/src/backend/jack/wrappers.rs b/third_party/rust/midir/src/backend/jack/wrappers.rs new file mode 100644 index 0000000000..ef00e2d73f --- /dev/null +++ b/third_party/rust/midir/src/backend/jack/wrappers.rs @@ -0,0 +1,250 @@ +#![allow(non_upper_case_globals, dead_code)] + +use std::{ptr, slice, str}; +use std::ffi::{CStr, CString}; +use std::ops::Index; + +use super::libc::{c_void, size_t}; + +use super::jack_sys::{ + jack_get_time, + jack_client_t, + jack_client_open, + jack_client_close, + jack_get_ports, + jack_port_t, + jack_port_register, + jack_port_unregister, + jack_port_name, + jack_free, + jack_activate, + jack_deactivate, + jack_nframes_t, + jack_set_process_callback, + jack_connect, + jack_port_get_buffer, + jack_midi_data_t, + jack_midi_get_event_count, + jack_midi_event_get, + jack_midi_event_t, + jack_midi_clear_buffer, + jack_midi_event_reserve, + jack_ringbuffer_t, + jack_ringbuffer_create, + jack_ringbuffer_read_space, + jack_ringbuffer_read, + jack_ringbuffer_free, + jack_ringbuffer_write, +}; + +pub const JACK_DEFAULT_MIDI_TYPE: &[u8] = b"8 bit raw midi\0"; + +bitflags! { + pub struct JackOpenOptions: u32 { + const NoStartServer = 1; + const UseExactName = 2; + const ServerName = 4; + const SessionID = 32; + } +} + +bitflags! { + pub struct PortFlags: u32 { + const PortIsInput = 1; + const PortIsOutput = 2; + const PortIsPhysical = 4; + const PortCanMonitor = 8; + const PortIsTerminal = 16; + } +} + +// TODO: hide this type +pub type ProcessCallback = extern "C" fn(nframes: jack_nframes_t, arg: *mut c_void) -> i32; + +pub struct Client { + p: *mut jack_client_t +} + +unsafe impl Send for Client {} + +impl Client { + pub fn get_time() -> u64 { + unsafe { jack_get_time() } + } + + pub fn open(name: &str, options: JackOpenOptions) -> Result<Client, ()> { + let c_name = CString::new(name).ok().expect("client name must not contain null bytes"); + let result = unsafe { jack_client_open(c_name.as_ptr(), options.bits(), ptr::null_mut()) }; + if result.is_null() { + Err(()) + } else { + Ok(Client { p: result }) + } + } + + pub fn get_midi_ports(&self, flags: PortFlags) -> PortInfos { + let ports_ptr = unsafe { jack_get_ports(self.p, ptr::null_mut(), JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const i8, flags.bits() as u64) }; + let slice = if ports_ptr.is_null() { + &[] + } else { + unsafe { + let count = (0isize..).find(|i| (*ports_ptr.offset(*i)).is_null()).unwrap() as usize; + slice::from_raw_parts(ports_ptr, count) + } + }; + PortInfos { p: slice } + } + + pub fn register_midi_port(&mut self, name: &str, flags: PortFlags) -> Result<MidiPort, ()> { + let c_name = CString::new(name).ok().expect("port name must not contain null bytes"); + let result = unsafe { jack_port_register(self.p, c_name.as_ptr(), JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const i8, flags.bits() as u64, 0) }; + if result.is_null() { + Err(()) + } else { + Ok(MidiPort { p: result }) + } + } + + /// This can not be implemented in Drop, because it needs a reference + /// to the client. But it consumes the MidiPort. + pub fn unregister_midi_port(&mut self, client: MidiPort) { + unsafe { jack_port_unregister(self.p, client.p) }; + } + + pub fn activate(&mut self) { + unsafe { jack_activate(self.p) }; + } + + pub fn deactivate(&mut self) { + unsafe { jack_deactivate(self.p) }; + } + + /// The code in the supplied function must be suitable for real-time + /// execution. That means that it cannot call functions that might block + /// for a long time. This includes all I/O functions (disk, TTY, network), + /// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select, + /// pthread_join, pthread_cond_wait, etc, etc. + pub fn set_process_callback(&mut self, callback: ProcessCallback, data: *mut c_void) { + unsafe { jack_set_process_callback(self.p, Some(callback), data) }; + } + + pub fn connect(&mut self, source_port: &CStr, destination_port: &CStr) -> Result<(), ()> { + let rc = unsafe { jack_connect(self.p, source_port.as_ptr(), destination_port.as_ptr()) }; + if rc == 0 { + Ok(()) + } else { + Err(()) // TODO: maybe handle EEXIST explicitly + } + } +} + +impl Drop for Client { + fn drop(&mut self) { + unsafe { jack_client_close(self.p) }; + } +} + +pub struct PortInfos<'a> { + p: &'a[*const i8], +} + +unsafe impl<'a> Send for PortInfos<'a> {} + +impl<'a> PortInfos<'a> { + pub fn count(&self) -> usize { + self.p.len() + } + + pub fn get_c_name(&self, index: usize) -> &CStr { + let ptr = self.p[index]; + unsafe { CStr::from_ptr(ptr) } + } +} + +impl<'a> Index<usize> for PortInfos<'a> { + type Output = str; + + fn index(&self, index: usize) -> &Self::Output { + let slice = self.get_c_name(index).to_bytes(); + str::from_utf8(slice).ok().expect("Error converting port name to UTF8") + } +} + +impl<'a> Drop for PortInfos<'a> { + fn drop(&mut self) { + if self.p.len() > 0 { + unsafe { jack_free(self.p.as_ptr() as *mut _) } + } + } +} + +pub struct MidiPort { + p: *mut jack_port_t +} + +unsafe impl Send for MidiPort {} + +impl MidiPort { + pub fn get_name(&self) -> &CStr { + unsafe { CStr::from_ptr(jack_port_name(self.p)) } + } + + pub fn get_midi_buffer(&self, nframes: jack_nframes_t) -> MidiBuffer { + let buf = unsafe { jack_port_get_buffer(self.p, nframes) }; + MidiBuffer { p: buf } + } +} + +pub struct MidiBuffer { + p: *mut c_void +} + +impl MidiBuffer { + pub fn get_event_count(&self) -> u32 { + unsafe { jack_midi_get_event_count(self.p) } + } + + pub unsafe fn get_event(&self, ev: *mut jack_midi_event_t, index: u32) { + jack_midi_event_get(ev, self.p, index); + } + + pub fn clear(&mut self) { + unsafe { jack_midi_clear_buffer(self.p) } + } + + pub fn event_reserve(&mut self, time: jack_nframes_t, data_size: usize) -> *mut jack_midi_data_t { + unsafe { jack_midi_event_reserve(self.p, time, data_size as size_t) } + } +} + +pub struct Ringbuffer { + p: *mut jack_ringbuffer_t +} + +unsafe impl Send for Ringbuffer {} + +impl Ringbuffer { + pub fn new(size: usize) -> Ringbuffer { + let result = unsafe { jack_ringbuffer_create(size as size_t) }; + Ringbuffer { p: result } + } + + pub fn get_read_space(&self) -> usize { + unsafe { jack_ringbuffer_read_space(self.p) as usize } + } + + pub fn read(&mut self, destination: *mut u8, count: usize) -> usize { + let bytes_read = unsafe { jack_ringbuffer_read(self.p, destination as *mut i8, count as size_t) }; + bytes_read as usize + } + + pub fn write(&mut self, source: &[u8]) -> usize { + unsafe { jack_ringbuffer_write(self.p, source.as_ptr() as *const i8, source.len() as size_t) as usize } + } +} + +impl Drop for Ringbuffer{ + fn drop(&mut self) { + unsafe { jack_ringbuffer_free(self.p) } + } +}
\ No newline at end of file diff --git a/third_party/rust/midir/src/backend/mod.rs b/third_party/rust/midir/src/backend/mod.rs new file mode 100644 index 0000000000..2940daeeff --- /dev/null +++ b/third_party/rust/midir/src/backend/mod.rs @@ -0,0 +1,22 @@ +// This module is not public + +// TODO: improve feature selection (make sure that there is always exactly one implementation, or enable dynamic backend selection) +// TODO: allow to disable build dependency on ALSA + +#[cfg(all(target_os="windows", not(feature = "winrt")))] mod winmm; +#[cfg(all(target_os="windows", not(feature = "winrt")))] pub use self::winmm::*; + +#[cfg(all(target_os="windows", feature = "winrt"))] mod winrt; +#[cfg(all(target_os="windows", feature = "winrt"))] pub use self::winrt::*; + +#[cfg(all(target_os="macos", not(feature = "jack")))] mod coremidi; +#[cfg(all(target_os="macos", not(feature = "jack")))] pub use self::coremidi::*; + +#[cfg(all(target_os="linux", not(feature = "jack")))] mod alsa; +#[cfg(all(target_os="linux", not(feature = "jack")))] pub use self::alsa::*; + +#[cfg(all(feature = "jack", not(target_os="windows")))] mod jack; +#[cfg(all(feature = "jack", not(target_os="windows")))] pub use self::jack::*; + +#[cfg(target_arch="wasm32")] mod webmidi; +#[cfg(target_arch="wasm32")] pub use self::webmidi::*; diff --git a/third_party/rust/midir/src/backend/webmidi/mod.rs b/third_party/rust/midir/src/backend/webmidi/mod.rs new file mode 100644 index 0000000000..b76f0b3617 --- /dev/null +++ b/third_party/rust/midir/src/backend/webmidi/mod.rs @@ -0,0 +1,250 @@ +//! Web MIDI Backend. +//! +//! Reference: +//! * [W3C Editor's Draft](https://webaudio.github.io/web-midi-api/) +//! * [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess) + +extern crate js_sys; +extern crate wasm_bindgen; +extern crate web_sys; + +use self::js_sys::{Map, Promise, Uint8Array}; +use self::wasm_bindgen::prelude::*; +use self::wasm_bindgen::JsCast; +use self::web_sys::{MidiAccess, MidiOptions, MidiMessageEvent}; + +use std::cell::RefCell; +use std::sync::{Arc, Mutex}; + +use ::errors::*; +use ::Ignore; + + + +thread_local! { + static STATIC : RefCell<Static> = RefCell::new(Static::new()); +} + +struct Static { + pub access: Option<MidiAccess>, + pub request: Option<Promise>, + pub ever_requested: bool, + + pub on_ok: Closure<dyn FnMut(JsValue)>, + pub on_err: Closure<dyn FnMut(JsValue)>, +} + +impl Static { + pub fn new() -> Self { + let mut s = Self { + access: None, + request: None, + ever_requested: false, + + on_ok: Closure::wrap(Box::new(|access| { + STATIC.with(|s|{ + let mut s = s.borrow_mut(); + let access : MidiAccess = access.dyn_into().unwrap(); + s.request = None; + s.access = Some(access); + }); + })), + on_err: Closure::wrap(Box::new(|_error| { + STATIC.with(|s|{ + let mut s = s.borrow_mut(); + s.request = None; + }); + })), + }; + // Some notes on sysex behavior: + // 1) Some devices (but not all!) may work without sysex + // 2) Chrome will only prompt the end user to grant permission if they requested sysex permissions for now... + // but that's changing soon for "security reasons" (reduced fingerprinting? poorly tested drivers?): + // https://www.chromestatus.com/feature/5138066234671104 + // + // I've chosen to hardcode sysex=true here, since that'll be compatible with more devices, *and* should change + // less behavior when Chrome's changes land. + s.request_midi_access(true); + s + } + + fn request_midi_access(&mut self, sysex: bool) { + self.ever_requested = true; + if self.access.is_some() { return; } // Already have access + if self.request.is_some() { return; } // Mid-request already + let window = if let Some(w) = web_sys::window() { w } else { return; }; + + let _request = match window.navigator().request_midi_access_with_options(MidiOptions::new().sysex(sysex)) { + Ok(p) => { self.request = Some(p.then2(&self.on_ok, &self.on_err)); }, + Err(_) => { return; } // node.js? brower doesn't support webmidi? other? + }; + } +} + +#[derive(Clone, PartialEq)] +pub struct MidiInputPort { + input: web_sys::MidiInput, +} + +pub struct MidiInput { + ignore_flags: Ignore +} + +impl MidiInput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + STATIC.with(|_|{}); + Ok(MidiInput { ignore_flags: Ignore::None }) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + STATIC.with(|s|{ + let mut v = Vec::new(); + let s = s.borrow(); + if let Some(access) = s.access.as_ref() { + let inputs : Map = access.inputs().unchecked_into(); + inputs.for_each(&mut |value, _|{ + v.push(::common::MidiInputPort { + imp: MidiInputPort { input: value.dyn_into().unwrap() } + }); + }); + } + v + }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub fn port_count(&self) -> usize { + STATIC.with(|s| { + let s = s.borrow(); + s.access.as_ref().map(|access| access.inputs().unchecked_into::<Map>().size() as usize).unwrap_or(0) + }) + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + Ok(port.input.name().unwrap_or_else(|| port.input.id())) + } + + pub fn connect<F, T: Send + 'static>( + self, port: &MidiInputPort, _port_name: &str, mut callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static + { + let input = port.input.clone(); + let _ = input.open(); // NOTE: asyncronous! + + let ignore_flags = self.ignore_flags; + let user_data = Arc::new(Mutex::new(Some(data))); + + let closure = { + let user_data = user_data.clone(); + + let closure = Closure::wrap(Box::new(move |event: MidiMessageEvent| { + let time = (event.time_stamp() * 1000.0) as u64; // ms -> us + let buffer = event.data().unwrap(); + + let status = buffer[0]; + if !(status == 0xF0 && ignore_flags.contains(Ignore::Sysex) || + status == 0xF1 && ignore_flags.contains(Ignore::Time) || + status == 0xF8 && ignore_flags.contains(Ignore::Time) || + status == 0xFE && ignore_flags.contains(Ignore::ActiveSense)) + { + callback(time, &buffer[..], user_data.lock().unwrap().as_mut().unwrap()); + } + }) as Box<dyn FnMut(MidiMessageEvent)>); + + input.set_onmidimessage(Some(closure.as_ref().unchecked_ref())); + + closure + }; + + Ok(MidiInputConnection { ignore_flags, input, user_data, closure }) + } +} + +pub struct MidiInputConnection<T> { + ignore_flags: Ignore, + input: web_sys::MidiInput, + user_data: Arc<Mutex<Option<T>>>, + #[allow(dead_code)] // Must be kept alive until we decide to unregister from input + closure: Closure<dyn FnMut(MidiMessageEvent)>, +} + +impl<T> MidiInputConnection<T> { + pub fn close(self) -> (MidiInput, T) { + let Self { ignore_flags, input, user_data, .. } = self; + + input.set_onmidimessage(None); + let mut user_data = user_data.lock().unwrap(); + + ( + MidiInput { ignore_flags }, + user_data.take().unwrap() + ) + } +} + +#[derive(Clone, PartialEq)] +pub struct MidiOutputPort { + output: web_sys::MidiOutput, +} + +pub struct MidiOutput { +} + +impl MidiOutput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + STATIC.with(|_|{}); + Ok(MidiOutput {}) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + STATIC.with(|s|{ + let mut v = Vec::new(); + let s = s.borrow(); + if let Some(access) = s.access.as_ref() { + access.outputs().unchecked_into::<Map>().for_each(&mut |value, _|{ + v.push(::common::MidiOutputPort { + imp: MidiOutputPort { output: value.dyn_into().unwrap() } + }); + }); + } + v + }) + } + + pub fn port_count(&self) -> usize { + STATIC.with(|s|{ + let s = s.borrow(); + s.access.as_ref().map(|access| access.outputs().unchecked_into::<Map>().size() as usize).unwrap_or(0) + }) + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + Ok(port.output.name().unwrap_or_else(|| port.output.id())) + } + + pub fn connect(self, port: &MidiOutputPort, _port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + let _ = port.output.open(); // NOTE: asyncronous! + Ok(MidiOutputConnection{ + output: port.output.clone() + }) + } +} + +pub struct MidiOutputConnection { + output: web_sys::MidiOutput, +} + +impl MidiOutputConnection { + pub fn close(self) -> MidiOutput { + let _ = self.output.close(); // NOTE: asyncronous! + MidiOutput {} + } + + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + self.output.send(unsafe { Uint8Array::view(message) }.as_ref()).map_err(|_| SendError::Other("JavaScript exception")) + } +} diff --git a/third_party/rust/midir/src/backend/winmm/handler.rs b/third_party/rust/midir/src/backend/winmm/handler.rs new file mode 100644 index 0000000000..b04d2ee908 --- /dev/null +++ b/third_party/rust/midir/src/backend/winmm/handler.rs @@ -0,0 +1,85 @@ +use std::{mem, slice}; +use std::io::{Write, stderr}; + +use super::winapi::shared::basetsd::DWORD_PTR; +use super::winapi::shared::minwindef::{DWORD, UINT}; +use super::winapi::um::mmeapi::midiInAddBuffer; +use super::winapi::um::mmsystem::{HMIDIIN, MIDIHDR, MMSYSERR_NOERROR, MM_MIM_DATA, + MM_MIM_LONGDATA, MM_MIM_LONGERROR}; +use super::HandlerData; +use Ignore; + +pub extern "system" fn handle_input<T>(_: HMIDIIN, + input_status: UINT, + instance_ptr: DWORD_PTR, + midi_message: DWORD_PTR, + timestamp: DWORD) { + if input_status != MM_MIM_DATA && input_status != MM_MIM_LONGDATA && input_status != MM_MIM_LONGERROR { return; } + + let data: &mut HandlerData<T> = unsafe { &mut *(instance_ptr as *mut HandlerData<T>) }; + + // Calculate time stamp. + data.message.timestamp = timestamp as u64 * 1000; // milliseconds -> microseconds + + if input_status == MM_MIM_DATA { // Channel or system message + // Make sure the first byte is a status byte. + let status: u8 = (midi_message & 0x000000FF) as u8; + if !(status & 0x80 != 0) { return; } + + // Determine the number of bytes in the MIDI message. + let nbytes: u16 = if status < 0xC0 { 3 } + else if status < 0xE0 { 2 } + else if status < 0xF0 { 3 } + else if status == 0xF1 { + if data.ignore_flags.contains(Ignore::Time) { return; } + else { 2 } + } else if status == 0xF2 { 3 } + else if status == 0xF3 { 2 } + else if status == 0xF8 && (data.ignore_flags.contains(Ignore::Time)) { + // A MIDI timing tick message and we're ignoring it. + return; + } else if status == 0xFE && (data.ignore_flags.contains(Ignore::ActiveSense)) { + // A MIDI active sensing message and we're ignoring it. + return; + } else { 1 }; + + // Copy bytes to our MIDI message. + let ptr = (&midi_message) as *const DWORD_PTR as *const u8; + let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, nbytes as usize) }; + data.message.bytes.extend_from_slice(bytes); + } else { // Sysex message (MIM_LONGDATA or MIM_LONGERROR) + let sysex = unsafe { &*(midi_message as *const MIDIHDR) }; + if !data.ignore_flags.contains(Ignore::Sysex) && input_status != MM_MIM_LONGERROR { + // Sysex message and we're not ignoring it + let bytes: &[u8] = unsafe { slice::from_raw_parts(sysex.lpData as *const u8, sysex.dwBytesRecorded as usize) }; + data.message.bytes.extend_from_slice(bytes); + // TODO: If sysex messages are longer than RT_SYSEX_BUFFER_SIZE, they + // are split in chunks. We could reassemble a single message. + } + + // The WinMM API requires that the sysex buffer be requeued after + // input of each sysex message. Even if we are ignoring sysex + // messages, we still need to requeue the buffer in case the user + // decides to not ignore sysex messages in the future. However, + // it seems that WinMM calls this function with an empty sysex + // buffer when an application closes and in this case, we should + // avoid requeueing it, else the computer suddenly reboots after + // one or two minutes. + if (unsafe {*data.sysex_buffer.0[sysex.dwUser as usize]}).dwBytesRecorded > 0 { + //if ( sysex->dwBytesRecorded > 0 ) { + let in_handle = data.in_handle.as_ref().unwrap().0.lock().unwrap(); + let result = unsafe { midiInAddBuffer(*in_handle, data.sysex_buffer.0[sysex.dwUser as usize], mem::size_of::<MIDIHDR>() as u32) }; + drop(in_handle); + if result != MMSYSERR_NOERROR { + let _ = writeln!(stderr(), "\nError in handle_input: Requeuing WinMM input sysex buffer failed.\n"); + } + + if data.ignore_flags.contains(Ignore::Sysex) { return; } + } else { return; } + } + + (data.callback)(data.message.timestamp, &data.message.bytes, data.user_data.as_mut().unwrap()); + + // Clear the vector for the next input message. + data.message.bytes.clear(); +}
\ No newline at end of file diff --git a/third_party/rust/midir/src/backend/winmm/mod.rs b/third_party/rust/midir/src/backend/winmm/mod.rs new file mode 100644 index 0000000000..2d20004ece --- /dev/null +++ b/third_party/rust/midir/src/backend/winmm/mod.rs @@ -0,0 +1,536 @@ +extern crate winapi; + +use std::{mem, ptr, slice}; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; +use std::sync::Mutex; +use std::io::{Write, stderr}; +use std::thread::sleep; +use std::time::Duration; +use memalloc::{allocate, deallocate}; +use std::mem::MaybeUninit; +use std::ptr::null_mut; + +use self::winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; +use self::winapi::shared::minwindef::{DWORD, UINT}; + +use self::winapi::um::mmeapi::{midiInAddBuffer, midiInClose, midiInGetDevCapsW, midiInGetNumDevs, + midiInOpen, midiInPrepareHeader, midiInReset, midiInStart, + midiInStop, midiInUnprepareHeader, midiOutClose, + midiOutGetDevCapsW, midiOutGetNumDevs, midiOutLongMsg, midiOutOpen, + midiOutPrepareHeader, midiOutReset, midiOutShortMsg, + midiOutUnprepareHeader}; + +use self::winapi::um::mmsystem::{CALLBACK_FUNCTION, CALLBACK_NULL, HMIDIIN, HMIDIOUT, LPMIDIHDR, + MIDIERR_NOTREADY, MIDIERR_STILLPLAYING, MIDIHDR, MIDIINCAPSW, + MIDIOUTCAPSW, MMSYSERR_BADDEVICEID, MMSYSERR_NOERROR, MMSYSERR_ALLOCATED}; + +use {Ignore, MidiMessage}; +use errors::*; + +mod handler; + +const DRV_QUERYDEVICEINTERFACE: UINT = 0x80c; +const DRV_QUERYDEVICEINTERFACESIZE: UINT = 0x80d; + +const RT_SYSEX_BUFFER_SIZE: usize = 1024; +const RT_SYSEX_BUFFER_COUNT: usize = 4; + +// helper for string conversion +fn from_wide_ptr(ptr: *const u16, max_len: usize) -> OsString { + unsafe { + assert!(!ptr.is_null()); + let len = (0..max_len as isize).position(|i| *ptr.offset(i) == 0).unwrap(); + let slice = slice::from_raw_parts(ptr, len); + OsString::from_wide(slice) + } +} + +#[derive(Debug)] +pub struct MidiInput { + ignore_flags: Ignore +} + +#[derive(Clone)] +pub struct MidiInputPort { + name: String, + interface_id: Box<[u16]> +} + +impl PartialEq for MidiInputPort { + fn eq(&self, other: &Self) -> bool { + self.interface_id == other.interface_id + } +} + +pub struct MidiInputConnection<T> { + handler_data: Box<HandlerData<T>>, +} + +impl MidiInputPort { + pub fn count() -> UINT { + unsafe { midiInGetNumDevs() } + } + + fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> { + let mut buffer_size: winapi::shared::minwindef::ULONG = 0; + let result = unsafe { winapi::um::mmeapi::midiInMessage(port_number as HMIDIIN, DRV_QUERYDEVICEINTERFACESIZE, &mut buffer_size as *mut _ as DWORD_PTR, 0) }; + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2); + unsafe { + let result = winapi::um::mmeapi::midiInMessage(port_number as HMIDIIN, DRV_QUERYDEVICEINTERFACE, buffer.as_mut_ptr() as DWORD_PTR, buffer_size as DWORD_PTR); + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + buffer.set_len(buffer_size as usize / 2); + } + //println!("{}", from_wide_ptr(buffer.as_ptr(), buffer.len()).to_string_lossy().into_owned()); + Ok(buffer.into_boxed_slice()) + } + + fn name(port_number: UINT) -> Result<String, PortInfoError> { + let mut device_caps: MaybeUninit<MIDIINCAPSW> = MaybeUninit::uninit(); + let result = unsafe { midiInGetDevCapsW(port_number as UINT_PTR, device_caps.as_mut_ptr(), mem::size_of::<MIDIINCAPSW>() as u32) }; + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + let device_caps = unsafe { device_caps.assume_init() }; + let pname = device_caps.szPname; + let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned(); + Ok(output) + } + + fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> { + Ok(MidiInputPort { + name: Self::name(port_number)?, + interface_id: Self::interface_id(port_number)? + }) + } + + fn current_port_number(&self) -> Option<UINT> { + for i in 0..Self::count() { + if let Ok(name) = Self::name(i) { + if name != self.name { continue; } + if let Ok(id) = Self::interface_id(i) { + if id == self.interface_id { + return Some(i); + } + } + } + } + None + } +} + +struct SysexBuffer([LPMIDIHDR; RT_SYSEX_BUFFER_COUNT]); +unsafe impl Send for SysexBuffer {} + +struct MidiInHandle(Mutex<HMIDIIN>); +unsafe impl Send for MidiInHandle {} + +/// 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<T> { + message: MidiMessage, + sysex_buffer: SysexBuffer, + in_handle: Option<MidiInHandle>, + ignore_flags: Ignore, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send + 'static>, + user_data: Option<T> +} + +impl MidiInput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + Ok(MidiInput { ignore_flags: Ignore::None }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + let count = MidiInputPort::count(); + let mut result = Vec::with_capacity(count as usize); + for i in 0..count { + let port = match MidiInputPort::from_port_number(i) { + Ok(p) => p, + Err(_) => continue + }; + result.push(::common::MidiInputPort { + imp: port + }); + } + result + } + + pub fn port_count(&self) -> usize { + MidiInputPort::count() as usize + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + Ok(port.name.clone()) + } + + pub fn connect<F, T: Send>( + self, port: &MidiInputPort, _port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let port_number = match port.current_port_number() { + Some(p) => p, + None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + + let mut handler_data = Box::new(HandlerData { + message: MidiMessage::new(), + sysex_buffer: SysexBuffer([null_mut(); RT_SYSEX_BUFFER_COUNT]), + in_handle: None, + ignore_flags: self.ignore_flags, + callback: Box::new(callback), + user_data: Some(data) + }); + + let mut in_handle: MaybeUninit<HMIDIIN> = MaybeUninit::uninit(); + let handler_data_ptr: *mut HandlerData<T> = &mut *handler_data; + let result = unsafe { midiInOpen(in_handle.as_mut_ptr(), + port_number as UINT, + handler::handle_input::<T> as DWORD_PTR, + handler_data_ptr as DWORD_PTR, + CALLBACK_FUNCTION) }; + if result == MMSYSERR_ALLOCATED { + return Err(ConnectError::other("could not create Windows MM MIDI input port (MMSYSERR_ALLOCATED)", self)); + } else if result != MMSYSERR_NOERROR { + return Err(ConnectError::other("could not create Windows MM MIDI input port", self)); + } + let in_handle = unsafe { in_handle.assume_init() }; + + // Allocate and init the sysex buffers. + for i in 0..RT_SYSEX_BUFFER_COUNT { + handler_data.sysex_buffer.0[i] = Box::into_raw(Box::new(MIDIHDR { + lpData: unsafe { allocate(RT_SYSEX_BUFFER_SIZE/*, mem::align_of::<u8>()*/) } as *mut i8, + dwBufferLength: RT_SYSEX_BUFFER_SIZE as u32, + dwBytesRecorded: 0, + dwUser: i as DWORD_PTR, // We use the dwUser parameter as buffer indicator + dwFlags: 0, + lpNext: ptr::null_mut(), + reserved: 0, + dwOffset: 0, + dwReserved: unsafe { mem::zeroed() }, + })); + + // TODO: are those buffers ever freed if an error occurs here (altough these calls probably only fail with out-of-memory)? + // TODO: close port in case of error? + + let result = unsafe { midiInPrepareHeader(in_handle, handler_data.sysex_buffer.0[i], mem::size_of::<MIDIHDR>() as u32) }; + if result != MMSYSERR_NOERROR { + return Err(ConnectError::other("could not initialize Windows MM MIDI input port (PrepareHeader)", self)); + } + + // Register the buffer. + let result = unsafe { midiInAddBuffer(in_handle, handler_data.sysex_buffer.0[i], mem::size_of::<MIDIHDR>() as u32) }; + if result != MMSYSERR_NOERROR { + return Err(ConnectError::other("could not initialize Windows MM MIDI input port (AddBuffer)", self)); + } + } + + handler_data.in_handle = Some(MidiInHandle(Mutex::new(in_handle))); + + // We can safely access (a copy of) `in_handle` here, although + // it has been copied into the Mutex already, because the callback + // has not been called yet. + let result = unsafe { midiInStart(in_handle) }; + if result != MMSYSERR_NOERROR { + unsafe { midiInClose(in_handle) }; + return Err(ConnectError::other("could not start Windows MM MIDI input port", self)); + } + + Ok(MidiInputConnection { + handler_data: handler_data + }) + } +} + +impl<T> MidiInputConnection<T> { + pub fn close(mut self) -> (MidiInput, T) { + self.close_internal(); + + (MidiInput { + ignore_flags: self.handler_data.ignore_flags, + }, self.handler_data.user_data.take().unwrap()) + } + + fn close_internal(&mut self) { + // for information about his lock, see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo + let in_handle_lock = self.handler_data.in_handle.as_ref().unwrap().0.lock().unwrap(); + + // TODO: Call both reset and stop here? The difference seems to be that + // reset "returns all pending input buffers to the callback function" + unsafe { + midiInReset(*in_handle_lock); + midiInStop(*in_handle_lock); + } + + for i in 0..RT_SYSEX_BUFFER_COUNT { + let result; + unsafe { + result = midiInUnprepareHeader(*in_handle_lock, self.handler_data.sysex_buffer.0[i], mem::size_of::<MIDIHDR>() as u32); + deallocate((*self.handler_data.sysex_buffer.0[i]).lpData as *mut u8, RT_SYSEX_BUFFER_SIZE/*, mem::align_of::<u8>()*/); + // recreate the Box so that it will be dropped/deallocated at the end of this scope + let _ = Box::from_raw(self.handler_data.sysex_buffer.0[i]); + } + + if result != MMSYSERR_NOERROR { + let _ = writeln!(stderr(), "Warning: Ignoring error shutting down Windows MM input port (UnprepareHeader)."); + } + } + + unsafe { midiInClose(*in_handle_lock) }; + } +} + +impl<T> Drop for MidiInputConnection<T> { + fn drop(&mut self) { + // If user_data has been emptied, we know that we already have closed the connection + if self.handler_data.user_data.is_some() { + self.close_internal() + } + } +} + +#[derive(Debug)] +pub struct MidiOutput; + +#[derive(Clone)] +pub struct MidiOutputPort { + name: String, + interface_id: Box<[u16]> +} + +impl PartialEq for MidiOutputPort { + fn eq(&self, other: &Self) -> bool { + self.interface_id == other.interface_id + } +} + +pub struct MidiOutputConnection { + out_handle: HMIDIOUT, +} + +unsafe impl Send for MidiOutputConnection {} + +impl MidiOutputPort { + pub fn count() -> UINT { + unsafe { midiOutGetNumDevs() } + } + + fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> { + let mut buffer_size: winapi::shared::minwindef::ULONG = 0; + let result = unsafe { winapi::um::mmeapi::midiOutMessage(port_number as HMIDIOUT, DRV_QUERYDEVICEINTERFACESIZE, &mut buffer_size as *mut _ as DWORD_PTR, 0) }; + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2); + unsafe { + let result = winapi::um::mmeapi::midiOutMessage(port_number as HMIDIOUT, DRV_QUERYDEVICEINTERFACE, buffer.as_mut_ptr() as DWORD_PTR, buffer_size as DWORD_PTR); + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + buffer.set_len(buffer_size as usize / 2); + } + //println!("{}", from_wide_ptr(buffer.as_ptr(), buffer.len()).to_string_lossy().into_owned()); + Ok(buffer.into_boxed_slice()) + } + + fn name(port_number: UINT) -> Result<String, PortInfoError> { + let mut device_caps: MaybeUninit<MIDIOUTCAPSW> = MaybeUninit::uninit(); + let result = unsafe { midiOutGetDevCapsW(port_number as UINT_PTR, device_caps.as_mut_ptr(), mem::size_of::<MIDIOUTCAPSW>() as u32) }; + if result == MMSYSERR_BADDEVICEID { + return Err(PortInfoError::PortNumberOutOfRange) + } else if result != MMSYSERR_NOERROR { + return Err(PortInfoError::CannotRetrievePortName) + } + let device_caps = unsafe { device_caps.assume_init() }; + let pname = device_caps.szPname; + let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned(); + Ok(output) + } + + fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> { + Ok(MidiOutputPort { + name: Self::name(port_number)?, + interface_id: Self::interface_id(port_number)? + }) + } + + fn current_port_number(&self) -> Option<UINT> { + for i in 0..Self::count() { + if let Ok(name) = Self::name(i) { + if name != self.name { continue; } + if let Ok(id) = Self::interface_id(i) { + if id == self.interface_id { + return Some(i); + } + } + } + } + None + } +} + +impl MidiOutput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + Ok(MidiOutput) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + let count = MidiOutputPort::count(); + let mut result = Vec::with_capacity(count as usize); + for i in 0..count { + let port = match MidiOutputPort::from_port_number(i) { + Ok(p) => p, + Err(_) => continue + }; + result.push(::common::MidiOutputPort { + imp: port + }); + } + result + } + + pub fn port_count(&self) -> usize { + MidiOutputPort::count() as usize + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + Ok(port.name.clone()) + } + + pub fn connect(self, port: &MidiOutputPort, _port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + let port_number = match port.current_port_number() { + Some(p) => p, + None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + let mut out_handle: MaybeUninit<HMIDIOUT> = MaybeUninit::uninit(); + let result = unsafe { midiOutOpen(out_handle.as_mut_ptr(), port_number as UINT, 0, 0, CALLBACK_NULL) }; + if result == MMSYSERR_ALLOCATED { + return Err(ConnectError::other("could not create Windows MM MIDI output port (MMSYSERR_ALLOCATED)", self)); + } else if result != MMSYSERR_NOERROR { + return Err(ConnectError::other("could not create Windows MM MIDI output port", self)); + } + Ok(MidiOutputConnection { + out_handle: unsafe { out_handle.assume_init() }, + }) + } +} + +impl MidiOutputConnection { + pub fn close(self) -> MidiOutput { + // The actual closing is done by the implementation of Drop + MidiOutput // In this API this is a noop + } + + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + let nbytes = message.len(); + if nbytes == 0 { + return Err(SendError::InvalidData("message to be sent must not be empty")); + } + + if message[0] == 0xF0 { // Sysex message + // Allocate buffer for sysex data and copy message + let mut buffer = message.to_vec(); + + // Create and prepare MIDIHDR structure. + let mut sysex = MIDIHDR { + lpData: buffer.as_mut_ptr() as *mut i8, + dwBufferLength: nbytes as u32, + dwBytesRecorded: 0, + dwUser: 0, + dwFlags: 0, + lpNext: ptr::null_mut(), + reserved: 0, + dwOffset: 0, + dwReserved: unsafe { mem::zeroed() }, + }; + + let result = unsafe { midiOutPrepareHeader(self.out_handle, &mut sysex, mem::size_of::<MIDIHDR>() as u32) }; + + if result != MMSYSERR_NOERROR { + return Err(SendError::Other("preparation for sending sysex message failed (OutPrepareHeader)")); + } + + // Send the message. + loop { + let result = unsafe { midiOutLongMsg(self.out_handle, &mut sysex, mem::size_of::<MIDIHDR>() as u32) }; + if result == MIDIERR_NOTREADY { + sleep(Duration::from_millis(1)); + continue; + } else { + if result != MMSYSERR_NOERROR { + return Err(SendError::Other("sending sysex message failed")); + } + break; + } + } + + loop { + let result = unsafe { midiOutUnprepareHeader(self.out_handle, &mut sysex, mem::size_of::<MIDIHDR>() as u32) }; + if result == MIDIERR_STILLPLAYING { + sleep(Duration::from_millis(1)); + continue; + } else { break; } + } + } else { // Channel or system message. + // Make sure the message size isn't too big. + if nbytes > 3 { + return Err(SendError::InvalidData("non-sysex message must not be longer than 3 bytes")); + } + + // Pack MIDI bytes into double word. + let packet: DWORD = 0; + let ptr = &packet as *const u32 as *mut u8; + for i in 0..nbytes { + unsafe { *ptr.offset(i as isize) = message[i] }; + } + + // Send the message immediately. + loop { + let result = unsafe { midiOutShortMsg(self.out_handle, packet) }; + if result == MIDIERR_NOTREADY { + sleep(Duration::from_millis(1)); + continue; + } else { + if result != MMSYSERR_NOERROR { + return Err(SendError::Other("sending non-sysex message failed")); + } + break; + } + } + } + + Ok(()) + } +} + +impl Drop for MidiOutputConnection { + fn drop(&mut self) { + unsafe { + midiOutReset(self.out_handle); + midiOutClose(self.out_handle); + } + } +} diff --git a/third_party/rust/midir/src/backend/winrt/mod.rs b/third_party/rust/midir/src/backend/winrt/mod.rs new file mode 100644 index 0000000000..a8204ec742 --- /dev/null +++ b/third_party/rust/midir/src/backend/winrt/mod.rs @@ -0,0 +1,299 @@ +extern crate winrt; + +use std::sync::{Arc, Mutex}; + +use ::errors::*; +use ::Ignore; + +use self::winrt::{AbiTransferable, HString, TryInto}; + +winrt::import!( + dependencies + os + types + windows::foundation::* + windows::devices::midi::* + windows::devices::enumeration::DeviceInformation + windows::storage::streams::{Buffer, DataWriter} +); + +use self::windows::foundation::*; +use self::windows::devices::midi::*; +use self::windows::devices::enumeration::DeviceInformation; +use self::windows::storage::streams::{Buffer, DataWriter}; + +#[derive(Clone, PartialEq)] +pub struct MidiInputPort { + id: HString +} + +unsafe impl Send for MidiInputPort {} // because HString doesn't ... + +pub struct MidiInput { + selector: HString, + ignore_flags: Ignore +} + +#[repr(C)] +pub struct abi_IMemoryBufferByteAccess { + __base: [usize; 3], + get_buffer: extern "system" fn( + winrt::NonNullRawComPtr<IMemoryBufferByteAccess>, + value: *mut *mut u8, + capacity: *mut u32, + ) -> winrt::ErrorCode, +} + +unsafe impl winrt::ComInterface for IMemoryBufferByteAccess { + type VTable = abi_IMemoryBufferByteAccess; + fn iid() -> winrt::Guid { + winrt::Guid::from_values(0x5b0d3235, 0x4dba, 0x4d44, [0x86, 0x5e, 0x8f, 0x1d, 0x0e, 0x4f, 0xd0, 0x4d]) + } +} + +unsafe impl AbiTransferable for IMemoryBufferByteAccess { + type Abi = winrt::RawComPtr<Self>; + + fn get_abi(&self) -> Self::Abi { + self.ptr.get_abi() + } + + fn set_abi(&mut self) -> *mut Self::Abi { + self.ptr.set_abi() + } +} + +#[repr(transparent)] +#[derive(Default, Clone)] +pub struct IMemoryBufferByteAccess { + ptr: winrt::ComPtr<IMemoryBufferByteAccess>, +} + +impl IMemoryBufferByteAccess { + pub unsafe fn get_buffer(&self) -> winrt::Result<&[u8]> { + match self.get_abi() { + None => panic!("The `this` pointer was null when calling method"), + Some(ptr) => { + let mut bufptr = std::ptr::null_mut(); + let mut capacity: u32 = 0; + (ptr.vtable().get_buffer)(ptr, &mut bufptr, &mut capacity).ok()?; + if capacity == 0 { + bufptr = 1 as *mut u8; // null pointer is not allowed + } + Ok(std::slice::from_raw_parts(bufptr, capacity as usize)) + } + } + } +} + + +unsafe impl Send for MidiInput {} // because HString doesn't ... + +impl MidiInput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + let device_selector = MidiInPort::get_device_selector().map_err(|_| InitError)?; + Ok(MidiInput { selector: device_selector, ignore_flags: Ignore::None }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + let count = device_collection.size().expect("get_size failed") as usize; + let mut result = Vec::with_capacity(count as usize); + for device_info in device_collection.into_iter() { + let device_id = device_info.id().expect("get_id failed"); + result.push(::common::MidiInputPort { + imp: MidiInputPort { id: device_id } + }); + } + result + } + + pub fn port_count(&self) -> usize { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + device_collection.size().expect("get_size failed") as usize + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + let device_info_async = DeviceInformation::create_from_id_async(&port.id).map_err(|_| PortInfoError::InvalidPort)?; + let device_info = device_info_async.get().map_err(|_| PortInfoError::InvalidPort)?; + let device_name = device_info.name().map_err(|_| PortInfoError::CannotRetrievePortName)?; + Ok(device_name.to_string()) + } + + fn handle_input<T>(args: &MidiMessageReceivedEventArgs, handler_data: &mut HandlerData<T>) { + let ignore = handler_data.ignore_flags; + let data = &mut handler_data.user_data.as_mut().unwrap(); + let timestamp; + let byte_access: IMemoryBufferByteAccess; + let message_bytes; + let message = args.message().expect("get_message failed"); + timestamp = message.timestamp().expect("get_timestamp failed").duration as u64 / 10; + let buffer = message.raw_data().expect("get_raw_data failed"); + let membuffer = Buffer::create_memory_buffer_over_ibuffer(&buffer).expect("create_memory_buffer_over_ibuffer failed"); + byte_access = membuffer.create_reference().expect("create_reference failed").try_into().unwrap(); + message_bytes = unsafe { byte_access.get_buffer().expect("get_buffer failed") }; // TODO: somehow make sure that the buffer is not invalidated while we're reading from it ... + + // The first byte in the message is the status + let status = message_bytes[0]; + + if !(status == 0xF0 && ignore.contains(Ignore::Sysex) || + status == 0xF1 && ignore.contains(Ignore::Time) || + status == 0xF8 && ignore.contains(Ignore::Time) || + status == 0xFE && ignore.contains(Ignore::ActiveSense)) + { + (handler_data.callback)(timestamp, message_bytes, data); + } + } + + pub fn connect<F, T: Send + 'static>( + self, port: &MidiInputPort, _port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let in_port = match MidiInPort::from_id_async(&port.id) { + Ok(port_async) => match port_async.get() { + Ok(port) => port, + _ => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + } + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + + let handler_data = Arc::new(Mutex::new(HandlerData { + ignore_flags: self.ignore_flags, + callback: Box::new(callback), + user_data: Some(data) + })); + let handler_data2 = handler_data.clone(); + + let handler = TypedEventHandler::new(move |_sender, args| { + MidiInput::handle_input(args, &mut *handler_data2.lock().unwrap()); + Ok(()) + }); + + let event_token = in_port.message_received(&handler).expect("add_message_received failed"); + + Ok(MidiInputConnection { port: RtMidiInPort(in_port), event_token: event_token, handler_data: handler_data }) + } +} + +struct RtMidiInPort(MidiInPort); +unsafe impl Send for RtMidiInPort {} + +pub struct MidiInputConnection<T> { + port: RtMidiInPort, + event_token: EventRegistrationToken, + // 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<Mutex<HandlerData<T>>> +} + + +impl<T> MidiInputConnection<T> { + pub fn close(self) -> (MidiInput, T) { + let _ = self.port.0.remove_message_received(self.event_token); + let closable: IClosable = self.port.0.try_into().unwrap(); + let _ = closable.close(); + let device_selector = MidiInPort::get_device_selector().expect("get_device_selector failed"); // probably won't ever fail here, because it worked previously + let mut handler_data_locked = self.handler_data.lock().unwrap(); + (MidiInput { + selector: device_selector, + 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<T> { + ignore_flags: Ignore, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>, + user_data: Option<T> +} + +#[derive(Clone, PartialEq)] +pub struct MidiOutputPort { + id: HString +} + +unsafe impl Send for MidiOutputPort {} // because HString doesn't ... + +pub struct MidiOutput { + selector: HString // TODO: change to FastHString? +} + +unsafe impl Send for MidiOutput {} // because HString doesn't ... + +impl MidiOutput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + let device_selector = MidiOutPort::get_device_selector().map_err(|_| InitError)?; + Ok(MidiOutput { selector: device_selector }) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + let count = device_collection.size().expect("get_size failed") as usize; + let mut result = Vec::with_capacity(count as usize); + for device_info in device_collection.into_iter() { + let device_id = device_info.id().expect("get_id failed"); + result.push(::common::MidiOutputPort { + imp: MidiOutputPort { id: device_id } + }); + } + result + } + + pub fn port_count(&self) -> usize { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + device_collection.size().expect("get_size failed") as usize + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + let device_info_async = DeviceInformation::create_from_id_async(&port.id).map_err(|_| PortInfoError::InvalidPort)?; + let device_info = device_info_async.get().map_err(|_| PortInfoError::InvalidPort)?; + let device_name = device_info.name().map_err(|_| PortInfoError::CannotRetrievePortName)?; + Ok(device_name.to_string()) + } + + pub fn connect(self, port: &MidiOutputPort, _port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + let out_port = match MidiOutPort::from_id_async(&port.id) { + Ok(port_async) => match port_async.get() { + Ok(port) => port, + _ => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + } + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + Ok(MidiOutputConnection { port: out_port }) + } +} + +pub struct MidiOutputConnection { + port: IMidiOutPort +} + +unsafe impl Send for MidiOutputConnection {} + +impl MidiOutputConnection { + pub fn close(self) -> MidiOutput { + let closable: IClosable = self.port.try_into().unwrap(); + let _ = closable.close(); + let device_selector = MidiOutPort::get_device_selector().expect("get_device_selector failed"); // probably won't ever fail here, because it worked previously + MidiOutput { selector: device_selector } + } + + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + let data_writer = DataWriter::new().unwrap(); + data_writer.write_bytes(message).map_err(|_| SendError::Other("write_bytes failed"))?; + let buffer = data_writer.detach_buffer().map_err(|_| SendError::Other("detach_buffer failed"))?; + self.port.send_buffer(&buffer).map_err(|_| SendError::Other("send_buffer failed"))?; + Ok(()) + } +} |