qambi
Version:
MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio
336 lines (293 loc) • 11.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Playhead = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _position = require('./position.js');
var _eventlistener = require('./eventlistener.js');
var _util = require('./util.js');
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); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var range = 10; // milliseconds or ticks
var instanceIndex = 0;
var Playhead = exports.Playhead = function () {
function Playhead(song) {
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
_classCallCheck(this, Playhead);
this.id = this.constructor.name + '_' + instanceIndex++ + '_' + new Date().getTime();
this.song = song;
this.type = type;
this.lastEvent = null;
this.data = {};
this.activeParts = [];
this.activeNotes = [];
this.activeEvents = [];
}
// unit can be 'millis' or 'ticks'
_createClass(Playhead, [{
key: 'set',
value: function set(unit, value) {
this.unit = unit;
this.currentValue = value;
this.eventIndex = 0;
this.noteIndex = 0;
this.partIndex = 0;
this.calculate();
return this.data;
}
}, {
key: 'get',
value: function get() {
return this.data;
}
}, {
key: 'update',
value: function update(unit, diff) {
if (diff === 0) {
return this.data;
}
this.unit = unit;
this.currentValue += diff;
this.calculate();
return this.data;
}
}, {
key: 'updateSong',
value: function updateSong() {
this.events = [].concat(_toConsumableArray(this.song._events), _toConsumableArray(this.song._timeEvents));
(0, _util.sortEvents)(this.events);
//console.log('events %O', this.events)
this.notes = this.song._notes;
this.parts = this.song._parts;
this.numEvents = this.events.length;
this.numNotes = this.notes.length;
this.numParts = this.parts.length;
this.set('millis', this.song._currentMillis);
}
}, {
key: 'calculate',
value: function calculate() {
var i = void 0;
var value = void 0;
var event = void 0;
var note = void 0;
var part = void 0;
var position = void 0;
var stillActiveNotes = [];
var stillActiveParts = [];
var collectedParts = new Set();
var collectedNotes = new Set();
this.data = {};
this.activeEvents = [];
var sustainpedalEvents = [];
for (i = this.eventIndex; i < this.numEvents; i++) {
event = this.events[i];
value = event[this.unit];
if (value <= this.currentValue) {
// if the playhead is set to a position of say 3000 millis, we don't want to add events more that 10 units before the playhead
if (value === 0 || value > this.currentValue - range) {
this.activeEvents.push(event);
// this doesn't work too well
if (event.type === 176) {
//console.log(event.type, event.data1, event.data2)
if (event.data1 === 64) {
(0, _eventlistener.dispatchEvent)({
type: 'sustainpedal2',
data: event.data2 === 127 ? 'down' : 'up'
});
sustainpedalEvents.push(event);
}
// }else{
// dispatchEvent({
// type: 'event',
// data: event
// })
}
(0, _eventlistener.dispatchEvent)({
type: 'event',
data: event
});
}
this.lastEvent = event;
this.eventIndex++;
} else {
break;
}
}
// let num = sustainpedalEvents.length
// if(num > 0){
// console.log(this.currentValue, num, sustainpedalEvents[num - 1].data2, sustainpedalEvents)
// }
//console.log('-----------------')
this.data.activeEvents = this.activeEvents;
// if a song has no events yet, use the first time event as reference
if (this.lastEvent === null) {
this.lastEvent = this.song._timeEvents[0];
}
position = (0, _position.getPosition2)(this.song, this.unit, this.currentValue, 'all', this.lastEvent);
this.data.eventIndex = this.eventIndex;
this.data.millis = position.millis;
this.data.ticks = position.ticks;
this.data.position = position;
if (this.type.indexOf('all') !== -1) {
var data = this.data;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Object.keys(position)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var key = _step.value;
data[key] = position[key];
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
} else if (this.type.indexOf('barsbeats') !== -1) {
this.data.bar = position.bar;
this.data.beat = position.beat;
this.data.sixteenth = position.sixteenth;
this.data.tick = position.tick;
this.data.barsAsString = position.barsAsString;
this.data.ticksPerBar = position.ticksPerBar;
this.data.ticksPerBeat = position.ticksPerBeat;
this.data.ticksPerSixteenth = position.ticksPerSixteenth;
this.data.numSixteenth = position.numSixteenth;
} else if (this.type.indexOf('time') !== -1) {
this.data.hour = position.hour;
this.data.minute = position.minute;
this.data.second = position.second;
this.data.millisecond = position.millisecond;
this.data.timeAsString = position.timeAsString;
} else if (this.type.indexOf('percentage') !== -1) {
this.data.percentage = position.percentage;
}
// get active notes
if (this.type.indexOf('notes') !== -1 || this.type.indexOf('all') !== -1) {
// get all notes between the noteIndex and the current playhead position
for (i = this.noteIndex; i < this.numNotes; i++) {
note = this.notes[i];
value = note.noteOn[this.unit];
if (value <= this.currentValue) {
this.noteIndex++;
if (typeof note.noteOff === 'undefined') {
continue;
}
// if the playhead is set to a position of say 3000 millis, we don't want to add notes before the playhead
if (this.currentValue === 0 || note.noteOff[this.unit] > this.currentValue) {
collectedNotes.add(note);
(0, _eventlistener.dispatchEvent)({
type: 'noteOn',
data: note.noteOn
});
}
} else {
break;
}
}
// filter notes that are no longer active
for (i = this.activeNotes.length - 1; i >= 0; i--) {
note = this.activeNotes[i];
//if(note.noteOn.state.indexOf('removed') === 0 || this.song._notesById.get(note.id) === false){
if (this.song._notesById.get(note.id) === false) {
//console.log('skipping removed note', note.id);
continue;
}
if (typeof note.noteOff === 'undefined') {
console.warn('note with id', note.id, 'has no noteOff event');
continue;
}
//if(note.noteOff[this.unit] > this.currentValue && collectedNotes.has(note) === false){
if (note.noteOff[this.unit] > this.currentValue) {
stillActiveNotes.push(note);
} else {
(0, _eventlistener.dispatchEvent)({
type: 'noteOff',
data: note.noteOff
});
}
}
// add the still active notes and the newly active events to the active notes array
this.activeNotes = [].concat(_toConsumableArray(collectedNotes.values()), stillActiveNotes);
this.data.activeNotes = this.activeNotes;
}
// get active parts
if (this.type.indexOf('parts') !== -1 || this.type.indexOf('all') !== -1) {
for (i = this.partIndex; i < this.numParts; i++) {
part = this.parts[i];
//console.log(part, this.unit, this.currentValue);
if (part._start[this.unit] <= this.currentValue) {
collectedParts.add(part);
(0, _eventlistener.dispatchEvent)({
type: 'partOn',
data: part
});
this.partIndex++;
} else {
break;
}
}
// filter parts that are no longer active
for (i = this.activeParts.length - 1; i >= 0; i--) {
part = this.activeParts[i];
//if(part.state.indexOf('removed') === 0 || this.song._partsById.get(part.id) === false){
if (this.song._partsById.get(part.id) === false) {
//console.log('skipping removed part', part.id);
continue;
}
//if(part._end[this.unit] > this.currentValue && collectedParts.has(part) === false){
if (part._end[this.unit] > this.currentValue) {
stillActiveParts.push(note);
} else {
(0, _eventlistener.dispatchEvent)({
type: 'partOff',
data: part
});
}
}
this.activeParts = [].concat(_toConsumableArray(collectedParts.values()), stillActiveParts);
this.data.activeParts = this.activeParts;
}
(0, _eventlistener.dispatchEvent)({
type: 'position',
data: this.data
});
}
/*
setType(t){
this.type = t;
this.set(this.unit, this.currentValue);
//console.log(type,activeParts);
}
addType(t){
this.type += ' ' + t;
this.set(this.unit, this.currentValue);
//console.log(type,activeParts);
}
removeType(t){
var arr = this.type.split(' ');
this.type = '';
arr.forEach(function(type){
if(type !== t){
this.type += t + ' ';
}
});
this.type.trim();
this.set(this.currentValue);
//console.log(type,activeParts);
}
*/
}]);
return Playhead;
}();