midiplayer.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #!/usr/bin/python
  2. from .midiparser import MidiFile, MidiType
  3. from .midiio import MidiOutputPort
  4. from .options import*
  5. from .midieventtrigger import*
  6. import time
  7. def i2str(s, n):
  8. prefix=""
  9. nstr=str(s)
  10. for i in range(n-len(nstr)):
  11. prefix+=" "
  12. return prefix+nstr
  13. def str2str(s, n):
  14. prefix=""
  15. for i in range(n-len(s)):
  16. prefix+=" "
  17. return prefix+s
  18. class _Event:
  19. def __init__(self, track, data):
  20. t, evt=data
  21. self.track=track
  22. self.tick=t
  23. self.type=evt.getType()
  24. self.evt=evt
  25. if self.type==MidiType.NOTE_OFF or self.type==MidiType.NOTE_ON or self.type==MidiType.KEY_PRESSURE or \
  26. self.type==MidiType.CONTROL_CHANGE or self.type==MidiType.PROGRAM_CHANGE or self.type==MidiType.CHANNEL_PRESSURE or\
  27. self.type==MidiType.PITCH_WHEEL_CHANGE:
  28. self.channel=evt.channel
  29. else:
  30. self.channel=-1
  31. if self.type==MidiType.NOTE_OFF or self.type==MidiType.NOTE_ON or self.type==MidiType.KEY_PRESSURE or \
  32. self.type==MidiType.CONTROL_CHANGE or self.type==MidiType.PROGRAM_CHANGE:
  33. self.key=evt.key
  34. else:
  35. self.key=-1
  36. def copy(self):
  37. return _Event(self.track, (self.tick, self.evt.copy()))
  38. def hasChannel(self):
  39. return self.channel>=0
  40. def hasKey(self):
  41. return self.key>=0
  42. def transpose(self, n):
  43. x=self.copy()
  44. if self.hasKey() :
  45. x.evt.transpose(n)
  46. x.key=self.evt.key
  47. return x
  48. """
  49. Retourne un evenement transposé
  50. """
  51. def __getitem__(self,key):
  52. if isinstance(key, tuple):
  53. return self.getButton(key[0], key[1])
  54. else:
  55. return self.getButton(key)
  56. def __lt__(self, e):
  57. self.tick < e.tick
  58. def __le__(self, e):
  59. self.tick <= e.tick
  60. def __lt__(self, e):
  61. self.tick > e.tick
  62. def __le__(self, e):
  63. self.tick >= e.tick
  64. def __str__(self):
  65. x=i2str(self.tick, 8)+" : "+str(self.track)+" "+str2str(type(self.evt).__name__, 16)
  66. if self.hasChannel(): x+=" "+i2str(self.channel,2)
  67. if self.hasKey(): x+=" "+i2str(self.key,2)
  68. return x;
  69. def sortTime(val):
  70. return val.tick
  71. """
  72. 'port_in_params': {
  73. 'client_name' : 'MidiPlayer',
  74. 'port_name' : 'Pad In'
  75. },
  76. 'port_in' : None,
  77. 'port_out_params': {
  78. 'client_name' : 'MidiPlayer',
  79. 'port_name' : 'Pad Out'
  80. },
  81. 'port_out' : None,
  82. """
  83. class MidiPlayer:
  84. _DEFAULT_PARAMS={
  85. 'port_sound_out': {
  86. 'client_name' : 'MidiPlayer',
  87. 'port_name' : 'Sound Out'
  88. },
  89. 'transpose': 0,
  90. 'bpm' : 120,
  91. 'bpm_ratio': 1.0
  92. }
  93. def __init__(self, path, params=_DEFAULT_PARAMS):
  94. if isinstance(path, str):
  95. self.file=MidiFile(path)
  96. else:
  97. self.file=path
  98. self.file.parse()
  99. self.header=self.file.header
  100. self.tracks=self.file.tracks
  101. self.nSoundTracks=self.header.tracks if self.header else 1
  102. self.div = self.file.header.division if self.header else 960
  103. self.nTotalTracks=len(self.file.tracks)
  104. self.trackToPlay=list(range(self.nSoundTracks))
  105. param=initParams(MidiPlayer._DEFAULT_PARAMS, params)
  106. self.port=MidiOutputPort.fromParams(param['port_sound_out'])
  107. self.port.open()
  108. self.transposeNote=param['transpose']
  109. self.bpm=param['bpm']
  110. self.ratio=param['bpm_ratio']
  111. self.events=[]
  112. def _load_track_event(self):
  113. for track in range(len(self.tracks)):
  114. for evt in self.tracks[track]:
  115. self.events.append(_Event(track, evt))
  116. self.events.sort(key=sortTime)
  117. def tick2float(self, tick):
  118. return tick*1.0/self.div
  119. def float2tick(self, f):
  120. return f*self.div
  121. def tickBlanche(self): return int(self.float2tick(2))
  122. def tickNoire(self): return int(self.float2tick(1))
  123. def tickCroche(self): return int(self.float2tick(0.5))
  124. def tickDoubleCroche(self): return int(self.float2tick(0.25))
  125. def setBpmRatio(self, f):
  126. self.ratio=f
  127. def tick2time(self, tick):
  128. return tick*60.0/self.bpm/self.div/self.ratio
  129. def transpose(self, n):
  130. self.transposeNote+=n
  131. #for e in self.events:
  132. # e.transpose(n)
  133. def waitUpTo(self, tick):
  134. if tick==0: return
  135. t=self.tick2time(tick-self.tick)
  136. time.sleep(t)
  137. self.tick=tick
  138. def waitForEvent(self, evt):
  139. tick=evt.tick
  140. DEBUG("waiting "+str(tick-self.tick)+" ticks for: ",evt)
  141. if tick<=0: return
  142. t=self.tick2time(tick-self.tick)
  143. time.sleep(t)
  144. self.tick=tick
  145. def __initPlay(self):
  146. self.events=[]
  147. self.tick=0
  148. self.startTime=time.time()
  149. self._load_track_event()
  150. def send(self, evt) :
  151. if not (evt.track in self.trackToPlay): return
  152. if evt.type==MidiType.NOTE_ON:
  153. self.port.send(evt.evt.bytes())
  154. elif evt.type==MidiType.NOTE_OFF:
  155. self.port.send(evt.evt)
  156. def play(self):
  157. self.__initPlay()
  158. for evt in self.events:
  159. evt=evt.transpose(self.transposeNote)
  160. self.waitForEvent(evt)
  161. self.send(evt)
  162. if __name__=="__main__":
  163. path="/home/fanch/Documents/tabs/Johann Pachelbel - Canon In D (ver 6 by Ezechiel).mid"
  164. #path="/home/fanch/Documents/tabs/Misc Traditional - Katyusha.mid"
  165. m=MidiPlayer(path)
  166. m.setBpmRatio(0.1)
  167. #m.transpose(24)
  168. m.open("Player")
  169. input("Press Enter to continue...")
  170. #m.connect((28,0))
  171. m.play()