midiparser.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/usr/bin/python
  2. from .midimessage import *
  3. class MIDITrack:
  4. def __init__(self):
  5. pass
  6. class InputStreamWrapper:
  7. def __init__(self, fd, mode="rb"):
  8. if isinstance(fd, str):
  9. fd=open(fd, mode)
  10. self.fd=fd
  11. self.buffer=[]
  12. self.n=0
  13. self.offset=0
  14. self.doRecord=False
  15. def close(self):
  16. self.fd.close()
  17. def read(self, n=-1):
  18. x=self.fd.read(n)
  19. self.offset+=n
  20. if self.doRecord: self.buffer+=x
  21. return x
  22. def record(self, rec=True):
  23. self.doRecord=rec
  24. def littleEndian(x, n):
  25. b=bytes()
  26. for i in range(n):
  27. b+=bytes([(x >> (8 * (n - 1 - i))) & 0xff])
  28. return b
  29. class MIDIChunk:
  30. def __init__(self, fd):
  31. self.fd=fd
  32. self.magick=""
  33. self.length=0
  34. def parse(self):
  35. pass
  36. def print(self):
  37. pass
  38. def write(self, fd):
  39. pass
  40. def readMagick(self):
  41. return self.fd.read(4).decode('ascii')
  42. def reverseMsb(self, x):
  43. out=0
  44. n=len(x)
  45. for i in reversed(range(n)):
  46. out+= (x[i]<< (8*(n-1-i)))
  47. return out
  48. def intN(self, n, x=None):
  49. if x!=None:
  50. for i in range(n):
  51. self.fd.write(bytes([ (x>>(8*(n-1-i)) )& 0xff]))
  52. else: return self.reverseMsb(self.fd.read(n))
  53. def int4(self, x=None):
  54. self.intN(4,x)
  55. def int2(self, x=None):
  56. return self.intN(2,x)
  57. def byte(self, x=None):
  58. return self.intN(1,x)
  59. def skip(self, n=1):
  60. self.fd.read(n)
  61. class MIDIHeaderChunk(MIDIChunk):
  62. def __init__(self, fd=None):
  63. MIDIChunk.__init__(self,fd)
  64. self.format=0
  65. self.tracks=0
  66. self.division=0
  67. MIDIHeaderChunk.FORMAT_SINGLE_CHANNEL=0
  68. MIDIHeaderChunk.FORMAT_MULTIPLE_CHANNEL=1
  69. MIDIHeaderChunk.FORMAT_MULTIPLE_INDEPENDANT_CHANNEL=2
  70. self.variableTicks=False
  71. self.ticksPerQuarter=0 # beat per quarter note
  72. def write(self, fd):
  73. self.fd=fd
  74. self.fd.write("MThd".encode())
  75. self.int4(self.length)
  76. self.int2(self.format)
  77. self.int2(self.tracks)
  78. self.int2(self.division)
  79. def parse(self):
  80. f=self.fd
  81. self.magick=self.readMagick()
  82. if self.magick!="MThd":
  83. raise MIDIParserException("Bad header magick number")
  84. self.length=self.int4()
  85. self.format=self.int2()
  86. self.tracks=self.int2()
  87. self.division=self.int2()
  88. self.variableTicks=True if self.division & 0x8000 else False
  89. if not self.variableTicks:
  90. self.ticksPerQuarter= self.division & 0x7fff
  91. class MIDITrackChunk(MIDIChunk):
  92. def __init__(self, fd=None, skipTrack=False):
  93. MIDIChunk.__init__(self,fd)
  94. self.time=0
  95. self.events=[]
  96. self.skipTrack=skipTrack
  97. def readDeltaTime(self):
  98. out=[]
  99. while True:
  100. x=self.byte()
  101. out.append(x&0x7F)
  102. if not (x&0x80):
  103. break
  104. n=len(out)
  105. ret=0
  106. for i in range(n):
  107. j=n-i-1
  108. ret+= ( (out[i]) <<(j*7) )
  109. self.time+=ret
  110. return ret
  111. def write(self, fd):
  112. self.fd=fd
  113. self.fd.write("MTrk".encode())
  114. self.int4(self.length)
  115. b=bytes()
  116. count=0
  117. for evt in self.events:
  118. e=littleEndian(int(evt[0]*960),1)+bytes([0xff])+bytes(evt[1].bytes())
  119. count+=len(e)
  120. print(evt[1].bytes())
  121. print(bytes(evt[1].bytes()))
  122. count+=evt[0]
  123. b+=e
  124. print(b)
  125. b+=bytes([0])+bytes(EndOfTrack().bytes())
  126. print(b)
  127. self.fd.write(b)
  128. def addEvent(self, time, evt):
  129. self.events.append((time, evt))
  130. def addNote(self, time, note, channel=0):
  131. non= NoteOn(channel)
  132. non.key=note
  133. non.velocity=0x7f
  134. self.events.append((self.time,non))
  135. nof= NoteOff(channel)
  136. nof.key=note
  137. self.events.append((self.time+time*960,nof))
  138. self.time+=time*960
  139. print(time*960)
  140. def parse(self):
  141. f=self.fd
  142. self.magick=self.readMagick()
  143. if self.magick!="MTrk":
  144. raise MIDIParserException("Bad track magick number")
  145. self.length=self.int4()
  146. events=[]
  147. if self.skipTrack: self.fd.read(self.length)
  148. else:
  149. while True:
  150. delta=self.readDeltaTime()
  151. evt=self.readEvent()
  152. events.append((self.time, evt))
  153. if evt.isEndTrack():
  154. break
  155. self.events=events
  156. return events
  157. def readEvent(self):
  158. return MidiMessage.parse(self.fd)
  159. class MidiFile:
  160. def __init__(self, path=None):
  161. if path:
  162. self.path=path
  163. self.tracksCount=0
  164. self.header=None
  165. self.tracks=[]
  166. self.loaded=False
  167. self.events=[]
  168. def parse(self):
  169. self.fd=0
  170. try:
  171. self.fd=open(self.path, "rb")
  172. except Exception as e:
  173. ERR(type(e).__name__+" : "+str(e))
  174. return e
  175. self.header=MIDIHeaderChunk(self.fd)
  176. self.header.parse()
  177. for t in range(self.header.tracks):
  178. track=MIDITrackChunk(self.fd)
  179. self.tracks.append(track.parse())
  180. self.fd.close()
  181. self.loaded=True
  182. def isLoaded(self):
  183. return self.loaded
  184. if __name__=="__main__":
  185. #path="/home/fanch/Documents/tabs/Johann Pachelbel - Canon In D (ver 6 by Ezechiel).mid"
  186. path="/home/fanch/Documents/tabs/Misc Traditional - Katyusha.mid"
  187. m=MidiFile(path)
  188. m.parse()