Skip to content

Commit 78e2ff5

Browse files
committed
Add example application midi_file_player.py
1 parent 4b111a5 commit 78e2ff5

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

doc/examples.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ MIDI Chord Generator
4545

4646
.. literalinclude:: ../examples/midi_chords.py
4747

48+
MIDI File Player
49+
----------------
50+
51+
:download:`midi_file_player.py <../examples/midi_file_player.py>`
52+
53+
.. literalinclude:: ../examples/midi_file_player.py
54+
4855
Simple MIDI Synth
4956
-----------------
5057

examples/midi_file_player.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
"""Play a MIDI file.
3+
4+
This uses the "mido" module for handling MIDI: https://mido.readthedocs.io/
5+
6+
Pass the MIDI file name as first command line argument.
7+
8+
If a MIDI port name is passed as second argument, a connection is made.
9+
10+
"""
11+
import sys
12+
import threading
13+
14+
import jack
15+
from mido import MidiFile
16+
17+
argv = iter(sys.argv)
18+
next(argv)
19+
filename = next(argv, '')
20+
connect_to = next(argv, '')
21+
if not filename:
22+
sys.exit('Please specify a MIDI file')
23+
try:
24+
mid = iter(MidiFile(filename))
25+
except Exception as e:
26+
sys.exit(type(e).__name__ + ' while loading MIDI: ' + str(e))
27+
28+
client = jack.Client('MIDI-File-Player')
29+
port = client.midi_outports.register('output')
30+
event = threading.Event()
31+
msg = next(mid)
32+
fs = None # sampling rate
33+
offset = 0
34+
35+
36+
@client.set_process_callback
37+
def process(frames):
38+
global offset
39+
global msg
40+
port.clear_buffer()
41+
while True:
42+
if offset >= frames:
43+
offset -= frames
44+
return # We'll take care of this in the next block ...
45+
# Note: This may raise an exception:
46+
port.write_midi_event(offset, msg.bytes())
47+
try:
48+
msg = next(mid)
49+
except StopIteration:
50+
event.set()
51+
raise jack.CallbackExit
52+
offset += round(msg.time * fs)
53+
54+
55+
@client.set_samplerate_callback
56+
def samplerate(samplerate):
57+
global fs
58+
fs = samplerate
59+
60+
61+
@client.set_shutdown_callback
62+
def shutdown(status, reason):
63+
print('JACK shutdown:', reason, status)
64+
event.set()
65+
66+
67+
with client:
68+
if connect_to:
69+
port.connect(connect_to)
70+
print('Playing', repr(filename), '... press Ctrl+C to stop')
71+
try:
72+
event.wait()
73+
except KeyboardInterrupt:
74+
print('\nInterrupted by user')

0 commit comments

Comments
 (0)