qambi
Version:
MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio
244 lines (198 loc) • 7.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.update = update;
exports._update = _update;
var _parse_events = require('./parse_events');
var _util = require('./util');
var _constants = require('./constants');
var _position = require('./position');
var _midi_event = require('./midi_event');
var _eventlistener = require('./eventlistener');
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } // called by song
function update() {
if (this.playing === false) {
_update.call(this);
} else {
this._performUpdate = true;
}
}
function _update() {
var _this = this;
if (this._updateTimeEvents === false && this._removedTracks.length === 0 && this._removedEvents.length === 0 && this._newEvents.length === 0 && this._movedEvents.length === 0 && this._newParts.length === 0 && this._removedParts.length === 0 && this._resized === false) {
return;
}
//debug
//this.isPlaying = true
//console.groupCollapsed('update song')
console.time('updating song took');
// TIME EVENTS
// check if time events are updated
if (this._updateTimeEvents === true) {
//console.log('updateTimeEvents', this._timeEvents.length)
(0, _parse_events.parseTimeEvents)(this, this._timeEvents, this.isPlaying);
//console.log('time events %O', this._timeEvents)
}
// only parse new and moved events
var tobeParsed = [];
// but parse all events if the time events have been updated
if (this._updateTimeEvents === true) {
tobeParsed = [].concat(_toConsumableArray(this._events));
}
// TRACKS
// removed tracks
if (this._removedTracks.length > 0) {
this._removedTracks.forEach(function (track) {
_this._tracksById.delete(track.id);
track.removeParts(track.getParts());
track._song = null;
track._gainNode.disconnect();
track._songGainNode = null;
});
}
// PARTS
// removed parts
//console.log('removed parts %O', this._changedParts)
if (this._removedParts.length > 0) {
this._removedParts.forEach(function (part) {
_this._partsById.delete(part.id);
});
this._parts = Array.from(this._partsById.values());
}
// add new parts
//console.log('new parts %O', this._newParts)
this._newParts.forEach(function (part) {
part._song = _this;
_this._partsById.set(part.id, part);
part.update();
});
// update changed parts
//console.log('changed parts %O', this._changedParts)
this._changedParts.forEach(function (part) {
part.update();
});
// EVENTS
// filter removed events
//console.log('removed events %O', this._removedEvents)
this._removedEvents.forEach(function (event) {
var track = event.midiNote._track;
// unschedule all removed events that already have been scheduled
if (event.time >= _this._currentMillis) {
track.unschedule(event);
}
_this._notesById.delete(event.midiNote.id);
_this._eventsById.delete(event.id);
});
// add new events
//console.log('new events %O', this._newEvents)
this._newEvents.forEach(function (event) {
_this._eventsById.set(event.id, event);
_this._events.push(event);
tobeParsed.push(event);
});
// moved events need to be parsed
//console.log('moved %O', this._movedEvents)
this._movedEvents.forEach(function (event) {
// don't add moved events if the time events have been updated -> they have already been added to the tobeParsed array
if (_this._updateTimeEvents === false) {
tobeParsed.push(event);
}
});
// parse all new and moved events
if (tobeParsed.length > 0) {
//console.time('parse')
//console.log('tobeParsed %O', tobeParsed)
//console.log('parseEvents', tobeParsed.length)
tobeParsed = [].concat(_toConsumableArray(tobeParsed), _toConsumableArray(this._timeEvents));
(0, _parse_events.parseEvents)(tobeParsed, this.isPlaying);
// add MIDI notes to song
tobeParsed.forEach(function (event) {
//console.log(event.id, event.type, event.midiNote)
if (event.type === _constants.MIDIEventTypes.NOTE_ON) {
if (event.midiNote) {
_this._notesById.set(event.midiNoteId, event.midiNote);
//console.log(event.midiNoteId, event.type)
//this._notes.push(event.midiNote)
}
}
});
//console.timeEnd('parse')
}
if (tobeParsed.length > 0 || this._removedEvents.length > 0) {
//console.time('to array')
this._events = Array.from(this._eventsById.values());
this._notes = Array.from(this._notesById.values());
//console.timeEnd('to array')
}
//console.time(`sorting ${this._events.length} events`)
(0, _util.sortEvents)(this._events);
this._notes.sort(function (a, b) {
return a.noteOn.ticks - b.noteOn.ticks;
});
//console.timeEnd(`sorting ${this._events.length} events`)
//console.log('notes %O', this._notes)
console.timeEnd('updating song took');
// SONG DURATION
// get the last event of this song
var lastEvent = this._events[this._events.length - 1];
var lastTimeEvent = this._timeEvents[this._timeEvents.length - 1];
//console.log(lastEvent, lastTimeEvent)
// check if song has already any events
if (lastEvent instanceof _midi_event.MIDIEvent === false) {
lastEvent = lastTimeEvent;
} else if (lastTimeEvent.ticks > lastEvent.ticks) {
lastEvent = lastTimeEvent;
}
//console.log(lastEvent, this.bars)
// get the position data of the first beat in the bar after the last bar
this.bars = Math.max(lastEvent.bar, this.bars);
var ticks = (0, _position.calculatePosition)(this, {
type: 'barsbeats',
target: [this.bars + 1],
result: 'ticks'
}).ticks;
// we want to put the END_OF_TRACK event at the very last tick of the last bar, so we calculate that position
var millis = (0, _position.calculatePosition)(this, {
type: 'ticks',
target: ticks - 1,
result: 'millis'
}).millis;
this._lastEvent.ticks = ticks - 1;
this._lastEvent.millis = millis;
//console.log('length', this._lastEvent.ticks, this._lastEvent.millis, this.bars)
this._durationTicks = this._lastEvent.ticks;
this._durationMillis = this._lastEvent.millis;
// METRONOME
// add metronome events
if (this._updateMetronomeEvents || this._metronome.bars !== this.bars || this._updateTimeEvents === true) {
this._metronomeEvents = (0, _parse_events.parseEvents)([].concat(_toConsumableArray(this._timeEvents), _toConsumableArray(this._metronome.getEvents())));
}
this._allEvents = [].concat(_toConsumableArray(this._metronomeEvents), _toConsumableArray(this._events));
(0, _util.sortEvents)(this._allEvents);
//console.log('all events %O', this._allEvents)
/*
this._metronome.getEvents()
this._allEvents = [...this._events]
sortEvents(this._allEvents)
*/
//console.log('current millis', this._currentMillis)
this._playhead.updateSong();
this._scheduler.updateSong();
if (this.playing === false) {
this._playhead.set('millis', this._currentMillis);
(0, _eventlistener.dispatchEvent)({
type: 'position',
data: this._playhead.get().position
});
}
// reset
this._newParts = [];
this._removedParts = [];
this._newEvents = [];
this._movedEvents = [];
this._removedEvents = [];
this._resized = false;
this._updateTimeEvents = false;
//console.groupEnd('update song')
}