midir: realtime MIDI in Rust across ALSA, CoreMIDI, WinMM and Web MIDI
Cross-platform realtime MIDI processing in Rust.
At a glance
- What is it?
- midir is a Rust binding layer over the native MIDI stacks of each platform, in the spirit of RtMidi but with a Rust API and optional Jack and WinRT backends. The callback design is the real feature, and the missing higher-level message parsing is the real gap.
- Who is it for?
- Adopt midir if you are writing a Rust MIDI application or a plugin that needs low-latency input and output and you want the same code to compile on Linux, macOS, Windows and in a browser. Do not adopt it expecting a parsing layer, because the README says a higher-level API for parsing and assembling MIDI messages might be added in the future, and do not expect message queues, which the project explicitly leaves out because they can be built on callbacks.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A binding layer over each platform's own MIDI stack
midir is a cross-platform, realtime MIDI processing library in Rust, and its approach is to sit on top of the MIDI stack that each operating system already ships. On Linux that is ALSA, on macOS and iOS it is CoreMIDI, on Windows it is WinMM, and on Windows 8 and later there is also WinRT as an opt-in alternative. The project states that midir is inspired by RtMidi and supports the same features, including virtual ports and full SysEx support, with the exception of message queues, and adds that those queues can be implemented on top of callbacks using Rust's channels. That single exception is worth understanding before you commit. RtMidi offers both a polling model and a callback model, and midir gives you the callback model only. For most realtime use that is the better half, since a callback fires when a message arrives rather than when you remember to look, but it does mean a design that assumed a queue has to be restructured.
Seven backends, and two of them are behind a feature flag
The supported platform list is explicit. ALSA on Linux, WinMM on Windows, CoreMIDI on macOS and iOS, WinRT on Windows 8 and later with the winrt feature enabled, Jack on Linux and macOS with the jack feature enabled, Web MIDI in Chrome and Opera and perhaps other browsers, and Android at API 29 and later using NDK AMidi with JNI. The dependency list in Cargo.toml shows how each one is wired. The Cargo features table has default = [], so nothing extra is compiled unless you ask for it, and jack pulls in jack-sys, libc and bitflags while winrt pulls a specific set of Windows crate features around Foundation, Devices_Midi and Devices_Enumeration. The Jack backend is worth a second look because it is not just another system MIDI API. Jack is an audio connection graph, so a Jack MIDI port can sit alongside your audio graph and be routed to a specific client. If you are building a JACK audio application, that is a materially different integration from talking to ALSA directly.
The dependency footprint is deliberately tiny
The Cargo.toml is short, and the reason is that almost all of the work is delegated to platform libraries rather than reimplemented. The unconditional dependencies are log at 0.4 and bitflags at version 2 as an optional dependency, plus jack-sys, libc and the platform crates. On Linux the ALSA crate is pinned with a wide version range, alsa = ">=0.9.0, <0.13", with a comment explaining the intent to be compatible with a wide range of alsa versions. On macOS and iOS it is coremidi 0.9.0, and on Windows the windows crate is bounded with a version range of >=0.59.0 and <=0.62 plus a set of Win32 features for multimedia and audio, alongside parking_lot 0.12.1 for locking. On wasm32 targets the dependencies are js-sys, wasm-bindgen and a web-sys feature list built around MidiAccess, MidiInput, MidiOutput and the port maps. A library that touches seven platforms usually accumulates a lot of conditional compilation, and here the platform surface is pushed down into per-target dependency blocks, which keeps the core portable.
Android needs an NDK, a target and a manifest declaration
The Android path is the one with the most setup, and the README spells it out. It requires Android API 29 or later and the Android NDK, r20b or newer. The build sequence for the example, which the README notes is a build to remove before merging, starts with installing cargo-ndk, then adding the aarch64-linux-android target through rustup, then running cargo ndk with the arm64-v8a target and an output directory at ./app/src/main/jniLibs for a release build. The manifest needs a uses-feature declaration for android.software.midi with android:required set to false, and the README notes this is not needed for USB or BLE MIDI. The false value is the interesting detail. Setting it to false means the app is not filtered out of the Play Store on devices without native MIDI support, which matters if you ship a single binary that also handles Bluetooth MIDI hardware.
Running the examples is the fastest way to see it work
The examples directory is the entry point for trying the library out, and the README points at it as well as at docs.rs. There are seven example programs: test_play, test_read_input, test_list_ports, test_forward, test_reuse, test_sysex, and a browser example that lives in its own workspace member, since the Cargo.toml declares a workspace with members of the root package and examples/browser. The quickest check after cloning is the play example:
cargo run --example test_playThat will open a MIDI output and send something, which on a desktop machine usually means a MIDI output becomes available in the system list. The test_list_ports example is the diagnostic one, since it enumerates the ports the library can see, and it is the fastest way to tell whether a backend is wired correctly on your machine before you write any application code. The presence of a test_reuse example suggests the API is designed around reusing a connection across reads rather than opening a fresh port per message, which is the pattern that matters for a realtime callback.
No releases, no version tags to pin, and a changelog instead
The package version in Cargo.toml is 0.11.1 and the repository has no GitHub releases at all, which means there is no tag to pin a dependency to and no release notes attached to a version. What exists instead is a CHANGELOG.md in the repository root, and that is the place to look before upgrading. The repository is not archived and the last push was on 2026-10-10, so work is landing. For a library whose surface is small and whose consumers tend to vendor or depend on a git revision anyway, missing tags is not fatal, but it does mean the upgrade decision rests on reading a changelog rather than on a semver bump and a release note. The other licensing point is simple. The licence is MIT, stated in Cargo.toml and in a LICENSE file, which is as close to no friction as a Rust dependency gets.
The missing parsing layer is the main functional gap
The README notes that a higher-level API for parsing and assembling MIDI messages might be added in the future. That single sentence describes the boundary of the library. midir moves bytes; it does not give you a Note On struct, a velocity value or a controller number. A typical application using midir receives a byte buffer in a callback and then has to interpret channel, status and data bytes itself, which is a small amount of work but the kind of work that gets rewritten inconsistently across a codebase. The trade-off is coherent, though. A library that only moves bytes stays small, stays portable across seven backends, and does not force a message model on applications whose needs differ, such as one handling MIDI time code or a non-standard extension. SysEx is a case where the boundary matters more, since full SysEx support is listed as a feature and a SysEx payload is a length-prefixed message that has to be reassembled before it is sent. The test_sysex example exists for exactly that.
Editorial conclusion
Adopt midir if you are writing a Rust MIDI application or a plugin that needs low-latency input and output and you want the same code to compile on Linux, macOS, Windows and in a browser. Do not adopt it expecting a parsing layer, because the README says a higher-level API for parsing and assembling MIDI messages might be added in the future, and do not expect message queues, which the project explicitly leaves out because they can be built on callbacks. Before you commit, check the backend you need is on the supported list and that its feature flag exists, since Jack and WinRT are opt-in, and read the CHANGELOG.md because the repository has no tagged releases to pin against.
Frequently asked questions
What is midir?
It is a cross-platform, realtime MIDI processing library for Rust, MIT licensed, published on crates.io as midir. It is inspired by RtMidi and supports the same features including virtual ports and full SysEx support, with the exception of message queues.
Which platforms and MIDI backends does midir support?
ALSA on Linux, WinMM on Windows, CoreMIDI on macOS and iOS, WinRT on Windows 8 and later with the winrt feature, Jack on Linux and macOS with the jack feature, Web MIDI in browsers such as Chrome and Opera, and Android at API 29 and later through NDK AMidi with JNI.
How do I try midir out after cloning the repository?
Run cargo run --example test_play to send MIDI, or use the test_list_ports example to see which ports the library can enumerate. API documentation is on docs.rs and the other examples cover forwarding, reusing a connection, reading input and SysEx.
What do I need to build midir for Android?
Android API 29 or later, the Android NDK at r20b or newer, cargo-ndk installed, the aarch64-linux-android rustup target, and a manifest declaring the android.software.midi feature with android:required set to false, which is not needed for USB or BLE MIDI.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/boddlnagg-midir)Was this useful?