Module audio::midi
real-time MIDI I/O (in the spirit of RtMidi).
API platform backend file
MIDI_API_ALSA Linux midi_alsa_linux.sc (ALSA sequencer, libasound loaded at run time)
MIDI_API_JACK Linux, macOS midi_jack_posix.sc (JACK MIDI, libjack loaded at run time)
MIDI_API_COREMIDI macOS midi_coremidi_macos.sc
MIDI_API_WINMM Windows midi_winmm_win32.sc (the Windows multimedia MIDI API)
MIDI_API_WEBMIDI wasm32 midi_webmidi_wasm.sc (+ std/audio/audio_web.js)
MIDI_API_DUMMY everywhere midi_dummy.sc (an in-process loopback, for tests)
A MidiIn or MidiOut is one client of an API. It lists the system's ports, connects to one (open_port) or creates its own that other programs connect to (open_virtual_port: ALSA, JACK, Core MIDI and dummy — WinMM and Web MIDI have no virtual ports and return AUDIO_ERR_UNSUPPORTED).
struct Result<MidiIn, AudioError> r = std::MidiIn::open(MIDI_API_UNSPECIFIED, "monitor", 0UL);
struct MidiIn in = r.unwrap_or(std::MidiIn::closed());
in.open_port(0U, "in");
in.set_callback(on_midi, (?&State)&state); // or poll: in.get_message(...)
struct MidiOut out = std::MidiOut::open(MIDI_API_UNSPECIFIED, "synth").unwrap_or(std::MidiOut::closed());
out.open_port(0U, "out");
out.send(std::MidiMsg::note_on(0, 60, 100));
Input arrives on the backend's thread (the ALSA sequencer thread std starts, JACK's process thread, Core MIDI's and WinMM's callback threads, the browser's event loop). With a callback set, it is called there with each complete message — sysex reassembled, running status expanded — and must follow the same real-time rules as an audio callback (see audio.h): no allocation, no locks, no blocking. Without one, messages go into a lock-free single-producer/single-consumer queue (sized at open, default MIDI_DEFAULT_QUEUE_BYTES) that get_message() drains from any one thread; a message that doesn't fit is dropped and counted (dropped()). Sysex, MIDI clock/time code and active sensing are filtered out by default (ignore_types(1, 1, 1)), as RtMidi does.
Timestamps are seconds since the port was opened (monotonic); each event also carries the time since the previous delivered message.
Structs
| MidiMsg | A MIDI message of up to 3 bytes: everything but sysex. |
| MidiParser | A byte-stream parser: running status, real-time bytes interleaved with other messages, sysex reassembly into a caller-owned buffer. Allocation- free; safe on a real-time thread. |
| MidiEncoder | The reverse: writes messages for a byte stream, dropping the status byte when running status allows (channel messages with the previous status). |
| MidiEvent | One input message, as a callback sees it. 'bytes' is valid only during the callback. |
| MidiPortInfo | |
| MidiIn | |
| MidiOut |
Functions
| midi_message_length | Expected length of a message starting with 'status': 1..3, 0 for sysex (variable), -1 for a data byte (< 0x80) or an undefined status. |
| midi_is_realtime | Whether 'status' is a real-time byte (0xF8..0xFF), which may appear between the bytes of any other message. |
| midi_build_sysex | F0 <payload> F7 into 'out'; the length written, or -1 if it doesn't fit or the payload has a byte >= 0x80. |
| midi_14bit | 14-bit value from LSB/MSB data bytes, and back. |
| midi_compiled_apis | The APIs compiled into this build, in preference order (up to out.len). |
| midi_api_compiled | |
| midi_default_api | Core MIDI, WinMM, Web MIDI, ALSA (JACK when its server is running); the SAFEC_MIDI_API environment variable ("alsa", "jack", "dummy", ...) overrides. |
| midi_api_name | |
| midi_api_display_name | |
| midi_api_by_name |
Macros
| MIDI_API_UNSPECIFIED | |
| MIDI_API_ALSA | |
| MIDI_API_JACK | |
| MIDI_API_COREMIDI | |
| MIDI_API_WINMM | |
| MIDI_API_WEBMIDI | |
| MIDI_API_DUMMY | |
| MIDI_API_COUNT | |
| MIDI_PORT_NAME_MAX | |
| MIDI_CLIENT_NAME_MAX | |
| MIDI_DEFAULT_QUEUE_BYTES | |
| MIDI_MAX_SYSEX | longest sysex std reassembles |
| MIDI_NOTE_OFF | |
| MIDI_NOTE_ON | |
| MIDI_POLY_PRESSURE | |
| MIDI_CONTROL_CHANGE | |
| MIDI_PROGRAM_CHANGE | |
| MIDI_CHANNEL_PRESSURE | |
| MIDI_PITCH_BEND | |
| MIDI_SYSEX | |
| MIDI_TIME_CODE | |
| MIDI_SONG_POSITION | |
| MIDI_SONG_SELECT | |
| MIDI_TUNE_REQUEST | |
| MIDI_SYSEX_END | |
| MIDI_CLOCK | |
| MIDI_START | |
| MIDI_CONTINUE | |
| MIDI_STOP | |
| MIDI_ACTIVE_SENSING | |
| MIDI_SYSTEM_RESET | |
| MIDI_PARSE_NONE | MidiParser::feed() results. |
| MIDI_PARSE_MESSAGE | a channel / system common / real-time message: message() |
| MIDI_PARSE_SYSEX | a complete sysex (F0 .. F7) in the sysex buffer |
| MIDI_PARSE_ERROR | a stray data byte or a sysex too long for the buffer (dropped) |