Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

midi file toolkit

17 views
Skip to first unread message

Sean McIlroy

unread,
Sep 6, 2009, 12:05:48 AM9/6/09
to
## python 2.6.2

from tkFileDialog import askopenfilename, askdirectory

def nowindow(function):
def windowless():
from Tkinter import Tk
Tk().withdraw()
return function()
return windowless

getfilename = nowindow(askopenfilename)
getdirectoryname = nowindow(askdirectory)

def haskellize(pathname):
return '\\'.join(pathname.split('/'))

def newname():
from time import time
return 'NAME_' + ''.join(str(time()).split('.'))

def mangle(name):
return '__' + name + '__'

def pathsafe(name):
return '_'.join(name.split('/'))

def changefilename(filename,directoryname=None,rewrite=lambda
name:name):
from os.path import split, splitext, join
dirname, filename = split(filename)
filename, extension = splitext(filename)
filename = rewrite(filename)
directoryname = directoryname or dirname
return join(directoryname,filename) + extension

def readbytes(filename):
return [ord(x) for x in file(filename,'rb').read()]

def writebytes(numbers,filename):
file(filename,'wb').write(''.join([chr(x) for x in numbers]))

def playbytes(numbers):
from os import startfile
filename = newname() + '.mid'
writebytes(numbers,filename)
startfile(filename)

def IfThenElse(criterion,optionTrue,optionFalse):
if bool(criterion)==True: return optionTrue
if bool(criterion)==False: return optionFalse

def flatten(homogeneouslist):
from itertools import chain
while type(homogeneouslist[0])==list: homogeneouslist = list(chain
(*homogeneouslist))
return homogeneouslist

def number2digits(number,base):
digits = [number]
while digits[0]>=base: digits[0:1] = [digits[0]//base, digits
[0]%base]
return digits

def digits2number(digits,base):
reversedigits = list(reversed(digits))
return sum([reversedigits[i]*(base**i) for i in range(len
(digits))])

def number2fixedlength(number,numbytes):
digits = number2digits(number,2**8)
numleadingzeros = IfThenElse(len(digits)<numbytes,numbytes-len
(digits),0)
return [0]*numleadingzeros + digits

def fixedlength2number(digits):
while digits[0]==0: digits = digits[1:]
return digits2number(digits,2**8)

def number2variablelength(number):
digits = number2digits(number,2**7)
padding = [2**7]*(len(digits)-1) + [0]
return [x+y for (x,y) in zip(digits,padding)]

def variablelength2number(variablelength):
digits = [x-(2**7) for x in variablelength[:-1]] + variablelength
[-1:]
return digits2number(digits,2**7)

def getfixedlength(numbers,startindex,numbytes):
endindex = startindex + numbytes
return numbers[startindex:endindex], endindex

def getvariablelength(numbers,startindex):
index = startindex
while numbers[index]>=(2**7): index = index + 1
endindex = index + 1
return numbers[startindex:endindex], endindex

headeridentifier = [ord(x) for x in 'MThd']
trackidentifier = [ord(x) for x in 'MTrk']

eventtypes = range(2**3)
noteoff = eventtypes[0] ## notenumber,
velocity = parameters
noteon = eventtypes[1] ## notenumber,
velocity = parameters
noteaftertouch = eventtypes[2] ## notenumber,
aftertouchvalue = parameters
controller = eventtypes[3] ## controllernumber,
controllervalue = parameters
programchange = eventtypes[4] ##
programnumber = parameters[0]
channelaftertouch = eventtypes[5] ##
aftertouchvalue = parameters[0]
pitchbend = eventtypes[6] ## pitchvalueLSB,
pitchvalueMSB = parameters
nonmidicontrol = eventtypes[7]

channels = range(2**4)
systemexclusive = channels[0]
meta = channels[15]

def chunk(identifier,flatdata):
return identifier + number2fixedlength(len(flatdata),4) + flatdata

def analyzeheaderdata(data):
formattype = data[0:2]
numtracks = data[2:4]
timedivision = data[4:6]
return [digits2number(x,2**8) for x in [formattype, numtracks,
timedivision]]

def synthesizeheaderdata(formattype,numtracks,timedivision):
datasegments = [number2fixedlength(x,2) for x in [formattype,
numtracks, timedivision]]
return datasegments[0] + datasegments[1] + datasegments[2]

