You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

549 lines
15KB

  1. #!/usr/bin/python2.5
  2. #
  3. # Copyright 2009 Olivier Gillet.
  4. #
  5. # Author: Olivier Gillet (ol.gillet@gmail.com)
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. # -----------------------------------------------------------------------------
  19. #
  20. # Midifile Writer and Reader.
  21. """Midifile writer.
  22. """
  23. import bisect
  24. import math
  25. import struct
  26. def PackInteger(value, size=4):
  27. """Packs a python integer into a n-byte big endian byte sequence."""
  28. return struct.pack('>%s' % {1: 'B', 2: 'H', 4: 'L'}[size], value)
  29. def UnpackInteger(value, size=4):
  30. """Packs a python integer into a n-byte big endian byte sequence."""
  31. return struct.unpack('>%s' % {1: 'B', 2: 'H', 4: 'L'}[size], value)[0]
  32. def PackVariableLengthInteger(value):
  33. """Packs a python integer into a variable length byte sequence."""
  34. if value == 0:
  35. return '\x00'
  36. s = value
  37. output = []
  38. while value:
  39. to_write = value & 0x7f
  40. value = value >> 7
  41. output.insert(0, to_write)
  42. for i in xrange(len(output) - 1):
  43. output[i] |= 0x80
  44. output = ''.join(map(chr, output))
  45. return output
  46. """Classes representing a MIDI event, with a Serialize method which encodes
  47. them into a string."""
  48. class Event(object):
  49. def __init__(self):
  50. pass
  51. def Serialize(self, running_status):
  52. raise NotImplementedError
  53. class MetaEvent(Event):
  54. def __init__(self, id, data):
  55. assert len(data) < 256
  56. self.id = id
  57. self.data = data
  58. def Serialize(self, running_status):
  59. return ''.join([
  60. '\xff',
  61. PackInteger(self.id, size=1),
  62. PackInteger(len(self.data), size=1),
  63. self.data]), None
  64. class TextEvent(MetaEvent):
  65. def __init__(self, text):
  66. self.text = text
  67. super(TextEvent, self).__init__(0x01, text)
  68. class CopyrightInfoEvent(MetaEvent):
  69. def __init__(self, text):
  70. self.text = text
  71. super(CopyrightInfoEvent, self).__init__(0x02, text)
  72. class TrackNameEvent(MetaEvent):
  73. def __init__(self, text):
  74. self.text = text
  75. super(TrackNameEvent, self).__init__(0x03, text)
  76. class TrackInstrumentNameEvent(MetaEvent):
  77. def __init__(self, text):
  78. self.text = text
  79. super(TrackInstrumentNameEvent, self).__init__(0x04, text)
  80. class LyricEvent(MetaEvent):
  81. def __init__(self, text):
  82. self.text = text
  83. super(LyricEvent, self).__init__(0x05, text)
  84. class MarkerEvent(MetaEvent):
  85. def __init__(self, text):
  86. self.text = text
  87. super(MarkerEvent, self).__init__(0x06, text)
  88. class CuePointEvent(MetaEvent):
  89. def __init__(self, text):
  90. self.text = text
  91. super(CuePointEvent, self).__init__(0x07, text)
  92. class EndOfTrackEvent(MetaEvent):
  93. def __init__(self):
  94. super(EndOfTrackEvent, self).__init__(0x2f, '')
  95. class TempoEvent(MetaEvent):
  96. def __init__(self, bpm):
  97. self.bpm = bpm
  98. value = 60000000.0 / bpm
  99. data = PackInteger(int(value), size=4)[1:]
  100. super(TempoEvent, self).__init__(0x51, data)
  101. class SMPTEOffsetEvent(MetaEvent):
  102. def __init__(self, h, m, s, f, sf):
  103. self.smpte_offset = (h, m, s, f, sf)
  104. data = ''.join(map(chr, [h, m, s, f, sf]))
  105. super(SMPTEOffsetEvent, self).__init__(0x54, data)
  106. class TimeSignatureEvent(MetaEvent):
  107. def __init__(self, numerator, denominator):
  108. self.numerator = numerator
  109. self.denominator = denominator
  110. data = ''.join([
  111. PackInteger(numerator, size=1),
  112. PackInteger(int(math.log(denominator) / math.log(2)), size=1),
  113. '\x16\x08'])
  114. super(TimeSignatureEvent, self).__init__(0x58, data)
  115. class KeyEvent(MetaEvent):
  116. def __init__(self, sharp_flats, major_minor):
  117. self.sharp_flats = sharp_flats
  118. self.major_minor = major_minor
  119. data = ''.join([
  120. PackInteger(sharp_flats, size=1),
  121. PackInteger(major_minor, size=1)])
  122. super(KeyEvent, self).__init__(0x59, data)
  123. class BlobEvent(MetaEvent):
  124. def __init__(self, blob):
  125. self.data = blob
  126. super(BlobEvent, self).__init__(0x7f, blob)
  127. """Classic channel-oriented messages."""
  128. class ChannelEvent(Event):
  129. def __init__(self, mask, channel, data):
  130. self.channel = channel
  131. self._status = mask | (channel - 1)
  132. self._data = PackInteger(self._status, size=1) + data
  133. def Serialize(self, running_status):
  134. if self._status == running_status:
  135. return self._data[1:], self._status
  136. else:
  137. return self._data, self._status
  138. class NoteOffEvent(ChannelEvent):
  139. def __init__(self, channel, note, velocity):
  140. data = PackInteger(note, size=1) + PackInteger(velocity, size=1)
  141. super(NoteOffEvent, self).__init__(0x80, channel, data)
  142. self.note = note
  143. self.velocity = velocity
  144. class NoteOnEvent(ChannelEvent):
  145. def __init__(self, channel, note, velocity):
  146. data = PackInteger(note, size=1) + PackInteger(velocity, size=1)
  147. super(NoteOnEvent, self).__init__(0x90, channel, data)
  148. self.note = note
  149. self.velocity = velocity
  150. class KeyAftertouchEvent(ChannelEvent):
  151. def __init__(self, channel, note, aftertouch):
  152. data = PackInteger(note, size=1) + PackInteger(aftertouch, size=1)
  153. super(KeyAftertouchEvent, self).__init__(0xa0, channel, data)
  154. self.note = note
  155. self.aftertouch = aftertouch
  156. class ControlChangeEvent(ChannelEvent):
  157. def __init__(self, channel, controller, value):
  158. data = PackInteger(controller, size=1) + PackInteger(value, size=1)
  159. super(ControlChangeEvent, self).__init__(0xb0, channel, data)
  160. self.controller = controller
  161. self.value = value
  162. class ProgramChangeEvent(ChannelEvent):
  163. def __init__(self, channel, program_number):
  164. data = PackInteger(program_number, size=1)
  165. super(ProgramChangeEvent, self).__init__(0xc0, channel, data)
  166. self.program_number = program_number
  167. class ChannelAftertouchEvent(ChannelEvent):
  168. def __init__(self, channel, aftertouch):
  169. data = PackInteger(aftertouch, size=1)
  170. super(ChannelAftertouchEvent, self).__init__(0xd0, channel, data)
  171. self.aftertouch = aftertouch
  172. class PitchBendEvent(ChannelEvent):
  173. def __init__(self, channel, pitch_bend_14_bits):
  174. data = PackInteger(pitch_bend_14_bits >> 7, size=1) + \
  175. PackInteger(pitch_bend_14_bits & 0x7f, size=1)
  176. super(PitchBendEvent, self).__init__(0xe0, channel, data)
  177. self.pitch_bend = pitch_bend_14_bits
  178. class SystemEvent(Event):
  179. def __init__(self, id):
  180. self._id = id
  181. def Serialize(self, running_status):
  182. return PackInteger(self._id, size=1), running_status
  183. class ClockEvent(SystemEvent):
  184. def __init__(self):
  185. super(ClockEvent, self).__init__(0xf8)
  186. class StartEvent(SystemEvent):
  187. def __init__(self):
  188. super(StartEvent, self).__init__(0xfa)
  189. class ContinueEvent(SystemEvent):
  190. def __init__(self):
  191. super(ContinueEvent, self).__init__(0xfb)
  192. class StopEvent(SystemEvent):
  193. def __init__(self):
  194. super(StopEvent, self).__init__(0xfc)
  195. # TODO(pichenettes): also support pauses within a block transmission (F7)
  196. class SysExEvent(Event):
  197. def __init__(self, manufacturer_id, device_id, data):
  198. self.data = data
  199. self.message = ''.join([
  200. manufacturer_id,
  201. device_id,
  202. data,
  203. '\xf7'])
  204. self.raw_message = '\xf0' + self.message
  205. assert all(ord(x) < 128 for x in self.message[:-1])
  206. self.message = ''.join([
  207. '\xf0',
  208. PackVariableLengthInteger(len(self.message)),
  209. self.message])
  210. def Serialize(self, running_status):
  211. return self.message, None
  212. def Nibblize(data, add_checksum=True):
  213. """Converts a byte string into a nibble string. Also adds checksum"""
  214. output = []
  215. if add_checksum:
  216. tail = [chr(sum(ord(char) for char in data) % 256)]
  217. else:
  218. tail = []
  219. for char in map(ord, list(data) + tail):
  220. output.append(chr(char >> 4))
  221. output.append(chr(char & 0x0f))
  222. return ''.join(output)
  223. class Track(object):
  224. def __init__(self):
  225. self._events = []
  226. def AddEvent(self, time, event):
  227. self._events.append((time, event))
  228. def Sort(self):
  229. self._events = sorted(self._events)
  230. def Serialize(self):
  231. self.Sort()
  232. last_time, last_event = self._events[-1]
  233. if type(last_event) != EndOfTrackEvent:
  234. self._events.append((last_time + 1, EndOfTrackEvent()))
  235. data = []
  236. current_time = 0
  237. running_status = None
  238. for time, event in self._events:
  239. delta = time - current_time
  240. data.append(PackVariableLengthInteger(delta))
  241. event_data, running_status = event.Serialize(running_status)
  242. data.append(event_data)
  243. current_time = time
  244. return ''.join(data)
  245. def Write(self, file_object):
  246. file_object.write('MTrk')
  247. track_data = self.Serialize()
  248. file_object.write(PackInteger(len(track_data)))
  249. file_object.write(track_data)
  250. @property
  251. def events(self):
  252. return self._events
  253. class Writer(object):
  254. def __init__(self, ppq=96):
  255. self._tracks = []
  256. self._ppq = ppq
  257. def AddTrack(self):
  258. new_track = Track()
  259. self._tracks.append(new_track)
  260. return new_track
  261. def _MergeTracks(self):
  262. new_track = Track()
  263. for track in self._tracks:
  264. for time_event in track.events:
  265. new_track.AddEvent(*time_event)
  266. new_track.Sort()
  267. return new_track
  268. def Write(self, file_object, format=0):
  269. tracks = self._tracks
  270. if format == 0:
  271. tracks = [self._MergeTracks()]
  272. # File header.
  273. file_object.write('MThd')
  274. file_object.write(PackInteger(6))
  275. file_object.write(PackInteger(format, size=2))
  276. if format == 0:
  277. file_object.write(PackInteger(1, size=2))
  278. else:
  279. file_object.write(PackInteger(len(self._tracks), size=2))
  280. file_object.write(PackInteger(self._ppq, size=2))
  281. # Tracks.
  282. for track in tracks:
  283. track.Write(file_object)
  284. class Reader(object):
  285. def __init__(self):
  286. self.tracks = []
  287. self.format = 0
  288. self.ppq = 96
  289. self._previous_status = 0
  290. def Read(self, f):
  291. assert f.read(4) == 'MThd'
  292. assert struct.unpack('>i', f.read(4))[0] == 6
  293. self.format = struct.unpack('>h', f.read(2))[0]
  294. assert self.format <= 2
  295. num_tracks = struct.unpack('>h', f.read(2))[0]
  296. self.ppq = struct.unpack('>h', f.read(2))[0]
  297. self._tempo_map = []
  298. for i in xrange(num_tracks):
  299. self.tracks.append(self._ReadTrack(f))
  300. self._CreateCumulativeTempoMap()
  301. def _ReadTrack(self, f):
  302. assert f.read(4) == 'MTrk'
  303. size = struct.unpack('>i', f.read(4))[0]
  304. t = 0
  305. events = []
  306. while size > 0:
  307. delta_t, e, event_size = self._ReadEvent(f)
  308. t += delta_t
  309. if e:
  310. events.append((t, e))
  311. if type(e) == TempoEvent:
  312. self._tempo_map.append((t, e.bpm))
  313. size -= event_size
  314. return events
  315. def _CreateCumulativeTempoMap(self):
  316. t = 0.0
  317. current_tempo = 120.0
  318. previous_beat = 0
  319. cumulative_tempo_map = [(0, 0.0, current_tempo)]
  320. for beat, tempo in sorted(self._tempo_map):
  321. beats = float(beat - previous_beat) / self.ppq
  322. t += beats * 60.0 / current_tempo
  323. cumulative_tempo_map.append((beat, t, tempo))
  324. current_tempo = tempo
  325. previous_beat = beat
  326. self._tempo_map = cumulative_tempo_map
  327. def AbsoluteTime(self, t):
  328. index = bisect.bisect_left(self._tempo_map, (t, 0, 0))
  329. index = max(index - 1, 0)
  330. start_beat, start_seconds, tempo = self._tempo_map[index]
  331. return start_seconds + float(t - start_beat) / self.ppq * 60.0 / tempo
  332. def _ReadVariableLengthInteger(self, f):
  333. v = 0
  334. size = 0
  335. while True:
  336. v <<= 7
  337. byte = UnpackInteger(f.read(1), size=1)
  338. size += 1
  339. v |= (byte & 0x7f)
  340. if not (byte & 0x80):
  341. break
  342. return v, size
  343. def _ReadEvent(self, f):
  344. delta_t, size = self._ReadVariableLengthInteger(f)
  345. event_byte = ord(f.read(1))
  346. size += 1
  347. if event_byte < 0x80:
  348. if self._previous_status:
  349. f.seek(f.tell() - 1)
  350. size -= 1
  351. event_byte = self._previous_status
  352. else:
  353. return delta_t, None, size
  354. event_type = event_byte & 0xf0
  355. channel = event_byte & 0xf
  356. channel += 1
  357. if event_type == 0x80:
  358. self._previous_status = event_type
  359. note = ord(f.read(1))
  360. velo = ord(f.read(1))
  361. event = NoteOffEvent(channel, note, velo)
  362. size += 2
  363. elif event_type == 0x90:
  364. self._previous_status = event_type
  365. event = NoteOnEvent(channel, ord(f.read(1)), ord(f.read(1)))
  366. size += 2
  367. elif event_type == 0xa0:
  368. self._previous_status = event_type
  369. event = KeyAftertouchEvent(channel, ord(f.read(1)), ord(f.read(1)))
  370. size += 2
  371. elif event_type == 0xb0:
  372. self._previous_status = event_type
  373. event = ControlChangeEvent(channel, ord(f.read(1)), ord(f.read(1)))
  374. size += 2
  375. elif event_type == 0xc0:
  376. self._previous_status = event_type
  377. event = ProgramChangeEvent(channel, ord(f.read(1)))
  378. size += 1
  379. elif event_type == 0xd0:
  380. self._previous_status = event_type
  381. event = ChannelAftertouchEvent(channel, ord(f.read(1)))
  382. size += 1
  383. elif event_type == 0xe0:
  384. self._previous_status = event_type
  385. event = PitchBendEvent(channel, (ord(f.read(1)) << 7) | ord(f.read(1)))
  386. size += 2
  387. elif event_byte == 0xff:
  388. event_type = ord(f.read(1))
  389. size += 1
  390. event_size, event_size_size = self._ReadVariableLengthInteger(f)
  391. size += event_size_size
  392. bytes = f.read(event_size)
  393. size += event_size
  394. if event_type == 0x01:
  395. event = TextEvent(bytes)
  396. elif event_type == 0x02:
  397. event = CopyrightEvent(bytes)
  398. elif event_type == 0x03:
  399. event = TrackNameEvent(bytes)
  400. elif event_type == 0x04:
  401. event = TrackInstrumentNameEvent(bytes)
  402. elif event_type == 0x05:
  403. event = LyricEvent(bytes)
  404. elif event_type == 0x06:
  405. event = MarkerEvent(bytes)
  406. elif event_type == 0x07:
  407. event = CuePointEvent(bytes)
  408. elif event_type == 0x20:
  409. current_channel = ord(bytes[0])
  410. event = None
  411. elif event_type == 0x2f:
  412. event = EndOfTrackEvent()
  413. elif event_type == 0x51:
  414. value = UnpackInteger('\x00' + bytes, size=4)
  415. event = TempoEvent(60000000.0 / value)
  416. elif event_type == 0x54:
  417. event = SMPTEOffsetEvent(*map(ord, bytes))
  418. elif event_type == 0x58:
  419. event = TimeSignatureEvent(ord(bytes[0]), 2 ** ord(bytes[1]))
  420. elif event_type == 0x59:
  421. event = KeyEvent(ord(bytes[0]), ord(bytes[1]))
  422. elif event_type == 0x7f:
  423. event = BlobEvent(bytes)
  424. elif event_byte == 0xf0:
  425. event_size, event_size_size = self._ReadVariableLengthInteger(f)
  426. size += event_size_size
  427. bytes = f.read(event_size)
  428. size += event_size
  429. event = SysExEvent(bytes[0:3], bytes[3:5], bytes[5:-1])
  430. else:
  431. print event_byte, '!!'
  432. event = None
  433. return delta_t, event, size
  434. if __name__ == '__main__':
  435. m = MidiFile()
  436. t = m.AddTrack()
  437. t.AddEvent(0, TempoEvent(120.0))
  438. t = m.AddTrack()
  439. t.AddEvent(1, SysExEvent(
  440. '\x00\x20\x77',
  441. '\x00\x01',
  442. '\x7f\x7f' + Nibblize('\xff\x00\xcc')))
  443. f = file('output.mid', 'wb')
  444. m.Write(f, format=0)
  445. f.close()