MIDI Sample Code (Python)¶
MIDI is an established protocol for transmitting, recording, and playing back musical event data. It is low-bandwidth as it transmits just ‘keyboard data’ such as note on, note off, and after-pressure, leaving interpretation to the receiver. It includes a data protocol for real-time events, a file format for storage, and a (mostly obsolete) physical interface format. Most modern devices use a USB connection instead of the original five-pin optoisolated serial connection.
We can use MIDI as the basis for incorporating physical controller interfaces into performance and authoring systems. The following examples demonstrate basic use of the python-rtmidi module to send and receive events.
midi_send.py¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #!/usr/bin/env python3
"""Example script to transmit a sequence of MIDI messages over time. Runs from
the command line, no GUI. Uses the rtmidi package for output.
"""
################################################################
# Written in 2018-2021 by Garth Zeglin <garthz@cmu.edu>
# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
################################################################
# Standard Python libraries.
import argparse, time, platform
# For documentation on python-rtmidi: https://pypi.org/project/python-rtmidi/
import rtmidi
################################################################
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""Demonstration of sending a timed sequence of MIDI messages.""")
parser.add_argument( '-v', '--verbose', action='store_true', help='Enable more detailed output.' )
parser.add_argument("--midi", type=str, default = "virtual MPD218", help = "Keyword identifying the MIDI output device (default: %(default)s).")
args = parser.parse_args()
# Initialize the MIDI output system and read the currently available ports.
midi_out = rtmidi.MidiOut()
for idx, name in enumerate(midi_out.get_ports()):
if args.midi in name:
print("Found preferred MIDI output device %d: %s" % (idx, name))
midi_out.open_port(idx)
break
else:
print("Ignoring unselected MIDI device: ", name)
if not midi_out.is_port_open():
if platform.system() == 'Windows':
print("Virtual MIDI outputs are not currently supported on Windows, see python-rtmidi documentation.")
else:
print("Creating virtual MIDI output.")
midi_out.open_virtual_port(args.midi)
if not midi_out.is_port_open():
print("No MIDI device opened, exiting.")
exit(1)
if args.verbose:
print(f"Starting message sequence.")
chan = 9 # MIDI channel 10 similar to the MPD218
note_on = [0x90 + chan, 36, 112] # note 36 is MPD218 pad 1 on bank A, with velocity 112
note_off = [0x80 + chan, 36, 0]
for i in range(10):
midi_out.send_message(note_on)
time.sleep(1.0)
midi_out.send_message(note_off)
time.sleep(0.25)
if args.verbose:
print(f"Sequence done.")
|
midi_receive.py¶
The midi_received
callback function is called in real time as messages are received.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #!/usr/bin/env python3
"""A demonstration MIDI receiver which displays events."""
################################################################
# Written in 2018-2021 by Garth Zeglin <garthz@cmu.edu>
# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
################################################################
# Standard Python libraries.
import argparse, time, platform
# For documentation on python-rtmidi: https://pypi.org/project/python-rtmidi/
import rtmidi
################################################################
def midi_received(data, unused):
msg, delta_time = data
if len(msg) > 2:
if msg[0] == 153: # note on, channel 9
key = (msg[1] - 36) % 16
row = key // 4
col = key % 4
velocity = msg[2]
print("MPD218 Pad (%d, %d): %d" % (row, col, velocity))
return
print("MIDI message: ", msg)
################################################################
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--midi", type=str, default = "MPD218 Port A", help = "Keyword identifying the MIDI input device (default: %(default)s).")
args = parser.parse_args()
# Initialize the MIDI input system and read the currently available ports.
midi_in = rtmidi.MidiIn()
for idx, name in enumerate(midi_in.get_ports()):
if args.midi in name:
print("Found preferred MIDI input device %d: %s" % (idx, name))
midi_in.open_port(idx)
midi_in.set_callback(midi_received)
break
else:
print("Ignoring unselected MIDI device: ", name)
if not midi_in.is_port_open():
if platform.system() == 'Windows':
print("Virtual MIDI inputs are not currently supported on Windows, see python-rtmidi documentation.")
else:
print("Creating virtual MIDI input.")
midi_in.open_virtual_port(args.midi)
if not midi_in.is_port_open():
print("No MIDI device opened, exiting.")
else:
print("Waiting for input.")
while True:
time.sleep(1)
|