def headerchunk(formattype,numtracks,timedivision):
return chunk(headeridentifier, synthesizeheaderdata(formattype,
numtracks, timedivision))

def trackchunk(events):
return chunk(trackidentifier,flatten(events))

def analyzestatus(status):
number = status[0]
eventtype = (number - 2**7) // (2**4)
channel = number % (2**4)
return eventtype, channel

def synthesizestatus(eventtype,channel):
number = (2**7) + eventtype*(2**4) + channel
return [number]

def midicontrolevent(deltatime,eventtype,channel,*parameters):
deltatime = number2variablelength(deltatime)
status = synthesizestatus(eventtype, channel)
return [deltatime, status, list(parameters)]

def nonmidicontrolevent(deltatime,messagedata,metatype=[]):
deltatime = number2variablelength(deltatime)
eventtype = nonmidicontrol
channel = IfThenElse(metatype, meta, systemexclusive)
status = synthesizestatus(eventtype, channel)
datalength = number2variablelength(len(messagedata))
return [deltatime, status, (metatype + datalength + messagedata)]

def getchunk(numbers,startindex):
identifier, startindex = getfixedlength(numbers,startindex,4)
chunksize, startindex = getfixedlength(numbers,startindex,4)
chunkdata, startindex = getfixedlength
(numbers,startindex,fixedlength2number(chunksize))
return [identifier, chunksize, chunkdata], startindex

def file2chunks(numbers):
strictupperbound = len(numbers)
startindex = 0
chunks = []
while startindex < strictupperbound:
chunk, startindex = getchunk(numbers,startindex)
chunks.append(chunk)
return chunks

def getevent(numbers,startindex,runningstatus):
deltatime, startindex = getvariablelength(numbers,startindex)
status, startindex = getfixedlength
(numbers,startindex,IfThenElse(numbers[startindex]>=(2**7),1,0))
runningstatus = status or runningstatus
eventtype, channel = analyzestatus(runningstatus)
if eventtype==nonmidicontrol:
metatype, startindex = getfixedlength
(numbers,startindex,IfThenElse(channel==meta,1,0))
messagelength, startindex = getvariablelength
(numbers,startindex)
message, startindex = getfixedlength
(numbers,startindex,variablelength2number(messagelength))
eventbody = metatype + messagelength + message
if eventtype<>nonmidicontrol:
numparameters = IfThenElse(eventtype in [programchange,
channelaftertouch], 1, 2)
eventbody, startindex = getfixedlength
(numbers,startindex,numparameters)
return [deltatime,runningstatus, eventbody], startindex,
runningstatus

def trackdata2events(numbers):
strictupperbound = len(numbers)
startindex = 0
runningstatus = [None]
events = []
while startindex < strictupperbound:
event, startindex, runningstatus = getevent(numbers,
startindex, runningstatus)
events.append(event)
return events

def parsefile(numbers):
chunks = file2chunks(numbers)
formattype, numtracks, timedivision = analyzeheaderdata(chunks[0]
[2])
eventstreams = [trackdata2events(chunkdata) for [identifier,
chunksize, chunkdata] in chunks[1:]]
return formattype, timedivision, eventstreams

def unparsefile(parsedfile):
formattype, timedivision, eventstreams = parsedfile
numtracks = len(eventstreams)
header = headerchunk(formattype, numtracks, timedivision)
tracks = [trackchunk(events) for events in eventstreams]
return header + flatten(tracks)

def takeindexedtracks(parsedfile,*indices):
formattype, timedivision, eventstreams = parsedfile
return formattype, timedivision, [eventstreams[i] for i in
indices]

def getname(events):
deltatime = [0]
status = synthesizestatus(nonmidicontrol, meta)
metatype = 3
for event in events:
if event[0:2]==[deltatime, status] and event[2][0]==metatype:
numbers = event[2]
namelength, startindex = getvariablelength(numbers,1)
namebytes, startindex = getfixedlength
(numbers,startindex,variablelength2number(namelength))
return ''.join([chr(x) for x in namebytes])

def noteevent(event):
return analyzestatus(event[1])[0] in [noteoff, noteon,
noteaftertouch]

