UNPKG

qambi

Version:

MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio

426 lines (359 loc) 13.6 kB
'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; }();