123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- #!/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()
|