Why Mido?

Messages as Objects

Have you ever had to write code like this?

NOTE_OFF = 0x80
NOTE_ON = 0x90
...
message = device.read()  # Returns [0x92, 0x40, 0x42]
status_byte = message[0]
if status_byte & 0xf0 in [NOTE_ON, NOTE_OFF]:
    is_note_on == status_byte & 0x10
    if is_note_on:
        message_type = 'note_on'
    else:
        message_type = 'note_off'
    channel = message[0] & 0x0f
    print('Got {} on channel {}'.format(message_type, channel))

I have, and let me tell you, I don’t ever want to that again.

That’s why I created Mido. Now I can do this instead:

message = port.receive()
if message.type in ['note_on', 'note_off']:
    print('Got {} on channel {}'.format(message.type, message.channel))

Type and Value Checking

Working directly with the bytes is also error prone. While MIDI data bytes have a valid range of 0..127, the size of Python integers is only limited by how much memory is available. If you make a mistake in your computation of a data value, it could easily travel around undetected until it blows up some seemingly unrelated part of your system. These kinds of errors are tricky to track down.

Mido messages come with type and value checking built in. This happens when you assign an out of range value to an attribute:

>>> n = mido.Message('note_on')
>>> n.channel = 2092389483249829834
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./mido/messages.py", line 327, in __setattr__
    ret = check(value)
  File "./mido/messages.py", line 128, in check_channel
    raise ValueError('channel must be in range 0..15')
ValueError: channel must be in range 0..15

and when you pass some nonsense as a keyword argument to the constructor or the copy() method:

>>> n.copy(note=['This', 'is', 'wrong'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./mido/messages.py", line 316, in copy
    return Message(self.type, **args)
  File "./mido/messages.py", line 290, in __init__
    setattr(self, name, value)
  File "./mido/messages.py", line 327, in __setattr__
    ret = check(value)
  File "./mido/messages.py", line 181, in check_databyte
    raise TypeError('data byte must be an integer')
TypeError: data byte must be an integer

This means that a Mido message object is always a valid MIDI message.