Streaming byte-by-byte MIDI parser.
Feed raw MIDI bytes (integers 0-255) in any chunking — the parser handles
running status, interleaved real-time messages, system exclusive frames,
and meta messages. Completed messages are queued and drained with parse/1
or flush/1.
Examples
parser = ExMidi.MidiParser.new()
parser = ExMidi.MidiParser.feed_bytes(parser, [144, 60, 100])
{msg, _parser} = ExMidi.MidiParser.parse(parser)
# => {{:midi, {:note_on, [channel: 1, pitch: 60, velocity: 100]}}, _parser}Notes:
- Real-time bytes (F8-FC, FE, and the undefined-but-reserved F9 "tick") are emitted immediately, even in the middle of an incomplete message or a system exclusive frame, as the MIDI specification allows.
- Channel voice messages support running status; a note-on with velocity 0 is reported as a note-off with velocity 64.
- System exclusive messages are reported with their full framing:
{:sys_ex, [0xF0 | data] ++ [0xF7]}.
Summary
Functions
Feed a single byte (0-255) into the parser.
Feed a list of bytes into the parser.
Return all completed messages in feed order and drain the queue.
Create a new parser.
Pop the oldest completed message. Returns {message, parser} or nil
when no message is available.
Number of completed messages waiting to be parsed.
Types
@type expectation() :: :idle | :sysex | :meta_length | {:meta_data, byte(), non_neg_integer()} | {:bytes, pos_integer()}
What the parser is waiting for to complete the current message.
@type message() :: {:midi, term()}
Functions
Feed a single byte (0-255) into the parser.
Feed a list of bytes into the parser.
Return all completed messages in feed order and drain the queue.
@spec new() :: t()
Create a new parser.
Pop the oldest completed message. Returns {message, parser} or nil
when no message is available.
@spec pending(t()) :: non_neg_integer()
Number of completed messages waiting to be parsed.