-
Notifications
You must be signed in to change notification settings - Fork 0
/
ticker.py
executable file
·168 lines (141 loc) · 5.28 KB
/
ticker.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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/python3
from sortedcontainers import SortedList
import threading
import time
import math
import mido
import sys
import random
import os
from settings import Settings
from buildMeasure import make_rhythm_track
from events import EventQue
from plotMeasure import plot_midi
''' TBD - move midi clock out of midi file into its own thread'''
class Ticker(threading.Thread):
def __init__(self, params = None):
super().__init__()
self.lock = threading.Lock()
self.stopping = threading.Event() # this is only used to exit
self.settings = None
self.trackmap = {}
self.midi_out = None
self.playlist = []
self.play_index = None
self.pause = threading.Event()
self.rhythmq = [] # list of SortedLists
self.rhythm_track = mido.MidiTrack()
self.rhythm_track.name = 'MClick'
self.song = mido.MidiFile()
self.shuffle = False
self.interrupt = threading.Event()
self.history = []
if params:
self.update(params)
def stop(self):
self.stopping.set()
self.midi_teardown()
def midi_teardown(self):
if self.midi_out:
self.midi_out.send(mido.Message('stop'))
self.midi_out.panic()
self.midi_out.close()
self.midi_out = None
def midi_setup(self):
if self.settings:
mido.set_backend(self.settings['midi_backend'])
if self.midi_out:
self.midi_teardown()
port = self.settings['midi_port']
if port and any (port in listed for listed in mido.get_output_names()):
self.midi_out = mido.open_output(port)
self.midi_out.send(mido.Message('start'))
def make_rhythm_from_song(self):
if not self.settings:
raise RuntimeError('No settings')
if self.rhythm_track in self.song.tracks:
self.song.tracks.remove(self.rhythm_track)
self.rhythm = self.song.tracks.append(make_rhythm_track(self.song, self.settings))
def update(self, params):
if params:
self.midi_setup()
#mido.bpm2tempo(self.settings['tempo'])
else:
self.midi_teardown()
self.settings = params
def load_song(self, filename = None):
def get_song_tempo(midifile):
initial_tempo = 120
for msg in midifile:
# any tempo change which occurs after the first note is not an 'initial' tempo
if msg.type == 'note_on':
break
if msg.type == 'set_tempo':
initial_tempo = mido.tempo2bpm(msg.tempo)
break;
return initial_tempo
self.song = mido.MidiFile(filename)
initial_tempo = get_song_tempo(self.song)
self.song.initial_tempo = initial_tempo # add it into the file objecjt for reference
self.settings['tempo'] = initial_tempo
def transport_action(self, source):
if source == 'id_play':
self.pause.clear()
elif source == 'id_pause':
self.pause.set()
elif source == 'id_next_song':
self.interrupt.set()
def run(self):
self.play_index = -1
while not self.stopping.is_set():
if not self.settings:
time.sleep(0.5)
continue
if self.playlist:
if self.shuffle:
self.play_index = random.randint(0,len(self.playlist)) - 1
else:
self.play_index += 1
if self.play_index >= len(self.playlist):
self.play_index = -1
self.load_song(self.playlist[self.play_index])
print(self.song.filename)
else:
self.load_song()
self.make_rhythm_from_song()
#plot_midi(self.song)
for msg in self.song.play():
while self.pause.is_set():
if self.interrupt.is_set() or self.stopping.is_set():
break
time.sleep(0.1)
if self.stopping.is_set() or self.interrupt.is_set():
break;
if self.midi_out:
self.midi_out.send(msg)
if self.stopping.is_set():
break
self.interrupt.clear()
if self.play_index >= len(self.playlist):
self.play_index = -1
def open_files(self, path):
self.playlist.clear()
self.play_index = None
if os.path.isdir(path):
with os.scandir(path) as it:
for entry in it:
if entry.is_file() and entry.name.endswith('.mid'):
self.playlist.append(entry)
elif os.path.isfile(path):
self.playlist = [path]
if __name__ == '__main__':
t = Ticker(Settings())
# still breaks on Regent Square
# redeemed speeds up on final measure
# tell me the story of jesus parses as one measure of 4/4 and one measure of 96/4
# the midi file looks ok in musescore
t.open_files('demo')
try:
t.run()
except KeyboardInterrupt:
t.stop()