#!/usr/bin/python from .midimessage import * class MIDITrack: def __init__(self): pass class InputStreamWrapper: def __init__(self, fd, mode="rb"): if isinstance(fd, str): fd=open(fd, mode) self.fd=fd self.buffer=[] self.n=0 self.offset=0 self.doRecord=False def close(self): self.fd.close() def read(self, n=-1): x=self.fd.read(n) self.offset+=n if self.doRecord: self.buffer+=x return x def record(self, rec=True): self.doRecord=rec class MIDIChunk: def __init__(self, fd): self.fd=fd self.magick="" self.length=0 def parse(self): pass def print(self): pass def readMagick(self): return self.fd.read(4).decode('ascii') def reverseMsb(self, x): out=0 n=len(x) for i in reversed(range(n)): out+= (x[i]<< (8*(n-1-i))) return out def intN(self, n): return self.reverseMsb(self.fd.read(n)) def int4(self): return self.intN(4) def int2(self): return self.intN(2) def byte(self): return self.intN(1) def skip(self, n=1): self.fd.read(n) class MIDIHeaderChunk(MIDIChunk): def __init__(self, fd): MIDIChunk.__init__(self,fd) self.format=0 self.tracks=0 self.division=0 MIDIHeaderChunk.FORMAT_SINGLE_CHANNEL=0 MIDIHeaderChunk.FORMAT_MULTIPLE_CHANNEL=1 MIDIHeaderChunk.FORMAT_MULTIPLE_INDEPENDANT_CHANNEL=2 self.variableTicks=False self.ticksPerQuarter=0 # beat per quarter note def parse(self): f=self.fd self.magick=self.readMagick() if self.magick!="MThd": raise MIDIParserException("Bad header magick number") self.length=self.int4() self.format=self.int2() self.tracks=self.int2() self.division=self.int2() self.variableTicks=True if self.division & 0x8000 else False if not self.variableTicks: self.ticksPerQuarter= self.division & 0x7fff class MIDITrackChunk(MIDIChunk): def __init__(self, fd, skipTrack=False): MIDIChunk.__init__(self,fd) self.time=0 self.events=[] self.skipTrack=skipTrack def readDeltaTime(self): out=[] while True: x=self.byte() out.append(x&0x7F) if not (x&0x80): break n=len(out) ret=0 for i in range(n): j=n-i-1 ret+= ( (out[i]) <<(j*7) ) self.time+=ret return ret def parse(self): f=self.fd self.magick=self.readMagick() if self.magick!="MTrk": raise MIDIParserException("Bad track magick number") self.length=self.int4() events=[] if self.skipTrack: self.fd.read(self.length) else: while True: delta=self.readDeltaTime() evt=self.readEvent() events.append((self.time, evt)) if evt.isEndTrack(): break return events def readEvent(self): return MidiMessage.parse(self.fd) class MidiFile: def __init__(self, path): self.path=path self.tracksCount=0 self.header=None self.tracks=[] self.loaded=False def parse(self): self.fd=0 try: self.fd=open(self.path, "rb") except Exception as e: ERR(type(e).__name__+" : "+str(e)) return e self.header=MIDIHeaderChunk(self.fd) self.header.parse() for t in range(self.header.tracks): track=MIDITrackChunk(self.fd) self.tracks.append(track.parse()) self.fd.close() self.loaded=True def isLoaded(self): return self.loaded if __name__=="__main__": #path="/home/fanch/Documents/tabs/Johann Pachelbel - Canon In D (ver 6 by Ezechiel).mid" path="/home/fanch/Documents/tabs/Misc Traditional - Katyusha.mid" m=MidiFile(path) m.parse()