var clone = function (o) { if (typeof o != 'object') return (o); if (o == null) return (o); var ret = (typeof o.length == 'number') ? [] : {}; for (var key in o) ret[key] = clone(o[key]); return ret; }; function Replayer(midiFile, timeWarp, eventProcessor) { var trackStates = []; var beatsPerMinute = 120; var ticksPerBeat = midiFile.header.ticksPerBeat; for (var i = 0; i < midiFile.tracks.length; i++) { trackStates[i] = { 'nextEventIndex': 0, 'ticksToNextEvent': ( midiFile.tracks[i].length ? midiFile.tracks[i][0].deltaTime : null ) }; } var nextEventInfo; var samplesToNextEvent = 0; function getNextEvent() { var ticksToNextEvent = null; var nextEventTrack = null; var nextEventIndex = null; for (var i = 0; i < trackStates.length; i++) { if ( trackStates[i].ticksToNextEvent != null && (ticksToNextEvent == null || trackStates[i].ticksToNextEvent < ticksToNextEvent) ) { ticksToNextEvent = trackStates[i].ticksToNextEvent; nextEventTrack = i; nextEventIndex = trackStates[i].nextEventIndex; } } if (nextEventTrack != null) { /* consume event from that track */ var nextEvent = midiFile.tracks[nextEventTrack][nextEventIndex]; if (midiFile.tracks[nextEventTrack][nextEventIndex + 1]) { trackStates[nextEventTrack].ticksToNextEvent += midiFile.tracks[nextEventTrack][nextEventIndex + 1].deltaTime; } else { trackStates[nextEventTrack].ticksToNextEvent = null; } trackStates[nextEventTrack].nextEventIndex += 1; /* advance timings on all tracks by ticksToNextEvent */ for (var i = 0; i < trackStates.length; i++) { if (trackStates[i].ticksToNextEvent != null) { trackStates[i].ticksToNextEvent -= ticksToNextEvent } } return { "ticksToEvent": ticksToNextEvent, "event": nextEvent, "track": nextEventTrack } } else { return null; } }; // var midiEvent; var temporal = []; // function processEvents() { function processNext() { if ( midiEvent.event.type == "meta" && midiEvent.event.subtype == "setTempo" ) { // tempo change events can occur anywhere in the middle and affect events that follow beatsPerMinute = 60000000 / midiEvent.event.microsecondsPerBeat; } if (midiEvent.ticksToEvent > 0) { var beatsToGenerate = midiEvent.ticksToEvent / ticksPerBeat; var secondsToGenerate = beatsToGenerate / (beatsPerMinute / 60); } var time = (secondsToGenerate * 1000 * timeWarp) || 0; temporal.push([ midiEvent, time]); midiEvent = getNextEvent(); }; // if (midiEvent = getNextEvent()) { while(midiEvent) processNext(true); } }; processEvents(); return { "getData": function() { return clone(temporal); } }; };