qambi
Version:
MIDI sequencer, loads MIDI files, can record and playback MIDI, uses WebMIDI and WebAudio
667 lines (553 loc) • 18.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
exports.millisToTicks = millisToTicks;
exports.ticksToMillis = ticksToMillis;
exports.barsToMillis = barsToMillis;
exports.barsToTicks = barsToTicks;
exports.ticksToBars = ticksToBars;
exports.millisToBars = millisToBars;
exports.getPosition2 = getPosition2;
exports.calculatePosition = calculatePosition;
var _util = require('./util');
var supportedTypes = 'barsandbeats barsbeats time millis ticks perc percentage',
supportedReturnTypes = 'barsandbeats barsbeats time millis ticks all',
floor = Math.floor,
round = Math.round;
var
//local
bpm = void 0,
nominator = void 0,
denominator = void 0,
ticksPerBeat = void 0,
ticksPerBar = void 0,
ticksPerSixteenth = void 0,
millisPerTick = void 0,
secondsPerTick = void 0,
numSixteenth = void 0,
ticks = void 0,
millis = void 0,
diffTicks = void 0,
diffMillis = void 0,
bar = void 0,
beat = void 0,
sixteenth = void 0,
tick = void 0,
// type,
index = void 0,
returnType = 'all',
beyondEndOfSong = true;
function getTimeEvent(song, unit, target) {
// finds the time event that comes the closest before the target position
var timeEvents = song._timeEvents;
//console.log(song._timeEvents, unit, target)
for (var i = timeEvents.length - 1; i >= 0; i--) {
var event = timeEvents[i];
//console.log(unit, target, event)
if (event[unit] <= target) {
index = i;
return event;
}
}
return null;
}
function millisToTicks(song, targetMillis) {
var beos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
beyondEndOfSong = beos;
fromMillis(song, targetMillis);
//return round(ticks);
return ticks;
}
function ticksToMillis(song, targetTicks) {
var beos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
beyondEndOfSong = beos;
fromTicks(song, targetTicks);
return millis;
}
function barsToMillis(song, position, beos) {
// beos = beyondEndOfSong
calculatePosition(song, {
type: 'barsbeat',
position: position,
result: 'millis',
beos: beos
});
return millis;
}
function barsToTicks(song, position, beos) {
// beos = beyondEndOfSong
calculatePosition(song, {
type: 'barsbeats',
position: position,
result: 'ticks',
beos: beos
});
//return round(ticks);
return ticks;
}
function ticksToBars(song, target) {
var beos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
beyondEndOfSong = beos;
fromTicks(song, target);
calculateBarsAndBeats();
returnType = 'barsandbeats';
return getPositionData();
}
function millisToBars(song, target) {
var beos = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
beyondEndOfSong = beos;
fromMillis(song, target);
calculateBarsAndBeats();
returnType = 'barsandbeats';
return getPositionData();
}
// main calculation function for millis position
function fromMillis(song, targetMillis, event) {
var lastEvent = song._lastEvent;
if (beyondEndOfSong === false) {
if (targetMillis > lastEvent.millis) {
targetMillis = lastEvent.millis;
}
}
if (typeof event === 'undefined') {
event = getTimeEvent(song, 'millis', targetMillis);
}
//console.log(event)
getDataFromEvent(event);
// if the event is not exactly at target millis, calculate the diff
if (event.millis === targetMillis) {
diffMillis = 0;
diffTicks = 0;
} else {
diffMillis = targetMillis - event.millis;
diffTicks = diffMillis / millisPerTick;
}
millis += diffMillis;
ticks += diffTicks;
return ticks;
}
// main calculation function for ticks position
function fromTicks(song, targetTicks, event) {
var lastEvent = song._lastEvent;
if (beyondEndOfSong === false) {
if (targetTicks > lastEvent.ticks) {
targetTicks = lastEvent.ticks;
}
}
if (typeof event === 'undefined') {
event = getTimeEvent(song, 'ticks', targetTicks);
}
//console.log(event)
getDataFromEvent(event);
// if the event is not exactly at target ticks, calculate the diff
if (event.ticks === targetTicks) {
diffTicks = 0;
diffMillis = 0;
} else {
diffTicks = targetTicks - ticks;
diffMillis = diffTicks * millisPerTick;
}
ticks += diffTicks;
millis += diffMillis;
return millis;
}
// main calculation function for bars and beats position
function fromBars(song, targetBar, targetBeat, targetSixteenth, targetTick) {
var event = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
//console.time('fromBars');
var i = 0,
diffBars = void 0,
diffBeats = void 0,
diffSixteenth = void 0,
diffTick = void 0,
lastEvent = song._lastEvent;
if (beyondEndOfSong === false) {
if (targetBar > lastEvent.bar) {
targetBar = lastEvent.bar;
}
}
if (event === null) {
event = getTimeEvent(song, 'bar', targetBar);
}
//console.log(event)
getDataFromEvent(event);
//correct wrong position data, for instance: '3,3,2,788' becomes '3,4,4,068' in a 4/4 measure at PPQ 480
while (targetTick >= ticksPerSixteenth) {
targetSixteenth++;
targetTick -= ticksPerSixteenth;
}
while (targetSixteenth > numSixteenth) {
targetBeat++;
targetSixteenth -= numSixteenth;
}
while (targetBeat > nominator) {
targetBar++;
targetBeat -= nominator;
}
event = getTimeEvent(song, 'bar', targetBar, index);
for (i = index; i >= 0; i--) {
event = song._timeEvents[i];
if (event.bar <= targetBar) {
getDataFromEvent(event);
break;
}
}
// get the differences
diffTick = targetTick - tick;
diffSixteenth = targetSixteenth - sixteenth;
diffBeats = targetBeat - beat;
diffBars = targetBar - bar; //bar is always less then or equal to targetBar, so diffBars is always >= 0
//console.log('diff',diffBars,diffBeats,diffSixteenth,diffTick);
//console.log('millis',millis,ticksPerBar,ticksPerBeat,ticksPerSixteenth,millisPerTick);
// convert differences to milliseconds and ticks
diffMillis = diffBars * ticksPerBar * millisPerTick;
diffMillis += diffBeats * ticksPerBeat * millisPerTick;
diffMillis += diffSixteenth * ticksPerSixteenth * millisPerTick;
diffMillis += diffTick * millisPerTick;
diffTicks = diffMillis / millisPerTick;
//console.log(diffBars, ticksPerBar, millisPerTick, diffMillis, diffTicks);
// set all current position data
bar = targetBar;
beat = targetBeat;
sixteenth = targetSixteenth;
tick = targetTick;
//console.log(tick, targetTick)
millis += diffMillis;
//console.log(targetBar, targetBeat, targetSixteenth, targetTick, ' -> ', millis);
ticks += diffTicks;
//console.timeEnd('fromBars');
}
function calculateBarsAndBeats() {
// spread the difference in tick over bars, beats and sixteenth
var tmp = round(diffTicks);
while (tmp >= ticksPerSixteenth) {
sixteenth++;
tmp -= ticksPerSixteenth;
while (sixteenth > numSixteenth) {
sixteenth -= numSixteenth;
beat++;
while (beat > nominator) {
beat -= nominator;
bar++;
}
}
}
tick = round(tmp);
}
// store properties of event in local scope
function getDataFromEvent(event) {
bpm = event.bpm;
nominator = event.nominator;
denominator = event.denominator;
ticksPerBar = event.ticksPerBar;
ticksPerBeat = event.ticksPerBeat;
ticksPerSixteenth = event.ticksPerSixteenth;
numSixteenth = event.numSixteenth;
millisPerTick = event.millisPerTick;
secondsPerTick = event.secondsPerTick;
bar = event.bar;
beat = event.beat;
sixteenth = event.sixteenth;
tick = event.tick;
ticks = event.ticks;
millis = event.millis;
//console.log(bpm, event.type);
//console.log('ticks', ticks, 'millis', millis, 'bar', bar);
}
function getPositionData(song) {
var timeData = void 0,
positionData = {};
switch (returnType) {
case 'millis':
//positionData.millis = millis;
positionData.millis = round(millis * 1000) / 1000;
positionData.millisRounded = round(millis);
break;
case 'ticks':
//positionData.ticks = ticks;
positionData.ticks = round(ticks);
//positionData.ticksUnrounded = ticks;
break;
case 'barsbeats':
case 'barsandbeats':
positionData.bar = bar;
positionData.beat = beat;
positionData.sixteenth = sixteenth;
positionData.tick = tick;
//positionData.barsAsString = (bar + 1) + ':' + (beat + 1) + ':' + (sixteenth + 1) + ':' + tickAsString;
positionData.barsAsString = bar + ':' + beat + ':' + sixteenth + ':' + getTickAsString(tick);
break;
case 'time':
timeData = (0, _util.getNiceTime)(millis);
positionData.hour = timeData.hour;
positionData.minute = timeData.minute;
positionData.second = timeData.second;
positionData.millisecond = timeData.millisecond;
positionData.timeAsString = timeData.timeAsString;
break;
case 'all':
// millis
//positionData.millis = millis;
positionData.millis = round(millis * 1000) / 1000;
positionData.millisRounded = round(millis);
// ticks
//positionData.ticks = ticks;
positionData.ticks = round(ticks);
//positionData.ticksUnrounded = ticks;
// barsbeats
positionData.bar = bar;
positionData.beat = beat;
positionData.sixteenth = sixteenth;
positionData.tick = tick;
//positionData.barsAsString = (bar + 1) + ':' + (beat + 1) + ':' + (sixteenth + 1) + ':' + tickAsString;
positionData.barsAsString = bar + ':' + beat + ':' + sixteenth + ':' + getTickAsString(tick);
// time
timeData = (0, _util.getNiceTime)(millis);
positionData.hour = timeData.hour;
positionData.minute = timeData.minute;
positionData.second = timeData.second;
positionData.millisecond = timeData.millisecond;
positionData.timeAsString = timeData.timeAsString;
// extra data
positionData.bpm = round(bpm * song.playbackSpeed, 3);
positionData.nominator = nominator;
positionData.denominator = denominator;
positionData.ticksPerBar = ticksPerBar;
positionData.ticksPerBeat = ticksPerBeat;
positionData.ticksPerSixteenth = ticksPerSixteenth;
positionData.numSixteenth = numSixteenth;
positionData.millisPerTick = millisPerTick;
positionData.secondsPerTick = secondsPerTick;
// use ticks to make tempo changes visible by a faster moving playhead
positionData.percentage = ticks / song._durationTicks;
//positionData.percentage = millis / song.durationMillis;
break;
default:
return null;
}
return positionData;
}
function getTickAsString(t) {
if (t === 0) {
t = '000';
} else if (t < 10) {
t = '00' + t;
} else if (t < 100) {
t = '0' + t;
}
return t;
}
// used by playhead
function getPosition2(song, unit, target, type, event) {
if (unit === 'millis') {
fromMillis(song, target, event);
} else if (unit === 'ticks') {
fromTicks(song, target, event);
}
returnType = type;
if (returnType === 'all') {
calculateBarsAndBeats();
}
return getPositionData(song);
}
// improved version of getPosition
function calculatePosition(song, settings) {
var type = settings.type,
target = settings.target,
_settings$result = settings.result,
result = _settings$result === undefined ? 'all' : _settings$result,
_settings$beos = settings.beos,
beos = _settings$beos === undefined ? true : _settings$beos,
_settings$snap = settings.snap,
snap = _settings$snap === undefined ? -1 : _settings$snap;
if (supportedReturnTypes.indexOf(result) === -1) {
console.warn('unsupported return type, \'all\' used instead of \'' + result + '\'');
result = 'all';
}
returnType = result;
beyondEndOfSong = beos;
if (supportedTypes.indexOf(type) === -1) {
console.error('unsupported type ' + type);
return false;
}
switch (type) {
case 'barsbeats':
case 'barsandbeats':
var _target = _slicedToArray(target, 4),
_target$ = _target[0],
targetbar = _target$ === undefined ? 1 : _target$,
_target$2 = _target[1],
targetbeat = _target$2 === undefined ? 1 : _target$2,
_target$3 = _target[2],
targetsixteenth = _target$3 === undefined ? 1 : _target$3,
_target$4 = _target[3],
targettick = _target$4 === undefined ? 0 : _target$4;
//console.log(targetbar, targetbeat, targetsixteenth, targettick)
fromBars(song, targetbar, targetbeat, targetsixteenth, targettick);
return getPositionData(song);
case 'time':
// calculate millis out of time array: hours, minutes, seconds, millis
var _target2 = _slicedToArray(target, 4),
_target2$ = _target2[0],
targethour = _target2$ === undefined ? 0 : _target2$,
_target2$2 = _target2[1],
targetminute = _target2$2 === undefined ? 0 : _target2$2,
_target2$3 = _target2[2],
targetsecond = _target2$3 === undefined ? 0 : _target2$3,
_target2$4 = _target2[3],
targetmillisecond = _target2$4 === undefined ? 0 : _target2$4;
var _millis = 0;
_millis += targethour * 60 * 60 * 1000; //hours
_millis += targetminute * 60 * 1000; //minutes
_millis += targetsecond * 1000; //seconds
_millis += targetmillisecond; //milliseconds
fromMillis(song, _millis);
calculateBarsAndBeats();
return getPositionData(song);
case 'millis':
fromMillis(song, target);
calculateBarsAndBeats();
return getPositionData(song);
case 'ticks':
//console.log(song, target)
fromTicks(song, target);
calculateBarsAndBeats();
return getPositionData(song);
case 'perc':
case 'percentage':
//millis = position[1] * song.durationMillis;
//fromMillis(song, millis);
//console.log(millis);
ticks = target * song._durationTicks; // target must be in ticks!
//console.log(ticks, song._durationTicks)
if (snap !== -1) {
ticks = floor(ticks / snap) * snap;
//fromTicks(song, ticks);
//console.log(ticks);
}
fromTicks(song, ticks);
calculateBarsAndBeats();
var tmp = getPositionData(song);
//console.log('diff', position[1] - tmp.percentage);
return tmp;
default:
return false;
}
}
/*
//@param: 'millis', 1000, [true]
//@param: 'ticks', 1000, [true]
//@param: 'barsandbeats', 1, ['all', true]
//@param: 'barsandbeats', 60, 4, 3, 120, ['all', true]
//@param: 'barsandbeats', 60, 4, 3, 120, [true, 'all']
function checkPosition(type, args, returnType = 'all'){
beyondEndOfSong = true;
console.log('----> checkPosition:', args, typeString(args));
if(typeString(args) === 'array'){
let
numArgs = args.length,
position,
i, a, positionLength;
type = args[0];
// support for [['millis', 3000]]
if(typeString(args[0]) === 'array'){
//console.warn('this shouldn\'t happen!');
args = args[0];
type = args[0];
numArgs = args.length;
}
position = [type];
console.log('check position', args, numArgs, supportedTypes.indexOf(type));
//console.log('arg', 0, '->', type);
if(supportedTypes.indexOf(type) !== -1){
for(i = 1; i < numArgs; i++){
a = args[i];
//console.log('arg', i, '->', a);
if(a === true || a === false){
beyondEndOfSong = a;
}else if(isNaN(a)){
if(supportedReturnTypes.indexOf(a) !== -1){
returnType = a;
}else{
return false;
}
}else {
position.push(a);
}
}
//check number of arguments -> either 1 number or 4 numbers in position, e.g. ['barsbeats', 1] or ['barsbeats', 1, 1, 1, 0],
// or ['perc', 0.56, numberOfTicksToSnapTo]
positionLength = position.length;
if(positionLength !== 2 && positionLength !== 3 && positionLength !== 5){
return false;
}
//console.log(position, returnType, beyondEndOfSong);
//console.log('------------------------------------')
return position;
}
}
return false;
}
export function getPosition(song, type, args){
//console.log('getPosition', args);
if(typeof args === 'undefined'){
return {
millis: 0
}
}
let position = checkPosition(type, args),
millis, tmp, snap;
if(position === false){
error('wrong position data');
return false;
}
switch(type){
case 'barsbeats':
case 'barsandbeats':
fromBars(song, position[1], position[2], position[3], position[4]);
return getPositionData(song);
case 'time':
// calculate millis out of time array: hours, minutes, seconds, millis
millis = 0;
tmp = position[1] || 0;
millis += tmp * 60 * 60 * 1000; //hours
tmp = position[2] || 0;
millis += tmp * 60 * 1000; //minutes
tmp = position[3] || 0;
millis += tmp * 1000; //seconds
tmp = position[4] || 0;
millis += tmp; //milliseconds
fromMillis(song, millis);
calculateBarsAndBeats();
return getPositionData(song);
case 'millis':
fromMillis(song, position[1]);
calculateBarsAndBeats();
return getPositionData(song);
case 'ticks':
fromTicks(song, position[1]);
calculateBarsAndBeats();
return getPositionData(song);
case 'perc':
case 'percentage':
snap = position[2];
//millis = position[1] * song.durationMillis;
//fromMillis(song, millis);
//console.log(millis);
ticks = position[1] * song.durationTicks;
if(snap !== undefined){
ticks = floor(ticks/snap) * snap;
//fromTicks(song, ticks);
//console.log(ticks);
}
fromTicks(song, ticks);
calculateBarsAndBeats();
tmp = getPositionData(song);
//console.log('diff', position[1] - tmp.percentage);
return tmp;
}
return false;
}
*/