RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut and API-specific classes) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), Windows (Multimedia Library, UWP & Windows MIDI Services), Web MIDI, iOS and Android systems. RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software. It was designed with the following goals:
Where applicable, multiple API support can be compiled and a particular API specified when creating an RtMidi instance.
MIDI input and output functionality are separated into two classes, RtMidiIn and RtMidiOut. Each class instance supports only a single MIDI connection. RtMidi does not provide timing functionality (i.e., output messages are sent immediately). Input messages are timestamped with delta times in seconds (via a double floating point type). MIDI data is passed to the user as raw bytes using an std::vector<unsigned char>.
Version 6.1.0 adds the Windows MIDI Services API and makes SysEx behave the same on every backend. Changes in this release include:
Latest Release (15 October 2026): Version 6.1.0
The first thing that must be done when using RtMidi is to create an instance of the RtMidiIn or RtMidiOut subclasses. RtMidi is an abstract base class, which itself cannot be instantiated. Each default constructor attempts to establish any necessary "connections" with the underlying MIDI system. RtMidi uses C++ exceptions to report errors, necessitating try/catch blocks around many member functions. An RtMidiError can be thrown during instantiation in some circumstances. A warning message may also be reported if no MIDI devices are found during instantiation. The RtMidi classes have been designed to work with "hot pluggable" or virtual (software) MIDI devices, making it possible to connect to MIDI devices that may not have been present when the classes were instantiated. The following code example demonstrates default object construction and destruction:
Obviously, this example doesn't demonstrate any of the real functionality of RtMidi. However, all uses of RtMidi must begin with construction and must end with class destruction. Further, it is necessary that all class methods that can throw a C++ exception be called within a try/catch block.
RtMidi uses a C++ exception handler called RtMidiError, which is declared and defined in RtMidi.h. The RtMidiError class is quite simple but it does allow errors to be "caught" by RtMidiError::Type. Many RtMidi methods can "throw" an RtMidiError, most typically if a driver error occurs or an invalid function argument is specified. There are a number of cases within RtMidi where warning messages may be displayed but an exception is not thrown. A client error callback function can be specified (via the RtMidi::setErrorCallback function) that is invoked when an error occurs. A default callback for objects that have none of their own can be set once, before any RtMidi object is created, with the static MidiApi::setDefaultErrorCallback() function; it also sees errors raised during construction, but fatal errors are still thrown after it returns. The C API provides rtmidi_set_error_callback(). Without a callback, warnings and errors are printed to std::cerr, and debug warnings are only displayed if the preprocessor definition RTMIDI_DEBUG is defined during compilation. Messages associated with caught exceptions can be displayed with, for example, the RtMidiError::printMessage() function.
A client generally must query the available MIDI ports before deciding which to use. The following example outlines how this can be done.
Note that the port enumeration is system specific and will change if any devices are unplugged or plugged (or a new virtual port opened or closed) by the user. Thus, the port numbers should be verified immediately before opening a port. As well, if a user unplugs a device (or closes a virtual port) while a port connection exists to that device/port, a MIDI system error will be generated.
The RtMidiOut class provides simple functionality to immediately send messages over a MIDI connection. No timing functionality is provided. Note that there is an overloaded RtMidiOut::sendMessage() function that does not use std::vectors. A large system exclusive message may have to be sent in pieces (see the sysexchunked.cpp program in the tests directory and the Linux: notes).
In the following example, we omit necessary error checking and details regarding OS-dependent sleep functions. For a complete example, see the midiout.cpp program in the tests directory.
The RtMidiIn class uses an internal callback function or thread to receive incoming MIDI messages from a port or device. These messages are then either queued and read by the user via calls to the RtMidiIn::getMessage() function or immediately passed to a user-specified callback function (which must be "registered" using the RtMidiIn::setCallback() function). Note that if you have multiple instances of RtMidiIn, each may have its own thread. We'll provide examples of both usages.
The RtMidiIn class provides the RtMidiIn::ignoreTypes() function to specify that certain MIDI message types be ignored. By default, system exclusive, timing, and active sensing messages are ignored. A system exclusive message is delivered as one message, however many pieces it arrives in, and real-time messages that arrive in the middle of it are delivered on their own.
The RtMidiIn::getMessage() function does not block. If a MIDI message is available in the queue, it is copied to the user-provided std::vector<unsigned char> container. When no MIDI message is available, the function returns an empty container. The default maximum MIDI queue size is 100 messages. This value may be modified with the RtMidiIn::setQueueSizeLimit() function. If the maximum queue size limit is reached, subsequent incoming MIDI messages are discarded until the queue size is reduced.
In the following example, we omit some necessary error checking and details regarding OS-dependent sleep functions. For a more complete example, see the qmidiin.cpp program in the tests directory.
When set, a user-provided callback function will be invoked after the input of a complete MIDI message. It is possible to provide a pointer to user data that can be accessed in the callback function (not shown here). It is necessary to set the callback function immediately after opening the port to avoid having incoming messages written to the queue (which is not emptied when a callback function is set). If you are worried about this happening, you can check the queue using the RtMidiIn::getMessage() function to verify it is empty (after the callback function is set).
In the following example, we omit some necessary error checking. For a more complete example, see the cmidiin.cpp program in the tests directory.
The Linux ALSA, Macintosh CoreMIDI and JACK APIs allow for the establishment of virtual input and output MIDI ports to which other software clients can connect. RtMidi incorporates this functionality with the RtMidiIn::openVirtualPort() and RtMidiOut::openVirtualPort() functions. Any messages sent with the RtMidiOut::sendMessage() function will also be transmitted through an open virtual output port. If a virtual input port is open and a user callback function is set, the callback function will be invoked when messages arrive via that port. If a callback function is not set, the user must poll the input queue to check whether messages have arrived. No notification is provided for the establishment of a client connection via a virtual port. The RtMidi::isPortOpen() function does not report the status of ports created with the RtMidi::openVirtualPort() function.
In order to compile RtMidi for a specific OS and API, it is necessary to supply the appropriate preprocessor definition and library within the compiler statement:
| OS: | MIDI API: | Preprocessor Definition: | Library or Framework: | Example Compiler Statement: |
| Linux | ALSA Sequencer | RTMIDI_API_ALSA | asound, pthread | g++ -Wall -DRTMIDI_API_ALSA -o midiprobe midiprobe.cpp RtMidi.cpp -lasound -lpthread |
| Linux or Mac | JACK MIDI | RTMIDI_API_JACK | jack | g++ -Wall -DRTMIDI_API_JACK -o midiprobe midiprobe.cpp RtMidi.cpp -ljack |
| Macintosh OS X | CoreMIDI | RTMIDI_API_CORE | CoreMIDI, CoreAudio, CoreFoundation | g++ -Wall -DRTMIDI_API_CORE -o midiprobe midiprobe.cpp RtMidi.cpp -framework CoreMIDI -framework CoreAudio -framework CoreFoundation |
| Windows | Multimedia Library | RTMIDI_API_WINMM | winmm.lib, multithreaded | compiler specific |
The example compiler statements above could be used to compile the midiprobe.cpp example file, assuming that midiprobe.cpp, RtMidi.h and RtMidi.cpp all exist in the same directory.
The other backends are RTMIDI_API_WMS (Windows MIDI Services), RTMIDI_API_UWP (Windows UWP), RTMIDI_API_WEBMIDI (Web MIDI) and RTMIDI_API_AMIDI (Android). Before 6.1.0 these definitions were LINUX_ALSA, UNIX_JACK, MACOSX_CORE, WINDOWS_MM, WINDOWS_UWP, WEB_MIDI_API and AMIDI, and RTMIDI_DEBUG and JACK_HAS_PORT_RENAME became RTMIDI_DEBUG and RTMIDI_JACK_HAS_PORT_RENAME. Names starting with underscores are reserved in C++, so the old ones are deprecated: they still work in 6.x and will be removed in 7.0.0. Windows MIDI Services and Android are built with CMake (see API Notes).
If you are having problems getting RtMidi to run on your system, try passing the preprocessor definition RTMIDI_DEBUG to the compiler (or define it in RtMidi.h). A variety of warning messages will be displayed that may help in determining the problem. Also try using the programs included in the tests directory. The program midiprobe displays the queried capabilities of all MIDI ports found.
Support for each MIDI API is encapsulated in specific MidiInApi or MidiOutApi subclasses, making it possible to compile and instantiate multiple API-specific subclasses on a given operating system. For example, one can compile both CoreMIDI and JACK support on the OS-X operating system by providing the appropriate preprocessor definitions for each. In a run-time situation, one might first attempt to determine whether any JACK ports are available. This can be done by specifying the api argument RtMidi::UNIX_JACK when attempting to create an instance of RtMidiIn or RtMidiOut. If no available ports are found, then an instance of RtMidi with the api argument RtMidi::MACOSX_CORE can be created. Alternately, if no api argument is specified, RtMidi will first look for CoreMIDI ports and if none are found, then JACK ports (in Linux, the search order is ALSA and then JACK; in Windows, Windows MIDI Services, the Multimedia Library and then UWP). If no compiled API has any ports, the first one that could be opened is used. In theory, it should also be possible to have separate instances of RtMidi open at the same time with different underlying API support, though this has not been tested.
The static function RtMidi::getCompiledApi() is provided to determine the available compiled API support. The function RtMidi::getCurrentApi() indicates the API selected for a given RtMidi instance. The static function RtMidi::checkApiAvailability() reports whether a compiled API can be used on the running system (Windows MIDI Services needs Windows 11 25H2 or later), and RtMidi::getVersion() returns the RtMidi version as a string.
RtMidi is designed to provide a common API across the various supported operating systems and audio libraries. Despite that, some issues should be mentioned with regard to each.
RtMidi for Linux was developed using the Fedora distribution. Two different MIDI APIs are supported on Linux platforms: ALSA and JACK. A decision was made to not include support for the OSS API because the OSS API provides very limited functionality and because ALSA support is now incorporated in the Linux kernel. The ALSA sequencer and JACK APIs allows for virtual software input and output ports.
A large system exclusive message may not survive a single RtMidiOut::sendMessage() call. Between two applications on ALSA, one larger than 5572 bytes is lost at the receiver (and an error is reported); to a hardware port on ALSA, only 4096 bytes fit the kernel's buffer and the rest is cut off without an error; and on JACK, one larger than 16379 bytes is not sent. Send such a message in pieces, each once the last has drained at the device's rate (see the sysexchunked.cpp program in the tests directory).
The Apple CoreMIDI API allows for the establishment of virtual input and output ports to which other software applications can connect.
The RtMidi JACK support can be compiled on Macintosh OS-X systems, as well as in Linux.
The configure script provides support for the MinGW compiler.
The Windows Multimedia library MIDI calls used in RtMidi do not make use of streaming functionality. Incoming system exclusive messages are received into buffers whose size and number can be set with RtMidiIn::setBufferSize() before the port is opened (the default is 4 buffers of 1024 bytes). A longer message is received a buffer at a time and delivered as one message, so these settings do not limit its length. There is no such limit for outgoing sysex messages via RtMidiOut.
RtMidi was originally developed with Visual C++ version 6.0 but has been tested with Visual Studio 2010.
The Windows MIDI Services backend uses the Windows.Devices.Midi2 API, which Microsoft is adding to Windows 11 25H2 and later. Until it ships in Windows, it comes from Microsoft's preview releases; WINDOWS.md explains where to get it and how to build with it. It is only compiled when requested (with CMake, -DRTMIDI_API_WMS=ON) and needs C++20. Port names are the ones Windows gives the device's MIDI 1.0 ports. Virtual ports are not supported.
An application can be built with both Windows MIDI Services and the Multimedia Library and use whichever works. With no api argument, RtMidi tries Windows MIDI Services first and uses it if it starts and has a port; on Windows without the API, or with Windows set to its legacy MIDI stack, it uses the Multimedia Library instead, with no extra code. To choose explicitly, and tell the user why, ask RtMidi::checkApiAvailability() first:
The midiprobe test program prints each compiled API's availability and the API chosen with no api argument.
The Android backend uses the AMidi API and must be built with CMake; see examples/android. Port names are "<product> <port>"; define RTMIDI_ANDROID_LEGACY_PORT_NAMES for the names RtMidi 6.0 gave, which were the same for every port of a device. RtMidi never asks for USB permission, but contrib/java/RtMidiUsbPermission.java lets the application ask. With permission, a USB device's port names come from its descriptors, and on Android 10 to 12 a device with fewer inputs than outputs, which would hang until unplugged, is refused at once; without it, opening such a device fails after 5 seconds. Virtual ports are not supported.
RtMidi is on github (https://github.com/thestk/rtmidi). Many thanks to the developers that are helping to maintain and improve RtMidi.
In years past, the following people provided bug fixes and improvements:
RtMidi: realtime MIDI i/o C++ classes<BR> Copyright (c) 2003-2019 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is asked to send the modifications to the original developer so that they can be incorporated into the canonical version. This is, however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
![]() |
©2003-2023 Gary P. Scavone, McGill University. All Rights Reserved. Maintained by Gary P. Scavone, gary at music.mcgill.ca |