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