A midi sequencer in Python
I’ve always thought it would be cool to be able to compose music in code. There are some languages out there that do this, but I was more interested in direct midi programming than in a DSL for composing music. Enter mido
.
mido is a midi library for Python that wraps python-rtmidi in a friendlier API. It can do a lot of things, but I was primarily interested in sending notes to a synth in Bitwig Studio. But playing notes was more a side effect of wanting to explore some ideas around an API for composing music.
I started by defining a Note class.
1import mido
2
3class Note:
4
5 # http://bradthemad.org/guitar/tempo_explanation.php
6 LENGTHS = {
7 'w': 240,
8 'h': 120,
9 'q': 60,
10 'e': 30,
11 's': 15,
12 't': 7.5,
13 'dq': 90,
14 'de': 45,
15 'ds': 22.5,
16 'tq': 40,
17 'te': 20,
18 'ts': 10
19 }
20
21 # what is the equivalent of '1' numeric duration value
22 DURATION_MULTIPLIER = LENGTHS['q']
23
24 def __init__(self, value:int, duration, multiplier=1, velocity:int=64):
25 self.value = value
26 self.duration = duration
27 self.multiplier = multiplier
28 self.velocity = velocity
29
30 # Converts a note length value like dotted eighth to a value in seconds based on default or provided bpm tempo.
31 # Length abbrs: w, h, q, e, s, t, dq, de, ds, tq, te, ts (whole/half/quarter/eight/sixteenth/32nd, dotted/triplet)
32 def seconds(self, tempo:int):
33 if type(self.duration) == str:
34 if self.duration in Note.LENGTHS:
35 len = Note.LENGTHS[self.duration]
36 return len * self.multiplier / tempo
37 else:
38 return 0
39 else:
40 return self.duration * Note.DURATION_MULTIPLIER / tempo
41
42 # Returns mido "note_on" message
43 def get_on(self):
44 print(f"On {self.value} {self.duration}")
45 return mido.Message('note_on', note=self.value, velocity=self.velocity)
46
47 # Returns mido "note_off" message
48 def get_off(self):
49 print(f"Off {self.value} {self.duration}")
50 return mido.Message('note_off', note=self.value)