diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a0987..f6f0e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ All major changes to this project will be documented in this file. -## [Unreleased] +## [0.8.0] - 2022-05-13 -- ... +- Upgrade `coremidi` and `alsa` dependencies +- Implement `PartialEq` for ports ## [0.7.0] - 2020-09-05 @@ -47,4 +48,4 @@ All major changes to this project will be documented in this file. ## [0.3.0] - 2017-03-21 -- Fix compilation on ARM platforms \ No newline at end of file +- Fix compilation on ARM platforms diff --git a/Cargo.toml b/Cargo.toml index 8f140de..61d6902 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "midir" -version = "0.7.0" +version = "0.8.0" authors = ["Patrick Reisert"] description = "A cross-platform, realtime MIDI processing library, inspired by RtMidi." repository = "https://github.com/Boddlnagg/midir" @@ -18,24 +18,26 @@ license = "MIT" [features] default = [] avoid_timestamping = [] +coremidi_send_timestamped = [] jack = ["jack-sys", "libc"] [dependencies] bitflags = "1.2" -memalloc = "0.1.0" -jack-sys = { version = "0.1.0", optional = true } +jack-sys = { version = "0.2", optional = true } libc = { version = "0.2.21", optional = true } winrt = { version = "0.7.0", optional = true} [target.'cfg(target_os = "linux")'.dependencies] -alsa = "0.4.3" -nix = "0.15" +alsa = "0.7" libc = "0.2.21" +[target.'cfg(target_os = "ios")'.dependencies] +coremidi = "0.6.0" + [target.'cfg(target_os = "macos")'.dependencies] -coremidi = "0.4.0" +coremidi = "0.6.0" -[target.'cfg(windows)'.dependencies] +[target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["mmsystem", "mmeapi"] } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/README.md b/README.md index 642848c..42e1012 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # midir [![crates.io](https://img.shields.io/crates/v/midir.svg)](https://crates.io/crates/midir) [![Build Status](https://dev.azure.com/Boddlnagg/midir/_apis/build/status/Boddlnagg.midir?branchName=master)](https://dev.azure.com/Boddlnagg/midir/_build/latest?definitionId=1) -Cross-platform, realtime MIDI processing in Rust. +Cross-platform, realtime MIDI processing in Rust. This is a friendly fork with +small changes required for vendoring the crate in Firefox. It will go away as +soon as we will be able to vendor the upstream crate. ## Features **midir** is inspired by [RtMidi](https://github.com/thestk/rtmidi) and supports the same features*, including virtual ports (except on Windows) and full SysEx support – but with a rust-y API! diff --git a/src/backend/alsa/mod.rs b/src/backend/alsa/mod.rs index 4971413..a8cd58c 100755 --- a/src/backend/alsa/mod.rs +++ b/src/backend/alsa/mod.rs @@ -1,6 +1,5 @@ extern crate libc; extern crate alsa; -extern crate nix; use std::mem; use std::thread::{Builder, JoinHandle}; @@ -589,6 +588,13 @@ impl Drop for MidiOutputConnection { fn handle_input(mut data: HandlerData, user_data: &mut T) -> HandlerData { use self::alsa::PollDescriptors; use self::alsa::seq::Connect; + use self::libc::pollfd; + + const INVALID_POLLFD: pollfd = pollfd { + fd: -1, + events: 0, + revents: 0, + }; let mut continue_sysex: bool = false; @@ -597,22 +603,17 @@ fn handle_input(mut data: HandlerData, user_data: &mut T) -> HandlerData; - { - 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 poll_desc_info = (&data.seq, Some(Direction::Capture)); + let mut poll_fds = vec![INVALID_POLLFD; poll_desc_info.count() + 1]; + poll_fds[0] = pollfd { + fd: data.trigger_rcv_fd, + events: self::libc::POLLIN, + revents: 0, + }; + + poll_desc_info.fill(&mut poll_fds[1..]).unwrap(); + let mut message = MidiMessage::new(); { // open scope where we can borrow data.seq @@ -651,11 +652,11 @@ fn handle_input(mut data: HandlerData, user_data: &mut T) -> HandlerData ev, - Err(ref e) if e.errno() == Some(self::nix::errno::Errno::ENOSPC) => { + 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() == Some(self::nix::errno::Errno::EAGAIN) => { + 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; }, diff --git a/src/backend/coremidi/mod.rs b/src/backend/coremidi/mod.rs index 31fd905..f129e04 100644 --- a/src/backend/coremidi/mod.rs +++ b/src/backend/coremidi/mod.rs @@ -337,7 +337,12 @@ impl MidiOutputConnection { } pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { - let packets = PacketBuffer::new(0, message); + let send_time = if cfg!(feature = "coremidi_send_timestamped") { + unsafe { external::AudioGetCurrentHostTime() } + } else { + 0 + }; + let packets = PacketBuffer::new(send_time, message); match self.details { OutputConnectionDetails::Explicit(ref port, ref dest) => { port.send(&dest, &packets).map_err(|_| SendError::Other("error sending MIDI message to port")) diff --git a/src/backend/dummy/mod.rs b/src/backend/dummy/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 2940dae..a948a9d 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -3,20 +3,37 @@ // 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", 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 = "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 = "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(target_os = "ios", not(feature = "jack")))] +mod coremidi; +#[cfg(all(target_os = "ios", not(feature = "jack")))] +pub use self::coremidi::*; -#[cfg(all(feature = "jack", not(target_os="windows")))] mod jack; -#[cfg(all(feature = "jack", not(target_os="windows")))] pub use self::jack::*; +#[cfg(all(target_os = "linux", not(feature = "jack")))] +mod alsa; +#[cfg(all(target_os = "linux", not(feature = "jack")))] +pub use self::alsa::*; -#[cfg(target_arch="wasm32")] mod webmidi; -#[cfg(target_arch="wasm32")] pub use self::webmidi::*; +#[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/src/backend/winmm/mod.rs b/src/backend/winmm/mod.rs index 8435878..9e75996 100644 --- a/src/backend/winmm/mod.rs +++ b/src/backend/winmm/mod.rs @@ -7,9 +7,9 @@ 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 std::alloc::{alloc, dealloc, Layout}; use self::winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; use self::winapi::shared::minwindef::{DWORD, UINT}; @@ -103,8 +103,8 @@ impl MidiInputPort { return Err(PortInfoError::CannotRetrievePortName) } let device_caps = unsafe { device_caps.assume_init() }; - let pname: &[u16] = unsafe { &device_caps.szPname }; // requires unsafe because of packed alignment ... - let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned(); + let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname); + let output = from_wide_ptr(pname_ptr as *const _, 32).to_string_lossy().into_owned(); Ok(output) } @@ -218,7 +218,7 @@ impl MidiInput { // 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::()*/) } as *mut i8, + lpData: unsafe { alloc(Layout::from_size_align_unchecked(RT_SYSEX_BUFFER_SIZE, 1)) } 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 @@ -285,7 +285,7 @@ impl MidiInputConnection { let result; unsafe { result = midiInUnprepareHeader(*in_handle_lock, self.handler_data.sysex_buffer.0[i], mem::size_of::() as u32); - deallocate((*self.handler_data.sysex_buffer.0[i]).lpData as *mut u8, RT_SYSEX_BUFFER_SIZE/*, mem::align_of::()*/); + dealloc((*self.handler_data.sysex_buffer.0[i]).lpData as *mut u8, Layout::from_size_align_unchecked(RT_SYSEX_BUFFER_SIZE, 1)); // 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]); } @@ -365,8 +365,8 @@ impl MidiOutputPort { return Err(PortInfoError::CannotRetrievePortName) } let device_caps = unsafe { device_caps.assume_init() }; - let pname: &[u16] = unsafe { &device_caps.szPname }; // requires unsafe because of packed alignment ... - let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned(); + let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname); + let output = from_wide_ptr(pname_ptr as *const _, 32).to_string_lossy().into_owned(); Ok(output) } diff --git a/src/backend/winrt/mod.rs b/src/backend/winrt/mod.rs index a8204ec..2cb52d5 100644 --- a/src/backend/winrt/mod.rs +++ b/src/backend/winrt/mod.rs @@ -14,13 +14,13 @@ winrt::import!( windows::foundation::* windows::devices::midi::* windows::devices::enumeration::DeviceInformation - windows::storage::streams::{Buffer, DataWriter} + windows::storage::streams::{DataReader, DataWriter} ); use self::windows::foundation::*; use self::windows::devices::midi::*; use self::windows::devices::enumeration::DeviceInformation; -use self::windows::storage::streams::{Buffer, DataWriter}; +use self::windows::storage::streams::{DataReader, DataWriter}; #[derive(Clone, PartialEq)] pub struct MidiInputPort { @@ -127,15 +127,13 @@ impl MidiInput { fn handle_input(args: &MidiMessageReceivedEventArgs, handler_data: &mut HandlerData) { 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 ... + let message = args.message().expect("Message failed"); + let timestamp = message.timestamp().expect("Timestamp failed").duration as u64 / 10; + let buffer = message.raw_data().expect("raw_data failed"); + let length = buffer.length().expect("length failed") as usize; + let data_reader = DataReader::from_buffer(&buffer).expect("from_buffer failed"); + let mut message_bytes = vec![0; length]; + data_reader.read_bytes(&mut message_bytes).expect("read_bytes failed"); // The first byte in the message is the status let status = message_bytes[0]; @@ -145,7 +143,7 @@ impl MidiInput { status == 0xF8 && ignore.contains(Ignore::Time) || status == 0xFE && ignore.contains(Ignore::ActiveSense)) { - (handler_data.callback)(timestamp, message_bytes, data); + (handler_data.callback)(timestamp, &message_bytes, data); } } diff --git a/src/lib.rs b/src/lib.rs index 99d6840..39911c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -extern crate memalloc; - #[cfg(feature = "jack")] #[macro_use] extern crate bitflags;