UNPKG

music21j-port

Version:

A toolkit for computer-aided musicology, Javascript version

1,515 lines (1,360 loc) 1.02 MB
/** * 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