Skip to main content
Version: Matrix OS 4.0 🚧

MIDI API

Overview​

Use MatrixOS.MIDI to receive and send MIDI packets from a Python app.

Value ranges:

  • MIDI channel: 0..15
  • Seven-bit MIDI data: 0..127
  • Pitch bend and song position: 0..16383

MatrixOS.MIDI.get​

get(timeout_ms: int = 0) -> MidiPacket | None

Reads the next MIDI packet.

Parameters:

  • timeout_ms: Maximum time to wait in milliseconds. 0 is non-blocking.

Returns:

  • MidiPacket | None: MIDI packet, or None when no packet is available.

MatrixOS.MIDI.send​

send(packet: MidiPacket, port: int = MatrixOS.MIDI.PORT_EACH_CLASS, timeout_ms: int = 0) -> bool

Sends one MIDI packet.

Parameters:

  • packet: Packet to send.
  • port: Target MIDI port mask.
  • timeout_ms: Maximum time to wait while sending.

Returns:

  • bool: True when sent.

MatrixOS.MIDI.send_sysex​

send_sysex(port: int, data: bytes, include_meta: bool = True) -> bool

Sends a SysEx payload.

Parameters:

  • port: Target MIDI port mask.
  • data: SysEx payload bytes.
  • include_meta: When True, Matrix OS includes SysEx start/end framing.

Returns:

  • bool: True when sent.

MatrixOS.MIDI.is_note_on​

is_note_on(packet: MidiPacket) -> bool

Checks whether a packet is a note-on message with velocity greater than 0.

Parameters:

  • packet: Packet to inspect.

Returns:

  • bool: True for note-on with nonzero velocity.

MatrixOS.MIDI.is_note_off​

is_note_off(packet: MidiPacket) -> bool

Checks whether a packet is a note-off message.

Parameters:

  • packet: Packet to inspect.

Returns:

  • bool: True for real note-off packets and for the common note-on with velocity 0 form.

Send A Note From Input​

import MatrixOS

def loop():
# Drain all input events so quick key presses do not pile up.
event = MatrixOS.Input.get_event()
while event is not None:
keypad = event.get("keypad")
point = event.get("point")
if keypad and point and keypad.get("pressed"):
# Map the X coordinate to a simple chromatic note row.
note = 60 + point[0]
MatrixOS.MIDI.send(MatrixOS.MIDI.note_on(0, note, 100))
elif keypad and point and keypad.get("released"):
note = 60 + point[0]
MatrixOS.MIDI.send(MatrixOS.MIDI.note_off(0, note, 0))
event = MatrixOS.Input.get_event()

Light An LED From MIDI​

import MatrixOS

def loop():
# Drain all pending MIDI packets in this loop call.
packet = MatrixOS.MIDI.get()
while packet is not None:
if MatrixOS.MIDI.is_note_on(packet):
# Reuse the note number as a stable LED index for visual feedback.
led = packet.note() % MatrixOS.LED.count()
level = min(255, packet.velocity() * 2)
MatrixOS.LED.set_index(led, (0, level, 0))
MatrixOS.LED.update()
elif MatrixOS.MIDI.is_note_off(packet):
led = packet.note() % MatrixOS.LED.count()
MatrixOS.LED.set_index(led, (0, 0, 0))
MatrixOS.LED.update()

packet = MatrixOS.MIDI.get()

This app-loop pattern drains all pending MIDI packets, lights the LED indexed by the MIDI note, and turns it off on note-off.

Packet Factory Methods​

Each factory returns a MidiPacket.

MatrixOS.MIDI.note_on​

note_on(channel: int, note: int, velocity: int) -> MidiPacket

Creates a note-on packet.

Parameters: channel is 0..15; note and velocity are 0..127.


MatrixOS.MIDI.note_off​

note_off(channel: int, note: int, velocity: int) -> MidiPacket

Creates a note-off packet.

Parameters: channel is 0..15; note and velocity are 0..127.


MatrixOS.MIDI.aftertouch​

aftertouch(channel: int, note: int, pressure: int) -> MidiPacket

Creates a polyphonic aftertouch packet.

Parameters: channel is 0..15; note and pressure are 0..127.


MatrixOS.MIDI.control_change​

control_change(channel: int, controller: int, value: int) -> MidiPacket

Creates a control-change packet.

Parameters: channel is 0..15; controller and value are 0..127.


MatrixOS.MIDI.program_change​

program_change(channel: int, program: int) -> MidiPacket

Creates a program-change packet.

Parameters: channel is 0..15; program is 0..127.


MatrixOS.MIDI.channel_pressure​

channel_pressure(channel: int, pressure: int) -> MidiPacket

Creates a channel-pressure packet.

Parameters: channel is 0..15; pressure is 0..127.


MatrixOS.MIDI.pitch_bend​

pitch_bend(channel: int, value: int) -> MidiPacket

