Last active
January 19, 2020 21:36
-
-
Save lg3bass/8748023 to your computer and use it in GitHub Desktop.
MEL - Midi 2 Maya Animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#maya.py [maya 2014] | |
#midi to animation script | |
import sys | |
import math | |
#sys.path.append('/Users/lg3bass/BW_MCP/BW_PROJECTS/BW_3D/MAYA_projects/MIDI_PY/midi/Up1_LR.mid') | |
#put the midiparser.py file in X:/Program Files/Autodesk/Maya2011/Python/lib so you don't need to specify the path explicitly | |
import midiparser | |
import pymel.core as pm | |
start_note="C,1" | |
end_note="E,8" | |
note_list = ['C','D','E','F','G','A','B'] | |
sharp_note='S' | |
fps=30 | |
offset=0 | |
ch_colors=[[0, 1, 0], [1, 0, 0]] #Color(R,G,B) for Right and Left Channel | |
noteCount=0 | |
cur_channel=0; | |
def getNote (note): | |
octave=0 | |
temp=0 | |
if note>=0 and note<=11 : | |
octave=0 | |
if note>11 and note<=23 : | |
octave=1 | |
if note>23 and note<=35 : | |
octave=2 | |
if note>35 and note<=47 : | |
octave=3 | |
if note>47 and note<=59 : | |
octave=4 | |
if note>59 and note<=71 : | |
octave=5 | |
if note>71 and note<=83 : | |
octave=6 | |
if note>83 and note<=95 : | |
octave=7 | |
if note>95 and note<=107 : | |
octave=8 | |
if note>107 and note<=119 : | |
octave=9 | |
if note>110 and note<=127 : | |
octave=10 | |
temp=note-((11*octave)+(octave-1)) | |
if temp==1: | |
return note_list[0]+'_' + repr(octave) | |
if temp==2: | |
return note_list[0]+'_' + repr(octave) + sharp_note | |
if temp==3: | |
return note_list[1]+'_' + repr(octave) | |
if temp==4: | |
return note_list[1]+'_' + repr(octave) + sharp_note | |
if temp==5: | |
return note_list[2]+'_' + repr(octave) | |
if temp==6: | |
return note_list[3]+'_' + repr(octave) | |
if temp==7: | |
return note_list[3]+'_' + repr(octave) + sharp_note | |
if temp==8: | |
return note_list[4]+'_' + repr(octave) | |
if temp==9: | |
return note_list[4]+'_' + repr(octave) + sharp_note | |
if temp==10: | |
return note_list[5]+'_' + repr(octave) | |
if temp==11: | |
return note_list[5]+'_' + repr(octave) + sharp_note | |
if temp==12: | |
return note_list[6]+'_' + repr(octave) | |
def getNote2 (note,vel): | |
global noteCount | |
noteCount += 1 | |
if vel>0: | |
return 'loftedSurface'+repr(noteCount)+'_end' | |
elif vel == 0: | |
return 'loftedSurface'+repr(noteCount)+'_start' | |
return | |
def keypress(obj_name,fnum,vel): | |
if vel>0: | |
#note on, velocity > 0 | |
temp=int(math.ceil(127/vel)) | |
pm.setKeyframe( 'RIG', attribute=obj_name, v=0,t=fnum-8) | |
pm.setKeyframe( 'RIG', attribute=obj_name, v=1,t=fnum) | |
#pm.selectKey( clear=True ) | |
#pm.selectKey( 'RIG_loftedSurface'+repr(noteCount)+'_end', addTo=True, inTangent=True, t=(fnum,fnum)) | |
#pm.keyTangent('RIG_loftedSurface'+repr(noteCount)+'_end', edit=True, a=True, t=(fnum,fnum), inAngle=16.0, inWeight=1) | |
elif vel == 0: | |
#note off | |
pm.setKeyframe( 'RIG', attribute=obj_name, v=0,t=fnum) | |
pm.setKeyframe( 'RIG', attribute=obj_name, v=1,t=fnum+10) | |
return | |
def keypress_debug(obj_name,fnum,vel): | |
filename = "/Users/lg3bass/BW_MCP/BW_PROJECTS/BW_3D/MAYA_projects/MIDI_PY/midi/midi_anim.txt" | |
print "Writing to file: %s" % filename | |
file = open(filename, 'w') | |
file.write(obj_name+str(fnum)+str(vel)) | |
file.close() | |
return | |
def parse_midi(fname): | |
midi = midiparser.File(fname) | |
#calculate 100bpm | |
tempo=600000 #formula: tempo(microseconds per quarter) = 60,000,000/BPM -- use google. | |
timebase=midi.division | |
frameno=0 | |
maxtime=0 | |
global cur_channel | |
cur_channel=-2 | |
for track in midi.tracks: | |
for event in track.events: | |
if event.type == midiparser.voice.NoteOn: | |
if event.absolute>maxtime: | |
maxtime=event.absolute | |
if event.type == midiparser.meta.SetTempo: | |
tempo=event.detail.tempo | |
print maxtime | |
print tempo | |
print fps | |
print timebase | |
numframes=int(math.ceil(maxtime * tempo*fps / timebase / 1000000) + offset)+10 | |
pm.playbackOptions( minTime='1', maxTime=numframes, animationEndTime=numframes, animationStartTime='1') | |
print numframes | |
for track in midi.tracks: | |
for event in track.events: | |
if event.type == midiparser.voice.NoteOn: | |
frameno=int(math.ceil(event.absolute * tempo*fps / timebase / 1000000) + offset) | |
print repr(event.absolute)+'--frameno:'+repr(frameno) | |
#keypress(getNote(event.detail.note_no),frameno,event.detail.velocity) | |
keypress(getNote2(event.detail.note_no,event.detail.velocity),frameno,event.detail.velocity) | |
if event.type == midiparser.voice.NoteOff: | |
print 'note off:'+repr(frameno) | |
pm.setKeyframe( 'RIG', attribute='loftedSurface'+repr(noteCount)+'_start', v=0,t=frameno+5) | |
pm.setKeyframe( 'RIG', attribute='loftedSurface'+repr(noteCount)+'_start', v=1,t=frameno+25) | |
if event.type == midiparser.meta.TrackName: | |
#if event.detail.text.strip!='untitled': | |
cur_channel=cur_channel+1#print event.detail.text.strip() | |
return | |
fname=pm.fileDialog( directoryMask='*.mid' ) | |
if fname == None : | |
print 'No file choosen' | |
else : | |
parse_midi(fname) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Placed into Public Domain in June 2006 by Sean D. Spencer | |
# Sean D. Spencer | |
# [email protected] | |
# 2/19/2006 | |
# Last Revision: 4/19/2007 | |
# MIDI Parsing Library for Python. | |
import sys | |
TRUE = -1 | |
FALSE = 0 | |
class format: | |
SingleTrack = 0 | |
MultipleTracksSync = 1 | |
MultipleTracksAsync = 2 | |
class voice: | |
NoteOff = 0x80 | |
NoteOn = 0x90 | |
PolyphonicKeyPressure = 0xA0 # note aftertouch | |
ControllerChange = 0xB0 | |
ProgramChange = 0xC0 | |
ChannelPressure = 0xD0 | |
PitchBend = 0xE0 | |
class meta: | |
FileMetaEvent = 0xFF | |
SMPTEOffsetMetaEvent = 0x54 | |
SystemExclusive = 0xF0 | |
SystemExclusivePacket = 0xF7 | |
SequenceNumber = 0x00 | |
TextMetaEvent = 0x01 | |
CopyrightMetaEvent = 0x02 | |
TrackName = 0x03 | |
InstrumentName = 0x04 | |
Lyric = 0x05 | |
Marker = 0x06 | |
CuePoint = 0x07 | |
ChannelPrefix = 0x20 | |
MidiPort = 0x21 | |
EndTrack = 0x2F | |
SetTempo = 0x51 | |
TimeSignature = 0x58 | |
KeySignature = 0x59 | |
SequencerSpecificMetaEvent = 0x7F | |
class EventNote: | |
def __init__(self): | |
self.note_no = None | |
self.velocity = None | |
class EventValue: | |
def __init__(self): | |
self.type = None | |
self.value = None | |
class EventAmount: | |
def __init__(self): | |
self.amount = None | |
class MetaEventKeySignature: | |
def __init__(self): | |
self.fifths = None | |
self.mode = None | |
class MetaEventTimeSignature: | |
def __init__(self): | |
self.numerator = None | |
self.log_denominator = None | |
self.midi_clocks = None | |
self.thirty_seconds = None | |
class MetaEventText: | |
def __init__(self): | |
self.length = None | |
self.text = None | |
class MetaEventSMPTEOffset: | |
def __init__(self): | |
self.hour = None | |
self.minute = None | |
self.second = None | |
self.frame = None | |
self.sub_frame = None | |
class MetaValues: | |
def __init__(self): | |
self.length = None | |
self.values = None | |
def getNumber(str, length): | |
# MIDI uses big-endian for everything | |
sum = 0 | |
for i in range(length): | |
#sum = (sum *256) + int(str[i]) | |
sum = (sum << 8) + ord(str[i]) | |
return sum, str[length:] | |
def getVariableLengthNumber(str): | |
sum = 0 | |
i = 0 | |
while 1: | |
x = ord(str[i]) | |
i = i + 1 | |
# sum = (sum * 127) + (x (mask) 127) # mask off the 7th bit | |
sum = (sum << 7) + (x & 0x7F) | |
# Is 7th bit clear? | |
if not (x & 0x80): | |
return sum, str[i:] | |
def getValues(str, n=16): | |
temp = [] | |
for x in str[:n]: | |
temp.append(repr(ord(x))) | |
return temp | |
class File: | |
def __init__(self, file): | |
self.file = file | |
self.format = None | |
self.num_tracks = None | |
self.division = None | |
self.tracks = [] | |
self.file = open(self.file, 'rb') | |
str = self.file.read() | |
self.file.close() | |
self.read(str) | |
def read(self, str): | |
assert str[:4] == "MThd" | |
str = str[4:] | |
length, str = getNumber(str, 4) | |
assert length == 6 | |
self.format, str = getNumber(str, 2) | |
self.num_tracks, str = getNumber(str, 2) | |
self.division, str = getNumber(str, 2) | |
for i in range(self.num_tracks): | |
track = Track(i+1) | |
str = track.read(str) | |
self.tracks.append(track) | |
class Track: | |
def __init__(self, index): | |
self.number = index | |
self.length = None | |
self.events = [] | |
def read(self, str): | |
self.length, str = getNumber(str[4:], 4) | |
track_str = str[:self.length] | |
prev_absolute = 0 | |
prev_status = 0 | |
i = 0 | |
while track_str: | |
event = Event(self.number, i+1) | |
track_str = event.read(prev_absolute, prev_status, track_str) | |
prev_absolute += event.delta | |
prev_status = event.status | |
self.events.append(event) | |
i += 1 | |
return str[self.length:] | |
class Event: | |
def __init__(self, track, index): | |
self.number = index | |
self.type = None | |
self.delta = None | |
self.absolute = None | |
self.status = None | |
self.channel = None | |
def read(self, prev_time, prev_status, str): | |
self.delta, str = getVariableLengthNumber(str) | |
self.absolute = prev_time + self.delta | |
# use running status? | |
if not (ord(str[0]) & 0x80): | |
# squeeze a duplication of the running status into the data string | |
str = prev_status + str | |
self.status = str[0] | |
self.channel = ord(self.status) & 0xF | |
# increment one byte, past the status | |
str = str[1:] | |
has_channel = has_meta = TRUE | |
# handle voice events | |
channel_msg = ord(self.status) & 0xF0 | |
if channel_msg == voice.NoteOn or \ | |
channel_msg == voice.NoteOff or \ | |
channel_msg == voice.PolyphonicKeyPressure: | |
self.detail = EventNote() | |
self.detail.note_no = ord(str[0]) | |
self.detail.velocity = ord(str[1]) | |
str = str[2:] | |
elif channel_msg == voice.ControllerChange: | |
self.detail = EventValue() | |
self.detail.type = ord(str[0]) | |
self.detail.value = ord(str[1]) | |
str = str[2:] | |
elif channel_msg == voice.ProgramChange or \ | |
channel_msg == voice.ChannelPressure: | |
self.detail = EventAmount() | |
self.detail.amount = ord(str[0]) | |
str = str[1:] | |
elif channel_msg == voice.PitchBend: | |
# Pitch bend uses high accuracy 14 bit unsigned integer. | |
self.detail = EventAmount() | |
self.detail.amount = (ord(str[0]) << 7) | ord(str[1]) | |
str = str[2:] | |
else: has_channel = FALSE | |
# handle meta events | |
meta_msg = ord(self.status) | |
if meta_msg == meta.FileMetaEvent: | |
meta_msg = type = ord(str[0]) | |
length, str = getVariableLengthNumber(str[1:]) | |
if type == meta.SetTempo or \ | |
type == meta.ChannelPrefix: | |
self.detail = EventAmount() | |
self.detail.tempo, str = getNumber(str, length) | |
elif type == meta.KeySignature: | |
self.detail = MetaEventKeySignature() | |
self.detail.fifths = ord(str[0]) | |
if ord(str[1]): self.detail.mode = "minor" | |
else: self.detail.mode = "major" | |
str = str[length:] | |
elif type == meta.TimeSignature: | |
self.detail = MetaEventTimeSignature() | |
self.detail.numerator = ord(str[0]) | |
self.detail.log_denominator = ord(str[1]) | |
self.detail.midi_clocks = ord(str[2]) | |
self.detail.thirty_seconds = ord(str[3]) | |
str = str[length:] | |
elif type == meta.TrackName or \ | |
type == meta.TextMetaEvent or \ | |
type == meta.Lyric or \ | |
type == meta.CuePoint or \ | |
type == meta.CopyrightMetaEvent: | |
self.detail = MetaEventText() | |
self.detail.length = length | |
self.detail.text = str[:length] | |
str = str[length:] | |
elif type == meta.SMPTEOffsetMetaEvent: | |
self.detail = MetaEventSMPTEOffset() | |
self.detail.hour = ord(str[0]) | |
self.detail.minute = ord(str[1]) | |
self.detail.second = ord(str[2]) | |
self.detail.frame = ord(str[3]) | |
self.detail.sub_frame = ord(str[4]) | |
str = str[length:] | |
elif type == meta.EndTrack: | |
str = str[length:] # pass on to next track | |
else: has_meta = FALSE | |
elif meta_msg == meta.SystemExclusive or \ | |
meta_msg == meta.SystemExclusivePacket: | |
self.detail = MetaValues() | |
self.detail.length, str = getVariableLengthNumber(str) | |
self.detail.values = getValues(str, self.detail.length) | |
str = str[self.detail.length:] | |
else: has_meta = FALSE | |
if has_channel: | |
self.type = channel_msg | |
elif has_meta: | |
self.type = meta_msg | |
else: | |
raise "Unknown event." | |
return str | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment