Tutorial¶
Creating Messages¶
Mido allows you to work with MIDI messages as Python objects. To create a new message, you can do:
>>> from mido import Message
>>>
>>> Message('note_on', note=60, velocity=100)
<note_on message channel=0, note=60, velocity=100, time=0>
All message parameters are optional, and if not explicitly set, will
default to 0
(or ()
for sysex data):
>>> Message('note_on')
<note_on message channel=0, note=0, velocity=0, time=0>
>>> Message('sysex')
<sysex message data=(), time=0>
This means that it’s important to remember to pass the velocity
parameter for note_on
messages, or the note will interpreted as a
note_off
on many devices.
The parameters for each message type are listed in Message Types.
Modifying and Copying Messages¶
When you have created a message, the parameters are available as attributes:
>>> msg = Message('note_off', channel=1, note=60, velocity=50)
>>> dir(msg)
[..., 'channel', 'note', 'time', 'type', 'velocity']
>>> msg.type
'note_on'
>>> msg.channel
1
>>> msg.note
60
>>> msg.channel = 2
>>> msg.note = 62
>>> msg
<note_off message channel=2, note=62, velocity=50, time=0>
You can copy a message, optionally passing keyword arguments to override attributes:
>>> msg.copy() # Make an identical copy.
<note_on message channel=2, note=62, velocity=50, time=0>
>>> msg.copy(channel=4)
<note_on message channel=4, note=62, velocity=50, time=0>
This is useful when you pass messages around in a large system, and you want to keep a copy for yourself while allowing other parts of the system to modify the original.
Changing the type of a message is not allowed:
>>> msg.type = 'note_off'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "mido/messages.py", line 320, in __setattr__
raise AttributeError('{} attribute is read only'.format(name))
AttributeError: type attribute is read only
Comparing Messages¶
You can compare two messages to see if they are identical:
>>> Message('note_on', note=22) == Message('note_on', note=22)
True
>>> Message('note_on') == Message('note_off')
False
>>> msg == msg.copy(note=100)
False
The time
parameter (see below) is ignored when comparing messages:
>>> msg == msg.copy(time=10000)
True
This allows you to compare messages that come from different sources and have different time stamps. If you want to include time in the comparison, you can do:
>>> msg1 = note_on(time=2)
>>> msg2 = note_on(time=3)
>>> (msg1, msg1.time) == (msg2, msg2.time)
False
System Exclusive (sysex) Messages¶
Sysex messages have a data
parameter, which is a sequence of bytes.
The data
parameter takes any object that generates bytes when
iterated over. This is converted internally into a tuple of integers:
>>> Message('sysex')
<sysex message data=(), time=0>
>>> Message('sysex', data=[1, 2, 3])
<sysex message data=(1, 2, 3), time=0>
>>> Message('sysex', data=bytearray('abc'))
<sysex message data=(97, 98, 99), time=0>
Sysex messages inlude the sysex_end
byte when sent and received, so
while there is a sysex_end
message type, it is never used:
>>> msg = Message('sysex', data=[1, 2, 3])
>>> msg.hex()
'F0 01 02 03 F7'
Time¶
All messages also have an extra parameter time
, which you can use
for anything you want. Typically this is used to tag messages with
time when storing them in files or sending them around in the
system. time
can have any value as long as it’s a float
or an int
.
copy()
will copy the time
attribute.
Opening Ports¶
There are three types of ports in Mido: input ports, output ports and I/O ports. They are created with:
mido.open_input(name=None)
mido.open_output(name=None)
mido.open_ioport(name=None)
(mido.open_ioport
will return a port which is a thin wrapper around
an input port and an output port, and allows you to use the methods of
both. This can be used for two-way communication with a device.
You can pass the name of the port, or leave it out to open the default port:
mido.open_input('SH-201') # Open the port 'SH-201'.
mido.open_input() # Open the default input port.
To get a list of names of available ports, you can call one of these functions:
>>> mido.get_input_names()
['Midi Through Port-0', 'SH-201']
>>> mido.get_output_names()
['Midi Through Port-0', 'SH-201']
>>> mido.get_ioport_names()
['Midi Through Port-0', 'SH-201']
Note: If a port is open, it will still be listed here.
Closing Ports¶
A port can be closed by calling the close()
method:
port.close()
but often it is better to use the with
statement, which will close
the block automatically when the block is over:
with mido.open_output() as port:
...
The closed
attribute will be True
if the port is closed.
Sending Messages¶
Messages can be sent on output or I/O ports by calling the send()
method:
port.send(Message('pitchwheel', channel=2, pitch=4000))
The message will be sent immediately.
Receiving Messages¶
There are several different ways to receive messages. The basic one is
to call receive()
:
message = port.receive()
This will block until a message arrives on the port. If you want to receive messages in a loop, you can do:
for message in port:
...
If you don’t want to block, you can use pending()
to see how many
messages are available:
>>> port.pending()
2
>>> port.receive()
<note_on message channel=2, note=60, velocity=50, time=0>
>>> port.receive()
<note_on message channel=2, note=72, velocity=50, time=0>
>>> port.receive()
*** blocks until the next message arrives ***
It is often easier to use iter_pending()
:
while 1:
for message in port.iter_pending():
... # Do something with message.
... Do other stuff.
Messages will be queued up inside the port object until you call
receive()
or iter_pending()
.
If you want to receive messages from multiple ports, you can use
multi_receive()
:
from mido.ports import multi_receive
while 1:
for message in multi_receive([port1, port2, port3]):
...
The ports are checked in random order to ensure fairness. There is also a non-blocking version of this function:
while 1:
for message in multi_iter_pending([port1, port2, port3]):
...