std1.0.0-beta-7 SafeC docs

Module audio::midi

#include <std/audio/midi.h> source

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

MidiMsgA MIDI message of up to 3 bytes: everything but sysex.
MidiParserA 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.
MidiEncoderThe reverse: writes messages for a byte stream, dropping the status byte when running status allows (channel messages with the previous status).
MidiEventOne input message, as a callback sees it. 'bytes' is valid only during the callback.
MidiPortInfo
MidiIn
MidiOut

Functions

midi_message_lengthExpected 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_realtimeWhether 'status' is a real-time byte (0xF8..0xFF), which may appear between the bytes of any other message.
midi_build_sysexF0 <payload> F7 into 'out'; the length written, or -1 if it doesn't fit or the payload has a byte >= 0x80.
midi_14bit14-bit value from LSB/MSB data bytes, and back.
midi_compiled_apisThe APIs compiled into this build, in preference order (up to out.len).
midi_api_compiled
midi_default_apiCore 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_SYSEXlongest 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_NONEMidiParser::feed() results.
MIDI_PARSE_MESSAGEa channel / system common / real-time message: message()
MIDI_PARSE_SYSEXa complete sysex (F0 .. F7) in the sysex buffer
MIDI_PARSE_ERRORa stray data byte or a sysex too long for the buffer (dropped)