replayer.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. var clone = function (o) {
  2. if (typeof o != 'object') return (o);
  3. if (o == null) return (o);
  4. var ret = (typeof o.length == 'number') ? [] : {};
  5. for (var key in o) ret[key] = clone(o[key]);
  6. return ret;
  7. };
  8. function Replayer(midiFile, timeWarp, eventProcessor, bpm) {
  9. var trackStates = [];
  10. var beatsPerMinute = bpm ? bpm : 120;
  11. var bpmOverride = bpm ? true : false;
  12. var ticksPerBeat = midiFile.header.ticksPerBeat;
  13. for (var i = 0; i < midiFile.tracks.length; i++) {
  14. trackStates[i] = {
  15. 'nextEventIndex': 0,
  16. 'ticksToNextEvent': (
  17. midiFile.tracks[i].length ?
  18. midiFile.tracks[i][0].deltaTime :
  19. null
  20. )
  21. };
  22. }
  23. var nextEventInfo;
  24. var samplesToNextEvent = 0;
  25. function getNextEvent() {
  26. var ticksToNextEvent = null;
  27. var nextEventTrack = null;
  28. var nextEventIndex = null;
  29. for (var i = 0; i < trackStates.length; i++) {
  30. if (
  31. trackStates[i].ticksToNextEvent != null
  32. && (ticksToNextEvent == null || trackStates[i].ticksToNextEvent < ticksToNextEvent)
  33. ) {
  34. ticksToNextEvent = trackStates[i].ticksToNextEvent;
  35. nextEventTrack = i;
  36. nextEventIndex = trackStates[i].nextEventIndex;
  37. }
  38. }
  39. if (nextEventTrack != null) {
  40. /* consume event from that track */
  41. var nextEvent = midiFile.tracks[nextEventTrack][nextEventIndex];
  42. if (midiFile.tracks[nextEventTrack][nextEventIndex + 1]) {
  43. trackStates[nextEventTrack].ticksToNextEvent += midiFile.tracks[nextEventTrack][nextEventIndex + 1].deltaTime;
  44. } else {
  45. trackStates[nextEventTrack].ticksToNextEvent = null;
  46. }
  47. trackStates[nextEventTrack].nextEventIndex += 1;
  48. /* advance timings on all tracks by ticksToNextEvent */
  49. for (var i = 0; i < trackStates.length; i++) {
  50. if (trackStates[i].ticksToNextEvent != null) {
  51. trackStates[i].ticksToNextEvent -= ticksToNextEvent
  52. }
  53. }
  54. return {
  55. "ticksToEvent": ticksToNextEvent,
  56. "event": nextEvent,
  57. "track": nextEventTrack
  58. }
  59. } else {
  60. return null;
  61. }
  62. };
  63. //
  64. var midiEvent;
  65. var temporal = [];
  66. //
  67. function processEvents() {
  68. function processNext() {
  69. if (!bpmOverride && midiEvent.event.type == "meta" && midiEvent.event.subtype == "setTempo" ) {
  70. // tempo change events can occur anywhere in the middle and affect events that follow
  71. beatsPerMinute = 60000000 / midiEvent.event.microsecondsPerBeat;
  72. }
  73. ///
  74. var beatsToGenerate = 0;
  75. var secondsToGenerate = 0;
  76. if (midiEvent.ticksToEvent > 0) {
  77. beatsToGenerate = midiEvent.ticksToEvent / ticksPerBeat;
  78. secondsToGenerate = beatsToGenerate / (beatsPerMinute / 60);
  79. }
  80. ///
  81. var time = (secondsToGenerate * 1000 * timeWarp) || 0;
  82. temporal.push([ midiEvent, time]);
  83. midiEvent = getNextEvent();
  84. };
  85. ///
  86. if (midiEvent = getNextEvent()) {
  87. while(midiEvent) processNext(true);
  88. }
  89. };
  90. processEvents();
  91. return {
  92. "getData": function() {
  93. return clone(temporal);
  94. }
  95. };
  96. };