Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
- Fix compilation on ARM platforms
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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!
Expand Down
37 changes: 19 additions & 18 deletions src/backend/alsa/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
extern crate libc;
extern crate alsa;
extern crate nix;

use std::mem;
use std::thread::{Builder, JoinHandle};
Expand Down Expand Up @@ -589,6 +588,13 @@ impl Drop for MidiOutputConnection {
fn handle_input<T>(mut data: HandlerData<T>, user_data: &mut T) -> HandlerData<T> {
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;

Expand All @@ -597,22 +603,17 @@ fn handle_input<T>(mut data: HandlerData<T>, user_data: &mut T) -> HandlerData<T
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 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
Expand Down Expand Up @@ -651,11 +652,11 @@ fn handle_input<T>(mut data: HandlerData<T>, user_data: &mut T) -> HandlerData<T
// If here, there should be data.
let mut ev = match seq_input.event_input() {
Ok(ev) => 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;
},
Expand Down
7 changes: 6 additions & 1 deletion src/backend/coremidi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
Empty file added src/backend/dummy/mod.rs
Empty file.
41 changes: 29 additions & 12 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
14 changes: 7 additions & 7 deletions src/backend/winmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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::<u8>()*/) } 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
Expand Down Expand Up @@ -285,7 +285,7 @@ impl<T> MidiInputConnection<T> {
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>()*/);
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]);
}
Expand Down Expand Up @@ -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)
}

Expand Down
22 changes: 10 additions & 12 deletions src/backend/winrt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -127,15 +127,13 @@ impl MidiInput {
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 ...
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];
Expand All @@ -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);
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate memalloc;

#[cfg(feature = "jack")]
#[macro_use] extern crate bitflags;

Expand Down