rendered paste bodyimport struct
import os
class MIDIEvent:
def __init__(self, time):
self.timeVal = time
self.vel = 0
self.note = 0
def setChannel(self, val):
self.channel = val
def setType(self, type):
self.type = type
def setOffset(self, type):
self.offset = type
def setNote(self, note):
self.note = note
def setVelocity(self, vel):
self.vel = vel
def setAftertouch(self, val):
self.aftertouch = val
def setProgram(self, val):
self.program = val
def setController(self, val):
self.controller = val
def setValue(self, val):
self.value = val
def setChannelAftertouch(self, val):
self.channelAftertouch = val
def setPitchBend(self, val):
self.pitchBend = val
class MIDITrack:
def __init__(self):
self.endian = '>'
self.events = []
self.setName('')
self.setMaxTime(0)
self.setHasNormalEvent(False)
def parseBuffer(self, buffer):
fmt = self.endian+'i'
c = struct.calcsize(fmt)
chunkSize, = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
#print "chunkSize=", chunkSize
# read the chunk
chunk = buffer[:chunkSize]
# get data after chunk
buffer = buffer[chunkSize:]
timeOffset = 0
#count = 0
# loop through all events
while len(chunk) > 0:
timeVal, chunk = self.readVarLen(chunk)
timeOffset += timeVal
### print "timeVal=", timeVal
eventType, = struct.unpack('B', chunk[:1])
#print "event=", event
if eventType == 0xff:
#print "meta"
chunk = chunk[1:]
chunk = self.parseMetaEvent(timeVal, chunk)
elif eventType == 0xF0:
print "SysEx"
chunk = chunk[1:]
val, chunk = self.readVarLen(chunk)
chunk = chunk[val:]
elif eventType == 0xF7:
print "DivSysEx"
else:
self.setHasNormalEvent(True)
chunk = self.parseEvent(eventType, timeOffset, chunk)
#count += 1
#if count > 10:
# break
self.setMaxTime(timeOffset)
return buffer
def setMaxTime(self, time):
self._maxTime = time
def maxTime(self):
return self._maxTime
def setHasNormalEvent(self, val):
self.normalEvent = val
def hasNormalEvent(self):
return self.normalEvent
def setName(self, name):
self._name = name
def name(self):
return self._name
def parseEvent(self, eventType, timeVal, buffer):
type = (eventType & 0xF0) >> 4;
### print "NORMAL EVENT : ", type
if type < 0x08:
type = self.lastType
# running status
### print "lastType=", type
else:
# advance
buffer = buffer[1:]
self.lastType = type
channel = eventType & 0x0F
e = MIDIEvent(timeVal)
e.setChannel(channel)
e.setType(type)
e.setOffset(timeVal)
if type == 0x08: # note off
fmt = '2B'
c = struct.calcsize(fmt)
note, vel = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setNote(note)
e.setVelocity(vel)
elif type == 0x09: # note on
fmt = '2B'
c = struct.calcsize(fmt)
note, vel = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setNote(note)
e.setVelocity(vel)
elif type == 0x0a: # note aftertouch
fmt = '2B'
c = struct.calcsize(fmt)
note, aftertouch = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setNote(note)
e.setAftertouch(aftertouch)
elif type == 0x0b: # controller
fmt = '2B'
c = struct.calcsize(fmt)
controller, value = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setController(controller)
e.setValue(value)
elif type == 0x0c: # program change
fmt = '1B'
c = struct.calcsize(fmt)
pgm, = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setProgram(pgm)
elif type == 0x0d: # channel aftertouch
fmt = '1B'
c = struct.calcsize(fmt)
chnAftertouch, = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
e.setChannelAftertouch(chnAftertouch)
elif type == 0x0e: # pitch bend
fmt = '2B'
c = struct.calcsize(fmt)
b1, b2 = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
val = (b1 << 7) | (b2 & 0x7f)
e.setPitchBend(val)
elif type == 0xf0: # sys ex
val, buffer = self.readVarLen(buffer)
buffer = buffer[val:]
elif type == 0xf7: # div sys ex
val, buffer = self.readVarLen(buffer)
buffer = buffer[val:]
elif type == 0xff: # meta event
val, buffer = self.readVarLen(buffer)
buffer = buffer[val:]
self.events.append(e)
return buffer
def parseMetaEvent(self, timeVal, buffer):
type, = struct.unpack('B', buffer[:1])
buffer = buffer[1:]
### print "parseMetaEvent = ", type
if type == 0: # seq number
seqLen, buffer = self.getByte(buffer)
fmt = '2B'
c = struct.calcsize(fmt)
msb, lsb = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
elif type == 1: # text event
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
txt, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "txtEvent=", txt
elif type == 2: # copyright
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
cr, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "cr=", cr
elif type == 3: # seq name
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
seqName, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "seqName=", seqName
self.setName(seqName)
elif type == 4: # instr name
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
instrName, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "instrName=", instrName
elif type == 5: # lyrics
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
lyrics, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "lyrics=", lyrics
elif type == 6: # marker
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
marker, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "marker=", marker
elif type == 7: # cue point
strlen, buffer = self.getByte(buffer)
fmt = str(strlen)+'s'
c = struct.calcsize(fmt)
cuePoint, = struct.unpack(fmt, buffer[:strlen])
buffer = buffer[c:]
#print "cuePoint=", cuePoint
elif type == 32: # chan prefix
val, buffer = self.getByte(buffer)
# TODO
len, buffer = self.getByte(buffer)
elif type == 47: # end of track
buffer = buffer[1:]
elif type == 81: # set tempo
len, buffer = self.getByte(buffer)
fmt = '3B'
c = struct.calcsize(fmt)
b1, b2, b3 = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
val = (b1 << 16) | (b2 << 8) | b3
self.bpm = 60000000 / val
#print "b1=", b1
#print "b2=", b2
#print "b3=", b3
#print "tempo=", val
#print "bpm=", bpm
elif type == 84: # smpte
len, buffer = self.getByte(buffer)
fmt = '5B'
c = struct.calcsize(fmt)
b1, b2, b3, b4, b5 = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
elif type == 88: # time sig
len, buffer = self.getByte(buffer)
fmt = '4B'
c = struct.calcsize(fmt)
num, denom, metro, _32nds = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
self.num = num
self.denom = denom
self.metro = metro
self._32nds = _32nds
#print "num=", num
#print "denom=", denom
#print "metro=", metro
#print "_32nds=", _32nds
elif type == 89: # key sig
len, buffer = self.getByte(buffer)
fmt = '2B'
c = struct.calcsize(fmt)
b1, b2 = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
elif type == 127: # seq specific
val, buffer = self.readVarLen(buffer)
buffer = buffer[val:]
else:
#print "unknown meta-event"
len, buffer = self.getByte(buffer)
buffer = buffer[len:]
return buffer
def getByte(self, buffer):
fmt = self.endian+'B'
c = struct.calcsize(fmt)
val, = struct.unpack(fmt, buffer[:c])
buffer = buffer[c:]
return val, buffer
def readVarLen(self, buffer):
result = 0
for i in range(4):
c, = struct.unpack('B', buffer[:1])
buffer = buffer[1:]
result = (result << 7) | (c & 0x7f)
if c & 0x80 == 0:
break
return (result, buffer)
class MIDIFile:
def __init__(self, filename):
self._filename = filename
f = file(filename)
buffer = f.read()
f.close()
#print "buffer=", buffer
#print len(buffer)
self.tracks = []
self.endian = '>'
fmt = self.endian+'4c'
header = struct.unpack(fmt, buffer[0:4])
if header[0] == 'M' and header[1] == 'T' and header[2] == 'h' and header[3] == 'd':
fmt = self.endian+'ihhh'
c = struct.calcsize(fmt)
buffer = buffer[4:]
chunkSize, formatType, nTracks, self.timeDivision = struct.unpack(fmt, buffer[0:c])
#print chunkSize, formatType, nTracks, timeDivision
self._numTracks = nTracks
buffer = buffer[c:]
# loop through tracks
for i in range(nTracks):
fmt = self.endian + '4c'
header = struct.unpack(fmt, buffer[0:4])
if header[0] == 'M' and header[1] == 'T' and header[2] == 'r' and header[3] == 'k':
buffer = buffer[4:]
#print "========== TRACK ", i
track = MIDITrack()
buffer = track.parseBuffer(buffer)
self.tracks.append(track)
else:
raise Exception('Invalid track')
else:
raise Exception('Invalid header')
def numTracks(self):
return self._numTracks
def track(self, index):
return self.tracks[index]
if __name__ == "__main__":
try:
m = MIDIFile('midiFiles/children/3blindmiceMelody.mid')
#print "num=", m.numTracks()
track = m.track(0)
print "name=", track.name()
print "bpm=", track.bpm
print track.num, "/", track.denom
print "time", m.timeDivision
print "metro=", track.metro
print "_32nds=", track._32nds
#print "events=", track.events
except Exception, msg:
print msg