123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- #!/usr/bin/python
- from .options import *
- import time
- import io
- class MIDIParserException(Exception):
- def __init__(self, message):
- super().__init__(message)
- def send_message(x, data):
- s="["
- for i in range(len(data)):
- if i>0: s+=", "
- s+=hex(data[i])
- MIDIDEBUG(s+"]")
- x.send_message(data)
- class MidiType:
- def __init__(self):
- MidiType.ERROR=0
- MidiType.NOTE_OFF=0x80
- MidiType.NOTE_ON=0x90
- MidiType.KEY_PRESSURE=0xA0
- MidiType.CONTROL_CHANGE=0xB0
- MidiType.PROGRAM_CHANGE=0xC0
- MidiType.CHANNEL_PRESSURE=0xD0
- MidiType.PITCH_WHEEL_CHANGE=0xE0
-
- MidiType.SYSTEM_EXCLUSIVE=0xF0
- MidiType.SONG_POSITION_POINTER=0xF2
- MidiType.SONG_SELECT=0xF2
- MidiType.TUNE_REQUEST=0xF6
- MidiType.END_OF_EXCUSIVE=0xF7 #not used
-
- MidiType.TIMING_CLOCK=0xF8 # a faire
- MidiType.START=0xFA
- MidiType.CONTINUE=0xFB
- MidiType.STOP=0xFC
- MidiType.ACTIVE_SENSING=0xFE
- MidiType.META=0xFF
- MidiType.RESET=0xFF
-
- MidiType.META_SEQ=0x00
- MidiType.META_TEXT=0x01
- MidiType.META_COPYRIGHT=0x02
- MidiType.META_TRACK_NAME=0x03
- MidiType.META_INSTRUMENT_NAME=0x04
- MidiType.META_LYRICS=0x05
- MidiType.META_TEXT_MARKER=0x06
- MidiType.META_CUE_POINT=0x07
-
- MidiType.META_CHANNEL_PREFIX=0x20
- MidiType.META_END_OF_TRACK=0x2F
- MidiType.META_SET_TEMPO=0x51
- MidiType.META_SMPTE_OFFSET=0x54
- MidiType.META_TIME_SIGNATURE=0x58
- MidiType.META_KEY_SIGNATURE=0x59
- MidiType.META_SPECIFIC=0x7F
-
-
-
- MidiType()
- class MidiMessage:
- def __init__(self, typ):
- self.type=typ
- self.__sleep=0.00001
- self.__doSleep=True
- self.key=-1
-
- def isEndTrack(self):
- return False
-
- def getType(self):
- if self.type>=0x80 and self.type<=0xE0:
- return self.type&0xf0
- return self.type
-
- def isTypeOf(self, x):
- return self.getType() == x
-
- def isMeta(self):
- return self.type>=0 and self.type<=0x58
-
- def isSysEx(self):
- return self.type>=0xF0 and self.type<=0xF7
-
- def write(self, oport):
- send_message(oport, self.bytes())
- #oport.send_message(self.bytes())
- if self.__doSleep:
- time.sleep(self.__sleep)
-
- def read(self, iport, n=1):
- if n>0:
- out=[]
- for i in range(n):
- out.append(self.byte())
- return out
-
- return iport.get_message()
-
- def syncRead(self, iport):
- x=None
- while True:
- x=self.read(iport)
- if x!=None:
- return x
-
- def __eq__(self, x):
-
- xx=x.bytes()
- ex=self.bytes()
-
- if len(xx) != len(ex): return False
- for i in len(xx):
- if xx[i] != ex[i]:
- return False
- return True
-
- def transpose(self, n):
- pass
-
- def copy(self):
- x=MidiMessage.parse(self.bytes())
- return x
-
- @staticmethod
- def byte(fd):
- vv=fd.read(1)
- if vv==None or len(vv)==0:
- raise MIDIParserException("End off file premature")
- return int(vv[0])
-
- @staticmethod
- def parse(fd):
- if isinstance(fd, (bytes, list)):
- b=bytes(fd)
- fd=io.BytesIO(b)
- return MidiMessage.parseStream(fd)
-
- @staticmethod
- def parseStream(fd):
- vv=fd.read(1)
- if vv==None:
- return MidiEndFile()
- first=int(vv[0])
-
- xa= first&0xf0
- xb= first&0x0f
-
- if xa==MidiType.NOTE_ON: return NoteOn(xb, fd)
- elif xa==MidiType.NOTE_OFF: return NoteOff(xb, fd)
- elif xa==MidiType.KEY_PRESSURE: return KeyPressure(xb, fd)
- elif xa==MidiType.CONTROL_CHANGE: return ControlChange(xb, fd)
- elif xa==MidiType.PROGRAM_CHANGE: return ProgramChange(xb, fd)
- elif xa==MidiType.CHANNEL_PRESSURE: return ChannelPressure(xb, fd)
- elif xa==MidiType.PITCH_WHEEL_CHANGE: return PitchWheelChange(xb, fd)
-
- elif first==MidiType.SYSTEM_EXCLUSIVE: return SystemExclusive(fd)
- elif first==MidiType.SONG_POSITION_POINTER: return SongPointerPosition(fd)
- elif first==MidiType.SONG_SELECT: return SongSelect(fd)
- elif first==MidiType.TUNE_REQUEST: return TuneRequest(fd)
-
- elif first==MidiType.TIMING_CLOCK: return TimingClock(fd)
- elif first==MidiType.START: return Start(fd)
- elif first==MidiType.CONTINUE: return Continue(fd)
- elif first==MidiType.STOP: return Stop(fd)
- elif first==MidiType.ACTIVE_SENSING: return ActiveSensing(fd)
- elif first==MidiType.META: return MidiMessage.parseMeta( fd)
- else: return MidiError(first)
-
- @staticmethod
- def parseMeta(fd):
- cmd=MidiMessage.byte(fd)
- if cmd==MidiType.META_SEQ: return SequenceNumber(fd)
- elif cmd==MidiType.META_TEXT: return TextEvent(fd)
- elif cmd==MidiType.META_COPYRIGHT: return Copyright(fd)
- elif cmd==MidiType.META_TRACK_NAME: return TrackName(fd)
- elif cmd==MidiType.META_INSTRUMENT_NAME: return InstrumentName(fd)
- elif cmd==MidiType.META_LYRICS: return TextLyrics(fd)
- elif cmd==MidiType.META_TEXT_MARKER: return TextMarker(fd)
- elif cmd==MidiType.META_CUE_POINT: return CuePoint(fd)
- elif cmd==MidiType.META_CHANNEL_PREFIX: return ChannelPrefix(fd)
- elif cmd==MidiType.META_END_OF_TRACK: return EndOfTrack(fd)
- elif cmd==MidiType.META_SET_TEMPO: return SetTempo(fd)
- elif cmd==MidiType.META_SMPTE_OFFSET: return SMPTEOffset(fd)
- elif cmd==MidiType.META_TIME_SIGNATURE: return TimeSignature(fd)
- elif cmd==MidiType.META_KEY_SIGNATURE: return KeySignature(fd)
- elif cmd==MidiType.META_SPECIFIC: return MetaSpecific(fd)
- else: return MidiError(cmd, fd.offset)
- """
- Voices Messages
- """
- class MidiVoiceMessage(MidiMessage):
- def __init__(self, typ, ch, fd=None):
- MidiMessage.__init__(self, typ)
- self.channel=ch+1
-
-
- class NoteOn(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.NOTE_ON, ch)
- if fd:
- self.key=self.byte(fd)
- self.velocity=self.byte(fd)
-
- def bytes(self):
- return [MidiType.NOTE_ON | (self.channel-1), self.key, self.velocity]
-
- @staticmethod
- def new(chan, key, vel):
- x=NoteOn(chan+-1)
- x.key=key
- x.velocity=vel
- return x
-
- def transpose(self, n):
- self.key+=n
- def __str__(self):
- return "NOTEON("+str(self.key)+", "+ str(self.channel) +")"
- class NoteOff(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.NOTE_OFF, ch)
- if fd:
- self.key=self.byte(fd)
- self.velocity=self.byte(fd)
-
- def bytes(self):
- return [MidiType.NOTE_OFF | (self.channel-1), self.key, self.velocity]
-
- @staticmethod
- def new( chan, key, vel):
- x=NoteOff(chan-1)
- x.key=key
- x.velocity=vel
- return x
-
- def transpose(self, n):
- self.key+=n
- class KeyPressure(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.KEY_PRESSURE, ch)
- if fd:
- self.key=self.byte(fd)
- self.pressure=self.byte(fd)
-
- def bytes(self):
- return [MidiType.KEY_PRESSURE | (self.channel-1), self.key, self.value]
-
- def new(self, chan, key, pressure):
- x=KeyPressure(chan-1)
- x.key=key
- x.pressure=pressure
- return x
-
- class ControlChange(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.CONTROL_CHANGE, ch)
- if fd:
- self.key=self.byte(fd)
- self.value=self.byte(fd)
-
- def bytes(self):
- return [MidiType.CONTROL_CHANGE | (self.channel-1), self.key, self.value]
-
- def new(self, chan, key, value):
- x=ControlChange(chan-1)
- x.key=key
- x.value=value
- return x
-
- class ProgramChange(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.PROGRAM_CHANGE, ch)
- if fd:
- self.key=self.byte(fd)
-
- def bytes(self):
- return [MidiType.PROGRAM_CHANGE | (self.channel-1), self.key]
-
- def new(self, chan, key):
- x=ProgramChange(chan-1)
- x.key=key
- return x
-
-
-
-
-
-
- class ChannelPressure(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.CHANNEL_PRESSURE, ch)
- if fd:
- self.pressure=self.byte(fd)
-
- def bytes(self):
- return [MidiType.CHANNEL_PRESSURE | (self.channel-1), self.key, self.pressure]
-
- def new(self, chan, pressure):
- x=ChannelPressure(chan-1)
- x.pressure=pressure
- return x
-
-
-
-
-
-
-
-
- class PitchWheelChange(MidiVoiceMessage):
- def __init__(self, ch, fd=None):
- MidiVoiceMessage.__init__(self, MidiType.PITCH_WHEEL_CHANGE, ch)
- if fd:
- self.pressure=self.byte(fd)
- self.pressure+=(self.byte(fd)<<7)
-
- def bytes(self):
- return [MidiType.PITCH_WHEEL_CHANGE | (self.channel-1),
- self.pressure & 0x7f, (self.pressure & 0x3f8)>>7]
-
- def new(self, chan, pressure):
- x=PitchWheelChange(chan-1)
- x.pressure=pressure
- return x
-
-
-
-
-
-
-
-
- """
- System Common Messages
- """
-
- class SystemExclusive(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.SYSTEM_EXCLUSIVE)
- if fd:
- self.data=[]
- while True:
- x=self.byte(fd)
- if x==MidiMessage.END_OF_EXCUSIVE:
- break
- else:
- self.data.append(x)
-
- def bytes(self):
- return [MidiType.SYSTEM_EXCLUSIVE]+self.data+[MidiType.END_OF_EXCUSIVE]
-
- def new(self, data):
- x=SystemExclusive(chan)
- x.data=data
- return x
-
-
-
-
-
-
-
- class SongPointerPosition(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.SONG_POSITION_POINTER)
- if fd:
- self.beats=self.byte(fd)
- self.beats+=(self.byte(fd)<<7)
-
- def bytes(self):
- return [MidiType.SONG_POSITION_POINTER, self.beats & 0x7f, (self.beats & 0x3f8)>>7]
-
- def new(self, beats):
- x=SongPointerPosition(chan)
- x.beats=beats
- return x
-
- class SongSelect(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.SONG_SELECT)
- if fd:
- self.song=fd.self.byte(fd)
-
- def bytes(self):
- return [MidiType.SONG_SELECT, self.song]
-
- def new(self, song):
- x=SongSelect(chan)
- x.song=song
- return x
-
-
-
-
-
-
-
-
- class TuneRequest(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.TUNE_REQUEST)
-
- def bytes(self):
- return [MidiType.TUNE_REQUEST]
-
- def new(self):
- return TuneRequest()
-
-
-
-
-
-
-
-
-
- """
- System RT Messages
- """
-
- class TimingClock(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.TimingClock)
-
- def bytes(self):
- return [MidiType.TIMING_CLOCK]
-
- def new(self):
- return TimingClock()
-
-
-
-
-
-
-
- class Start(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.START)
-
- def bytes(self):
- return [MidiType.START]
-
- def new(self):
- return Start()
-
-
-
-
-
-
-
-
-
- class Continue(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.CONTINUE)
-
- def bytes(self):
- return [MidiType.CONTINUE]
-
- def new(self):
- return Continue()
-
-
-
-
-
-
-
- class Stop(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.STOP)
-
- def bytes(self):
- return [MidiType.STOP]
-
- def new(self):
- return Stop()
-
-
-
-
-
-
- class ActiveSensing(MidiMessage):
- def __init__(self, fd=None):
- MidiMessage.__init__(self, MidiType.ACTIVE_SENSING)
-
- def bytes(self):
- return [MidiType.ACTIVE_SENSING]
-
- def new(self):
- return ActiveSensing()
-
-
-
-
-
-
-
- """
- Meta
- """
- class Meta(MidiMessage):
- def __init__(self, cmd):
- MidiMessage.__init__(self, MidiType.META)
- self.command=cmd
- class EndOfTrack(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_END_OF_TRACK)
- if fd:
- self.byte(fd)
-
- def isEndTrack(self):
- return True
-
- def bytes(self):
- return [MidiType.META, self.command, 0]
-
- def new(self):
- return EndOfTrack()
- class SequenceNumber(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_SEQ)
- if fd:
- self.number=self.byte(fd)
-
- def bytes(self):
- return [MidiType.META, self.command, 0x2]
-
- def new(self):
- return SequenceNumber()
-
-
-
-
-
-
-
- class TextEvent(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_TEXT)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=TextEvent()
- self.text=text
- return x
-
-
-
-
-
-
- class Copyright(Meta):
- def __init__(self,cmd, fd=None):
- Meta.__init__(self, MidiType.META_COPYRIGHT)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=Copyright()
- self.text=text
- return x
-
-
-
-
-
- class TrackName(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_TRACK_NAME)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=TrackName()
- self.text=text
- return x
-
-
-
-
-
-
- class InstrumentName(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_INSTRUMENT_NAME)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=InstrumentName()
- self.text=text
- return x
-
-
-
-
-
-
- class TextLyrics(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_LYRICS)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=TextLyrics()
- self.text=text
- return x
-
-
-
-
-
-
-
- class TextMarker(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_TEXT_MARKER)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=TextMarker()
- self.text=text
- return x
-
-
-
-
-
-
-
- class CuePoint(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_CUE_POINT)
- if fd:
- self.text=fd.read(self.byte(fd)).decode("utf-8")
-
- def bytes(self):
- return [MidiType.META, self.command,
- len(self.text)]+[e.encode("hex") for e in self.text]
-
- def new(self, text):
- x=CuePoint()
- self.text=text
- return x
-
-
-
-
-
-
-
-
- class ChannelPrefix(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_CHANNEL_PREFIX)
- if fd:
- self.byte(fd)
- self.channelPrefix=self.byte(fd)
-
- def bytes(self):
- return [MidiType.META, self.command, 1, self.channelPrefix]
-
- def new(self, channelPrefix):
- x=ChannelPrefix()
- self.channelPrefix=channelPrefix
- return x
-
-
-
-
-
-
-
- class SetTempo(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_SET_TEMPO)
- if fd:
- self.byte(fd)
- self.tempo=self.byte(fd)<<16
- self.tempo+=self.byte(fd)<<8
- self.tempo+=self.byte(fd)
-
- def bytes(self):
- x=self.tempo
- return [MidiType.META, self.command, 3, (x&0xFF0000)>>16,
- (x&0xFF00)>>8, x&0xFF ]
-
- def new(self, tempo):
- x=SetTempo()
- self.tempo=tempo
- return x
-
-
-
-
- class SMPTEOffset(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_SMPTE_OFFSET)
- if fd:
- self.byte(fd)
- self.hour=self.byte(fd)
- self.minutes=self.byte(fd)
- self.seconds=self.byte(fd)
- self.frame=self.byte(fd)
- self.fraction=self.byte(fd)
-
- def bytes(self):
- return [MidiType.META, self.command, 5, self.hour, self.minutes,
- self.seconds, self.frame, self.fraction]
-
- def new(self, hh, mm, ss, fr, ff):
- x=SMPTEOffset()
- x.hour=hh
- x.minutes=mm
- x.seconds=ss
- x.frame=fr
- x.fraction=ff
- return x
- class TimeSignature(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_TIME_SIGNATURE)
- if fd:
- self.byte(fd)
- self.numerator=self.byte(fd)
- self.denominator=self.byte(fd)
- self.clic=self.byte(fd)
- self.notes=self.byte(fd)
-
- def bytes(self):
- return [MidiType.META, self.command, 4, self.numerator, self.denominator,
- self.clic, self.notes]
-
- def new(self, numerator, denominator, clic, notes):
- x=TimeSignature()
- x.numerator=numerator
- x.denominator=denominator
- x.clic=clic
- x.notes=notes
- return x
-
- class KeySignature(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_KEY_SIGNATURE)
- if fd:
- self.byte(fd)
- self.sf=self.byte(fd)
- self.mi=self.byte(fd)
-
- def bytes(self):
- return [MidiType.META, self.command, 2, self.sf, self.mi]
-
- def new(self, sf, mi):
- x=KeySignature()
- self.sf=sf
- self.mi=mi
- return x
-
-
- class MetaSpecific(Meta):
- def __init__(self, fd=None):
- Meta.__init__(self, MidiType.META_SPECIFIC)
- if fd:
- self.length=self.byte(fd)
- self.data=self.read(fd, self.length)
-
- def bytes(self):
- return [MidiType.META, self.command, self.length, self.data]
-
- def new(self, data):
- x=KeySignature()
- self.data=data
- self.length=len(data)
- return x
- """
- Error
- """
- class MidiError(MidiMessage):
- def __init__(self, first, offset):
- MidiMessage.__init__(self, MidiType.ERROR)
- raise MIDIParserException("First Midi byte ("+hex(first)+") is unknown at: "+hex(offset))
-
|