def firstnoteindex(events):
for i in range(len(events)):
if noteevent(events[i]): return i

def lastnoteindex(events):
for i in reversed(range(len(events))):
if noteevent(events[i]): return i

def explodefile(filename,directoryname=None):
formattype, timedivision, eventstreams = parsefile(readbytes
(filename))
index = IfThenElse(formattype==1 and firstnoteindex(eventstreams
[0])==None, 1, 0)
temposettings, eventstreams = eventstreams[:index], eventstreams
[index:]
for i in range(len(eventstreams)):
trackname = getname(eventstreams[i]) or ('track_' + str(i))
rewrite = lambda name: name + '_' + pathsafe(trackname)
singletrackfilename = changefilename
(filename,directoryname,rewrite)
singletrackfile = (formattype, timedivision, temposettings +
eventstreams[i:i+1])
writebytes(unparsefile(singletrackfile),singletrackfilename)

def reflectpitch(event):
if not noteevent(event): return event
notenumber, velocity = event[2]
newnotenumber = (2**7) - notenumber
return event[:2] + [[newnotenumber, velocity]]

def translatepitch(event,deltapitch):
if not noteevent(event): return event
notenumber, velocity = event[2]
newnotenumber = notenumber + deltapitch
assert newnotenumber in range(2**7)
return event[:2] + [[newnotenumber, velocity]]

def invert(events):
return [reflectpitch(event) for event in events]

def transpose(event,deltapitch):
return [translatepitch(event,deltapitch) for event in events]

def withoutemptytracks(parsedfile):
formattype, timedivision, eventstreams = parsedfile
index = IfThenElse(formattype==1 and firstnoteindex(eventstreams
[0])==None, 1, 0)
temposettings, eventstreams = eventstreams[:index], eventstreams
[index:]
neweventstreams = temposettings + [events for events in
eventstreams if firstnoteindex(events)<>None]
return formattype, timedivision, neweventstreams

def withoutcountin(parsedfile):
formattype, timedivision, eventstreams = parsedfile
firstnoteindices = [firstnoteindex(events) for events in
eventstreams]
time = lambda i,j: variablelength2number(eventstreams[i][j][0])
StreamEventTime = [(i,j,time(i,j)) for (i,j) in enumerate
(firstnoteindices) if j<>None]
starttime = min([t for (i,j,t) in StreamEventTime])
time = lambda t: number2variablelength(t-starttime)
StreamEventTime = [(i,j,time(t)) for (i,j,t) in StreamEventTime]
neweventstreams = eventstreams[:]
for (i,j,t) in StreamEventTime:
newevent = [t] + neweventstreams[i][j][1:]
neweventstreams[i] = neweventstreams[i][:j] + [newevent] +
neweventstreams[i][j+1:]
return formattype, timedivision, neweventstreams

def preprocessfiles():
from os import listdir, mkdir
directoryname = getdirectoryname()
filenames = listdir(directoryname)
subdirectoryname = directoryname + '/preprocessed'
mkdir(subdirectoryname)
for filename in filenames:
oldfilepath = directoryname + '/' + filename
newfilepath = subdirectoryname + '/' + filename
writebytes(unparsefile(withoutcountin(withoutemptytracks
(parsefile(readbytes(oldfilepath))))),newfilepath)

def retrograde(events):
prefixindex = firstnoteindex(events)
suffixindex = lastnoteindex(events) + 1
prefix, noteevents, suffix = events[:prefixindex], events
[prefixindex: suffixindex], events[suffixindex:]
noteevents = list(reversed(noteevents))
nextdeltatime = noteevents[-1][0]
for i in range(len(noteevents)):
deltatime, status, body = noteevents[i]
eventtype, channel = analyzestatus(status)
neweventtype = IfThenElse(eventtype==noteoff,noteon,IfThenElse
(eventtype==noteon,noteoff,eventtype))
newstatus = synthesizestatus(neweventtype,channel)
noteevents[i] = [nextdeltatime, newstatus, body]
nextdeltatime = deltatime
return prefix + noteevents + suffix

"""################################################################################"""

"""
filename = getfilename()
formattype, timedivision, eventstreams = parsedfile = parsefile
(readbytes(filename))
newfile = formattype, timedivision, ###
playbytes(unparsefile(newfile))
"""


0 new messages