music21j-port
Version:
A toolkit for computer-aided musicology, Javascript version
1,515 lines (1,360 loc) • 1.02 MB
JavaScript
/**
* music21j 0.9.0 built on * 2019-07-23.
* Copyright (c) 2013-2019 Michael Scott Cuthbert and cuthbertLab
* BSD License, see LICENSE
*
* http://github.com/cuthbertLab/music21j
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vexflow'), require('MIDI'), require('jquery'), require('jsonpickle'), require('eventjs')) :
typeof define === 'function' && define.amd ? define(['vexflow', 'MIDI', 'jquery', 'jsonpickle', 'eventjs'], factory) :
(global.music21 = factory(global.Vex,global.MIDI,global.$,global.jsonpickle,global.eventjs));
}(this, (function (Vex,MIDI,$,jsonpickle,eventjs) { 'use strict';
var MIDI__default = MIDI['default'];
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
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 _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var get = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var set = function set(object, property, value, receiver) {
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent !== null) {
set(parent, property, value, receiver);
}
} else if ("value" in desc && desc.writable) {
desc.value = value;
} else {
var setter = desc.set;
if (setter !== undefined) {
setter.call(receiver, value);
}
}
return value;
};
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");
}
};
}();
var toConsumableArray = function (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);
}
};
// not working properly...
var ExtendableError = function (_Error) {
inherits(ExtendableError, _Error);
function ExtendableError(message) {
classCallCheck(this, ExtendableError);
var _this = possibleConstructorReturn(this, (ExtendableError.__proto__ || Object.getPrototypeOf(ExtendableError)).call(this, message));
_this.name = _this.constructor.name;
_this.message = message;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(_this, _this.constructor);
} else {
_this.stack = new Error(message).stack;
}
return _this;
}
return ExtendableError;
}(Error);
var Music21Exception = function (_ExtendableError) {
inherits(Music21Exception, _ExtendableError);
function Music21Exception() {
classCallCheck(this, Music21Exception);
return possibleConstructorReturn(this, (Music21Exception.__proto__ || Object.getPrototypeOf(Music21Exception)).apply(this, arguments));
}
return Music21Exception;
}(ExtendableError);
var StreamException = function (_Music21Exception) {
inherits(StreamException, _Music21Exception);
function StreamException() {
classCallCheck(this, StreamException);
return possibleConstructorReturn(this, (StreamException.__proto__ || Object.getPrototypeOf(StreamException)).apply(this, arguments));
}
return StreamException;
}(Music21Exception);
var exceptions21 = Object.freeze({
Music21Exception: Music21Exception,
StreamException: StreamException
});
var debug = false;
/**
* common functions
*
* @exports music21/common
*/
/**
* functions that are useful everywhere...
*
* @namespace music21.common
* @memberof music21
*/
var common = {};
/**
* concept borrowed from Vex.Flow.Merge, though here the source can be undefined;
* http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
* recursive parts used in .clone()
*
* @function music21.common.merge
* @param {Object} destination - object to have attributes placed into
* @param {Object} source - object to take attributes from.
* @memberof music21.common
* @returns {Object} destination
*/
common.merge = function mergeRecursive(destination, source) {
if (source === undefined || source === null) {
return destination;
}
for (var p in source) {
if (!{}.hasOwnProperty.call(source, p)) {
continue;
}
try {
// Property in destination object set; update its value.
if (source[p] && source[p].constructor === Object) {
destination[p] = mergeRecursive(destination[p], source[p]);
} else {
destination[p] = source[p];
}
} catch (e) {
// Property in destination object not set; create it and set its value.
destination[p] = source[p];
}
}
return destination;
};
common.range = function common_range(start, stop, step) {
if (step === undefined) {
step = 1;
}
if (stop === undefined) {
stop = start;
start = 0;
}
var count = Math.ceil((stop - start) / step);
return Array.apply(0, Array(count)).map(function (e, i) {
return i * step + start;
});
};
/**
* Mix in another class into this class -- a simple form of multiple inheritance.
* See articulations.Marcato for an example.
*
*/
common.mixin = function common_mixin(OtherParent, thisClassOrObject) {
var proto = Object.getPrototypeOf(OtherParent);
var classProto = Object.getPrototypeOf(thisClassOrObject);
while (proto) {
for (var key in Object.keys(proto)) {
if (!{}.hasOwnProperty.call(proto, key)) {
continue;
}
if (!(key in classProto)) {
classProto[key] = proto[key];
}
}
proto = Object.getPrototypeOf(proto);
}
};
/**
* Aggregation -- closer to true multiple inheritance -- prefers last class's functions. See
* https://stackoverflow.com/questions/29879267/es6-class-multiple-inheritance
*
* not currently used...
*/
common.aggregation = function (baseClass) {
for (var _len = arguments.length, mixins = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
mixins[_key - 1] = arguments[_key];
}
var base = function (_baseClass) {
inherits(base, _baseClass);
function base() {
var _ref;
classCallCheck(this, base);
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var _this = possibleConstructorReturn(this, (_ref = base.__proto__ || Object.getPrototypeOf(base)).call.apply(_ref, [this].concat(args)));
mixins.forEach(function (mixin) {
copyProps(_this, new mixin());
});
return _this;
}
return base;
}(baseClass);
var copyProps = function copyProps(target, source) {
// this function copies all properties and symbols, filtering out some special ones
// noinspection JSUnresolvedFunction
Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source)).forEach(function (prop) {
if (!prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) {
Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
}
});
};
mixins.forEach(function (mixin) {
// outside constructor() to allow aggregation(A,B,C).staticFunction() to be called etc.
copyProps(base.prototype, mixin.prototype);
copyProps(base, mixin);
});
return base;
};
/**
* posMod - return a modulo value that is not negative
*
* @param {int} a value
* @param {int} b modulo
* @return {int} a mod b between 0 and b - 1
*/
common.posMod = function posMod(a, b) {
if (a === undefined || b === undefined) {
throw new Error('Modulo needs two numbers');
}
return (a % b + b) % b;
};
/**
*
* Returns the statistical mode (most commonly appearing element)
* in a.
*
* In case of tie, returns the first element to reach the maximum
* number of occurrences.
*
* @function music21.common.statisticalMode
* @param {Array<*>} a - an array to analyze
* @returns {Object} element with the highest frequency in a
*/
common.statisticalMode = function statisticalMode(a) {
if (a.length === 0) {
return null;
}
var modeMap = {};
var maxEl = a[0];
var maxCount = 1;
for (var i = 0; i < a.length; i++) {
var el = a[i];
if (modeMap[el] == null) {
modeMap[el] = 0;
}
modeMap[el] += 1;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
};
/**
* fromRoman - Convert a Roman numeral (upper or lower) to an int.
*
* @param {string} num roman numeral representation of a number
* @return {int} integer value of roman numeral;
*/
common.fromRoman = function fromRoman(num) {
var inputRoman = num.toUpperCase();
var subtractionValues = [1, 10, 100];
var nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I'];
var ints = [1000, 500, 100, 50, 10, 5, 1];
var places = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = inputRoman[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _c = _step.value;
if (!nums.includes(_c)) {
throw new Error('Value is not a valid roman numeral: ' + inputRoman);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
for (var i = 0; i < inputRoman.length; i++) {
var c = inputRoman[i];
var value = ints[nums.indexOf(c)];
if (i < inputRoman.length - 1) {
var nextValue = ints[nums.indexOf(inputRoman[i + 1])];
if (nextValue > value && subtractionValues.includes(value)) {
value *= -1;
}
}
places.push(value);
}
var summation = 0;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = places[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var n = _step2.value;
summation += n;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return summation;
};
/**
* toRoman - Convert a number from 1 to 3999 to a roman numeral
*
* @param {int} num number to convert
* @return {string} as roman numeral
*/
common.toRoman = function toRoman(num) {
if (typeof num !== 'number') {
throw new Error('expected integer, got ' + (typeof num === 'undefined' ? 'undefined' : _typeof(num)));
}
if (num < 0 || num > 4000) {
throw new Error('Argument must be between 1 and 3999');
}
var ints = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
var nums = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
var result = '';
for (var i = 0; i < ints.length; i++) {
var count = Math.floor(num / ints[i]);
result += nums[i].repeat(count);
num -= ints[i] * count;
}
return result;
};
/**
* Creates an SVGElement of an SVG figure using the correct `document.createElementNS` call.
*
* @function music21.common.makeSVGright
* @param {string} [tag='svg'] - a tag, such as 'rect', 'circle', 'text', or 'svg'
* @param {Object} [attrs] - attributes to pass to the tag.
* @memberof music21.common
* @returns {SVGElement}
*/
common.makeSVGright = function makeSVGright() {
var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'svg';
var attrs = arguments[1];
// see http://stackoverflow.com/questions/3642035/jquerys-append-not-working-with-svg-element
// normal JQuery does not work.
if (attrs === undefined) {
attrs = {};
}
var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (var k in attrs) {
if (!{}.hasOwnProperty.call(attrs, k)) {
continue;
}
el.setAttribute(k, attrs[k]);
}
return el;
};
/**
* Take a number such as 32 and return a string such as "nd"
* (for "32nd") etc.
*
* @function music21.common.ordinalAbbreviation
* @param {int} value
* @param {Boolean} [plural=false] - make plural (note that "21st" plural is "21st")
* @return {string}
*/
common.ordinalAbbreviation = function ordinalAbbreviation(value, plural) {
var post = '';
var valueHundreths = value % 100;
if (valueHundreths === 11 || valueHundreths === 12 || valueHundreths === 13) {
post = 'th';
} else {
var valueMod = value % 10;
if (valueMod === 1) {
post = 'st';
} else if (valueMod === 2) {
post = 'nd';
} else if (valueMod === 3) {
post = 'rd';
} else {
post = 'th';
}
}
if (post !== 'st' && plural) {
post += 's';
}
return post;
};
/**
* Find a rational number approximation of this floating point.
*
* @function music21.common.rationalize
* @param {number} ql - number to rationalize
* @param {number} [epsilon=0.001] - how close to get
* @param {int} [maxDenominator=50] - maximum denominator
* @returns {object|undefined} {'numerator: numerator, 'denominator': denominator}
*/
common.rationalize = function rationalize(ql, epsilon, maxDenominator) {
epsilon = epsilon || 0.001;
maxDenominator = maxDenominator || 50;
for (var i = 2; i < maxDenominator; i++) {
if (Math.abs(ql * i - Math.round(ql * i)) < epsilon) {
var numerator = Math.round(ql * i);
var denominator = i;
return { numerator: numerator, denominator: denominator };
}
}
return undefined;
};
/**
* Change something that could be a string or number and might
* end with "px" to a number.
*
* "400px" -> 400
*
* @function music21.common.stripPx
* @param {number|string} str -- string that might have 'px' at the end or not
* @returns {number} a number to use
*/
common.stripPx = function stripPx(str) {
if (typeof str === 'string') {
var pxIndex = str.indexOf('px');
str = str.slice(0, pxIndex);
return parseInt(str);
} else {
return str;
}
};
/**
* Find name in the query string (?name=value) and return value.
*
* @function music21.common.urlParam
* @param {string} name - url parameter to find
* @returns {string} may be "" if empty.
*/
common.urlParam = function urlParam(name) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results == null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
common.arrayEquals = function arrayEquals(a1, a2) {
if (a1.length !== a2.length) {
return false;
}
for (var i = 0; i < a1.length; i++) {
if (a1[i] instanceof Array && a2[i] instanceof Array) {
if (!arrayEquals(a1[i], a2[i])) {
return false;
}
} else if (a1[i] !== a2[i]) {
return false;
}
}
return true;
};
var _singletonCounter = {};
_singletonCounter.value = 0;
var SingletonCounter = function () {
function SingletonCounter() {
classCallCheck(this, SingletonCounter);
}
createClass(SingletonCounter, [{
key: 'call',
value: function call() {
var post = _singletonCounter.value;
_singletonCounter.value += 1;
return post;
}
}]);
return SingletonCounter;
}();
common.SingletonCounter = SingletonCounter;
/**
* runs a callback with either "visible" or "hidden" as the argument anytime the
* window or document state changes.
*
* Depending on the browser, may be called multiple times with the same argument
* for a single event. For instance, Safari calls once on losing focus completely
* but twice for a tab change.
*
* @function music21.common.setWindowVisibilityWatcher
* @param {function} callback
*/
common.setWindowVisibilityWatcher = function setWindowVisibilityWatcher(callback) {
var hidden = 'hidden';
// Standards:
if (hidden in document) {
document.addEventListener('visibilitychange', windowFocusChanged);
} else if ('mozHidden' in document) {
hidden = 'mozHidden';
document.addEventListener('mozvisibilitychange', windowFocusChanged);
} else if ('webkitHidden' in document) {
hidden = 'webkitHidden';
document.addEventListener('webkitvisibilitychange', windowFocusChanged);
} else if ('msHidden' in document) {
hidden = 'msHidden';
document.addEventListener('msvisibilitychange', windowFocusChanged);
} else if ('onfocusin' in document) {
// IE 9 and lower:
document.onfocusin = windowFocusChanged;
document.onfocusout = windowFocusChanged;
}
// Also catch window... -- get two calls for a tab shift, but one for window losing focus
// noinspection AssignmentResultUsedJS
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = windowFocusChanged;
function windowFocusChanged(evt) {
var v = 'visible';
var h = 'hidden';
var evtMap = {
focus: v,
focusin: v,
pageshow: v,
blur: h,
focusout: h,
pagehide: h
};
// noinspection JSDeprecatedSymbols
evt = evt || window.event;
var callbackState = '';
if (evt.type in evtMap) {
callbackState = evtMap[evt.type];
} else {
callbackState = this[hidden] ? 'hidden' : 'visible';
}
callback(callbackState, evt);
}
// set the initial state
var initialState = document.visibilityState === 'visible' ? 'focus' : 'blur';
var initialStateEvent = { type: initialState };
windowFocusChanged(initialStateEvent);
};
common.urls = {
css: '/css',
webResources: '/webResources',
midiPlayer: '/webResources/midiPlayer',
soundfontUrl: '/src/ext/soundfonts/FluidR3_GM/'
};
common.hyphenToCamelCase = function hyphenToCamelCase(usrStr) {
return usrStr.replace(/-([a-zA-Z])/, function (all, match) {
return match.toUpperCase();
});
};
common.numToIntOrFloat = function numToIntOrFloat(value) {
var intVal = Math.round(value);
if (Math.abs(value - intVal) < 0.000001) {
return intVal;
} else {
return value;
}
};
/**
* module for things that all music21-created objects, not just objects that can live in
* Stream.elements should inherit. See the {@link music21.prebase} namespace.
* Copyright (c) 2013-17, Michael Scott Cuthbert and cuthbertLab
*
* @exports music21/prebase
*/
/**
* module for things that all music21-created objects, not just objects that can live in
* Stream.elements should inherit
*
* @namespace music21.prebase
* @memberof music21
*/
var prebase = {};
/**
* Class for pseudo-m21 objects to inherit from. The most important attributes that nearly
* everything in music21 should inherit from are given below.
*
* @class ProtoM21Object
* @memberof music21.prebase
* @property {Array<string>} classes - An Array of strings of classes that the object belongs to (default ['ProtoM21Object'])
* @property {Boolean} isProtoM21Object - Does this object descend from {@link music21.prebase.ProtoM21Object}: obviously true.
* @property {Boolean} isMusic21Object - Does this object descend from {@link music21.base.Music21Object}; default false.
*/
var ProtoM21Object = function () {
function ProtoM21Object() {
classCallCheck(this, ProtoM21Object);
/**
*
* @type {[string[]]}
* @private
*/
this._storedClasses = undefined;
this.isProtoM21Object = true;
this.isMusic21Object = false;
this._cloneCallbacks = {};
}
createClass(ProtoM21Object, [{
key: 'clone',
/**
* Makes (as much as possible) a complete duplicate copy of the object called with .clone()
*
* Works similarly to Python's copy.deepcopy().
*
* Every ProtoM21Object has a `._cloneCallbacks` object which maps `{attribute: callbackFunction}`
* to handle custom clone cases. See, for instance, {@link music21.base.Music21Object} which
* uses a custom callback to NOT clone the `.activeSite` attribute.
*
* @returns {music21.prebase.ProtoM21Object}
* @example
* var n1 = new music21.note.Note("C#");
* n1.duration.quarterLength = 4;
* var n2 = n1.clone();
* n2.duration.quarterLength == 4; // true
* n2 === n1; // false
*/
value: function clone() {
var deep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var memo = arguments[1];
if (!deep) {
return _extends(Object.create(Object.getPrototypeOf(this)), this);
}
var ret = new this.constructor();
if (memo === undefined) {
memo = new WeakMap();
}
// todo: do Arrays work?
for (var key in this) {
// not that we ONLY copy the keys in Ret -- it's easier that way.
if ({}.hasOwnProperty.call(this, key) === false) {
continue;
}
if (key in this._cloneCallbacks) {
if (this._cloneCallbacks[key] === true) {
ret[key] = this[key];
} else if (this._cloneCallbacks[key] === false) {
ret[key] = undefined;
} else {
// call the cloneCallbacks function
this._cloneCallbacks[key](key, ret, this);
}
} else if (Object.getOwnPropertyDescriptor(this, key).get !== undefined || Object.getOwnPropertyDescriptor(this, key).set !== undefined) {
// do nothing
} else if (typeof this[key] === 'function') {
// do nothing -- events might not be copied.
} else if (_typeof(this[key]) === 'object' && this[key] !== null && this[key].isProtoM21Object) {
// console.log('cloning ', key);
var m21Obj = this[key];
var clonedVersion = void 0;
if (memo.has(m21Obj)) {
clonedVersion = memo.get(m21Obj);
} else {
clonedVersion = this[key].clone(deep, memo);
}
ret[key] = clonedVersion;
} else {
try {
ret[key] = this[key];
// music21.common.merge(ret[key], this[key]); // not really necessary?
} catch (e) {
if (e instanceof TypeError) {
console.log('typeError:', e, key);
// do nothing
} else {
throw e;
}
}
}
}
return ret;
}
/**
* Check to see if an object is of this class or subclass.
*
* @param {(string|string[])} testClass - a class or Array of classes to test
* @returns {Boolean}
* @example
* var n = new music21.note.Note();
* n.isClassOrSubclass('Note'); // true
* n.isClassOrSubclass('Music21Object'); // true
* n.isClassOrSubclass(['Duration', 'NotRest']); // true // NotRest
*/
}, {
key: 'isClassOrSubclass',
value: function isClassOrSubclass(testClass) {
if (testClass instanceof Array === false) {
testClass = [testClass];
}
for (var i = 0; i < testClass.length; i++) {
if (this.classes.includes(testClass[i])) {
return true;
}
}
return false;
}
}, {
key: 'toString',
value: function toString() {
var si = this.stringInfo();
if (si !== '') {
si = ' ' + si;
}
return '<' + this.classes[0] + si + '>';
}
}, {
key: 'stringInfo',
value: function stringInfo() {
return '';
}
}, {
key: 'classes',
get: function get() {
if (this._storedClasses !== undefined) {
return this._storedClasses;
}
var classList = [];
var thisConstructor = this.constructor;
var maxLinks = 20;
while (thisConstructor !== null && thisConstructor !== undefined && maxLinks) {
maxLinks -= 1;
if (thisConstructor.name === '') {
break;
}
classList.push(thisConstructor.name);
thisConstructor = Object.getPrototypeOf(thisConstructor);
}
classList.push('object');
this._storedClasses = classList;
return classList;
}
}]);
return ProtoM21Object;
}();
prebase.ProtoM21Object = ProtoM21Object;
var Derivation = function () {
/**
*
* @param {music21.base.Music21Object} [client]
* @property {str} [method]
* @property {music21.base.Music21Object} [origin]
*/
function Derivation(client) {
classCallCheck(this, Derivation);
this.client = client;
this.method = undefined;
this.origin = undefined;
}
createClass(Derivation, [{
key: "clone",
value: function clone() {
var newThing = new Derivation(this.client);
newThing.method = this.method;
newThing.origin = this.origin;
}
}, {
key: "chain",
value: regeneratorRuntime.mark(function chain() {
var origin;
return regeneratorRuntime.wrap(function chain$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
origin = this.origin;
case 1:
if (!(origin !== undefined)) {
_context.next = 7;
break;
}
_context.next = 4;
return origin;
case 4:
origin = origin.derivation.origin;
_context.next = 1;
break;
case 7:
case "end":
return _context.stop();
}
}
}, chain, this);
})
}, {
key: "rootDerivation",
value: function rootDerivation() {
var derivationChain = Array.from(this.chain());
if (derivationChain.length) {
return derivationChain[derivationChain.length - 1];
} else {
return undefined;
}
}
}]);
return Derivation;
}();
var derivation = Object.freeze({
Derivation: Derivation,
default: Derivation
});
/**
* music21j -- Javascript reimplementation of Core music21 features.
* music21/duration -- duration routines
*
* Copyright (c) 2013-16, Michael Scott Cuthbert and cuthbertLab
* Based on music21, Copyright (c) 2006-18, Michael Scott Cuthbert and cuthbertLab
*
*/
/**
* Duration module. See {@link music21.duration}
*
* @requires music21/common
* @requires music21/prebase
* @exports music21/duration
*/
/**
* Module that holds **music21** classes and
* tools for dealing with durations, especially
* the {@link music21.duration.Duration} class.
*
* @namespace music21.duration
* @memberof music21
*/
var duration = {};
/**
* Object mapping int to name, as in `{1: 'whole'}` etc.
*
* @memberof music21.duration
* @type {Object}
*/
duration.typeFromNumDict = {
1: 'whole',
2: 'half',
4: 'quarter',
8: 'eighth',
16: '16th',
32: '32nd',
64: '64th',
128: '128th',
256: '256th',
512: '512th',
1024: '1024th',
0: 'zero',
'0.5': 'breve',
'0.25': 'longa',
'0.125': 'maxima',
'0.0625': 'duplex-maxima'
};
duration.quarterTypeIndex = 6; // where is quarter in the following array.
duration.ordinalTypeFromNum = ['duplex-maxima', 'maxima', 'longa', 'breve', 'whole', 'half', 'quarter', 'eighth', '16th', '32nd', '64th', '128th', '256th', '512th', '1024th'];
duration.vexflowDurationArray = [undefined, undefined, undefined, undefined, 'w', 'h', 'q', '8', '16', '32', undefined, undefined, undefined, undefined, undefined];
/**
* Duration object; found as the `.duration` attribute on {@link music21.base.Music21Object} instances
* such as {@link music21.note.Note}
*
* @class Duration
* @memberof music21.duration
* @extends music21.prebase.ProtoM21Object
* @param {(number|undefined)} ql - quarterLength (default 1.0)
*/
var Duration = function (_prebase$ProtoM21Obje) {
inherits(Duration, _prebase$ProtoM21Obje);
function Duration(ql) {
classCallCheck(this, Duration);
var _this = possibleConstructorReturn(this, (Duration.__proto__ || Object.getPrototypeOf(Duration)).call(this));
_this._quarterLength = 0.0;
_this._dots = 0;
_this._durationNumber = undefined;
_this._type = 'zero';
_this._tuplets = [];
if (typeof ql === 'string') {
_this.type = ql;
} else if (ql !== undefined) {
_this.quarterLength = ql;
}
_this._cloneCallbacks._tuplets = _this.cloneCallbacksTupletFunction;
return _this;
}
createClass(Duration, [{
key: 'stringInfo',
value: function stringInfo() {
return this.quarterLength.toString();
}
/**
* Read or sets the number of dots on the duration.
*
* Updates the quarterLength
*
* @type Number
* @instance
* @default 0
* @example
* var d = new music21.duration.Duration(2);
* d.dots === 0; // true
* d.dots = 1;
* d.quarterLength == 3; // true;
*/
}, {
key: 'cloneCallbacksTupletFunction',
value: function cloneCallbacksTupletFunction(tupletKey, ret, obj) {
// make sure that tuplets clone properly
var newTuplets = [];
for (var i = 0; i < obj[tupletKey].length; i++) {
var newTuplet = obj[tupletKey][i].clone();
// console.log('cloning tuplets', obj[tupletKey][i], newTuplet);
newTuplets.push(newTuplet);
}
ret[tupletKey] = newTuplets;
}
}, {
key: '_findDots',
value: function _findDots(ql) {
if (ql === 0) {
return 0;
} // zero length stream probably;
var typeNumber = duration.ordinalTypeFromNum.indexOf(this._type);
var powerOfTwo = Math.pow(2, duration.quarterTypeIndex - typeNumber);
// alert(undottedQL * 1.5 + " " + ql)
// console.log('find dots called on ql: ', ql, typeNumber, powerOfTwo);
for (var dotsNum = 0; dotsNum <= 4; dotsNum++) {
var dotMultiplier = (Math.pow(2, dotsNum) - 1.0) / Math.pow(2, dotsNum);
var durationMultiplier = 1 + dotMultiplier;
if (Math.abs(powerOfTwo * durationMultiplier - ql) < 0.0001) {
return dotsNum;
}
}
if (debug) {
console.log('no dots available for ql; probably a tuplet', ql);
}
return 0;
}
}, {
key: 'updateQlFromFeatures',
value: function updateQlFromFeatures() {
var typeNumber = duration.ordinalTypeFromNum.indexOf(this._type); // must be set property
var undottedQuarterLength = Math.pow(2, duration.quarterTypeIndex - typeNumber);
var dottedMultiplier = 1 + (Math.pow(2, this._dots) - 1) / Math.pow(2, this._dots);
var unTupletedQl = undottedQuarterLength * dottedMultiplier;
var tupletCorrectedQl = unTupletedQl;
this._tuplets.forEach(function (tuplet) {
tupletCorrectedQl *= tuplet.tupletMultiplier();
});
this._quarterLength = tupletCorrectedQl;
}
}, {
key: 'updateFeaturesFromQl',
value: function updateFeaturesFromQl() {
var ql = this._quarterLength;
this._tuplets = [];
if (ql === 0) {
this._type = 'zero';
this._dots = 0;
return;
}
var powerOfTwo = Math.floor(Math.log(ql + 0.00001) / Math.log(2));
var typeNumber = duration.quarterTypeIndex - powerOfTwo;
this._type = duration.ordinalTypeFromNum[typeNumber];
// console.log(this._findDots);
this._dots = this._findDots(ql);
var undottedQuarterLength = Math.pow(2, duration.quarterTypeIndex - typeNumber);
var dottedMultiplier = 1 + (Math.pow(2, this._dots) - 1) / Math.pow(2, this._dots);
var unTupletedQl = undottedQuarterLength * dottedMultiplier;
if (unTupletedQl !== ql && ql !== 0) {
typeNumber -= 1;
this._type = duration.ordinalTypeFromNum[typeNumber]; // increase type: eighth to quarter etc.
unTupletedQl *= 2;
var tupletRatio = ql / unTupletedQl;
var ratioRat = common.rationalize(tupletRatio);
if (ratioRat === undefined) {
// probably a Stream with a length that is inexpressable;
} else {
var t = new duration.Tuplet(ratioRat.denominator, ratioRat.numerator, new duration.Duration(unTupletedQl));
this.appendTuplet(t, true); // skipUpdateQl
}
// console.log(ratioRat, ql, unTupletedQl);
}
}
/**
* Add a tuplet to music21j
*
* @param {music21.duration.Tuplet} newTuplet - tuplet to add to `.tuplets`
* @param {boolean} [skipUpdateQl=false] - update the quarterLength afterwards?
*/
}, {
key: 'appendTuplet',
value: function appendTuplet(newTuplet, skipUpdateQl) {
newTuplet.frozen = true;
this._tuplets.push(newTuplet);
if (skipUpdateQl !== true) {
this.updateQlFromFeatures();
}
}
}, {
key: 'dots',
get: function get() {
return this._dots;
},
set: function set(numDots) {
this._dots = numDots;
this.updateQlFromFeatures();
}
/**
* Read or sets the quarterLength of the Duration
*
* Updates the type, dots, tuplets(?)
*
* @type Number
* @instance
* @default 1.0
* @example
* var d = new music21.duration.Duration(2);
* d.quarterLength == 2.0; // true;
* d.quarterLength = 1.75;
* d.dots == 2; // true
* d.type == 'quarter'; // true
*/
}, {
key: 'quarterLength',
get: function get() {
return this._quarterLength;
},
set: function set(ql) {
if (ql === undefined) {
ql = 1.0;
}
this._quarterLength = ql;
this.updateFeaturesFromQl();
}
/**
* Read or sets the type of the duration.
*
* Updates the quarterLength
*
* @type String
* @instance
* @default 'quarter'
* @example
* var d = new music21.duration.Duration(2);
* d.type == 'half; // true
* d.type = 'breve';
* d.quarterLength == 8.0; // true
* d.dots = 1;
* d.type = 'quarter'; // will not change dots
* d.quarterLength == 1.5; // true
*/
}, {
key: 'type',
get: function get() {
return this._type;
},
set: function set(typeIn) {
var typeNumber = duration.ordinalTypeFromNum.indexOf(typeIn);
if (typeNumber === -1) {
console.log('invalid type ' + typeIn);
throw new Music21Exception('invalid type ' + typeIn);
}
this._type = typeIn;
this.updateQlFromFeatures();
}
/**
* Reads the tuplet Array for the duration.
*
* The tuplet array should be considered Read Only.
* Use {@link music21.duration.Duration#appendTuplet} to
* add a tuplet (no way to remove yet)
*
* @type Array<music21.duration.Tuplet>
* @instance
* @default []
*/
}, {
key: 'tuplets',
get: function get() {
return this._tuplets;
}
/**
* Read-only: the duration expressed for VexFlow
*
* @type String
* @instance
* @default 'd'
* @example
* var d = new music21.duration.Duration(2);
* d.vexflowDuration == 'h'; // true;
* d.dots = 2;
* d.vexflowDuration == 'hdd'; // true;
*/
}, {
key: 'vexflowDuration',
get: function get() {
var typeNumber = duration.ordinalTypeFromNum.indexOf(this.type);
var vd = duration.vexflowDurationArray[typeNumber];
if (this.dots > 0) {
for (var i = 0; i < this.dots; i++) {
vd += 'd'; // vexflow does not handle double dots .. or does it???
}
}
return vd;
}
}]);
return Duration;
}(prebase.ProtoM21Object);
duration.Duration = Duration;
/**
* Represents a Tuplet; found in {@link music21.duration.Duration#tuplets}
*
* @class Tuplet
* @memberof music21.duration
* @extends music21.prebase.ProtoM21Object
* @param {number} numberNotesActual - numerator of the tuplet, default 3
* @param {number} numberNotesNormal - denominator of the tuplet, default 2
* @param {(music21.duration.Duration|number)} durationActual - duration or quarterLength of duration type, default music21.duration.Duration(0.5)
* @param {(music21.duration.Duration|number)} durationNormal - unused; see music21p for description
*/
var Tuplet = function (_prebase$ProtoM21Obje2) {
inherits(Tuplet, _prebase$ProtoM21Obje2);
function Tuplet(numberNotesActual, numberNotesNormal, durationActual, durationNormal) {
classCallCheck(this, Tuplet);
var _this2 = possibleConstructorReturn(this, (Tuplet.__proto__ || Object.getPrototypeOf(Tuplet)).call(this));
_this2.numberNotesActual = numberNotesActual || 3;
_this2.numberNotesNormal = numberNotesNormal || 2;
_this2.durationActual = durationActual || new duration.Duration(0.5);
if (typeof _this2.durationActual === 'number') {
_this2.durationActual = new duration.Duration(_this2.durationActual);
}
_this2.durationNormal = durationNormal || _this2.durationActual;
_this2.frozen = false;
_this2.type = undefined;
/**
* Show a bracket above the tuplet
*
* @memberof music21.duration.Tuplet#
* @member {Boolean} bracket
* @default true
*/
_this2.bracket = true;
/**
* Bracket placement. Options are `above` or `below`.
*
* @memberof music21.duration.Tuplet#
* @member {String} placement
* @default 'above'
*/
_this2.placement = 'above';
/**
* What to show above the Tuplet. Options are `number`, `type`, or (string) `none`.
*
* @memberof music21.duration.Tuplet#
* @member {String} tupletActualShow
* @default 'number'
*/
_this2.tupletActualShow = 'number';
_this2.tupletNormalShow = undefined; // undefined, 'ratio' for ratios, 'type' for ratioed notes (does not work)
return _this2;
}
/**
* A nice name for the tuplet.
*
* @type String
* @insta