-
Notifications
You must be signed in to change notification settings - Fork 4
/
midi_manipulation.py
134 lines (111 loc) · 4.31 KB
/
midi_manipulation.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
import midi
import numpy as np
import glob
from tqdm import tqdm
lowerBound = 24
upperBound = 102
span = upperBound - lowerBound
def midiToNoteStateMatrix(midifile, squash=True, span=span):
pattern = midi.read_midifile(midifile)
timeleft = [track[0].tick for track in pattern]
posns = [0 for track in pattern]
statematrix = []
time = 0
state = [[0, 0] for x in range(span)]
statematrix.append(state)
condition = True
while condition:
if time % (pattern.resolution / 4) == (pattern.resolution / 8):
# Crossed a note boundary. Create a new state, defaulting to holding notes
oldstate = state
state = [[oldstate[x][0], 0] for x in range(span)]
statematrix.append(state)
for i in range(len(timeleft)): # For each track
if not condition:
break
while timeleft[i] == 0:
track = pattern[i]
pos = posns[i]
evt = track[pos]
if isinstance(evt, midi.NoteEvent):
if (evt.pitch < lowerBound) or (evt.pitch >= upperBound):
pass
# print "Note {} at time {} out of bounds (ignoring)".format(evt.pitch, time)
else:
if isinstance(evt, midi.NoteOffEvent) or evt.velocity == 0:
state[evt.pitch - lowerBound] = [0, 0]
else:
state[evt.pitch - lowerBound] = [1, 1]
elif isinstance(evt, midi.TimeSignatureEvent):
if evt.numerator not in (2, 4):
# We don't want to worry about non-4 time signatures. Bail early!
# print "Found time signature event {}. Bailing!".format(evt)
out = statematrix
condition = False
break
try:
timeleft[i] = track[pos + 1].tick
posns[i] += 1
except IndexError:
timeleft[i] = None
if timeleft[i] is not None:
timeleft[i] -= 1
if all(t is None for t in timeleft):
break
time += 1
S = np.array(statematrix)
statematrix = np.hstack((S[:, :, 0], S[:, :, 1]))
statematrix = np.asarray(statematrix).tolist()
return statematrix
def noteStateMatrixToMidi(statematrix, name="example", span=span):
statematrix = np.array(statematrix)
if not len(statematrix.shape) == 3:
statematrix = np.dstack((statematrix[:, :span], statematrix[:, span:]))
statematrix = np.asarray(statematrix)
pattern = midi.Pattern()
track = midi.Track()
pattern.append(track)
span = upperBound - lowerBound
tickscale = 55
lastcmdtime = 0
prevstate = [[0, 0] for x in range(span)]
for time, state in enumerate(statematrix + [prevstate[:]]):
offNotes = []
onNotes = []
for i in range(span):
n = state[i]
p = prevstate[i]
if p[0] == 1:
if n[0] == 0:
offNotes.append(i)
elif n[1] == 1:
offNotes.append(i)
onNotes.append(i)
elif n[0] == 1:
onNotes.append(i)
for note in offNotes:
track.append(midi.NoteOffEvent(tick=(time - lastcmdtime) * tickscale, pitch=note + lowerBound))
lastcmdtime = time
for note in onNotes:
track.append(midi.NoteOnEvent(tick=(time - lastcmdtime) * tickscale, velocity=40, pitch=note + lowerBound))
lastcmdtime = time
prevstate = state
eot = midi.EndOfTrackEvent(tick=1)
track.append(eot)
midi.write_midifile("{}.mid".format(name), pattern)
def get_songs(path, model_name, max=None):
'''
:param path: path to the songs directory
:return: array of songs w/ timestamp events
'''
files = glob.glob('{}/*.mid*'.format(path))
files = files[:max] if max is not None else files
songs = []
c = 0
for f in tqdm(files, desc='{0}.get_songs({1})'.format(model_name, path), ascii=True):
try:
song = np.array(midiToNoteStateMatrix(f))
songs.append(song)
except Exception as e:
raise e
return songs