forked from HackerPoet/Composer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
midi.py
93 lines (88 loc) · 2.98 KB
/
midi.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
from mido import MidiFile, MidiTrack, Message
import numpy as np
num_notes = 96
samples_per_measure = 96
def midi_to_samples(fname):
has_time_sig = False
flag_warning = False
mid = MidiFile(fname)
ticks_per_beat = mid.ticks_per_beat
ticks_per_measure = 4 * ticks_per_beat
for i, track in enumerate(mid.tracks):
for msg in track:
if msg.type == 'time_signature':
new_tpm = msg.numerator * ticks_per_beat * 4 / msg.denominator
if has_time_sig and new_tpm != ticks_per_measure:
flag_warning = True
ticks_per_measure = new_tpm
has_time_sig = True
if flag_warning:
print " ^^^^^^ WARNING ^^^^^^"
print " " + fname
print " Detected multiple distinct time signatures."
print " ^^^^^^ WARNING ^^^^^^"
return []
all_notes = {}
for i, track in enumerate(mid.tracks):
abs_time = 0
for msg in track:
abs_time += msg.time
if msg.type == 'note_on':
if msg.velocity == 0:
continue
note = msg.note - (128 - num_notes)/2
assert(note >= 0 and note < num_notes)
if note not in all_notes:
all_notes[note] = []
else:
single_note = all_notes[note][-1]
if len(single_note) == 1:
single_note.append(single_note[0] + 1)
all_notes[note].append([abs_time * samples_per_measure / ticks_per_measure])
elif msg.type == 'note_off':
if len(all_notes[note][-1]) != 1:
continue
all_notes[note][-1].append(abs_time * samples_per_measure / ticks_per_measure)
for note in all_notes:
for start_end in all_notes[note]:
if len(start_end) == 1:
start_end.append(start_end[0] + 1)
samples = []
for note in all_notes:
for start, end in all_notes[note]:
sample_ix = start / samples_per_measure
while len(samples) <= sample_ix:
samples.append(np.zeros((samples_per_measure, num_notes), dtype=np.uint8))
sample = samples[sample_ix]
start_ix = start - sample_ix * samples_per_measure
if False:
end_ix = min(end - sample_ix * samples_per_measure, samples_per_measure)
while start_ix < end_ix:
sample[start_ix, note] = 1
start_ix += 1
else:
sample[start_ix, note] = 1
return samples
def samples_to_midi(samples, fname, ticks_per_sample, thresh=0.5):
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
ticks_per_beat = mid.ticks_per_beat
ticks_per_measure = 4 * ticks_per_beat
ticks_per_sample = ticks_per_measure / samples_per_measure
abs_time = 0
last_time = 0
for sample in samples:
for y in xrange(sample.shape[0]):
abs_time += ticks_per_sample
for x in xrange(sample.shape[1]):
note = x + (128 - num_notes)/2
if sample[y,x] >= thresh and (y == 0 or sample[y-1,x] < thresh):
delta_time = abs_time - last_time
track.append(Message('note_on', note=note, velocity=127, time=delta_time))
last_time = abs_time
if sample[y,x] >= thresh and (y == sample.shape[0]-1 or sample[y+1,x] < thresh):
delta_time = abs_time - last_time
track.append(Message('note_off', note=note, velocity=127, time=delta_time))
last_time = abs_time
mid.save(fname)