Creates a pitch-bend packet.

Parameters: channel is 0..15; value is 0..16383.


MatrixOS.MIDI.song_position​

song_position(position: int) -> MidiPacket

Creates a song-position packet.

Parameters: position is 0..16383.


MatrixOS.MIDI.song_select​

song_select(song: int) -> MidiPacket

Creates a song-select packet.

Parameters: song is 0..127.


MatrixOS.MIDI.mtc_quarter_frame​

mtc_quarter_frame(value: int) -> MidiPacket

Creates an MTC quarter-frame packet.

Parameters: value is 0..127.


MatrixOS.MIDI.tune_request​

tune_request() -> MidiPacket

Creates a tune-request packet.


MatrixOS.MIDI.clock​

clock() -> MidiPacket

Creates a MIDI clock packet.


MatrixOS.MIDI.tick​

tick() -> MidiPacket

Creates a MIDI tick packet.


MatrixOS.MIDI.start​

start() -> MidiPacket

Creates a MIDI start packet.


MatrixOS.MIDI.continue_​

continue_() -> MidiPacket

Creates a MIDI continue packet.

continue is a Python keyword, so the factory is named continue_().


MatrixOS.MIDI.stop​

stop() -> MidiPacket

Creates a MIDI stop packet.


MatrixOS.MIDI.active_sense​

active_sense() -> MidiPacket

Creates an active-sense packet.


MatrixOS.MIDI.reset​

reset() -> MidiPacket

Creates a MIDI reset packet.

MidiPacket​

You can construct a packet directly when a factory is not convenient:

MatrixOS.MIDI.MidiPacket()
MatrixOS.MIDI.MidiPacket(other_packet)
MatrixOS.MIDI.MidiPacket(status, data0, data1, data2)

packet.status​

status() -> int

Returns the packet status byte.


packet.set_status​

set_status(status: int) -> bool

Sets the packet status.

Returns: True when the status is valid for the packet.


packet.port​

port() -> int

Returns the packet port mask.


packet.set_port​

set_port(port: int) -> None

Sets the packet port mask.


packet.channel​

channel() -> int | None

Returns the channel for channel messages, or None when the packet has no channel.


packet.set_channel​

set_channel(channel: int) -> bool

Sets the packet channel.

Parameters: channel is 0..15.

Returns: True when the packet supports channel data.


packet.note​

note() -> int | None

Returns the packet note number, or None when not applicable.


packet.set_note​

set_note(note: int) -> bool

Sets the packet note number.

Parameters: note is 0..127.

Returns: True when the packet supports note data.


packet.controller​

controller() -> int | None

Returns the controller number, or None when not applicable.


packet.set_controller​

set_controller(controller: int) -> bool

Sets the controller number.

Parameters: controller is 0..127.

Returns: True when the packet supports controller data.


packet.velocity​

velocity() -> int | None

Returns note velocity, or None when not applicable.


packet.set_velocity​

set_velocity(velocity: int) -> bool

Sets note velocity.

Parameters: velocity is 0..127.

Returns: True when the packet supports velocity data.


packet.value​

value() -> int | None

Returns the packet value field, or None when not applicable.


packet.set_value​

set_value(value: int) -> bool

Sets the packet value field.

Parameters: value is 0..16383.

Returns: True when the packet supports a value field.


packet.length​

length() -> int

Returns the MIDI byte length represented by the packet.


packet.is_sysex​

is_sysex() -> bool

Returns whether the packet is part of a SysEx message.


packet.is_sysex_start​

is_sysex_start() -> bool

Returns whether the packet starts a SysEx message.


packet.data​

data() -> tuple[int, int, int]

Returns the packet's three raw data bytes.

Status Constants​

  • STATUS_NONE
  • STATUS_NOTE_OFF
  • STATUS_NOTE_ON
  • STATUS_AFTERTOUCH
  • STATUS_CONTROL_CHANGE
  • STATUS_PROGRAM_CHANGE
  • STATUS_CHANNEL_PRESSURE
  • STATUS_PITCH_BEND
  • STATUS_MTC_QUARTER_FRAME
  • STATUS_SONG_POSITION
  • STATUS_SONG_SELECT
  • STATUS_TUNE_REQUEST
  • STATUS_CLOCK
  • STATUS_TICK
  • STATUS_START
  • STATUS_CONTINUE
  • STATUS_STOP
  • STATUS_ACTIVE_SENSE
  • STATUS_RESET
  • STATUS_SYSEX_DATA
  • STATUS_SYSEX_END

Port Constants​

  • PORT_EACH_CLASS
  • PORT_ALL
  • PORT_USB
  • PORT_PHYSICAL
  • PORT_BLUETOOTH
  • PORT_WIRELESS
  • PORT_RTP
  • PORT_DEVICE_CUSTOM
  • PORT_SYNTH
  • PORT_OS
  • PORT_INVALID

Comments