midifile.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. class to parse the .mid file format
  3. (depends on stream.js)
  4. */
  5. function MidiFile(data) {
  6. function readChunk(stream) {
  7. var id = stream.read(4);
  8. var length = stream.readInt32();
  9. return {
  10. 'id': id,
  11. 'length': length,
  12. 'data': stream.read(length)
  13. };
  14. }
  15. var lastEventTypeByte;
  16. function readEvent(stream) {
  17. var event = {};
  18. event.deltaTime = stream.readVarInt();
  19. var eventTypeByte = stream.readInt8();
  20. if ((eventTypeByte & 0xf0) == 0xf0) {
  21. /* system / meta event */
  22. if (eventTypeByte == 0xff) {
  23. /* meta event */
  24. event.type = 'meta';
  25. var subtypeByte = stream.readInt8();
  26. var length = stream.readVarInt();
  27. switch(subtypeByte) {
  28. case 0x00:
  29. event.subtype = 'sequenceNumber';
  30. if (length != 2) throw "Expected length for sequenceNumber event is 2, got " + length;
  31. event.number = stream.readInt16();
  32. return event;
  33. case 0x01:
  34. event.subtype = 'text';
  35. event.text = stream.read(length);
  36. return event;
  37. case 0x02:
  38. event.subtype = 'copyrightNotice';
  39. event.text = stream.read(length);
  40. return event;
  41. case 0x03:
  42. event.subtype = 'trackName';
  43. event.text = stream.read(length);
  44. return event;
  45. case 0x04:
  46. event.subtype = 'instrumentName';
  47. event.text = stream.read(length);
  48. return event;
  49. case 0x05:
  50. event.subtype = 'lyrics';
  51. event.text = stream.read(length);
  52. return event;
  53. case 0x06:
  54. event.subtype = 'marker';
  55. event.text = stream.read(length);
  56. return event;
  57. case 0x07:
  58. event.subtype = 'cuePoint';
  59. event.text = stream.read(length);
  60. return event;
  61. case 0x20:
  62. event.subtype = 'midiChannelPrefix';
  63. if (length != 1) throw "Expected length for midiChannelPrefix event is 1, got " + length;
  64. event.channel = stream.readInt8();
  65. return event;
  66. case 0x2f:
  67. event.subtype = 'endOfTrack';
  68. if (length != 0) throw "Expected length for endOfTrack event is 0, got " + length;
  69. return event;
  70. case 0x51:
  71. event.subtype = 'setTempo';
  72. if (length != 3) throw "Expected length for setTempo event is 3, got " + length;
  73. event.microsecondsPerBeat = (
  74. (stream.readInt8() << 16)
  75. + (stream.readInt8() << 8)
  76. + stream.readInt8()
  77. )
  78. return event;
  79. case 0x54:
  80. event.subtype = 'smpteOffset';
  81. if (length != 5) throw "Expected length for smpteOffset event is 5, got " + length;
  82. var hourByte = stream.readInt8();
  83. event.frameRate = {
  84. 0x00: 24, 0x20: 25, 0x40: 29, 0x60: 30
  85. }[hourByte & 0x60];
  86. event.hour = hourByte & 0x1f;
  87. event.min = stream.readInt8();
  88. event.sec = stream.readInt8();
  89. event.frame = stream.readInt8();
  90. event.subframe = stream.readInt8();
  91. return event;
  92. case 0x58:
  93. event.subtype = 'timeSignature';
  94. if (length != 4) throw "Expected length for timeSignature event is 4, got " + length;
  95. event.numerator = stream.readInt8();
  96. event.denominator = Math.pow(2, stream.readInt8());
  97. event.metronome = stream.readInt8();
  98. event.thirtyseconds = stream.readInt8();
  99. return event;
  100. case 0x59:
  101. event.subtype = 'keySignature';
  102. if (length != 2) throw "Expected length for keySignature event is 2, got " + length;
  103. event.key = stream.readInt8(true);
  104. event.scale = stream.readInt8();
  105. return event;
  106. case 0x7f:
  107. event.subtype = 'sequencerSpecific';
  108. event.data = stream.read(length);
  109. return event;
  110. default:
  111. // console.log("Unrecognised meta event subtype: " + subtypeByte);
  112. event.subtype = 'unknown'
  113. event.data = stream.read(length);
  114. return event;
  115. }
  116. event.data = stream.read(length);
  117. return event;
  118. } else if (eventTypeByte == 0xf0) {
  119. event.type = 'sysEx';
  120. var length = stream.readVarInt();
  121. event.data = stream.read(length);
  122. return event;
  123. } else if (eventTypeByte == 0xf7) {
  124. event.type = 'dividedSysEx';
  125. var length = stream.readVarInt();
  126. event.data = stream.read(length);
  127. return event;
  128. } else {
  129. throw "Unrecognised MIDI event type byte: " + eventTypeByte;
  130. }
  131. } else {
  132. /* channel event */
  133. var param1;
  134. if ((eventTypeByte & 0x80) == 0) {
  135. /* running status - reuse lastEventTypeByte as the event type.
  136. eventTypeByte is actually the first parameter
  137. */
  138. param1 = eventTypeByte;
  139. eventTypeByte = lastEventTypeByte;
  140. } else {
  141. param1 = stream.readInt8();
  142. lastEventTypeByte = eventTypeByte;
  143. }
  144. var eventType = eventTypeByte >> 4;
  145. event.channel = eventTypeByte & 0x0f;
  146. event.type = 'channel';
  147. switch (eventType) {
  148. case 0x08:
  149. event.subtype = 'noteOff';
  150. event.noteNumber = param1;
  151. event.velocity = stream.readInt8();
  152. return event;
  153. case 0x09:
  154. event.noteNumber = param1;
  155. event.velocity = stream.readInt8();
  156. if (event.velocity == 0) {
  157. event.subtype = 'noteOff';
  158. } else {
  159. event.subtype = 'noteOn';
  160. }
  161. return event;
  162. case 0x0a:
  163. event.subtype = 'noteAftertouch';
  164. event.noteNumber = param1;
  165. event.amount = stream.readInt8();
  166. return event;
  167. case 0x0b:
  168. event.subtype = 'controller';
  169. event.controllerType = param1;
  170. event.value = stream.readInt8();
  171. return event;
  172. case 0x0c:
  173. event.subtype = 'programChange';
  174. event.programNumber = param1;
  175. return event;
  176. case 0x0d:
  177. event.subtype = 'channelAftertouch';
  178. event.amount = param1;
  179. return event;
  180. case 0x0e:
  181. event.subtype = 'pitchBend';
  182. event.value = param1 + (stream.readInt8() << 7);
  183. return event;
  184. default:
  185. throw "Unrecognised MIDI event type: " + eventType
  186. /*
  187. console.log("Unrecognised MIDI event type: " + eventType);
  188. stream.readInt8();
  189. event.subtype = 'unknown';
  190. return event;
  191. */
  192. }
  193. }
  194. }
  195. stream = Stream(data);
  196. var headerChunk = readChunk(stream);
  197. if (headerChunk.id != 'MThd' || headerChunk.length != 6) {
  198. throw "Bad .mid file - header not found";
  199. }
  200. var headerStream = Stream(headerChunk.data);
  201. var formatType = headerStream.readInt16();
  202. var trackCount = headerStream.readInt16();
  203. var timeDivision = headerStream.readInt16();
  204. if (timeDivision & 0x8000) {
  205. throw "Expressing time division in SMTPE frames is not supported yet"
  206. } else {
  207. ticksPerBeat = timeDivision;
  208. }
  209. var header = {
  210. 'formatType': formatType,
  211. 'trackCount': trackCount,
  212. 'ticksPerBeat': ticksPerBeat
  213. }
  214. var tracks = [];
  215. for (var i = 0; i < header.trackCount; i++) {
  216. tracks[i] = [];
  217. var trackChunk = readChunk(stream);
  218. if (trackChunk.id != 'MTrk') {
  219. throw "Unexpected chunk - expected MTrk, got "+ trackChunk.id;
  220. }
  221. var trackStream = Stream(trackChunk.data);
  222. while (!trackStream.eof()) {
  223. var event = readEvent(trackStream);
  224. tracks[i].push(event);
  225. //console.log(event);
  226. }
  227. }
  228. return {
  229. 'header': header,
  230. 'tracks': tracks
  231. }
  232. }