#!/usr/bin/python from .midiparser import MidiFile, MidiType from .midiio import MidiOutputPort from .options import* from .midieventtrigger import* import time def i2str(s, n): prefix="" nstr=str(s) for i in range(n-len(nstr)): prefix+=" " return prefix+nstr def str2str(s, n): prefix="" for i in range(n-len(s)): prefix+=" " return prefix+s class _Event: def __init__(self, track, data): t, evt=data self.track=track self.tick=t self.type=evt.getType() self.evt=evt if self.type==MidiType.NOTE_OFF or self.type==MidiType.NOTE_ON or self.type==MidiType.KEY_PRESSURE or \ self.type==MidiType.CONTROL_CHANGE or self.type==MidiType.PROGRAM_CHANGE or self.type==MidiType.CHANNEL_PRESSURE or\ self.type==MidiType.PITCH_WHEEL_CHANGE: self.channel=evt.channel else: self.channel=-1 if self.type==MidiType.NOTE_OFF or self.type==MidiType.NOTE_ON or self.type==MidiType.KEY_PRESSURE or \ self.type==MidiType.CONTROL_CHANGE or self.type==MidiType.PROGRAM_CHANGE: self.key=evt.key else: self.key=-1 def copy(self): return _Event(self.track, (self.tick, self.evt.copy())) def hasChannel(self): return self.channel>=0 def hasKey(self): return self.key>=0 def transpose(self, n): x=self.copy() if self.hasKey() : x.evt.transpose(n) x.key=self.evt.key return x """ Retourne un evenement transposé """ def __getitem__(self,key): if isinstance(key, tuple): return self.getButton(key[0], key[1]) else: return self.getButton(key) def __lt__(self, e): self.tick < e.tick def __le__(self, e): self.tick <= e.tick def __lt__(self, e): self.tick > e.tick def __le__(self, e): self.tick >= e.tick def __str__(self): x=i2str(self.tick, 8)+" : "+str(self.track)+" "+str2str(type(self.evt).__name__, 16) if self.hasChannel(): x+=" "+i2str(self.channel,2) if self.hasKey(): x+=" "+i2str(self.key,2) return x; def sortTime(val): return val.tick """ 'port_in_params': { 'client_name' : 'MidiPlayer', 'port_name' : 'Pad In' }, 'port_in' : None, 'port_out_params': { 'client_name' : 'MidiPlayer', 'port_name' : 'Pad Out' }, 'port_out' : None, """ class MidiPlayer: _DEFAULT_PARAMS={ 'port_sound_out': { 'client_name' : 'MidiPlayer', 'port_name' : 'Sound Out' }, 'transpose': 0, 'bpm' : 120, 'bpm_ratio': 1.0 } def __init__(self, path, params=_DEFAULT_PARAMS): if isinstance(path, str): self.file=MidiFile(path) else: self.file=path self.file.parse() self.header=self.file.header self.tracks=self.file.tracks self.nSoundTracks=self.header.tracks if self.header else 1 self.div = self.file.header.division if self.header else 960 self.nTotalTracks=len(self.file.tracks) self.trackToPlay=list(range(self.nSoundTracks)) param=initParams(MidiPlayer._DEFAULT_PARAMS, params) self.port=MidiOutputPort.fromParams(param['port_sound_out']) self.port.open() self.transposeNote=param['transpose'] self.bpm=param['bpm'] self.ratio=param['bpm_ratio'] self.events=[] def _load_track_event(self): for track in range(len(self.tracks)): for evt in self.tracks[track]: self.events.append(_Event(track, evt)) self.events.sort(key=sortTime) def tick2float(self, tick): return tick*1.0/self.div def float2tick(self, f): return f*self.div def tickBlanche(self): return int(self.float2tick(2)) def tickNoire(self): return int(self.float2tick(1)) def tickCroche(self): return int(self.float2tick(0.5)) def tickDoubleCroche(self): return int(self.float2tick(0.25)) def setBpmRatio(self, f): self.ratio=f def tick2time(self, tick): return tick*60.0/self.bpm/self.div/self.ratio def transpose(self, n): self.transposeNote+=n #for e in self.events: # e.transpose(n) def waitUpTo(self, tick): if tick==0: return t=self.tick2time(tick-self.tick) time.sleep(t) self.tick=tick def waitForEvent(self, evt): tick=evt.tick DEBUG("waiting "+str(tick-self.tick)+" ticks for: ",evt) if tick<=0: return t=self.tick2time(tick-self.tick) time.sleep(t) self.tick=tick def __initPlay(self): self.events=[] self.tick=0 self.startTime=time.time() self._load_track_event() def send(self, evt) : if not (evt.track in self.trackToPlay): return if evt.type==MidiType.NOTE_ON: self.port.send(evt.evt.bytes()) elif evt.type==MidiType.NOTE_OFF: self.port.send(evt.evt) def play(self): self.__initPlay() for evt in self.events: evt=evt.transpose(self.transposeNote) self.waitForEvent(evt) self.send(evt) 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=MidiPlayer(path) m.setBpmRatio(0.1) #m.transpose(24) m.open("Player") input("Press Enter to continue...") #m.connect((28,0)) m.play()