qambi
Version:
MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio
426 lines (359 loc) • 13.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Metronome = 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 _instrument = require('./instrument');
var _track = require('./track');
var _part3 = require('./part');
var _parse_events = require('./parse_events');
var _midi_event = require('./midi_event');
var _util = require('./util');
var _position = require('./position');
var _sampler = require('./sampler');
var _init_audio = require('./init_audio');
var _constants = require('./constants');
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 methodMap = new Map([['volume', 'setVolume'], ['instrument', 'setInstrument'], ['noteNumberAccentedTick', 'setNoteNumberAccentedTick'], ['noteNumberNonAccentedTick', 'setNoteNumberNonAccentedTick'], ['velocityAccentedTick', 'setVelocityAccentedTick'], ['velocityNonAccentedTick', 'setVelocityNonAccentedTick'], ['noteLengthAccentedTick', 'setNoteLengthAccentedTick'], ['noteLengthNonAccentedTick', 'setNoteLengthNonAccentedTick']]);
var Metronome = exports.Metronome = function () {
function Metronome(song) {
_classCallCheck(this, Metronome);
this.song = song;
this.track = new _track.Track({ name: this.song.id + '_metronome' });
this.part = new _part3.Part();
this.track.addParts(this.part);
this.track._gainNode.connect(this.song._gainNode);
this.events = [];
this.precountEvents = [];
this.precountDuration = 0;
this.bars = 0;
this.index = 0;
this.index2 = 0;
this.precountIndex = 0;
this.reset();
}
_createClass(Metronome, [{
key: 'reset',
value: function reset() {
var data = (0, _init_audio.getInitData)();
var instrument = new _sampler.Sampler('metronome');
instrument.updateSampleData({
note: 60,
buffer: data.lowtick
}, {
note: 61,
buffer: data.hightick
});
this.track.setInstrument(instrument);
this.volume = 1;
this.noteNumberAccented = 61;
this.noteNumberNonAccented = 60;
this.velocityAccented = 100;
this.velocityNonAccented = 100;
this.noteLengthAccented = this.song.ppq / 4; // sixteenth notes -> don't make this too short if your sample has a long attack!
this.noteLengthNonAccented = this.song.ppq / 4;
}
}, {
key: 'createEvents',
value: function createEvents(startBar, endBar) {
var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'init';
var i = void 0,
j = void 0;
var position = void 0;
var velocity = void 0;
var noteLength = void 0;
var noteNumber = void 0;
var beatsPerBar = void 0;
var ticksPerBeat = void 0;
var ticks = 0;
var noteOn = void 0,
noteOff = void 0;
var events = [];
//console.log(startBar, endBar);
for (i = startBar; i <= endBar; i++) {
position = (0, _position.calculatePosition)(this.song, {
type: 'barsbeats',
target: [i]
});
beatsPerBar = position.nominator;
ticksPerBeat = position.ticksPerBeat;
ticks = position.ticks;
for (j = 0; j < beatsPerBar; j++) {
noteNumber = j === 0 ? this.noteNumberAccented : this.noteNumberNonAccented;
noteLength = j === 0 ? this.noteLengthAccented : this.noteLengthNonAccented;
velocity = j === 0 ? this.velocityAccented : this.velocityNonAccented;
noteOn = new _midi_event.MIDIEvent(ticks, 144, noteNumber, velocity);
noteOff = new _midi_event.MIDIEvent(ticks + noteLength, 128, noteNumber, 0);
if (id === 'precount') {
noteOn._track = this.track;
noteOff._track = this.track;
noteOn._part = {};
noteOff._part = {};
}
events.push(noteOn, noteOff);
ticks += ticksPerBeat;
}
}
return events;
}
}, {
key: 'getEvents',
value: function getEvents() {
var startBar = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var _part;
var endBar = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.song.bars;
var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'init';
this.part.removeEvents(this.part.getEvents());
this.events = this.createEvents(startBar, endBar, id);
(_part = this.part).addEvents.apply(_part, _toConsumableArray(this.events));
this.bars = this.song.bars;
//console.log('getEvents %O', this.events)
this.allEvents = [].concat(_toConsumableArray(this.events), _toConsumableArray(this.song._timeEvents));
// console.log(this.allEvents)
(0, _util.sortEvents)(this.allEvents);
(0, _parse_events.parseMIDINotes)(this.events);
return this.events;
}
}, {
key: 'setIndex2',
value: function setIndex2(millis) {
this.index2 = 0;
}
}, {
key: 'getEvents2',
value: function getEvents2(maxtime, timeStamp) {
var result = [];
for (var i = this.index2, maxi = this.allEvents.length; i < maxi; i++) {
var event = this.allEvents[i];
if (event.type === _constants.MIDIEventTypes.TEMPO || event.type === _constants.MIDIEventTypes.TIME_SIGNATURE) {
if (event.millis < maxtime) {
this.millisPerTick = event.millisPerTick;
this.index2++;
} else {
break;
}
} else {
var millis = event.ticks * this.millisPerTick;
if (millis < maxtime) {
event.time = millis + timeStamp;
event.millis = millis;
result.push(event);
this.index2++;
} else {
break;
}
}
}
return result;
}
}, {
key: 'addEvents',
value: function addEvents() {
var startBar = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var _events, _part2;
var endBar = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.song.bars;
var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'add';
// console.log(startBar, endBar)
var events = this.createEvents(startBar, endBar, id);
(_events = this.events).push.apply(_events, _toConsumableArray(events));
(_part2 = this.part).addEvents.apply(_part2, _toConsumableArray(events));
this.bars = endBar;
//console.log('getEvents %O', this.events, endBar)
return events;
}
}, {
key: 'createPrecountEvents',
value: function createPrecountEvents(startBar, endBar, timeStamp) {
this.timeStamp = timeStamp;
// let songStartPosition = this.song.getPosition()
var songStartPosition = (0, _position.calculatePosition)(this.song, {
type: 'barsbeats',
target: [startBar],
result: 'millis'
});
//console.log('starBar', songStartPosition.bar)
var endPos = (0, _position.calculatePosition)(this.song, {
type: 'barsbeats',
//target: [songStartPosition.bar + precount, songStartPosition.beat, songStartPosition.sixteenth, songStartPosition.tick],
target: [endBar],
result: 'millis'
});
//console.log(songStartPosition, endPos)
this.precountIndex = 0;
this.startMillis = songStartPosition.millis;
this.endMillis = endPos.millis;
this.precountDuration = endPos.millis - this.startMillis;
// do this so you can start precounting at any position in the song
this.timeStamp -= this.startMillis;
//console.log(this.precountDuration, this.startMillis, this.endMillis)
this.precountEvents = this.createEvents(startBar, endBar - 1, 'precount');
this.precountEvents = (0, _parse_events.parseEvents)([].concat(_toConsumableArray(this.song._timeEvents), _toConsumableArray(this.precountEvents)));
//console.log(songStartPosition.bar, endPos.bar, precount, this.precountEvents.length);
//console.log(this.precountEvents.length, this.precountDuration);
return this.precountDuration;
}
}, {
key: 'setPrecountIndex',
value: function setPrecountIndex(millis) {
var i = 0;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.events[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var event = _step.value;
if (event.millis >= millis) {
this.precountIndex = i;
break;
}
i++;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
console.log(this.precountIndex);
}
// called by scheduler.js
}, {
key: 'getPrecountEvents',
value: function getPrecountEvents(maxtime) {
var events = this.precountEvents,
maxi = events.length,
i = void 0,
evt = void 0,
result = [];
//maxtime += this.precountDuration
for (i = this.precountIndex; i < maxi; i++) {
evt = events[i];
//console.log(event.millis, maxtime, this.millis);
if (evt.millis < maxtime) {
evt.time = this.timeStamp + evt.millis;
result.push(evt);
this.precountIndex++;
} else {
break;
}
}
//console.log(result.length);
return result;
}
}, {
key: 'mute',
value: function mute(flag) {
this.track.muted = flag;
}
}, {
key: 'allNotesOff',
value: function allNotesOff() {
this.track._instrument.allNotesOff();
}
// =========== CONFIGURATION ===========
}, {
key: 'updateConfig',
value: function updateConfig() {
this.init(1, this.bars, 'update');
this.allNotesOff();
this.song.update();
}
// added to public API: Song.configureMetronome({})
}, {
key: 'configure',
value: function configure(config) {
Object.keys(config).forEach(function (key) {
this[methodMap.get(key)](config.key);
}, this);
this.updateConfig();
}
}, {
key: 'setInstrument',
value: function setInstrument(instrument) {
if (!instrument instanceof _instrument.Instrument) {
console.warn('not an instance of Instrument');
return;
}
this.track.setInstrument(instrument);
this.updateConfig();
}
}, {
key: 'setNoteLengthAccentedTick',
value: function setNoteLengthAccentedTick(value) {
if (isNaN(value)) {
console.warn('please provide a number');
}
this.noteLengthAccented = value;
this.updateConfig();
}
}, {
key: 'setNoteLengthNonAccentedTick',
value: function setNoteLengthNonAccentedTick(value) {
if (isNaN(value)) {
console.warn('please provide a number');
}
this.noteLengthNonAccented = value;
this.updateConfig();
}
}, {
key: 'setVelocityAccentedTick',
value: function setVelocityAccentedTick(value) {
value = (0, _util.checkMIDINumber)(value);
if (value !== false) {
this.velocityAccented = value;
} else {
console.warn('please provide a number');
}
this.updateConfig();
}
}, {
key: 'setVelocityNonAccentedTick',
value: function setVelocityNonAccentedTick(value) {
value = (0, _util.checkMIDINumber)(value);
if (value !== false) {
this.velocityNonAccented = value;
} else {
console.warn('please provide a number');
}
this.updateConfig();
}
}, {
key: 'setNoteNumberAccentedTick',
value: function setNoteNumberAccentedTick(value) {
value = (0, _util.checkMIDINumber)(value);
if (value !== false) {
this.noteNumberAccented = value;
} else {
console.warn('please provide a number');
}
this.updateConfig();
}
}, {
key: 'setNoteNumberNonAccentedTick',
value: function setNoteNumberNonAccentedTick(value) {
value = (0, _util.checkMIDINumber)(value);
if (value !== false) {
this.noteNumberNonAccented = value;
} else {
console.warn('please provide a number');
}
this.updateConfig();
}
}, {
key: 'setVolume',
value: function setVolume(value) {
this.track.setVolume(value);
}
}]);
return Metronome;
}();