Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*~
*.pyc
midi/
output/
Empty file modified Animator.py
100644 → 100755
Empty file.
Empty file modified ConvertColor.py
100644 → 100755
Empty file.
Empty file modified MidiLexer.py
100644 → 100755
Empty file.
150 changes: 150 additions & 0 deletions MusAnimLauncher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# -*- coding: utf-8 -*-
from MusAnimRenderer import MusAnimRenderer
from MusAnimLexer import MidiLexer
import sys,os.path

PITCH_GRACE=5
TRACK_WIDTH=24

def main():
tracks = [
{ 'name': "track0",
'color': (0.000, 0.996, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track1",
'color': (0.996, 0.000, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track2",
'color': (0.004, 0.996, 0.992),
'width': TRACK_WIDTH,
},
{ 'name': "track3",
'color': (0.996, 0.855, 0.398),
'width': TRACK_WIDTH,
},
{ 'name': "track4",
'color': (0.563, 0.980, 0.570),
'width': TRACK_WIDTH,
},
{ 'name': "track5",
'color': (0.000, 0.461, 0.996),
'width': TRACK_WIDTH,
},
{ 'name': "track6",
'color': (0.832, 0.996, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track7",
'color': (0.996, 0.574, 0.492),
'width': TRACK_WIDTH,
},
{ 'name': "track8",
'color': (0.992, 0.535, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track9",
'color': (0.520, 0.660, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track10",
'color': (0.000, 0.680, 0.492),
'width': TRACK_WIDTH,
},
{ 'name': "track11",
'color': (0.738, 0.773, 0.996),
'width': TRACK_WIDTH,
},
{ 'name': "track12",
'color': (0.738, 0.824, 0.574),
'width': TRACK_WIDTH,
},
{ 'name': "track13",
'color': (0.000, 0.723, 0.090),
'width': TRACK_WIDTH,
},
{ 'name': "track14",
'color': (0.004, 0.813, 0.996),
'width': TRACK_WIDTH,
},
{ 'name': "track15",
'color': (0.566, 0.813, 0.793),
'width': TRACK_WIDTH,
},
{ 'name': "track16",
'color': (0.730, 0.531, 0.000),
'width': TRACK_WIDTH,
},
{ 'name': "track17",
'color': (0.867, 0.996, 0.453),
'width': TRACK_WIDTH,
},
{ 'name': "track18",
'color': (0.000, 0.996, 0.773),
'width': TRACK_WIDTH,
},
{ 'name': "track19",
'color': (0.996, 0.895, 0.008),
'width': TRACK_WIDTH,
},
{ 'name': "track20",
'color': (0.594, 0.996, 0.320),
'width': TRACK_WIDTH,
},
{ 'name': "track21",
'color': (0.000, 0.996, 0.469),
'width': TRACK_WIDTH,
},
{ 'name': "track22",
'color': (0.996, 0.430, 0.254),
'width': TRACK_WIDTH,
},
{ 'name': "track23",
'color': (0.645, 0.996, 0.820),
'width': TRACK_WIDTH,
},
{ 'name': "track24",
'color': (0.996, 0.691, 0.402),
'width': TRACK_WIDTH,
},
{ 'name': "track25",
'color': (0.000, 0.605, 0.996),
'width': TRACK_WIDTH,
},
]

ntracks=len(tracks)
for i in range(ntracks):
tracks[i]['z-index']=ntracks-i

if len(sys.argv)<3:
print("Usage: python MusAnimLauncher.py input.mid outputDirectory [--dynamic]")
sys.exit(1)
return

mid=sys.argv[1]

lexer = MidiLexer()
lexer.lex(mid)

frames_dir = sys.argv[2]+os.sep
if not os.path.isdir(frames_dir):
os.makedirs(frames_dir)

dynamic=len(sys.argv)>=4 and sys.argv[3]=='--dynamic'

speed_map = [{'time': 0.0, 'speed': 4}]

dimensions = 1920, 1080
#dimensions = 720, 480
#dimensions = 426, 240

fps = 25

renderer=MusAnimRenderer()
renderer.introduction=False
renderer.render(mid,frames_dir,tracks,speed_map=speed_map,dimensions=dimensions,min_pitch=lexer.minPitch-PITCH_GRACE,max_pitch=lexer.maxPitch+PITCH_GRACE,fps=fps,dynamicmode=dynamic)

if __name__ == '__main__':
main()
46 changes: 32 additions & 14 deletions MusAnimLexer.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import sys

class MidiLexer:
Expand All @@ -7,6 +8,11 @@ class MidiLexer:
# beats, without making any pretenses about figuring out timing in seconds.
# That has to be done later, once we have all the timing events sorted
midi_events = []
minPitch = sys.maxsize
maxPitch = -sys.maxsize-1

def debug(self, event):
print('Unknown event: ' + bin(event) + " (" + hex(event) + ")")

def get_v_time(self, data):
"""Picks off the variable-length time from a block of data and returns
Expand All @@ -27,11 +33,12 @@ def read_midi_event(self, track_data, time, track_num):
# have to read off vtime first!
d_time, track_data = self.get_v_time(track_data)
time += d_time
#print time
event=((ord(track_data[0]) & 0xF0) >> 4)

if track_data[0] == '\xff':
# event is meta event, we do nothing unless it's a tempo event
if ord(track_data[1]) == 0x51:
#print track_num, list(track_data)
# tempo event
mpqn = ((ord(track_data[3]) << 16) + (ord(track_data[4]) << 8)
+ ord(track_data[5])) # microseconds per quarter note
Expand All @@ -44,7 +51,7 @@ def read_midi_event(self, track_data, time, track_num):
return track_data[length+3:], time

# otherwise we we assume it's a midi event
elif ((ord(track_data[0]) & 0xF0) >> 4) == 0x8:
elif event == 0x8:
# note off event
# don't add a note off event if keyswitch (pitch below 12)
pitch = ord(track_data[1])
Expand All @@ -53,7 +60,7 @@ def read_midi_event(self, track_data, time, track_num):
'pitch': pitch, 'track_num': track_num})
return track_data[3:], time

elif ((ord(track_data[0]) & 0xF0) >> 4) == 0x9:
elif event == 0x9:
# note on event
pitch = ord(track_data[1])
if pitch < 12: # it's a keyswitch!
Expand All @@ -62,19 +69,30 @@ def read_midi_event(self, track_data, time, track_num):
elif pitch == 1:
mode = "pizz"
else:
raise Exception("Unknown keyswitch")
self.midi_events.append({'type': 'keyswitch', 'time': time,
'track_num': track_num, 'mode': mode})
print(("Unknown keyswitch: "+str(pitch)))
pitch=False
if pitch != False:
self.midi_events.append({'type': 'keyswitch', 'time': time,
'track_num': track_num, 'mode': mode})
else:
if pitch > self.maxPitch:
self.maxPitch = pitch
if pitch < self.minPitch:
self.minPitch = pitch
self.midi_events.append({'type': 'note_on', 'time': time,
'pitch': ord(track_data[1]), 'track_num': track_num})
return track_data[3:], time
elif ((ord(track_data[0]) & 0xF0) >> 4) == 0xC:
elif event == 0xC:
return track_data[2:], time # ignore some other events
elif ((ord(track_data[0]) & 0xF0) >> 4) == 0xB:
elif event == 0xB:
return track_data[3:], time
#kek
elif event == 0xF or event == 0xD:
self.debug(event)
return track_data[2:], time
else:
raise Exception("Unknown midi file data event: " + str(ord(track_data[0])))
self.debug(event)
return track_data[3:], time

def lex(self, filename):
"""Returns block list for musanim from a midi file given in filename"""
Expand All @@ -88,12 +106,12 @@ def lex(self, filename):

# open and read file
f = open(filename, 'rb')
s = f.read()
s = f.read().decode('latin-1')

# grab header
header = s[0:14]
f_format = ord(s[8]) << 8 | ord(s[9])
num_tracks = ord(s[10]) << 8 | ord(s[11])
self.num_tracks = ord(s[10]) << 8 | ord(s[11])
self.ticks_per_quarter = ord(s[12]) << 8 | ord(s[13])

tracks_chunk = s[14:]
Expand All @@ -110,13 +128,13 @@ def lex(self, filename):

# convert all times from ticks to beats, for convenience
for event in self.midi_events:
event['time'] = (event['time'] + 0.0) / 960
event['time'] = (event['time'] + 0.0) / self.ticks_per_quarter #960

self.midi_events.sort(lambda a, b: cmp(a['time'], b['time']))
self.midi_events.sort(key=lambda a: a['time'])
return self.midi_events


if __name__ == '__main__':
lexer = MidiLexer()
blocks = lexer.lex('multitrackmidi01.MID')
print blocks
print(blocks)
Loading