Skip to content

Instantly share code, notes, and snippets.

@lg3bass
Last active January 19, 2020 21:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lg3bass/8748023 to your computer and use it in GitHub Desktop.
Save lg3bass/8748023 to your computer and use it in GitHub Desktop.
MEL - Midi 2 Maya Animation
#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)
# Placed into Public Domain in June 2006 by Sean D. Spencer
# Sean D. Spencer
# sean_don4@lycos.com
# 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