midi file toolkit

Sean McIlroy sean_mcilroy at yahoo.com
Sun Sep 6 06:05:48 CEST 2009


## 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))
"""





















More information about the Python-list mailing list