(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["abcjs"] = factory();
else
root["ABCJS"] = factory();
})(this, function() {
return /******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./index.js":
/*!******************!*\
!*** ./index.js ***!
\******************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/**!
Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**This text is from: http://opensource.org/licenses/MIT**
!**/
var version = __webpack_require__(/*! ./version */ "./version.js");
var animation = __webpack_require__(/*! ./src/api/abc_animation */ "./src/api/abc_animation.js");
var tuneBook = __webpack_require__(/*! ./src/api/abc_tunebook */ "./src/api/abc_tunebook.js");
var sequence = __webpack_require__(/*! ./src/synth/abc_midi_sequencer */ "./src/synth/abc_midi_sequencer.js");
var strTranspose = __webpack_require__(/*! ./src/str/output */ "./src/str/output.js");
var abcjs = {};
abcjs.signature = "abcjs-basic v" + version;
Object.keys(animation).forEach(function (key) {
abcjs[key] = animation[key];
});
Object.keys(tuneBook).forEach(function (key) {
abcjs[key] = tuneBook[key];
});
abcjs.renderAbc = __webpack_require__(/*! ./src/api/abc_tunebook_svg */ "./src/api/abc_tunebook_svg.js");
abcjs.tuneMetrics = __webpack_require__(/*! ./src/api/tune-metrics */ "./src/api/tune-metrics.js");
abcjs.TimingCallbacks = __webpack_require__(/*! ./src/api/abc_timing_callbacks */ "./src/api/abc_timing_callbacks.js");
var glyphs = __webpack_require__(/*! ./src/write/creation/glyphs */ "./src/write/creation/glyphs.js");
abcjs.setGlyph = glyphs.setSymbol;
abcjs.strTranspose = strTranspose;
var CreateSynth = __webpack_require__(/*! ./src/synth/create-synth */ "./src/synth/create-synth.js");
var instrumentIndexToName = __webpack_require__(/*! ./src/synth/instrument-index-to-name */ "./src/synth/instrument-index-to-name.js");
var pitchToNoteName = __webpack_require__(/*! ./src/synth/pitch-to-note-name */ "./src/synth/pitch-to-note-name.js");
var SynthSequence = __webpack_require__(/*! ./src/synth/synth-sequence */ "./src/synth/synth-sequence.js");
var CreateSynthControl = __webpack_require__(/*! ./src/synth/create-synth-control */ "./src/synth/create-synth-control.js");
var registerAudioContext = __webpack_require__(/*! ./src/synth/register-audio-context */ "./src/synth/register-audio-context.js");
var activeAudioContext = __webpack_require__(/*! ./src/synth/active-audio-context */ "./src/synth/active-audio-context.js");
var supportsAudio = __webpack_require__(/*! ./src/synth/supports-audio */ "./src/synth/supports-audio.js");
var playEvent = __webpack_require__(/*! ./src/synth/play-event */ "./src/synth/play-event.js");
var SynthController = __webpack_require__(/*! ./src/synth/synth-controller */ "./src/synth/synth-controller.js");
var getMidiFile = __webpack_require__(/*! ./src/synth/get-midi-file */ "./src/synth/get-midi-file.js");
var midiRenderer = __webpack_require__(/*! ./src/synth/abc_midi_renderer */ "./src/synth/abc_midi_renderer.js");
abcjs.synth = {
CreateSynth: CreateSynth,
instrumentIndexToName: instrumentIndexToName,
pitchToNoteName: pitchToNoteName,
SynthController: SynthController,
SynthSequence: SynthSequence,
CreateSynthControl: CreateSynthControl,
registerAudioContext: registerAudioContext,
activeAudioContext: activeAudioContext,
supportsAudio: supportsAudio,
playEvent: playEvent,
getMidiFile: getMidiFile,
sequence: sequence,
midiRenderer: midiRenderer
};
abcjs['Editor'] = __webpack_require__(/*! ./src/edit/abc_editor */ "./src/edit/abc_editor.js");
abcjs['EditArea'] = __webpack_require__(/*! ./src/edit/abc_editarea */ "./src/edit/abc_editarea.js");
module.exports = abcjs;
/***/ }),
/***/ "./src/api/abc_animation.js":
/*!**********************************!*\
!*** ./src/api/abc_animation.js ***!
\**********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_animation.js: handles animating the music in real time.
var TimingCallbacks = __webpack_require__(/*! ./abc_timing_callbacks */ "./src/api/abc_timing_callbacks.js");
var animation = {};
(function () {
"use strict";
var timer;
var cursor;
animation.startAnimation = function (paper, tune, options) {
//options.bpm
//options.showCursor
//options.hideCurrentMeasure
//options.hideFinishedMeasures
if (timer) {
timer.stop();
timer = undefined;
}
if (options.showCursor) {
cursor = paper.querySelector('.abcjs-cursor');
if (!cursor) {
cursor = document.createElement('DIV');
cursor.className = 'abcjs-cursor cursor';
cursor.style.position = 'absolute';
paper.appendChild(cursor);
paper.style.position = 'relative';
}
}
function hideMeasures(elements) {
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (!element.classList.contains('abcjs-bar')) element.style.display = "none";
}
}
var lastMeasure;
function disappearMeasuresAfter(selector) {
if (lastMeasure) {
var elements = paper.querySelectorAll(lastMeasure);
hideMeasures(elements);
}
lastMeasure = selector;
}
function disappearMeasuresBefore(selector) {
var elements = paper.querySelectorAll(selector);
hideMeasures(elements);
}
function measureCallback(selector) {
if (options.hideCurrentMeasure) {
disappearMeasuresBefore(selector);
} else if (options.hideFinishedMeasures) {
disappearMeasuresAfter(selector);
}
}
function getLineAndMeasure(element) {
return '.abcjs-l' + element.line + '.abcjs-m' + element.measureNumber;
}
function setCursor(range) {
if (range) {
if (range.measureStart) {
var selector = getLineAndMeasure(range);
if (selector) measureCallback(selector);
}
if (cursor) {
cursor.style.left = range.left + "px";
cursor.style.top = range.top + "px";
cursor.style.width = range.width + "px";
cursor.style.height = range.height + "px";
}
} else {
timer.stop();
timer = undefined;
}
}
timer = new TimingCallbacks(tune, {
qpm: options.bpm,
eventCallback: setCursor
});
timer.start();
};
animation.pauseAnimation = function (pause) {
if (timer) {
if (pause) timer.pause();else timer.start();
}
};
animation.stopAnimation = function () {
if (timer) {
timer.stop();
timer = undefined;
}
};
})();
module.exports = animation;
/***/ }),
/***/ "./src/api/abc_timing_callbacks.js":
/*!*****************************************!*\
!*** ./src/api/abc_timing_callbacks.js ***!
\*****************************************/
/***/ (function(module) {
var TimingCallbacks = function TimingCallbacks(target, params) {
var self = this;
if (!params) params = {};
self.qpm = params.qpm ? parseInt(params.qpm, 10) : null;
if (!self.qpm) {
var tempo = target.metaText ? target.metaText.tempo : null;
self.qpm = target.getBpm(tempo);
}
self.extraMeasuresAtBeginning = params.extraMeasuresAtBeginning ? parseInt(params.extraMeasuresAtBeginning, 10) : 0;
self.beatCallback = params.beatCallback; // This is called for each beat.
self.eventCallback = params.eventCallback; // This is called for each note or rest encountered.
self.lineEndCallback = params.lineEndCallback; // This is called when the end of a line is approaching.
self.lineEndAnticipation = params.lineEndAnticipation ? parseInt(params.lineEndAnticipation, 10) : 0; // How many milliseconds before the end should the call happen.
self.beatSubdivisions = params.beatSubdivisions ? parseInt(params.beatSubdivisions, 10) : 1; // how many callbacks per beat is desired.
self.joggerTimer = null;
self.replaceTarget = function (newTarget) {
self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
if (newTarget.noteTimings.length === 0) self.noteTimings = newTarget.setTiming(0, 0);
if (self.lineEndCallback) {
self.lineEndTimings = getLineEndTimings(newTarget.noteTimings, self.lineEndAnticipation);
}
self.startTime = null;
self.currentBeat = 0;
self.currentEvent = 0;
self.currentLine = 0;
self.currentTime = 0;
self.isPaused = false;
self.isRunning = false;
self.pausedPercent = null;
self.justUnpaused = false;
self.newSeekPercent = 0;
self.lastTimestamp = 0;
if (self.noteTimings.length === 0) return;
// noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
self.lastMoment = self.noteTimings[self.noteTimings.length - 1].milliseconds;
self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
};
self.replaceTarget(target);
self.doTiming = function (timestamp) {
// This is called 60 times a second, that is, every 16 msecs.
//console.log("doTiming", timestamp, timestamp-self.lastTimestamp);
if (self.lastTimestamp === timestamp) return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same instant.
self.lastTimestamp = timestamp;
if (!self.startTime) {
self.startTime = timestamp;
}
if (!self.isPaused && self.isRunning) {
self.currentTime = timestamp - self.startTime;
self.currentTime += 16; // Add a little slop because this function isn't called exactly.
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
if (self.eventCallback && self.noteTimings[self.currentEvent].type === 'event') {
var thisStartTime = self.startTime; // the event callback can call seek and change the position from beneath us.
self.eventCallback(self.noteTimings[self.currentEvent]);
if (thisStartTime !== self.startTime) {
self.currentTime = timestamp - self.startTime;
}
}
self.currentEvent++;
}
if (self.lineEndCallback && self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds < self.currentTime && self.currentEvent < self.noteTimings.length) {
var leftEvent = self.noteTimings[self.currentEvent].milliseconds === self.currentTime ? self.noteTimings[self.currentEvent] : self.noteTimings[self.currentEvent - 1];
self.lineEndCallback(self.lineEndTimings[self.currentLine], leftEvent, {
line: self.currentLine,
endTimings: self.lineEndTimings,
currentTime: self.currentTime
});
self.currentLine++;
}
if (self.currentTime < self.lastMoment) {
requestAnimationFrame(self.doTiming);
if (self.currentBeat * self.millisecondsPerBeat < self.currentTime) {
var ret = self.doBeatCallback(timestamp);
if (ret !== null) self.currentTime = ret;
}
} else if (self.currentBeat <= self.totalBeats) {
// Because of timing issues (for instance, if the browser tab isn't active), the beat callbacks might not have happened when they are supposed to. To keep the client programs from having to deal with that, this will keep calling the loop until all of them have been sent.
if (self.beatCallback) {
var ret2 = self.doBeatCallback(timestamp);
if (ret2 !== null) self.currentTime = ret2;
requestAnimationFrame(self.doTiming);
}
}
if (self.currentTime >= self.lastMoment) {
if (self.eventCallback) {
// At the end, the event callback can return "continue" to keep from stopping.
// The event callback can either be a promise or not.
var promise = self.eventCallback(null);
self.shouldStop(promise).then(function (shouldStop) {
if (shouldStop) self.stop();
});
} else self.stop();
}
}
};
self.shouldStop = function (promise) {
// The return of the last event callback can be "continue" or a promise that returns "continue".
// If it is then don't call stop. Any other value calls stop.
return new Promise(function (resolve) {
if (!promise) return resolve(true);
if (promise === "continue") return resolve(false);
if (promise.then) {
promise.then(function (result) {
resolve(result !== "continue");
});
}
});
};
self.doBeatCallback = function (timestamp) {
if (self.beatCallback) {
var next = self.currentEvent;
while (next < self.noteTimings.length && self.noteTimings[next].left === null) {
next++;
}
var endMs;
var ev;
if (next < self.noteTimings.length) {
endMs = self.noteTimings[next].milliseconds;
next = Math.max(0, self.currentEvent - 1);
while (next >= 0 && self.noteTimings[next].left === null) {
next--;
}
ev = self.noteTimings[next];
}
var position = {};
var debugInfo = {};
if (ev) {
position.top = ev.top;
position.height = ev.height;
// timestamp = the time passed in from the animation timer
// self.startTime = the time that the tune was started (if there was seeking or pausing, it is adjusted to keep the math the same)
// ev = the event that is either happening now or has most recently passed.
// ev.milliseconds = the time that the current event starts (relative to self.startTime)
// endMs = the time that the next event starts
// ev.endX = the x coordinate that the next event happens (or the end of the line or repeat measure)
// ev.left = the x coordinate of the current event
//
// The output is the X coordinate of the current cursor location. It is calculated with the ratio of the length of the event and the width of it.
var offMs = Math.max(0, timestamp - self.startTime - ev.milliseconds); // Offset in time from the last beat
var gapMs = endMs - ev.milliseconds; // Length of this event in time
var gapPx = ev.endX - ev.left; // The length in pixels
var offPx = gapMs ? offMs * gapPx / gapMs : 0;
position.left = ev.left + offPx;
// See if this is before the first event - that is the case where there are "prep beats"
if (self.currentEvent === 0 && ev.milliseconds > timestamp - self.startTime) position.left = undefined;
debugInfo = {
timestamp: timestamp,
startTime: self.startTime,
ev: ev,
endMs: endMs,
offMs: offMs,
offPx: offPx,
gapMs: gapMs,
gapPx: gapPx
};
} else {
debugInfo = {
timestamp: timestamp,
startTime: self.startTime
};
}
var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
self.beatCallback(self.currentBeat / self.beatSubdivisions, self.totalBeats / self.beatSubdivisions, self.lastMoment, position, debugInfo);
if (thisStartTime !== self.startTime) {
return timestamp - self.startTime;
} else self.currentBeat++;
}
return null;
};
// In general music doesn't need a timer at 60 fps because notes don't happen that fast.
// For instance, at 120 beats per minute, a sixteenth note takes 125ms. So just as a
// compromise value between performance and jank this is set about half that.
var JOGGING_INTERVAL = 60;
self.animationJogger = function () {
// There are some cases where the animation timer doesn't work: for instance when
// this isn't running in a visible tab and sometimes on mobile devices. We compensate
// by having a backup timer using setTimeout. This won't be accurate so the performance
// will be jerky, but without it the requestAnimationFrame might be skipped and so
// not called again.
if (self.isRunning) {
self.doTiming(performance.now());
self.joggerTimer = setTimeout(self.animationJogger, JOGGING_INTERVAL);
}
};
self.start = function (offsetPercent, units) {
self.isRunning = true;
if (self.isPaused) {
self.isPaused = false;
if (offsetPercent === undefined) self.justUnpaused = true;
}
if (offsetPercent) {
self.setProgress(offsetPercent, units);
} else if (offsetPercent === 0) {
self.reset();
} else if (self.pausedPercent !== null) {
var now = performance.now();
self.currentTime = self.lastMoment * self.pausedPercent;
self.startTime = now - self.currentTime;
self.pausedPercent = null;
self.reportNext = true;
}
requestAnimationFrame(self.doTiming);
self.joggerTimer = setTimeout(self.animationJogger, JOGGING_INTERVAL);
};
self.pause = function () {
self.isPaused = true;
var now = performance.now();
self.pausedPercent = (now - self.startTime) / self.lastMoment;
self.isRunning = false;
if (self.joggerTimer) {
clearTimeout(self.joggerTimer);
self.joggerTimer = null;
}
};
self.currentMillisecond = function () {
return self.currentTime;
};
self.reset = function () {
self.currentBeat = 0;
self.currentEvent = 0;
self.currentLine = 0;
self.startTime = null;
self.pausedPercent = null;
};
self.stop = function () {
self.pause();
self.reset();
};
self.setProgress = function (position, units) {
// the effect of this function is to move startTime so that the callbacks happen correctly for the new seek.
var percent;
switch (units) {
case "seconds":
self.currentTime = position * 1000;
if (self.currentTime < 0) self.currentTime = 0;
if (self.currentTime > self.lastMoment) self.currentTime = self.lastMoment;
percent = self.currentTime / self.lastMoment;
break;
case "beats":
self.currentTime = position * self.millisecondsPerBeat * self.beatSubdivisions;
if (self.currentTime < 0) self.currentTime = 0;
if (self.currentTime > self.lastMoment) self.currentTime = self.lastMoment;
percent = self.currentTime / self.lastMoment;
break;
default:
// this is "percent" or any illegal value
// this is passed a value between 0 and 1.
percent = position;
if (percent < 0) percent = 0;
if (percent > 1) percent = 1;
self.currentTime = self.lastMoment * percent;
break;
}
if (!self.isRunning) self.pausedPercent = percent;
var now = performance.now();
self.startTime = now - self.currentTime;
var oldEvent = self.currentEvent;
self.currentEvent = 0;
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
self.currentEvent++;
}
if (self.lineEndCallback) {
self.currentLine = 0;
while (self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds + self.lineEndAnticipation < self.currentTime) {
self.currentLine++;
}
}
var oldBeat = self.currentBeat;
self.currentBeat = Math.floor(self.currentTime / self.millisecondsPerBeat);
if (self.beatCallback && oldBeat !== self.currentBeat)
// If the movement caused the beat to change, then immediately report it to the client.
self.doBeatCallback(self.startTime + self.currentTime);
if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event') self.eventCallback(self.noteTimings[self.currentEvent]);
if (self.lineEndCallback) self.lineEndCallback(self.lineEndTimings[self.currentLine], self.noteTimings[self.currentEvent], {
line: self.currentLine,
endTimings: self.lineEndTimings
});
self.joggerTimer = setTimeout(self.animationJogger, JOGGING_INTERVAL);
};
};
function getLineEndTimings(timings, anticipation) {
// Returns an array of milliseconds to call the lineEndCallback.
// This figures out the timing of the beginning of each line and subtracts the anticipation from it.
var callbackTimes = [];
var lastTop = null;
for (var i = 0; i < timings.length; i++) {
var timing = timings[i];
if (timing.type !== 'end' && timing.top !== lastTop) {
callbackTimes.push({
measureNumber: timing.measureNumber,
milliseconds: timing.milliseconds - anticipation,
top: timing.top,
bottom: timing.top + timing.height
});
lastTop = timing.top;
}
}
return callbackTimes;
}
module.exports = TimingCallbacks;
/***/ }),
/***/ "./src/api/abc_tunebook.js":
/*!*********************************!*\
!*** ./src/api/abc_tunebook.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
// abc_tunebook.js: splits a string representing ABC Music Notation into individual tunes.
var Parse = __webpack_require__(/*! ../parse/abc_parse */ "./src/parse/abc_parse.js");
var bookParser = __webpack_require__(/*! ../parse/abc_parse_book */ "./src/parse/abc_parse_book.js");
var tablatures = __webpack_require__(/*! ../tablatures/abc_tablatures */ "./src/tablatures/abc_tablatures.js");
var tunebook = {};
(function () {
"use strict";
tunebook.numberOfTunes = function (abc) {
var tunes = abc.split("\nX:");
var num = tunes.length;
if (num === 0) num = 1;
return num;
};
var TuneBook = tunebook.TuneBook = function (book) {
var parsed = bookParser(book);
this.header = parsed.header;
this.tunes = parsed.tunes;
};
TuneBook.prototype.getTuneById = function (id) {
for (var i = 0; i < this.tunes.length; i++) {
if (this.tunes[i].id === '' + id) return this.tunes[i];
}
return null;
};
TuneBook.prototype.getTuneByTitle = function (title) {
for (var i = 0; i < this.tunes.length; i++) {
if (this.tunes[i].title === title) return this.tunes[i];
}
return null;
};
tunebook.parseOnly = function (abc, params) {
var numTunes = tunebook.numberOfTunes(abc);
// this just needs to be passed in because this tells the engine how many tunes to process.
var output = [];
for (var i = 0; i < numTunes; i++) {
output.push(1);
}
function callback() {
// Don't need to do anything with the parsed tunes.
}
return tunebook.renderEngine(callback, output, abc, params);
};
tunebook.renderEngine = function (callback, output, abc, params) {
var ret = [];
var isArray = function isArray(testObject) {
return testObject && !testObject.propertyIsEnumerable('length') && _typeof(testObject) === 'object' && typeof testObject.length === 'number';
};
// check and normalize input parameters
if (output === undefined || abc === undefined) return;
if (!isArray(output)) output = [output];
if (params === undefined) params = {};
var currentTune = params.startingTune ? parseInt(params.startingTune, 10) : 0;
// parse the abc string
var book = new TuneBook(abc);
var abcParser = new Parse();
// output each tune, if it exists. Otherwise clear the div.
for (var i = 0; i < output.length; i++) {
var div = output[i];
if (div === "*") {
// This is for "headless" rendering: doing the work but not showing the svg.
} else if (typeof div === "string") div = document.getElementById(div);
if (div) {
if (currentTune >= 0 && currentTune < book.tunes.length) {
abcParser.parse(book.tunes[currentTune].abc, params, book.tunes[currentTune].startPos - book.header.length);
var tune = abcParser.getTune();
//
// Init tablatures plugins
//
if (params.tablature) {
tune.tablatures = tablatures.preparePlugins(tune, currentTune, params);
}
var warnings = abcParser.getWarnings();
if (warnings) tune.warnings = warnings;
var override = callback(div, tune, i, book.tunes[currentTune].abc);
ret.push(override ? override : tune);
} else {
if (div['innerHTML']) div.innerHTML = "";
}
}
currentTune++;
}
return ret;
};
function flattenTune(tuneObj) {
// This removes the line breaks and removes the non-music lines.
var staves = [];
for (var j = 0; j < tuneObj.lines.length; j++) {
var line = tuneObj.lines[j];
if (line.staff) {
for (var k = 0; k < line.staff.length; k++) {
var staff = line.staff[k];
if (!staves[k]) staves[k] = staff;else {
for (var i = 0; i < staff.voices.length; i++) {
if (staves[k].voices[i]) staves[k].voices[i] = staves[k].voices[i].concat(staff.voices[i]);
// TODO-PER: If staves[k].voices[i] doesn't exist, that means a voice appeared in the middle of the tune. That isn't handled yet.
}
}
}
}
}
return staves;
}
function measuresParser(staff, tune) {
var voices = [];
var lastChord = null;
var measureStartChord = null;
var fragStart = null;
var hasNotes = false;
for (var i = 0; i < staff.voices.length; i++) {
var voice = staff.voices[i];
voices.push([]);
for (var j = 0; j < voice.length; j++) {
var elem = voice[j];
if (fragStart === null && elem.startChar >= 0) {
fragStart = elem.startChar;
if (elem.chord === undefined) measureStartChord = lastChord;else measureStartChord = null;
}
if (elem.chord) lastChord = elem;
if (elem.el_type === 'bar') {
if (hasNotes) {
var frag = tune.abc.substring(fragStart, elem.endChar);
var measure = {
abc: frag
};
lastChord = measureStartChord && measureStartChord.chord && measureStartChord.chord.length > 0 ? measureStartChord.chord[0].name : null;
if (lastChord) measure.lastChord = lastChord;
if (elem.startEnding) measure.startEnding = elem.startEnding;
if (elem.endEnding) measure.endEnding = elem.endEnding;
voices[i].push(measure);
fragStart = null;
hasNotes = false;
}
} else if (elem.el_type === 'note') {
hasNotes = true;
}
}
}
return voices;
}
tunebook.extractMeasures = function (abc) {
var tunes = [];
var book = new TuneBook(abc);
for (var i = 0; i < book.tunes.length; i++) {
var tune = book.tunes[i];
var arr = tune.abc.split("K:");
var arr2 = arr[1].split("\n");
var header = arr[0] + "K:" + arr2[0] + "\n";
var lastChord = null;
var measureStartChord = null;
var fragStart = null;
var measures = [];
var hasNotes = false;
var tuneObj = tunebook.parseOnly(tune.abc)[0];
var hasPickup = tuneObj.getPickupLength() > 0;
// var staves = flattenTune(tuneObj);
// for (var s = 0; s < staves.length; s++) {
// var voices = measuresParser(staves[s], tune);
// if (s === 0)
// measures = voices;
// else {
// for (var ss = 0; ss < voices.length; ss++) {
// var voice = voices[ss];
// if (measures.length <= ss)
// measures.push([]);
// var measureVoice = measures[ss];
// for (var sss = 0; sss < voice.length; sss++) {
// if (measureVoice.length > sss)
// measureVoice[sss].abc += "\n" + voice[sss].abc;
// else
// measures.push(voice[sss]);
// }
// }
// }
// console.log(voices);
// }
// measures = measures[0];
for (var j = 0; j < tuneObj.lines.length; j++) {
var line = tuneObj.lines[j];
if (line.staff) {
for (var k = 0; k < 1 /*line.staff.length*/; k++) {
var staff = line.staff[k];
for (var kk = 0; kk < 1 /*staff.voices.length*/; kk++) {
var voice = staff.voices[kk];
for (var kkk = 0; kkk < voice.length; kkk++) {
var elem = voice[kkk];
if (fragStart === null && elem.startChar >= 0) {
fragStart = elem.startChar;
if (elem.chord === undefined) measureStartChord = lastChord;else measureStartChord = null;
}
if (elem.chord) lastChord = elem;
if (elem.el_type === 'bar') {
if (hasNotes) {
var frag = tune.abc.substring(fragStart, elem.endChar);
var measure = {
abc: frag
};
lastChord = measureStartChord && measureStartChord.chord && measureStartChord.chord.length > 0 ? measureStartChord.chord[0].name : null;
if (lastChord) measure.lastChord = lastChord;
if (elem.startEnding) measure.startEnding = elem.startEnding;
if (elem.endEnding) measure.endEnding = elem.endEnding;
measures.push(measure);
fragStart = null;
hasNotes = false;
}
} else if (elem.el_type === 'note') {
hasNotes = true;
}
}
}
}
}
}
tunes.push({
header: header,
measures: measures,
hasPickup: hasPickup
});
}
return tunes;
};
})();
module.exports = tunebook;
/***/ }),
/***/ "./src/api/abc_tunebook_svg.js":
/*!*************************************!*\
!*** ./src/api/abc_tunebook_svg.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var tunebook = __webpack_require__(/*! ./abc_tunebook */ "./src/api/abc_tunebook.js");
var Tune = __webpack_require__(/*! ../data/abc_tune */ "./src/data/abc_tune.js");
var EngraverController = __webpack_require__(/*! ../write/engraver-controller */ "./src/write/engraver-controller.js");
var Parse = __webpack_require__(/*! ../parse/abc_parse */ "./src/parse/abc_parse.js");
var wrap = __webpack_require__(/*! ../parse/wrap_lines */ "./src/parse/wrap_lines.js");
var resizeDivs = {};
function resizeOuter() {
var width = window.innerWidth;
for (var id in resizeDivs) {
if (resizeDivs.hasOwnProperty(id)) {
var outer = resizeDivs[id];
var ofs = outer.offsetLeft;
width -= ofs * 2;
outer.style.width = width + "px";
}
}
}
try {
window.addEventListener("resize", resizeOuter);
window.addEventListener("orientationChange", resizeOuter);
} catch (e) {
// if we aren't in a browser, this code will crash, but it is not needed then either.
}
function renderOne(div, tune, params, tuneNumber, lineOffset) {
if (params.viewportHorizontal) {
// Create an inner div that holds the music, so that the passed in div will be the viewport.
div.innerHTML = '
\n';
if (hasLoop) {
var repeatTitle = options.repeatTitle ? options.repeatTitle : "Click to toggle play once/repeat.";
var repeatAria = options.repeatAria ? options.repeatAria : repeatTitle;
html += '\n';
}
if (hasRestart) {
var restartTitle = options.restartTitle ? options.restartTitle : "Click to go to beginning.";
var restartAria = options.restartAria ? options.restartAria : restartTitle;
html += '\n';
}
if (hasPlay) {
var playTitle = options.playTitle ? options.playTitle : "Click to play/pause.";
var playAria = options.playAria ? options.playAria : playTitle;
html += '\n';
}
if (hasProgress) {
var randomTitle = options.randomTitle ? options.randomTitle : "Click to change the playback position.";
var randomAria = options.randomAria ? options.randomAria : randomTitle;
html += '\n';
}
if (hasClock) {
html += '\n';
}
if (hasWarp) {
var warpTitle = options.warpTitle ? options.warpTitle : "Change the playback speed.";
var warpAria = options.warpAria ? options.warpAria : warpTitle;
var bpm = options.bpm ? options.bpm : "BPM";
html += ' ( ' + bpm + ')\n';
}
html += '
CSS required: load abcjs-audio.css
';
html += '
\n';
parent.innerHTML = html;
}
function acResumerMiddleWare(next, ev, playBtn, afterResume, isPromise) {
var needsInit = true;
if (!activeAudioContext()) {
registerAudioContext();
} else {
needsInit = activeAudioContext().state === "suspended";
}
if (!supportsAudio()) {
throw {
status: "NotSupported",
message: "This browser does not support audio."
};
}
if ((needsInit || isPromise) && playBtn) playBtn.classList.add("abcjs-loading");
if (needsInit) {
activeAudioContext().resume().then(function () {
if (afterResume) {
afterResume().then(function (response) {
doNext(next, ev, playBtn, isPromise);
});
} else {
doNext(next, ev, playBtn, isPromise);
}
});
} else {
doNext(next, ev, playBtn, isPromise);
}
}
function doNext(next, ev, playBtn, isPromise) {
if (isPromise) {
next(ev).then(function () {
if (playBtn) playBtn.classList.remove("abcjs-loading");
});
} else {
next(ev);
if (playBtn) playBtn.classList.remove("abcjs-loading");
}
}
function attachListeners(self) {
var hasLoop = !!self.options.loopHandler;
var hasRestart = !!self.options.restartHandler;
var hasPlay = !!self.options.playHandler || !!self.options.playPromiseHandler;
var hasProgress = !!self.options.progressHandler;
var hasWarp = !!self.options.warpHandler;
var playBtn = self.parent.querySelector(".abcjs-midi-start");
if (hasLoop) self.parent.querySelector(".abcjs-midi-loop").addEventListener("click", function (ev) {
acResumerMiddleWare(self.options.loopHandler, ev, playBtn, self.options.afterResume);
});
if (hasRestart) self.parent.querySelector(".abcjs-midi-reset").addEventListener("click", function (ev) {
acResumerMiddleWare(self.options.restartHandler, ev, playBtn, self.options.afterResume);
});
if (hasPlay) playBtn.addEventListener("click", function (ev) {
acResumerMiddleWare(self.options.playPromiseHandler || self.options.playHandler, ev, playBtn, self.options.afterResume, !!self.options.playPromiseHandler);
});
if (hasProgress) self.parent.querySelector(".abcjs-midi-progress-background").addEventListener("click", function (ev) {
acResumerMiddleWare(self.options.progressHandler, ev, playBtn, self.options.afterResume);
});
if (hasWarp) self.parent.querySelector(".abcjs-midi-tempo").addEventListener("change", function (ev) {
acResumerMiddleWare(self.options.warpHandler, ev, playBtn, self.options.afterResume);
});
}
module.exports = CreateSynthControl;
/***/ }),
/***/ "./src/synth/create-synth.js":
/*!***********************************!*\
!*** ./src/synth/create-synth.js ***!
\***********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var getNote = __webpack_require__(/*! ./load-note */ "./src/synth/load-note.js");
var createNoteMap = __webpack_require__(/*! ./create-note-map */ "./src/synth/create-note-map.js");
var registerAudioContext = __webpack_require__(/*! ./register-audio-context */ "./src/synth/register-audio-context.js");
var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
var supportsAudio = __webpack_require__(/*! ./supports-audio */ "./src/synth/supports-audio.js");
var pitchToNoteName = __webpack_require__(/*! ./pitch-to-note-name */ "./src/synth/pitch-to-note-name.js");
var instrumentIndexToName = __webpack_require__(/*! ./instrument-index-to-name */ "./src/synth/instrument-index-to-name.js");
var downloadBuffer = __webpack_require__(/*! ./download-buffer */ "./src/synth/download-buffer.js");
var placeNote = __webpack_require__(/*! ./place-note */ "./src/synth/place-note.js");
var soundsCache = __webpack_require__(/*! ./sounds-cache */ "./src/synth/sounds-cache.js");
// TODO-PER: remove the midi tests from here: I don't think the object can be constructed unless it passes.
var notSupportedMessage = "MIDI is not supported in this browser.";
var originalSoundFontUrl = "https://paulrosen.github.io/midi-js-soundfonts/abcjs/";
// These are the original soundfonts supplied. They will need a volume boost:
var defaultSoundFontUrl = "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/";
var alternateSoundFontUrl = "https://paulrosen.github.io/midi-js-soundfonts/MusyngKite/";
function CreateSynth() {
var self = this;
self.audioBufferPossible = undefined;
self.directSource = []; // type: AudioBufferSourceNode
self.startTimeSec = undefined; // the time (in seconds) that the audio started: used for pause to get the pausedTimeSec.
self.pausedTimeSec = undefined; // the position (in seconds) that the audio was paused: used for resume.
self.audioBuffers = []; // cache of the buffers so starting play can be fast.
self.duration = undefined; // the duration of the tune in seconds.
self.isRunning = false; // whether there is currently a sound buffer running.
self.options = undefined;
self.pickupLength = 0;
// Load and cache all needed sounds
self.init = function (options) {
if (!options) options = {};
if (options.options) self.options = options.options;
registerAudioContext(options.audioContext); // This works no matter what - if there is already an ac it is a nop; if the context is not passed in, then it creates one.
var startTime = activeAudioContext().currentTime;
self.debugCallback = options.debugCallback;
if (self.debugCallback) self.debugCallback("init called");
self.audioBufferPossible = self._deviceCapable();
if (!self.audioBufferPossible) return Promise.reject({
status: "NotSupported",
message: notSupportedMessage
});
var params = options.options ? options.options : {};
self.soundFontUrl = params.soundFontUrl ? params.soundFontUrl : defaultSoundFontUrl;
if (self.soundFontUrl[self.soundFontUrl.length - 1] !== '/') self.soundFontUrl += '/';
if (params.soundFontVolumeMultiplier || params.soundFontVolumeMultiplier === 0) self.soundFontVolumeMultiplier = params.soundFontVolumeMultiplier;else if (self.soundFontUrl === defaultSoundFontUrl || self.soundFontUrl === alternateSoundFontUrl) self.soundFontVolumeMultiplier = 3.0;else if (self.soundFontUrl === originalSoundFontUrl) self.soundFontVolumeMultiplier = 0.4;else self.soundFontVolumeMultiplier = 1.0;
if (params.programOffsets) self.programOffsets = params.programOffsets;else if (self.soundFontUrl === originalSoundFontUrl) self.programOffsets = {
"bright_acoustic_piano": 20,
"honkytonk_piano": 20,
"electric_piano_1": 30,
"electric_piano_2": 30,
"harpsichord": 40,
"clavinet": 20,
"celesta": 20,
"glockenspiel": 40,
"vibraphone": 30,
"marimba": 35,
"xylophone": 30,
"tubular_bells": 35,
"dulcimer": 30,
"drawbar_organ": 20,
"percussive_organ": 25,
"rock_organ": 20,
"church_organ": 40,
"reed_organ": 40,
"accordion": 40,
"harmonica": 40,
"acoustic_guitar_nylon": 20,
"acoustic_guitar_steel": 30,
"electric_guitar_jazz": 25,
"electric_guitar_clean": 15,
"electric_guitar_muted": 35,
"overdriven_guitar": 25,
"distortion_guitar": 20,
"guitar_harmonics": 30,
"electric_bass_finger": 15,
"electric_bass_pick": 30,
"fretless_bass": 40,
"violin": 105,
"viola": 50,
"cello": 40,
"contrabass": 60,
"trumpet": 10,
"trombone": 90,
"alto_sax": 20,
"tenor_sax": 20,
"clarinet": 20,
"flute": 50,
"banjo": 50,
"woodblock": 20
};else self.programOffsets = {};
var p = params.fadeLength !== undefined ? parseInt(params.fadeLength, 10) : NaN;
self.fadeLength = isNaN(p) ? 200 : p;
p = params.noteEnd !== undefined ? parseInt(params.noteEnd, 10) : NaN;
self.noteEnd = isNaN(p) ? 0 : p;
self.pan = params.pan;
self.meterSize = 1;
if (options.visualObj) {
self.flattened = options.visualObj.setUpAudio(params);
var meter = options.visualObj.getMeterFraction();
if (meter.den) self.meterSize = options.visualObj.getMeterFraction().num / options.visualObj.getMeterFraction().den;
self.pickupLength = options.visualObj.getPickupLength();
} else if (options.sequence) self.flattened = options.sequence;else return Promise.reject(new Error("Must pass in either a visualObj or a sequence"));
self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : options.visualObj ? options.visualObj.millisecondsPerMeasure(self.flattened.tempo) : 1000;
self.beatsPerMeasure = options.visualObj ? options.visualObj.getBeatsPerMeasure() : 4;
self.sequenceCallback = params.sequenceCallback;
self.callbackContext = params.callbackContext;
self.onEnded = params.onEnded;
self.meterFraction = options.visualObj ? options.visualObj.getMeterFraction() : {
den: 1
}; // If we are given a sequence instead of a regular visual obj, then don't do the swing
var allNotes = {};
var cached = [];
var errorNotes = [];
var currentInstrument = instrumentIndexToName[0];
self.flattened.tracks.forEach(function (track) {
track.forEach(function (event) {
if (event.cmd === "program" && instrumentIndexToName[event.instrument]) currentInstrument = instrumentIndexToName[event.instrument];
if (event.pitch !== undefined) {
var pitchNumber = event.pitch;
var noteName = pitchToNoteName[pitchNumber];
var inst = event.instrument !== undefined ? instrumentIndexToName[event.instrument] : currentInstrument;
if (noteName) {
if (!allNotes[inst]) allNotes[inst] = {};
if (!soundsCache[inst] || !soundsCache[inst][noteName]) allNotes[inst][noteName] = true;else {
var label2 = inst + ":" + noteName;
if (cached.indexOf(label2) < 0) cached.push(label2);
}
} else {
var label = inst + ":" + noteName;
console.log("Can't find note: ", pitchNumber, label);
if (errorNotes.indexOf(label) < 0) errorNotes.push(label);
}
}
});
});
if (self.debugCallback) self.debugCallback("note gathering time = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
startTime = activeAudioContext().currentTime;
var notes = [];
Object.keys(allNotes).forEach(function (instrument) {
Object.keys(allNotes[instrument]).forEach(function (note) {
notes.push({
instrument: instrument,
note: note
});
});
});
if (self.debugCallback) self.debugCallback("notes " + JSON.stringify(notes));
// If there are lots of notes, load them in batches
var batches = [];
var CHUNK = 256;
for (var i = 0; i < notes.length; i += CHUNK) {
batches.push(notes.slice(i, i + CHUNK));
}
return new Promise(function (resolve, reject) {
var results = {
cached: cached,
error: errorNotes,
loaded: []
};
var index = 0;
var next = function next() {
if (self.debugCallback) self.debugCallback("loadBatch idx=" + index + " len=" + batches.length);
if (index < batches.length) {
self._loadBatch(batches[index], self.soundFontUrl, startTime).then(function (data) {
if (self.debugCallback) self.debugCallback("loadBatch then");
startTime = activeAudioContext().currentTime;
if (data) {
if (data.error) results.error = results.error.concat(data.error);
if (data.loaded) results.loaded = results.loaded.concat(data.loaded);
}
index++;
next();
}, reject);
} else {
if (self.debugCallback) self.debugCallback("resolve init");
resolve(results);
}
};
next();
});
};
self._loadBatch = function (batch, soundFontUrl, startTime, delay) {
// This is called recursively to see if the sounds have loaded. The "delay" parameter is how long it has been since the original call.
var promises = [];
batch.forEach(function (item) {
if (self.debugCallback) self.debugCallback("getNote " + item.instrument + ':' + item.note);
promises.push(getNote(soundFontUrl, item.instrument, item.note, activeAudioContext()));
});
return Promise.all(promises).then(function (response) {
if (self.debugCallback) self.debugCallback("mp3 load time = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
var loaded = [];
var cached = [];
var pending = [];
var error = [];
for (var i = 0; i < response.length; i++) {
var oneResponse = response[i];
var which = oneResponse.instrument + ":" + oneResponse.name;
if (oneResponse.status === "loaded") loaded.push(which);else if (oneResponse.status === "pending") pending.push(which);else if (oneResponse.status === "cached") cached.push(which);else error.push(which + ' ' + oneResponse.message);
}
if (pending.length > 0) {
if (self.debugCallback) self.debugCallback("pending " + JSON.stringify(pending));
// There was probably a second call for notes before the first one finished, so just retry a few times to see if they stop being pending.
// Retry quickly at first so that there isn't an unnecessary delay, but increase the delay each time.
if (!delay) delay = 50;else delay = delay * 2;
if (delay < 90000) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
var newBatch = [];
for (i = 0; i < pending.length; i++) {
which = pending[i].split(":");
newBatch.push({
instrument: which[0],
note: which[1]
});
}
if (self.debugCallback) self.debugCallback("retry " + JSON.stringify(newBatch));
self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
resolve(response);
})["catch"](function (error) {
reject(error);
});
}, delay);
});
} else {
var list = [];
for (var j = 0; j < batch.length; j++) {
list.push(batch[j].instrument + '/' + batch[j].note);
}
if (self.debugCallback) self.debugCallback("loadBatch timeout");
return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
}
} else {
if (self.debugCallback) self.debugCallback("loadBatch resolve");
return Promise.resolve({
loaded: loaded,
cached: cached,
error: error
});
}
})["catch"](function (error) {
if (self.debugCallback) self.debugCallback("loadBatch catch " + error.message);
});
};
self.prime = function () {
// At this point all of the notes are loaded. This function writes them into the output buffer.
// Most music has a lot of repeating notes. If a note is the same pitch, volume, length, etc. as another one,
// It saves a lot of time to just create it once and place it repeatedly where ever it needs to be.
var fadeTimeSec = self.fadeLength / 1000;
self.isRunning = false;
if (!self.audioBufferPossible) return Promise.reject(new Error(notSupportedMessage));
if (self.debugCallback) self.debugCallback("prime called");
return new Promise(function (resolve) {
var startTime = activeAudioContext().currentTime;
var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
self.duration = self.flattened.totalDuration * tempoMultiplier;
if (self.duration <= 0) {
self.audioBuffers = [];
return resolve({
status: "empty",
seconds: 0
});
}
self.duration += fadeTimeSec;
var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
// There might be a previous run that needs to be turned off.
self.stop();
var noteMapTracks = createNoteMap(self.flattened);
if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
var panDistances = setPan(noteMapTracks.length, self.pan);
// Create a simple list of all the unique sounds in this music and where they should be placed.
// There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
var uniqueSounds = {};
noteMapTracks.forEach(function (noteMap, trackNumber) {
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
noteMap.forEach(function (note) {
var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
if (!uniqueSounds[key]) uniqueSounds[key] = [];
uniqueSounds[key].push(note.start);
});
});
// Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
var allPromises = [];
var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
var k = Object.keys(uniqueSounds)[key2];
var parts = k.split(":");
var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
parts = {
instrument: parts[0],
pitch: parseInt(parts[1], 10),
volume: parseInt(parts[2], 10),
len: parseFloat(parts[3]),
pan: parseFloat(parts[4]),
tempoMultiplier: parseFloat(parts[5]),
cents: cents
};
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
}
self.audioBuffers = [audioBuffer];
if (self.debugCallback) {
self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
self.debugCallback("totalSamples = " + totalSamples);
self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
}
function resolveData(me) {
var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
return {
status: activeAudioContext().state,
duration: duration
};
}
Promise.all(allPromises).then(function () {
// Safari iOS can mess with the audioContext state, so resume if needed.
if (activeAudioContext().state === "suspended") {
activeAudioContext().resume().then(function () {
resolve(resolveData(self));
});
} else if (activeAudioContext().state === "interrupted") {
activeAudioContext().suspend().then(function () {
activeAudioContext().resume().then(function () {
resolve(resolveData(self));
});
});
} else {
resolve(resolveData(self));
}
});
});
};
function setPan(numTracks, panParam) {
// panParam, if it is set, can be either a number representing the separation between each track,
// or an array, which is the absolute pan position for each track.
if (panParam === null || panParam === undefined) return null;
var panDistances = [];
if (panParam.length) {
// We received an array. If there are the same number of items in the pan array as the number of tracks,
// it all lines up perfectly. If there are more items in the pan array than the tracks then the excess items are ignored.
// If there are more tracks than items in the pan array then the remaining tracks are placed in the middle.
// If any of the pan numbers are out of range then they are adjusted.
for (var pp = 0; pp < numTracks; pp++) {
if (pp < panParam.length) {
var x = parseFloat(panParam[pp]);
if (x < -1) x = -1;else if (x > 1) x = 1;
panDistances.push(x);
} else panDistances.push(0);
}
return panDistances;
} else {
var panNumber = parseFloat(panParam);
// the separation needs to be no further than 2 (i.e. -1 to 1) so test to see if there are too many tracks for the passed in distance
if (panNumber * (numTracks - 1) > 2) return null;
// If there are an even number of tracks, then offset so that the first two are centered around the middle
var even = numTracks % 2 === 0;
var currLow = even ? 0 - panNumber / 2 : 0;
var currHigh = currLow + panNumber;
// Now add the tracks to either side
for (var p = 0; p < numTracks; p++) {
even = p % 2 === 0;
if (even) {
panDistances.push(currLow);
currLow -= panNumber;
} else {
panDistances.push(currHigh);
currHigh += panNumber;
}
}
return panDistances;
}
// There was either no panning, or the parameters were illegal
return null;
}
// This is called after everything is set up, so it can quickly make sound
self.start = function () {
if (!self.audioBufferPossible) throw new Error(notSupportedMessage);
if (self.debugCallback) self.debugCallback("start called");
var resumePosition = self.pausedTimeSec ? self.pausedTimeSec : 0;
self._kickOffSound(resumePosition);
self.startTimeSec = activeAudioContext().currentTime - resumePosition;
self.pausedTimeSec = undefined;
if (self.debugCallback) self.debugCallback("MIDI STARTED", self.startTimeSec);
};
self.pause = function () {
if (!self.audioBufferPossible) throw new Error(notSupportedMessage);
if (self.debugCallback) self.debugCallback("pause called");
self.pausedTimeSec = self.stop();
return self.pausedTimeSec;
};
self.resume = function () {
self.start();
};
self.seek = function (position, units) {
var offset;
switch (units) {
case "seconds":
offset = position;
break;
case "beats":
offset = position * self.millisecondsPerMeasure / self.beatsPerMeasure / 1000;
break;
default:
// this is "percent" or any illegal value
offset = (self.duration - self.fadeLength / 1000) * position;
break;
}
// TODO-PER: can seek when paused or when playing
if (!self.audioBufferPossible) throw new Error(notSupportedMessage);
if (self.debugCallback) self.debugCallback("seek called sec=" + offset);
if (self.isRunning) {
self.stop();
self._kickOffSound(offset);
} else {
self.pausedTimeSec = offset;
}
self.pausedTimeSec = offset;
};
self.stop = function () {
self.isRunning = false;
self.pausedTimeSec = undefined;
self.directSource.forEach(function (source) {
try {
source.stop();
} catch (error) {
// We don't care if self succeeds: it might fail if something else turned off the sound or it ended for some reason.
console.log("direct source didn't stop:", error);
}
});
self.directSource = [];
var elapsed = activeAudioContext().currentTime - self.startTimeSec;
return elapsed;
};
self.finished = function () {
self.startTimeSec = undefined;
self.pausedTimeSec = undefined;
self.isRunning = false;
};
self.download = function () {
return downloadBuffer(self);
};
self.getAudioBuffer = function () {
return self.audioBuffers[0];
};
self.getIsRunning = function () {
return self.isRunning;
};
/////////////// Private functions //////////////
self._deviceCapable = function () {
if (!supportsAudio()) {
console.warn(notSupportedMessage);
if (self.debugCallback) self.debugCallback(notSupportedMessage);
return false;
}
return true;
};
self._kickOffSound = function (seconds) {
self.isRunning = true;
self.directSource = [];
self.audioBuffers.forEach(function (audioBuffer, trackNum) {
self.directSource[trackNum] = activeAudioContext().createBufferSource(); // creates a sound source
self.directSource[trackNum].buffer = audioBuffer; // tell the source which sound to play
self.directSource[trackNum].connect(activeAudioContext().destination); // connect the source to the context's destination (the speakers)
});
self.directSource.forEach(function (source) {
source.start(0, seconds);
});
if (self.onEnded) {
self.directSource[0].onended = function () {
self.onEnded(self.callbackContext);
};
}
};
function addSwing(noteMapTracks, swing, meterFraction, pickupLength) {
// we can only swing in X/4 and X/8 meters.
if (meterFraction.den != 4 && meterFraction.den != 8) return;
swing = parseFloat(swing);
// 50 (or less) is no swing,
if (isNaN(swing) || swing <= 50) return;
// 66 is triplet swing 2:1, and
// 60 is swing with a ratio of 3:2.
// 75 is the maximum swing where the first eight is played as a dotted eight and the second as a sixteenth.
if (swing > 75) swing = 75;
// convert the swing percentage to a percentage of increase for the first half of the beat
swing = swing / 50 - 1;
// The volume of the swung notes is increased by this factor
// could be also in the settings. Try out values such 0.1, 0.2
var volumeIncrease = 0.0;
// the beatLength in X/8 meters
var beatLength = 0.25;
// in X/8 meters the 16s swing so the beatLength is halved
if (meterFraction.den === 8) beatLength = beatLength / 2;
// duration of a half beat
var halfbeatLength = beatLength / 2;
// the extra duration of the first swung notes and the delay of the second notes
var swingDuration = halfbeatLength * swing;
for (var t = 0; t < noteMapTracks.length; t++) {
var track = noteMapTracks[t];
for (var i = 0; i < track.length; i++) {
var event = track[i];
if (
// is halfbeat
(event.start - pickupLength) % halfbeatLength == 0 && (event.start - pickupLength) % beatLength != 0 && (
// the previous note is on the beat or before OR there is no previous note
i == 0 || track[i - 1].start <= track[i].start - halfbeatLength) && (
// the next note is on the beat or after OR there is no next note
i == track.length - 1 || track[i + 1].start >= track[i].start + halfbeatLength)) {
var oldEventStart = event.start;
event.start += swingDuration;
// Increase volume of swung notes
event.volume *= 1 + volumeIncrease;
// if there is a previous note ending at the start of this note, extend its end
// and decrease its volume
if (i > 0 && track[i - 1].end == oldEventStart) {
track[i - 1].end = event.start;
track[i - 1].volume *= 1 - volumeIncrease;
}
}
}
}
}
}
module.exports = CreateSynth;
/***/ }),
/***/ "./src/synth/download-buffer.js":
/*!**************************************!*\
!*** ./src/synth/download-buffer.js ***!
\**************************************/
/***/ (function(module) {
var downloadBuffer = function downloadBuffer(buffer) {
return window.URL.createObjectURL(bufferToWave(buffer.audioBuffers));
};
// Convert an AudioBuffer to a Blob using WAVE representation
function bufferToWave(audioBuffers) {
var audioBuffer = audioBuffers[0];
var numOfChan = audioBuffer.numberOfChannels;
var length = audioBuffer.length * numOfChan * 2 + 44;
var buffer = new ArrayBuffer(length);
var view = new DataView(buffer);
var channels = [];
var i;
var sample;
var offset = 0;
var pos = 0;
// write WAVE header
setUint32(0x46464952); // "RIFF"
setUint32(length - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(audioBuffer.sampleRate);
setUint32(audioBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for (i = 0; i < numOfChan; i++) {
channels.push(audioBuffer.getChannelData(i));
}
while (pos < length) {
for (i = 0; i < channels.length; i++) {
// interleave channels
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // scale to 16-bit signed int
view.setInt16(pos, sample, true); // write 16-bit sample
pos += 2;
}
offset++; // next source sample
}
// create Blob
return new Blob([buffer], {
type: "audio/wav"
});
function setUint16(data) {
view.setUint16(pos, data, true);
pos += 2;
}
function setUint32(data) {
view.setUint32(pos, data, true);
pos += 4;
}
}
module.exports = downloadBuffer;
/***/ }),
/***/ "./src/synth/get-midi-file.js":
/*!************************************!*\
!*** ./src/synth/get-midi-file.js ***!
\************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var tunebook = __webpack_require__(/*! ../api/abc_tunebook */ "./src/api/abc_tunebook.js");
var midiCreate = __webpack_require__(/*! ../midi/abc_midi_create */ "./src/midi/abc_midi_create.js");
var getMidiFile = function getMidiFile(source, options) {
var params = {};
if (options) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
params[key] = options[key];
}
}
}
params.generateInline = false;
function callback(div, tune, index) {
var downloadMidi = midiCreate(tune, params);
switch (params.midiOutputType) {
case "encoded":
return downloadMidi;
case "binary":
var decoded = downloadMidi.replace("data:audio/midi,", "");
decoded = decoded.replace(/MThd/g, "%4d%54%68%64");
decoded = decoded.replace(/MTrk/g, "%4d%54%72%6b");
var buffer = new ArrayBuffer(decoded.length / 3);
var output = new Uint8Array(buffer);
for (var i = 0; i < decoded.length / 3; i++) {
var p = i * 3 + 1;
var d = parseInt(decoded.substring(p, p + 2), 16);
output[i] = d;
}
return output;
case "link":
default:
return generateMidiDownloadLink(tune, params, downloadMidi, index);
}
}
if (typeof source === "string") return tunebook.renderEngine(callback, "*", source, params);else return callback(null, source, 0);
};
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
var generateMidiDownloadLink = function generateMidiDownloadLink(tune, midiParams, midi, index) {
var divClasses = ['abcjs-download-midi', 'abcjs-midi-' + index];
if (midiParams.downloadClass) divClasses.push(midiParams.downloadClass);
var html = '
';
if (midiParams.preTextDownload) html += midiParams.preTextDownload;
var title = tune.metaText && tune.metaText.title ? tune.metaText.title : 'Untitled';
var label;
if (midiParams.downloadLabel && isFunction(midiParams.downloadLabel)) label = midiParams.downloadLabel(tune, index);else if (midiParams.downloadLabel) label = midiParams.downloadLabel.replace(/%T/, title);else label = "Download MIDI for \"" + title + "\"";
title = title.toLowerCase().replace(/'/g, '').replace(/\W/g, '_').replace(/__/g, '_');
var filename = midiParams.fileName ? midiParams.fileName : title + '.midi';
html += '' + label + '';
if (midiParams.postTextDownload) html += midiParams.postTextDownload;
return html + "
";
};
module.exports = getMidiFile;
/***/ }),
/***/ "./src/synth/images/loading.svg.js":
/*!*****************************************!*\
!*** ./src/synth/images/loading.svg.js ***!
\*****************************************/
/***/ (function(module) {
var svg = "\n\n";
module.exports = svg;
/***/ }),
/***/ "./src/synth/images/loop.svg.js":
/*!**************************************!*\
!*** ./src/synth/images/loop.svg.js ***!
\**************************************/
/***/ (function(module) {
var svg = "\n\n";
module.exports = svg;
/***/ }),
/***/ "./src/synth/images/pause.svg.js":
/*!***************************************!*\
!*** ./src/synth/images/pause.svg.js ***!
\***************************************/
/***/ (function(module) {
var svg = "\n\n";
module.exports = svg;
/***/ }),
/***/ "./src/synth/images/play.svg.js":
/*!**************************************!*\
!*** ./src/synth/images/play.svg.js ***!
\**************************************/
/***/ (function(module) {
var svg = "\n\n";
module.exports = svg;
/***/ }),
/***/ "./src/synth/images/reset.svg.js":
/*!***************************************!*\
!*** ./src/synth/images/reset.svg.js ***!
\***************************************/
/***/ (function(module) {
var svg = "\n\n";
module.exports = svg;
/***/ }),
/***/ "./src/synth/instrument-index-to-name.js":
/*!***********************************************!*\
!*** ./src/synth/instrument-index-to-name.js ***!
\***********************************************/
/***/ (function(module) {
var instrumentIndexToName = ["acoustic_grand_piano", "bright_acoustic_piano", "electric_grand_piano", "honkytonk_piano", "electric_piano_1", "electric_piano_2", "harpsichord", "clavinet", "celesta", "glockenspiel", "music_box", "vibraphone", "marimba", "xylophone", "tubular_bells", "dulcimer", "drawbar_organ", "percussive_organ", "rock_organ", "church_organ", "reed_organ", "accordion", "harmonica", "tango_accordion", "acoustic_guitar_nylon", "acoustic_guitar_steel", "electric_guitar_jazz", "electric_guitar_clean", "electric_guitar_muted", "overdriven_guitar", "distortion_guitar", "guitar_harmonics", "acoustic_bass", "electric_bass_finger", "electric_bass_pick", "fretless_bass", "slap_bass_1", "slap_bass_2", "synth_bass_1", "synth_bass_2", "violin", "viola", "cello", "contrabass", "tremolo_strings", "pizzicato_strings", "orchestral_harp", "timpani", "string_ensemble_1", "string_ensemble_2", "synth_strings_1", "synth_strings_2", "choir_aahs", "voice_oohs", "synth_choir", "orchestra_hit", "trumpet", "trombone", "tuba", "muted_trumpet", "french_horn", "brass_section", "synth_brass_1", "synth_brass_2", "soprano_sax", "alto_sax", "tenor_sax", "baritone_sax", "oboe", "english_horn", "bassoon", "clarinet", "piccolo", "flute", "recorder", "pan_flute", "blown_bottle", "shakuhachi", "whistle", "ocarina", "lead_1_square", "lead_2_sawtooth", "lead_3_calliope", "lead_4_chiff", "lead_5_charang", "lead_6_voice", "lead_7_fifths", "lead_8_bass_lead", "pad_1_new_age", "pad_2_warm", "pad_3_polysynth", "pad_4_choir", "pad_5_bowed", "pad_6_metallic", "pad_7_halo", "pad_8_sweep", "fx_1_rain", "fx_2_soundtrack", "fx_3_crystal", "fx_4_atmosphere", "fx_5_brightness", "fx_6_goblins", "fx_7_echoes", "fx_8_scifi", "sitar", "banjo", "shamisen", "koto", "kalimba", "bagpipe", "fiddle", "shanai", "tinkle_bell", "agogo", "steel_drums", "woodblock", "taiko_drum", "melodic_tom", "synth_drum", "reverse_cymbal", "guitar_fret_noise", "breath_noise", "seashore", "bird_tweet", "telephone_ring", "helicopter", "applause", "gunshot", "percussion"];
module.exports = instrumentIndexToName;
/***/ }),
/***/ "./src/synth/load-note.js":
/*!********************************!*\
!*** ./src/synth/load-note.js ***!
\********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// Load one mp3 file for one note.
// url = the base url for the soundfont
// instrument = the instrument name (e.g. "acoustic_grand_piano")
// name = the pitch name (e.g. "A3")
var soundsCache = __webpack_require__(/*! ./sounds-cache */ "./src/synth/sounds-cache.js");
var getNote = function getNote(url, instrument, name, audioContext) {
if (!soundsCache[instrument]) soundsCache[instrument] = {};
var instrumentCache = soundsCache[instrument];
if (!instrumentCache[name]) instrumentCache[name] = new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
var noteUrl = url + instrument + "-mp3/" + name + ".mp3";
xhr.open("GET", noteUrl, true);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
if (xhr.status !== 200) {
reject(Error("Can't load sound at " + noteUrl + ' status=' + xhr.status));
return;
}
var noteDecoded = function noteDecoded(audioBuffer) {
resolve({
instrument: instrument,
name: name,
status: "loaded",
audioBuffer: audioBuffer
});
};
var maybePromise = audioContext.decodeAudioData(xhr.response, noteDecoded, function () {
reject(Error("Can't decode sound at " + noteUrl));
});
// In older browsers `BaseAudioContext.decodeAudio()` did not return a promise
if (maybePromise && typeof maybePromise["catch"] === "function") maybePromise["catch"](reject);
};
xhr.onerror = function () {
reject(Error("Can't load sound at " + noteUrl));
};
xhr.send();
})["catch"](function (err) {
console.error("Didn't load note", instrument, name, ":", err.message);
throw err;
});
return instrumentCache[name];
};
module.exports = getNote;
/***/ }),
/***/ "./src/synth/note-to-midi.js":
/*!***********************************!*\
!*** ./src/synth/note-to-midi.js ***!
\***********************************/
/***/ (function(module) {
var accidentals = {
"__": -2,
"_": -1,
"_/": -0.5,
"=": 0,
"": 0,
"^/": 0.5,
"^": 1,
"^^": 2
};
var notesInOrder = ['C', '-', 'D', '-', 'E', 'F', '-', 'G', '-', 'A', '-', 'B', 'c', '-', 'd', '-', 'e', 'f', '-', 'g', '-', 'a', '-', 'b'];
function noteToMidi(note) {
var reg = note.match(/([_^\/]*)([ABCDEFGabcdefg])(,*)('*)/);
if (reg && reg.length === 5) {
var acc = accidentals[reg[1]];
var pitch = notesInOrder.indexOf(reg[2]);
var octave = reg[4].length - reg[3].length;
return 48 + pitch + acc + octave * 12;
}
return 0;
}
function midiToNote(midi) {
midi = parseInt(midi, 10); // TODO-PER: not sure how to handle quarter sharps and flats, so strip them for now.
var octave = Math.floor(midi / 12);
var pitch = midi % 12;
var name = notesInOrder[pitch];
if (name === '-') {
name = '^' + notesInOrder[pitch - 1];
}
if (octave > 4) {
name = name.toLowerCase();
octave -= 5;
while (octave > 0) {
name += "'";
octave--;
}
} else {
while (octave < 4) {
name += ',';
octave++;
}
}
return name;
}
module.exports = {
noteToMidi: noteToMidi,
midiToNote: midiToNote
};
/***/ }),
/***/ "./src/synth/pitch-to-note-name.js":
/*!*****************************************!*\
!*** ./src/synth/pitch-to-note-name.js ***!
\*****************************************/
/***/ (function(module) {
var pitchToNoteName = {
21: 'A0',
22: 'Bb0',
23: 'B0',
24: 'C1',
25: 'Db1',
26: 'D1',
27: 'Eb1',
28: 'E1',
29: 'F1',
30: 'Gb1',
31: 'G1',
32: 'Ab1',
33: 'A1',
34: 'Bb1',
35: 'B1',
36: 'C2',
37: 'Db2',
38: 'D2',
39: 'Eb2',
40: 'E2',
41: 'F2',
42: 'Gb2',
43: 'G2',
44: 'Ab2',
45: 'A2',
46: 'Bb2',
47: 'B2',
48: 'C3',
49: 'Db3',
50: 'D3',
51: 'Eb3',
52: 'E3',
53: 'F3',
54: 'Gb3',
55: 'G3',
56: 'Ab3',
57: 'A3',
58: 'Bb3',
59: 'B3',
60: 'C4',
61: 'Db4',
62: 'D4',
63: 'Eb4',
64: 'E4',
65: 'F4',
66: 'Gb4',
67: 'G4',
68: 'Ab4',
69: 'A4',
70: 'Bb4',
71: 'B4',
72: 'C5',
73: 'Db5',
74: 'D5',
75: 'Eb5',
76: 'E5',
77: 'F5',
78: 'Gb5',
79: 'G5',
80: 'Ab5',
81: 'A5',
82: 'Bb5',
83: 'B5',
84: 'C6',
85: 'Db6',
86: 'D6',
87: 'Eb6',
88: 'E6',
89: 'F6',
90: 'Gb6',
91: 'G6',
92: 'Ab6',
93: 'A6',
94: 'Bb6',
95: 'B6',
96: 'C7',
97: 'Db7',
98: 'D7',
99: 'Eb7',
100: 'E7',
101: 'F7',
102: 'Gb7',
103: 'G7',
104: 'Ab7',
105: 'A7',
106: 'Bb7',
107: 'B7',
108: 'C8',
109: 'Db8',
110: 'D8',
111: 'Eb8',
112: 'E8',
113: 'F8',
114: 'Gb8',
115: 'G8',
116: 'Ab8',
117: 'A8',
118: 'Bb8',
119: 'B8',
120: 'C9',
121: 'Db9'
};
module.exports = pitchToNoteName;
/***/ }),
/***/ "./src/synth/pitches-to-perc.js":
/*!**************************************!*\
!*** ./src/synth/pitches-to-perc.js ***!
\**************************************/
/***/ (function(module) {
var pitchMap = {
f0: "_C",
n0: "=C",
s0: "^C",
x0: "C",
f1: "_D",
n1: "=D",
s1: "^D",
x1: "D",
f2: "_E",
n2: "=E",
s2: "^E",
x2: "E",
f3: "_F",
n3: "=F",
s3: "^F",
x3: "F",
f4: "_G",
n4: "=G",
s4: "^G",
x4: "G",
f5: "_A",
n5: "=A",
s5: "^A",
x5: "A",
f6: "_B",
n6: "=B",
s6: "^B",
x6: "B",
f7: "_c",
n7: "=c",
s7: "^c",
x7: "c",
f8: "_d",
n8: "=d",
s8: "^d",
x8: "d",
f9: "_e",
n9: "=e",
s9: "^e",
x9: "e",
f10: "_f",
n10: "=f",
s10: "^f",
x10: "f",
f11: "_g",
n11: "=g",
s11: "^g",
x11: "g",
f12: "_a",
n12: "=a",
s12: "^a",
x12: "a",
f13: "_b",
n13: "=b",
s13: "^b",
x13: "b",
f14: "_c'",
n14: "=c'",
s14: "^c'",
x14: "c'",
f15: "_d'",
n15: "=d'",
s15: "^d'",
x15: "d'",
f16: "_e'",
n16: "=e'",
s16: "^e'",
x16: "e'"
};
function pitchesToPerc(pitchObj) {
var pitch = (pitchObj.accidental ? pitchObj.accidental[0] : 'x') + pitchObj.verticalPos;
return pitchMap[pitch];
}
module.exports = pitchesToPerc;
/***/ }),
/***/ "./src/synth/place-note.js":
/*!*********************************!*\
!*** ./src/synth/place-note.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var soundsCache = __webpack_require__(/*! ./sounds-cache */ "./src/synth/sounds-cache.js");
var pitchToNoteName = __webpack_require__(/*! ./pitch-to-note-name */ "./src/synth/pitch-to-note-name.js");
var centsToFactor = __webpack_require__(/*! ./cents-to-factor */ "./src/synth/cents-to-factor.js");
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) {
// sound contains { instrument, pitch, volume, len, pan, tempoMultiplier
// len is in whole notes. Multiply by tempoMultiplier to get seconds.
// ofsMs is an offset to subtract from the note to line up programs that have different length onsets.
var OfflineAC = window.OfflineAudioContext || window.webkitOfflineAudioContext;
var len = sound.len * sound.tempoMultiplier;
if (ofsMs) len += ofsMs / 1000;
len -= noteEndSec;
if (len < 0) len = 0.005; // Have some small audible length no matter how short the note is.
var offlineCtx = new OfflineAC(2, Math.floor((len + fadeTimeSec) * sampleRate), sampleRate);
var noteName = pitchToNoteName[sound.pitch];
if (!soundsCache[sound.instrument]) {
// It shouldn't happen that the entire instrument cache wasn't created, but this has been seen in practice, so guard against it.
if (debugCallback) debugCallback('placeNote skipped (instrument empty): ' + sound.instrument + ':' + noteName);
return Promise.resolve();
}
var noteBufferPromise = soundsCache[sound.instrument][noteName];
if (!noteBufferPromise) {
// if the note isn't present then just skip it - it will leave a blank spot in the audio.
if (debugCallback) debugCallback('placeNote skipped: ' + sound.instrument + ':' + noteName);
return Promise.resolve();
}
return noteBufferPromise.then(function (response) {
// create audio buffer
var source = offlineCtx.createBufferSource();
source.buffer = response.audioBuffer;
// add gain
// volume can be between 1 to 127. This translation to gain is just trial and error.
// The smaller the first number, the more dynamic range between the quietest to loudest.
// The larger the second number, the louder it will be in general.
var volume = sound.volume / 96 * volumeMultiplier;
source.gainNode = offlineCtx.createGain();
// add pan if supported and present
if (sound.pan && offlineCtx.createStereoPanner) {
source.panNode = offlineCtx.createStereoPanner();
source.panNode.pan.setValueAtTime(sound.pan, 0);
}
source.gainNode.gain.value = volume; // Math.min(2, Math.max(0, volume));
source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len);
source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec);
if (sound.cents) {
source.playbackRate.value = centsToFactor(sound.cents);
}
// connect all the nodes
if (source.panNode) {
source.panNode.connect(offlineCtx.destination);
source.gainNode.connect(source.panNode);
} else {
source.gainNode.connect(offlineCtx.destination);
}
source.connect(source.gainNode);
// Do the process of creating the sound and placing it in the buffer
source.start(0);
if (source.noteOff) {
source.noteOff(len + fadeTimeSec);
} else {
source.stop(len + fadeTimeSec);
}
var fnResolve;
offlineCtx.oncomplete = function (e) {
if (e.renderedBuffer && e.renderedBuffer.getChannelData) {
// If the system gets overloaded or there are network problems then this can start failing. Just drop the note if so.
for (var i = 0; i < startArray.length; i++) {
//Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
var start = startArray[i] * sound.tempoMultiplier;
if (ofsMs) start -= ofsMs / 1000;
if (start < 0) start = 0; // If the item that is moved back is at the very beginning of the buffer then don't move it back. To do that would be to push everything else forward. TODO-PER: this should probably be done at some point but then it would change timing in existing apps.
start = Math.floor(start * sampleRate);
copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
}
}
if (debugCallback) debugCallback('placeNote: ' + sound.instrument + ':' + noteName);
fnResolve();
};
offlineCtx.startRendering();
return new Promise(function (resolve) {
fnResolve = resolve;
});
})["catch"](function (error) {
if (debugCallback) debugCallback('placeNote catch: ' + error.message);
return Promise.resolve();
});
}
var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
for (var ch = 0; ch < 2; ch++) {
var fromData = fromBuffer.getChannelData(ch);
var toData = toBuffer.getChannelData(ch);
// Mix the current note into the existing track
for (var n = 0; n < fromData.length; n++) {
toData[n + start] += fromData[n];
}
}
};
module.exports = placeNote;
/***/ }),
/***/ "./src/synth/play-event.js":
/*!*********************************!*\
!*** ./src/synth/play-event.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var SynthSequence = __webpack_require__(/*! ./synth-sequence */ "./src/synth/synth-sequence.js");
var CreateSynth = __webpack_require__(/*! ./create-synth */ "./src/synth/create-synth.js");
var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure, soundFontUrl, debugCallback) {
var sequence = new SynthSequence();
for (var i = 0; i < midiPitches.length; i++) {
var note = midiPitches[i];
var trackNum = sequence.addTrack();
sequence.setInstrument(trackNum, note.instrument);
if (i === 0 && midiGracePitches) {
for (var j = 0; j < midiGracePitches.length; j++) {
var grace = midiGracePitches[j];
sequence.appendNote(trackNum, grace.pitch, 1 / 64, grace.volume, grace.cents);
}
}
sequence.appendNote(trackNum, note.pitch, note.duration, note.volume, note.cents);
}
var ac = activeAudioContext();
if (ac.state === "suspended") {
return ac.resume().then(function () {
return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
});
} else {
return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
}
}
function doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback) {
var buffer = new CreateSynth();
return buffer.init({
sequence: sequence,
millisecondsPerMeasure: millisecondsPerMeasure,
options: {
soundFontUrl: soundFontUrl
},
debugCallback: debugCallback
}).then(function () {
return buffer.prime();
}).then(function () {
buffer.start();
return Promise.resolve();
});
}
module.exports = playEvent;
/***/ }),
/***/ "./src/synth/register-audio-context.js":
/*!*********************************************!*\
!*** ./src/synth/register-audio-context.js ***!
\*********************************************/
/***/ (function(module) {
// Call this when it is safe for the abcjs to produce sound. This is after the first user gesture on the page.
// If you call it with no parameters, then an AudioContext is created and stored.
// If you call it with a parameter, that is used as an already created AudioContext.
function registerAudioContext(ac) {
// If one is passed in, that is the one to use even if there was already one created.
if (ac) window.abcjsAudioContext = ac;else {
// no audio context passed in, so create it unless there is already one from before.
if (!window.abcjsAudioContext) {
var AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) window.abcjsAudioContext = new AudioContext();else return false;
}
}
return window.abcjsAudioContext.state !== "suspended";
}
module.exports = registerAudioContext;
/***/ }),
/***/ "./src/synth/sounds-cache.js":
/*!***********************************!*\
!*** ./src/synth/sounds-cache.js ***!
\***********************************/
/***/ (function(module) {
var soundsCache = {};
module.exports = soundsCache;
/***/ }),
/***/ "./src/synth/supports-audio.js":
/*!*************************************!*\
!*** ./src/synth/supports-audio.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
//
// Support for audio depends on three things: support for Promise, support for AudioContext, and support for AudioContext.resume.
// Unfortunately, AudioContext.resume cannot be detected unless an AudioContext is created, and creating an AudioContext can't
// be done until a user click, so there is no way to know for sure if audio is supported until the user tries.
// We can get close, though - we can test for Promises and AudioContext - there are just a few evergreen browsers that supported
// that before supporting resume, so we'll test what we can.
// The best use of this routine is to call it before doing any audio related stuff to decide whether to bother.
// But then, call it again after a user interaction to test for resume.
function supportsAudio() {
if (!window.Promise) return false;
if (!window.AudioContext && !window.webkitAudioContext && !navigator.mozAudioContext && !navigator.msAudioContext) return false;
var aac = activeAudioContext();
if (aac) return aac.resume !== undefined;
}
module.exports = supportsAudio;
/***/ }),
/***/ "./src/synth/synth-controller.js":
/*!***************************************!*\
!*** ./src/synth/synth-controller.js ***!
\***************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var CreateSynthControl = __webpack_require__(/*! ./create-synth-control */ "./src/synth/create-synth-control.js");
var CreateSynth = __webpack_require__(/*! ./create-synth */ "./src/synth/create-synth.js");
var TimingCallbacks = __webpack_require__(/*! ../api/abc_timing_callbacks */ "./src/api/abc_timing_callbacks.js");
var activeAudioContext = __webpack_require__(/*! ./active-audio-context */ "./src/synth/active-audio-context.js");
function SynthController() {
var self = this;
self.warp = 100;
self.cursorControl = null;
self.visualObj = null;
self.timer = null;
self.midiBuffer = null;
self.options = null;
self.currentTempo = null;
self.control = null;
self.isLooping = false;
self.isStarted = false;
self.isLoaded = false;
self.isLoading = false;
self.load = function (selector, cursorControl, visualOptions) {
if (!visualOptions) visualOptions = {};
if (visualOptions.displayPlay === undefined) visualOptions.displayPlay = true;
if (visualOptions.displayProgress === undefined) visualOptions.displayProgress = true;
self.control = new CreateSynthControl(selector, {
loopHandler: visualOptions.displayLoop ? self.toggleLoop : undefined,
restartHandler: visualOptions.displayRestart ? self.restart : undefined,
playPromiseHandler: visualOptions.displayPlay ? self.play : undefined,
progressHandler: visualOptions.displayProgress ? self.randomAccess : undefined,
warpHandler: visualOptions.displayWarp ? self.onWarp : undefined,
afterResume: self.init
});
self.cursorControl = cursorControl;
self.disable(true);
};
self.disable = function (isDisabled) {
if (self.control) self.control.disable(isDisabled);
};
self.setTune = function (visualObj, userAction, audioParams) {
self.visualObj = visualObj;
self.disable(false);
self.options = audioParams ? audioParams : {};
if (self.control) {
self.pause();
self.setProgress(0, 1);
self.control.resetAll();
self.restart();
self.isStarted = false;
}
self.isLooping = false;
if (userAction) return self.go();else {
return Promise.resolve({
status: "no-audio-context"
});
}
};
self.go = function () {
self.isLoading = true;
var millisecondsPerMeasure = self.visualObj.millisecondsPerMeasure() * 100 / self.warp;
self.currentTempo = Math.round(self.visualObj.getBeatsPerMeasure() / millisecondsPerMeasure * 60000);
if (self.control) self.control.setTempo(self.currentTempo);
self.percent = 0;
var loadingResponse;
if (!self.midiBuffer) self.midiBuffer = new CreateSynth();
return activeAudioContext().resume().then(function (response) {
return self.midiBuffer.init({
visualObj: self.visualObj,
options: self.options,
millisecondsPerMeasure: millisecondsPerMeasure
});
}).then(function (response) {
loadingResponse = response;
return self.midiBuffer.prime();
}).then(function () {
var subdivisions = 16;
if (self.cursorControl && self.cursorControl.beatSubdivisions !== undefined && parseInt(self.cursorControl.beatSubdivisions, 10) >= 1 && parseInt(self.cursorControl.beatSubdivisions, 10) <= 64) subdivisions = parseInt(self.cursorControl.beatSubdivisions, 10);
// Need to create the TimingCallbacks after priming the midi so that the midi data is available for the callbacks.
self.timer = new TimingCallbacks(self.visualObj, {
beatCallback: self.beatCallback,
eventCallback: self.eventCallback,
lineEndCallback: self.lineEndCallback,
qpm: self.currentTempo,
extraMeasuresAtBeginning: self.cursorControl ? self.cursorControl.extraMeasuresAtBeginning : undefined,
lineEndAnticipation: self.cursorControl ? self.cursorControl.lineEndAnticipation : 0,
beatSubdivisions: subdivisions
});
if (self.cursorControl && self.cursorControl.onReady && typeof self.cursorControl.onReady === 'function') self.cursorControl.onReady(self);
self.isLoaded = true;
self.isLoading = false;
return Promise.resolve({
status: "created",
notesStatus: loadingResponse
});
});
};
self.destroy = function () {
if (self.timer) {
self.timer.reset();
self.timer.stop();
self.timer = null;
}
if (self.midiBuffer) {
self.midiBuffer.stop();
self.midiBuffer = null;
}
self.setProgress(0, 1);
if (self.control) self.control.resetAll();
};
self.play = function () {
return self.runWhenReady(self._play, undefined);
};
function sleep(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
self.runWhenReady = function (fn, arg1) {
if (!self.visualObj) return Promise.resolve({
status: "loading"
});
if (self.isLoading) {
// Some other promise is waiting for the tune to be loaded, so just wait.
return sleep(500).then(function () {
if (self.isLoading) return self.runWhenReady(fn, arg1);
return fn(arg1);
});
} else if (!self.isLoaded) {
return self.go().then(function () {
return fn(arg1);
});
} else {
return fn(arg1);
}
};
self._play = function () {
return activeAudioContext().resume().then(function () {
self.isStarted = !self.isStarted;
if (self.isStarted) {
if (self.cursorControl && self.cursorControl.onStart && typeof self.cursorControl.onStart === 'function') self.cursorControl.onStart();
self.midiBuffer.start();
self.timer.start(self.percent);
if (self.control) self.control.pushPlay(true);
} else {
self.pause();
}
return Promise.resolve({
status: "ok"
});
});
};
self.pause = function () {
if (self.timer) {
self.timer.pause();
self.midiBuffer.pause();
if (self.control) self.control.pushPlay(false);
}
};
self.toggleLoop = function () {
self.isLooping = !self.isLooping;
if (self.control) self.control.pushLoop(self.isLooping);
};
self.restart = function () {
if (self.timer) {
self.timer.setProgress(0);
self.midiBuffer.seek(0);
}
};
self.randomAccess = function (ev) {
return self.runWhenReady(self._randomAccess, ev);
};
self._randomAccess = function (ev) {
var background = ev.target.classList.contains('abcjs-midi-progress-indicator') ? ev.target.parentNode : ev.target;
var percent = (ev.x - background.getBoundingClientRect().left) / background.offsetWidth;
if (percent < 0) percent = 0;
if (percent > 1) percent = 1;
self.seek(percent);
return Promise.resolve({
status: "ok"
});
};
self.seek = function (percent, units) {
if (self.timer && self.midiBuffer) {
self.timer.setProgress(percent, units);
self.midiBuffer.seek(percent, units);
}
};
self.setWarp = function (newWarp) {
if (parseInt(newWarp, 10) > 0) {
self.warp = parseInt(newWarp, 10);
var wasPlaying = self.isStarted;
var startPercent = self.percent;
self.destroy();
self.isStarted = false;
return self.go().then(function () {
self.setProgress(startPercent, self.midiBuffer.duration * 1000);
if (self.control) self.control.setWarp(self.currentTempo, self.warp);
if (wasPlaying) {
return self.play().then(function () {
self.seek(startPercent);
return Promise.resolve();
});
}
self.seek(startPercent);
return Promise.resolve();
});
}
return Promise.resolve();
};
self.onWarp = function (ev) {
var newWarp = ev.target.value;
return self.setWarp(newWarp);
};
self.setProgress = function (percent, totalTime) {
self.percent = percent;
if (self.control) self.control.setProgress(percent, totalTime);
};
self.finished = function () {
self.timer.reset();
if (self.isLooping) {
self.timer.start(0);
self.midiBuffer.finished();
self.midiBuffer.start();
return "continue";
} else {
self.timer.stop();
if (self.isStarted) {
if (self.control) self.control.pushPlay(false);
self.isStarted = false;
self.midiBuffer.finished();
if (self.cursorControl && self.cursorControl.onFinished && typeof self.cursorControl.onFinished === 'function') self.cursorControl.onFinished();
self.setProgress(0, 1);
}
}
};
self.beatCallback = function (beatNumber, totalBeats, totalTime, position) {
var percent = beatNumber / totalBeats;
self.setProgress(percent, totalTime);
if (self.cursorControl && self.cursorControl.onBeat && typeof self.cursorControl.onBeat === 'function') self.cursorControl.onBeat(beatNumber, totalBeats, totalTime, position);
};
self.eventCallback = function (event) {
if (event) {
if (self.cursorControl && self.cursorControl.onEvent && typeof self.cursorControl.onEvent === 'function') self.cursorControl.onEvent(event);
} else {
return self.finished();
}
};
self.lineEndCallback = function (lineEvent, leftEvent) {
if (self.cursorControl && self.cursorControl.onLineEnd && typeof self.cursorControl.onLineEnd === 'function') self.cursorControl.onLineEnd(lineEvent, leftEvent);
};
self.getUrl = function () {
return self.midiBuffer.download();
};
self.download = function (fileName) {
var url = self.getUrl();
var link = document.createElement('a');
document.body.appendChild(link);
link.setAttribute("style", "display: none;");
link.href = url;
link.download = fileName ? fileName : 'output.wav';
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
};
}
module.exports = SynthController;
/***/ }),
/***/ "./src/synth/synth-sequence.js":
/*!*************************************!*\
!*** ./src/synth/synth-sequence.js ***!
\*************************************/
/***/ (function(module) {
var SynthSequence = function SynthSequence() {
var self = this;
self.tracks = [];
self.totalDuration = 0;
self.currentInstrument = [];
self.starts = [];
self.addTrack = function () {
self.tracks.push([]);
self.currentInstrument.push(0);
self.starts.push(0);
return self.tracks.length - 1;
};
self.setInstrument = function (trackNumber, instrumentNumber) {
self.tracks[trackNumber].push({
channel: 0,
cmd: "program",
instrument: instrumentNumber
});
self.currentInstrument[trackNumber] = instrumentNumber;
};
self.appendNote = function (trackNumber, pitch, durationInMeasures, volume, cents) {
var note = {
cmd: "note",
duration: durationInMeasures,
gap: 0,
instrument: self.currentInstrument[trackNumber],
pitch: pitch,
start: self.starts[trackNumber],
volume: volume
};
if (cents) note.cents = cents;
self.tracks[trackNumber].push(note);
self.starts[trackNumber] += durationInMeasures;
self.totalDuration = Math.max(self.totalDuration, self.starts[trackNumber]);
};
};
module.exports = SynthSequence;
/***/ }),
/***/ "./src/tablatures/abc_tablatures.js":
/*!******************************************!*\
!*** ./src/tablatures/abc_tablatures.js ***!
\******************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/*
* Tablature Plugins
* tablature are defined dynamically and registered inside abcjs
* by calling abcTablatures.register(plugin)
* where plugin represents a plugin instance
*
*/
// This is the only entry point to the tablatures. It is called both after parsing a tune and just before engraving
var TabString = __webpack_require__(/*! ./instruments/tab-string */ "./src/tablatures/instruments/tab-string.js");
/* extend the table below when adding a new instrument plugin */
// Existing tab classes
var pluginTab = {
'violin': {
name: 'StringTab',
defaultTuning: ['G,', 'D', 'A', 'e'],
isTabBig: false,
tabSymbolOffset: 0
},
'fiddle': {
name: 'StringTab',
defaultTuning: ['G,', 'D', 'A', 'e'],
isTabBig: false,
tabSymbolOffset: 0
},
'mandolin': {
name: 'StringTab',
defaultTuning: ['G,', 'D', 'A', 'e'],
isTabBig: false,
tabSymbolOffset: 0
},
'guitar': {
name: 'StringTab',
defaultTuning: ['E,', 'A,', 'D', 'G', 'B', 'e'],
isTabBig: true,
tabSymbolOffset: 0
},
'fiveString': {
name: 'StringTab',
defaultTuning: ['C,', 'G,', 'D', 'A', 'e'],
isTabBig: false,
tabSymbolOffset: -.95
}
};
var abcTablatures = {
inited: false,
plugins: {},
/**
* to be called once per plugin for registration
* @param {*} plugin
*/
register: function register(plugin) {
var name = plugin.name;
var tablature = plugin.tablature;
this.plugins[name] = tablature;
},
setError: function setError(tune, msg) {
if (tune.warnings) {
tune.warning.push(msg);
} else {
tune.warnings = [msg];
}
},
/**
* handle params for current processed score
* @param {*} tune current tune
* @param {*} tuneNumber number in tune list
* @param {*} params params to be processed for tablature
* @return prepared tablatures plugin instances for current tune
*/
preparePlugins: function preparePlugins(tune, tuneNumber, params) {
// Called after parsing a tune and before engraving it
if (!this.inited) {
// TODO-PER: I don't think this is needed - the plugin array can be hard coded, right?
this.register(new TabString());
this.inited = true;
}
var returned = null;
var nbPlugins = 0;
if (params.tablature) {
// validate requested plugins
var tabs = params.tablature;
returned = [];
for (var ii = 0; ii < tabs.length; ii++) {
var args = tabs[ii];
var instrument = args['instrument'];
if (instrument == null) {
this.setError(tune, "tablature 'instrument' is missing");
return returned;
}
var tabName = pluginTab[instrument];
var plugin = null;
if (tabName) {
plugin = this.plugins[tabName.name];
}
if (plugin) {
if (params.visualTranspose != 0) {
// populate transposition request to tabs
args.visualTranspose = params.visualTranspose;
}
args.abcSrc = params.tablature.abcSrc;
var pluginInstance = {
classz: plugin,
tuneNumber: tuneNumber,
params: args,
instance: null,
tabType: tabName
};
// proceed with tab plugin init
// plugin.init(tune, tuneNumber, args, ii);
returned.push(pluginInstance);
nbPlugins++;
} else if (instrument === '') {
// create a placeholder - there is no tab for this staff
returned.push(null);
} else {
// unknown tab plugin
//this.emit_error('Undefined tablature plugin: ' + tabName)
this.setError(tune, 'Undefined tablature plugin: ' + instrument);
return returned;
}
}
}
return returned;
},
/**
* Call requested plugin
* @param {*} renderer
* @param {*} abcTune
*/
layoutTablatures: function layoutTablatures(renderer, abcTune) {
var tabs = abcTune.tablatures;
// chack tabs request for each staffs
var staffLineCount = 0;
// Clear the suppression flag
if (tabs && tabs.length > 0) {
var nTabs = tabs.length;
for (var kk = 0; kk < nTabs; ++kk) {
if (tabs[kk] && tabs[kk].params.firstStaffOnly) {
tabs[kk].params.suppress = false;
}
}
}
for (var ii = 0; ii < abcTune.lines.length; ii++) {
var line = abcTune.lines[ii];
if (line.staff) {
staffLineCount++;
}
// MAE 27Nov2023
// If tab param "firstStaffOnly", remove the tab label after the first staff
if (staffLineCount > 1) {
if (tabs && tabs.length > 0) {
var nTabs = tabs.length;
for (var kk = 0; kk < nTabs; ++kk) {
if (tabs[kk].params.firstStaffOnly) {
// Set the staff draw suppression flag
tabs[kk].params.suppress = true;
}
}
}
}
var curStaff = line.staff;
if (curStaff) {
var maxStaves = curStaff.length;
for (var jj = 0; jj < curStaff.length; jj++) {
if (tabs[jj] && jj < maxStaves) {
// tablature requested for staff
var tabPlugin = tabs[jj];
if (tabPlugin.instance == null) {
//console.log("★★★★ Tab Init line: " + ii + " staff: " + jj)
tabPlugin.instance = new tabPlugin.classz();
// plugin.init(tune, tuneNumber, args, ii);
// call initer first
tabPlugin.instance.init(abcTune, tabPlugin.tuneNumber, tabPlugin.params, tabPlugin.tabType);
}
// render next
//console.log("★★★★ Tab Render line: " + ii + " staff: " + jj)
tabPlugin.instance.render(renderer, line, jj);
}
}
}
}
}
};
module.exports = abcTablatures;
/***/ }),
/***/ "./src/tablatures/instruments/string-patterns.js":
/*!*******************************************************!*\
!*** ./src/tablatures/instruments/string-patterns.js ***!
\*******************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var _require = __webpack_require__(/*! ../../synth/note-to-midi */ "./src/synth/note-to-midi.js"),
noteToMidi = _require.noteToMidi;
var TabNote = __webpack_require__(/*! ./tab-note */ "./src/tablatures/instruments/tab-note.js");
var tabNotes = __webpack_require__(/*! ./tab-notes */ "./src/tablatures/instruments/tab-notes.js");
function buildCapo(self) {
var capoTuning = null;
var tuning = self.tuning;
if (self.capo > 0) {
capoTuning = [];
for (var iii = 0; iii < tuning.length; iii++) {
var curNote = new TabNote(tuning[iii]);
for (var jjj = 0; jjj < self.capo; jjj++) {
curNote = curNote.nextNote();
}
capoTuning[iii] = curNote.emit();
}
}
return capoTuning;
}
function buildPatterns(self) {
var strings = [];
var tuning = self.tuning;
if (self.capo > 0) {
tuning = self.capoTuning;
}
var pos = tuning.length - 1;
for (var iii = 0; iii < tuning.length; iii++) {
var nextNote = self.highestNote; // highest handled note
if (iii != tuning.length - 1) {
nextNote = tuning[iii + 1];
}
var stringNotes = tabNotes(tuning[iii], nextNote);
if (stringNotes.error) {
return stringNotes;
}
strings[pos--] = stringNotes;
}
return strings;
}
function buildSecond(first) {
var seconds = [];
seconds[0] = [];
var strings = first.strings;
for (var iii = 1; iii < strings.length; iii++) {
seconds[iii] = strings[iii - 1];
}
return seconds;
}
function sameString(self, chord) {
for (var jjjj = 0; jjjj < chord.length - 1; jjjj++) {
var curPos = chord[jjjj];
var nextPos = chord[jjjj + 1];
if (curPos.str == nextPos.str) {
// same String
// => change lower pos
if (curPos.str == self.strings.length - 1) {
// Invalid tab Chord position for instrument
curPos.num = "?";
nextPos.num = "?";
return;
}
// change lower pitch on lowest string
if (nextPos.num < curPos.num) {
nextPos.str++;
nextPos = noteToNumber(self, nextPos.note, nextPos.str, self.secondPos, self.strings[nextPos.str].length);
} else {
curPos.str++;
curPos = noteToNumber(self, curPos.note, curPos.str, self.secondPos, self.strings[curPos.str].length);
}
// update table
chord[jjjj] = curPos;
chord[jjjj + 1] = nextPos;
}
}
return null;
}
function handleChordNotes(self, notes) {
var retNotes = [];
for (var iiii = 0; iiii < notes.length; iiii++) {
if (notes[iiii].endTie) continue;
var note = new TabNote(notes[iiii].name, self.clefTranspose);
note.checkKeyAccidentals(self.accidentals, self.measureAccidentals);
var curPos = toNumber(self, note);
retNotes.push(curPos);
}
sameString(self, retNotes);
return retNotes;
}
function noteToNumber(self, note, stringNumber, secondPosition, firstSize) {
var strings = self.strings;
note.checkKeyAccidentals(self.accidentals, self.measureAccidentals);
if (secondPosition) {
strings = secondPosition;
}
var noteName = note.emitNoAccidentals();
var num = strings[stringNumber].indexOf(noteName);
var acc = note.acc;
if (num != -1) {
if (secondPosition) {
num += firstSize;
}
if ((note.isFlat || note.acc == -1) && num == 0) {
// flat on 0 pos => previous string 7th position
var noteEquiv = note.getAccidentalEquiv();
stringNumber++;
num = strings[stringNumber].indexOf(noteEquiv.emit());
acc = 0;
}
return {
num: num + acc,
str: stringNumber,
note: note
};
}
return null;
}
function toNumber(self, note) {
if (note.isAltered || note.natural) {
var acc;
if (note.isFlat) {
if (note.isDouble) acc = "__";else acc = "_";
} else if (note.isSharp) {
if (note.isDouble) acc = "^^";else acc = "^";
} else if (note.natural) acc = "=";
self.measureAccidentals[note.name.toUpperCase()] = acc;
}
for (var i = self.stringPitches.length - 1; i >= 0; i--) {
if (note.pitch + note.pitchAltered >= self.stringPitches[i]) {
var num = note.pitch + note.pitchAltered - self.stringPitches[i];
if (note.quarter === '^') num -= 0.5;else if (note.quarter === "v") num += 0.5;
return {
num: Math.round(num),
str: self.stringPitches.length - 1 - i,
// reverse the strings because string 0 is on the bottom
note: note
};
}
}
return {
num: "?",
str: self.stringPitches.length - 1,
note: note
};
}
StringPatterns.prototype.stringToPitch = function (stringNumber) {
var startingPitch = 5.3;
var bottom = this.strings.length - 1;
return startingPitch + (bottom - stringNumber) * this.linePitch;
};
function invalidNumber(retNotes, note) {
var number = {
num: "?",
str: 0,
note: note
};
retNotes.push(number);
retNotes.error = note.emit() + ': unexpected note for instrument';
}
StringPatterns.prototype.notesToNumber = function (notes, graces) {
var note;
var number;
var error = null;
var retNotes = null;
if (notes) {
retNotes = [];
if (notes.length > 1) {
retNotes = handleChordNotes(this, notes);
if (retNotes.error) {
error = retNotes.error;
}
} else {
if (!notes[0].endTie) {
note = new TabNote(notes[0].name, this.clefTranspose);
note.checkKeyAccidentals(this.accidentals, this.measureAccidentals);
number = toNumber(this, note);
if (number) {
retNotes.push(number);
} else {
invalidNumber(retNotes, note);
error = retNotes.error;
}
}
}
}
if (error) return retNotes;
var retGraces = null;
if (graces) {
retGraces = [];
for (var iiii = 0; iiii < graces.length; iiii++) {
note = new TabNote(graces[iiii].name, this.clefTranspose);
note.checkKeyAccidentals(this.accidentals, this.measureAccidentals);
number = toNumber(this, note);
if (number) {
retGraces.push(number);
} else {
invalidNumber(retGraces, note);
error = retNotes.error;
}
}
}
return {
notes: retNotes,
graces: retGraces,
error: error
};
};
StringPatterns.prototype.toString = function () {
var arr = [];
for (var i = 0; i < this.tuning.length; i++) {
var str = this.tuning[i].replaceAll(',', '').replaceAll("'", '').toUpperCase();
if (str[0] === '_') str = str[1] + 'b ';else if (str[0] === '^') str = str[1] + "# ";
arr.push(str);
}
return arr.join('');
};
StringPatterns.prototype.tabInfos = function (plugin) {
var name = plugin.params.label;
if (name) {
var tunePos = name.indexOf('%T');
var tuning = "";
if (tunePos != -1) {
tuning = this.toString();
if (plugin.capo > 0) {
tuning += ' capo:' + plugin.capo;
}
name = name.replace('%T', tuning);
}
return name;
}
return '';
};
// MAE 27 Nov 2023
StringPatterns.prototype.suppress = function (plugin) {
var suppress = plugin.params.suppress;
if (suppress) {
return true;
}
return false;
};
// MAE 27 Nov 2023 End
/**
* Common patterns for all string instruments
* @param {} plugin
* @param {} tuning
* @param {*} capo
* @param {*} highestNote
*/
function StringPatterns(plugin) {
//console.log("INIT StringPatterns constructor")
var tuning = plugin.tuning;
var capo = plugin.capo;
var highestNote = plugin.params.highestNote;
this.linePitch = plugin.linePitch;
this.highestNote = "a'";
if (highestNote) {
// override default
this.highestNote = highestNote;
}
this.measureAccidentals = {};
this.capo = 0;
if (capo) {
this.capo = parseInt(capo, 10);
}
this.transpose = plugin.transpose ? plugin.transpose : 0;
this.tuning = tuning;
this.stringPitches = [];
for (var i = 0; i < this.tuning.length; i++) {
var pitch = noteToMidi(this.tuning[i]) + this.capo;
this.stringPitches.push(pitch);
}
if (this.capo > 0) {
this.capoTuning = buildCapo(this);
}
this.strings = buildPatterns(this);
if (this.strings.error) {
plugin.setError(this.strings.error);
plugin.inError = true;
return;
}
// second position pattern per string
this.secondPos = buildSecond(this);
}
module.exports = StringPatterns;
/***/ }),
/***/ "./src/tablatures/instruments/string-tablature.js":
/*!********************************************************!*\
!*** ./src/tablatures/instruments/string-tablature.js ***!
\********************************************************/
/***/ (function(module) {
/**
* Layout tablature informations for draw
* @param {*} numLines
* @param {*} lineSpace
*/
function StringTablature(numLines, lineSpace) {
//console.log("INIT StringTablature constructor")
this.numLines = numLines;
this.lineSpace = lineSpace;
this.verticalSize = this.numLines * this.lineSpace;
var pitch = 3;
this.bar = {
pitch: pitch,
pitch2: lineSpace * numLines,
height: 5
};
}
/**
* return true if current line should not produce a tab
* @param {} line
*/
StringTablature.prototype.bypass = function (line) {
//console.log("RENDER StringTablature bypass")
var voices = line.staffGroup.voices;
if (voices.length > 0) {
if (voices[0].isPercussion) return true;
}
return false;
};
StringTablature.prototype.setRelative = function (child, relative, first) {
//console.log("RENDER StringTablature setRelative")
switch (child.type) {
case 'bar':
relative.pitch = this.bar.pitch;
relative.pitch2 = this.bar.pitch2;
relative.height = this.height;
break;
case 'symbol':
var top = this.bar.pitch2 / 2;
if (child.name == 'dots.dot') {
if (first) {
relative.pitch = top;
return false;
} else {
relative.pitch = top + this.lineSpace;
return true;
}
}
break;
}
return first;
};
module.exports = StringTablature;
/***/ }),
/***/ "./src/tablatures/instruments/tab-note.js":
/*!************************************************!*\
!*** ./src/tablatures/instruments/tab-note.js ***!
\************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var _require = __webpack_require__(/*! ../../synth/note-to-midi */ "./src/synth/note-to-midi.js"),
noteToMidi = _require.noteToMidi,
midiToNote = _require.midiToNote;
/**
*
* Note structure for Tabs
*
*/
function TabNote(note, clefTranspose) {
//console.log("INIT/RENDER TabNote constructor")
var pitch = noteToMidi(note);
if (clefTranspose) pitch += clefTranspose;
var newNote = midiToNote(pitch);
var isFlat = false;
var isSharp = false;
var isAltered = false;
var natural = null;
var quarter = null;
var isDouble = false;
var acc = 0;
if (note.startsWith('_')) {
isFlat = true;
acc = -1;
// check quarter flat
if (note[1] == '/') {
isFlat = false;
quarter = "v";
acc = 0;
} else if (note[1] == '_') {
// double flat
isDouble = true;
acc -= 1;
}
} else if (note.startsWith('^')) {
isSharp = true;
acc = +1;
// check quarter sharp
if (note[1] == '/') {
isSharp = false;
quarter = "^";
acc = 0;
} else if (note[1] == '^') {
// double sharp
isDouble = true;
acc += 1;
}
} else if (note.startsWith('=')) {
natural = true;
acc = 0;
}
isAltered = isFlat || isSharp || quarter != null;
if (isAltered || natural) {
if (quarter != null || isDouble) {
newNote = note.slice(2);
} else {
newNote = note.slice(1);
}
}
var hasComma = (newNote.match(/,/g) || []).length;
var hasQuote = (newNote.match(/'/g) || []).length;
this.pitch = pitch;
this.pitchAltered = 0;
this.name = newNote;
this.acc = acc;
this.isSharp = isSharp;
this.isKeySharp = false;
this.isDouble = isDouble;
this.isAltered = isAltered;
this.isFlat = isFlat;
this.isKeyFlat = false;
this.natural = natural;
this.quarter = quarter;
this.isLower = this.name == this.name.toLowerCase();
this.name = this.name[0].toUpperCase();
this.hasComma = hasComma;
this.isQuoted = hasQuote;
}
function cloneNote(self) {
var newNote = self.name;
var newTabNote = new TabNote(newNote);
newTabNote.pitch = self.pitch;
newTabNote.hasComma = self.hasComma;
newTabNote.isLower = self.isLower;
newTabNote.isQuoted = self.isQuoted;
newTabNote.isSharp = self.isSharp;
newTabNote.isKeySharp = self.isKeySharp;
newTabNote.isFlat = self.isFlat;
newTabNote.isKeyFlat = self.isKeyFlat;
return newTabNote;
}
TabNote.prototype.sameNoteAs = function (note) {
//console.log("INIT TabNote sameNoteAs")
return note.pitch === this.pitch;
};
TabNote.prototype.isLowerThan = function (note) {
//console.log("INIT TabNote isLowerThan")
return note.pitch > this.pitch;
};
TabNote.prototype.checkKeyAccidentals = function (accidentals, measureAccidentals) {
//console.log("RENDER TabNote checkKeyAccidentals")
if (this.isAltered || this.natural) return;
if (measureAccidentals[this.name.toUpperCase()]) {
switch (measureAccidentals[this.name.toUpperCase()]) {
case "__":
this.acc = -2;
this.pitchAltered = -2;
return;
case "_":
this.acc = -1;
this.pitchAltered = -1;
return;
case "=":
this.acc = 0;
this.pitchAltered = 0;
return;
case "^":
this.acc = 1;
this.pitchAltered = 1;
return;
case "^^":
this.acc = 2;
this.pitchAltered = 2;
return;
}
} else if (accidentals) {
var curNote = this.name;
for (var iii = 0; iii < accidentals.length; iii++) {
var curAccidentals = accidentals[iii];
if (curNote == curAccidentals.note.toUpperCase()) {
if (curAccidentals.acc == 'flat') {
this.acc = -1;
this.isKeyFlat = true;
this.pitchAltered = -1;
}
if (curAccidentals.acc == 'sharp') {
this.acc = +1;
this.isKeySharp = true;
this.pitchAltered = 1;
}
}
}
}
};
TabNote.prototype.getAccidentalEquiv = function () {
//console.log("TabNote getAccidentalEquiv")
var cloned = cloneNote(this);
if (cloned.isSharp || cloned.isKeySharp) {
cloned = cloned.nextNote();
cloned.isFlat = true;
cloned.isSharp = false;
cloned.isKeySharp = false;
} else if (cloned.isFlat || cloned.isKeyFlat) {
cloned = cloned.prevNote();
cloned.isSharp = true;
cloned.isFlat = false;
cloned.isKeyFlat = false;
}
return cloned;
};
TabNote.prototype.nextNote = function () {
//console.log("INIT TabNote nextNote")
var note = midiToNote(this.pitch + 1 + this.pitchAltered);
return new TabNote(note);
};
TabNote.prototype.prevNote = function () {
//console.log("TabNote prevNote")
var note = midiToNote(this.pitch - 1 + this.pitchAltered);
return new TabNote(note);
};
TabNote.prototype.emitNoAccidentals = function () {
//console.log("TabNote emitNoAccidentals")
var returned = this.name;
if (this.isLower) {
returned = returned.toLowerCase();
}
for (var ii = 0; ii < this.isQuoted; ii++) {
returned += "'";
}
for (var jj = 0; jj < this.hasComma; jj++) {
returned += ",";
}
return returned;
};
TabNote.prototype.emit = function () {
//console.log("INIT/RENDER TabNote emit")
var returned = this.name;
if (this.isSharp || this.isKeySharp) {
returned = '^' + returned;
if (this.isDouble) {
returned = '^' + returned;
}
}
if (this.isFlat || this.isKeyFlat) {
returned = '_' + returned;
if (this.isDouble) {
returned = '_' + returned;
}
}
if (this.quarter) {
if (this.quarter == "^") {
returned = "^/" + returned;
} else {
returned = "_/" + returned;
}
}
if (this.natural) {
returned = '=' + returned;
}
for (var ii = 1; ii <= this.hasComma; ii++) {
returned += ',';
}
if (this.isLower) {
returned = returned.toLowerCase();
for (var jj = 1; jj <= this.isQuoted; jj++) {
returned += "'";
}
}
return returned;
};
module.exports = TabNote;
/***/ }),
/***/ "./src/tablatures/instruments/tab-notes.js":
/*!*************************************************!*\
!*** ./src/tablatures/instruments/tab-notes.js ***!
\*************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var TabNote = __webpack_require__(/*! ./tab-note */ "./src/tablatures/instruments/tab-note.js");
var notes = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
function tabNotes(fromNote, toNote) {
//console.log("INIT TabNotes")
var fromN = new TabNote(fromNote);
var toN = new TabNote(toNote);
// check that toN is not lower than fromN
if (toN.isLowerThan(fromN)) {
var from = fromN.emit();
var tn = toN.emit();
return {
error: 'Invalid string Instrument tuning : ' + tn + ' string lower than ' + from + ' string'
};
}
var buildReturned = [];
var startIndex = notes.indexOf(fromN.name);
var toIndex = notes.indexOf(toN.name);
if (startIndex == -1 || toIndex == -1) {
return buildReturned;
}
var finished = false;
while (!finished) {
buildReturned.push(fromN.emit());
fromN = fromN.nextNote();
if (fromN.sameNoteAs(toN)) {
finished = true;
}
}
return buildReturned;
}
module.exports = tabNotes;
/***/ }),
/***/ "./src/tablatures/instruments/tab-string.js":
/*!**************************************************!*\
!*** ./src/tablatures/instruments/tab-string.js ***!
\**************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var StringTablature = __webpack_require__(/*! ./string-tablature */ "./src/tablatures/instruments/string-tablature.js");
var tabRenderer = __webpack_require__(/*! ../render/tab-renderer */ "./src/tablatures/render/tab-renderer.js");
var StringPatterns = __webpack_require__(/*! ./string-patterns */ "./src/tablatures/instruments/string-patterns.js");
/**
* upon init mainly store provided instances for later usage
* @param {*} abcTune the parsed tune AST tree
* @param {*} tuneNumber the parsed tune AST tree
* @param {*} params complementary args provided to Tablature Plugin
*/
Plugin.prototype.init = function (abcTune, tuneNumber, params, tabSettings) {
//console.log("INIT AbcStringTab Plugin.init")
this.tune = abcTune;
this.params = params;
this.tuneNumber = tuneNumber;
this.inError = false;
this.abcTune = abcTune;
this.linePitch = 3;
this.nbLines = tabSettings.defaultTuning.length;
this.isTabBig = tabSettings.isTabBig;
this.tabSymbolOffset = tabSettings.tabSymbolOffset;
this.capo = params.capo;
this.transpose = params.visualTranspose;
this.hideTabSymbol = params.hideTabSymbol;
this.tablature = new StringTablature(this.nbLines, this.linePitch);
var tuning = params.tuning;
if (!tuning) {
tuning = tabSettings.defaultTuning;
}
this.tuning = tuning;
this.semantics = new StringPatterns(this);
};
Plugin.prototype.setError = function (error) {
//console.log("Plugin setError")
if (error) {
this.error = error;
this.inError = true;
if (this.tune.warnings) {
this.tune.warnings.push(error);
} else {
this.tune.warnings = [error];
}
}
};
Plugin.prototype.render = function (renderer, line, staffIndex) {
//console.log("RENDER AbcStringTab Plugin.render")
if (this.inError) return;
if (this.tablature.bypass(line)) return;
tabRenderer(this, renderer, line, staffIndex);
};
function Plugin() {}
//
// Tablature plugin definition
//
var AbcStringTab = function AbcStringTab() {
return {
name: 'StringTab',
tablature: Plugin
};
};
module.exports = AbcStringTab;
/***/ }),
/***/ "./src/tablatures/render/tab-absolute-elements.js":
/*!********************************************************!*\
!*** ./src/tablatures/render/tab-absolute-elements.js ***!
\********************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/**
* Tablature Absolute elements factory
*/
var AbsoluteElement = __webpack_require__(/*! ../../write/creation/elements/absolute-element */ "./src/write/creation/elements/absolute-element.js");
var RelativeElement = __webpack_require__(/*! ../../write/creation/elements/relative-element */ "./src/write/creation/elements/relative-element.js");
function isObject(a) {
return a != null && a.constructor === Object;
}
function cloneObject(dest, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
if (!(Array.isArray(src[prop]) || isObject(src[prop]))) {
dest[prop] = src[prop];
}
}
}
}
function cloneAbsolute(absSrc) {
var returned = new AbsoluteElement('', 0, 0, '', 0);
cloneObject(returned, absSrc);
returned.top = 0;
returned.bottom = -1;
if (absSrc.abcelem) {
returned.abcelem = {};
cloneObject(returned.abcelem, absSrc.abcelem);
if (returned.abcelem.el_type === "note") returned.abcelem.el_type = 'tabNumber';
}
// TODO-PER: This fixes the classes because the element isn't created at the right time.
absSrc.cloned = returned;
return returned;
}
function cloneAbsoluteAndRelatives(absSrc, plugin) {
var returned = cloneAbsolute(absSrc);
if (plugin) {
var children = absSrc.children;
// proceed with relative as well
var first = true;
for (var ii = 0; ii < children.length; ii++) {
var child = children[ii];
var relative = new RelativeElement('', 0, 0, 0, '');
cloneObject(relative, child);
first = plugin.tablature.setRelative(child, relative, first);
returned.children.push(relative);
}
}
return returned;
}
function buildTabAbsolute(plugin, absX, relX) {
var tabIcon = 'tab.tiny';
var tabYPos = 7.5;
if (plugin.isTabBig) {
tabIcon = 'tab.big';
tabYPos = 10;
}
var element = {
el_type: "tab",
icon: tabIcon,
Ypos: tabYPos
};
// Offset the TAB symbol position if specified in the tab description
tabYPos += plugin.tabSymbolOffset;
// For tablature like whistle tab where you want the TAB symbol hidden
if (!plugin.hideTabSymbol) {
var tabAbsolute = new AbsoluteElement(element, 0, 0, "symbol", 0);
tabAbsolute.x = absX;
var tabRelative = new RelativeElement(tabIcon, 0, 0, 7.5, "tab");
tabRelative.x = relX;
tabAbsolute.children.push(tabRelative);
if (tabAbsolute.abcelem.el_type == 'tab') {
tabRelative.pitch = tabYPos;
}
}
return tabAbsolute;
}
function lyricsDim(abs) {
if (abs.extra) {
for (var ii = 0; ii < abs.extra.length; ii++) {
var extra = abs.extra[ii];
if (extra.type == 'lyric') {
return {
bottom: extra.bottom,
height: extra.height
};
}
}
}
return null;
}
function TabAbsoluteElements() {
//console.log("RENDER TabAbsoluteElements constructor")
this.accidentals = null;
}
function getInitialStaffSize(staffGroup) {
var returned = 0;
for (var ii = 0; ii < staffGroup.length; ii++) {
if (!staffGroup[ii].tabNameInfos) returned++;
}
return returned;
}
function buildRelativeTabNote(plugin, relX, def, curNote, isGrace) {
var strNote = curNote.num;
if (curNote.note.quarter != null) {
// add tab quarter => needs to string conversion then
strNote = strNote.toString();
strNote += curNote.note.quarter;
}
var pitch = plugin.semantics.stringToPitch(curNote.str);
def.notes.push({
num: strNote,
str: curNote.str,
pitch: curNote.note.emit()
});
var opt = {
type: 'tabNumber'
};
var tabNoteRelative = new RelativeElement(strNote, 0, 0, pitch + 0.3, opt);
tabNoteRelative.x = relX;
tabNoteRelative.isGrace = isGrace;
tabNoteRelative.isAltered = curNote.note.isAltered;
return tabNoteRelative;
}
function getXGrace(abs, index) {
var found = 0;
if (abs.extra) {
for (var ii = 0; ii < abs.extra.length; ii++) {
if (abs.extra[ii].c.indexOf('noteheads') >= 0) {
if (found === index) {
return abs.extra[ii].x + abs.extra[ii].w / 2;
} else {
found++;
}
}
}
}
return -1;
}
function graceInRest(absElem) {
if (absElem.abcelem) {
var elem = absElem.abcelem;
if (elem.rest) {
return elem.gracenotes;
}
}
return null;
}
function convertToNumber(plugin, pitches, graceNotes) {
var tabPos = plugin.semantics.notesToNumber(pitches, graceNotes);
if (tabPos.error) {
plugin.setError(tabPos.error);
return tabPos; // give up on error here
}
if (tabPos.graces && tabPos.notes) {
// add graces to last note in notes
var posNote = tabPos.notes.length - 1;
tabPos.notes[posNote].graces = tabPos.graces;
}
return tabPos;
}
function buildGraceRelativesForRest(plugin, abs, absChild, graceNotes, tabVoice) {
for (var mm = 0; mm < graceNotes.length; mm++) {
var defGrace = {
el_type: "note",
startChar: absChild.abcelem.startChar,
endChar: absChild.abcelem.endChar,
notes: [],
grace: true
};
var graceX = getXGrace(absChild, mm);
var curGrace = graceNotes[mm];
var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
abs.children.push(tabGraceRelative);
tabVoice.push(defGrace);
}
}
/**
* Build tab absolutes by scanning current staff line absolute array
* @param {*} staffAbsolute
*/
TabAbsoluteElements.prototype.build = function (plugin, staffAbsolute, tabVoice, voiceIndex, staffIndex, keySig, tabVoiceIndex) {
//console.log("RENDER TabAbsoluteElements build")
var staffSize = getInitialStaffSize(staffAbsolute);
var source = staffAbsolute[staffIndex + voiceIndex];
var dest = staffAbsolute[tabVoiceIndex];
var tabPos = null;
var defNote = null;
if (source.children[0].abcelem.el_type != 'clef') {
// keysig missing => provide one for tabs
if (keySig != 'none') {
source.children.splice(0, 0, keySig);
}
}
for (var ii = 0; ii < source.children.length; ii++) {
var absChild = source.children[ii];
var absX = absChild.x;
var relX = absX;
// if (absChild.children.length > 0) {
// relX = absChild.children[0].x;
// }
if (absChild.isClef) {
dest.children.push(buildTabAbsolute(plugin, absX, relX));
if (absChild.abcelem.type.indexOf('-8') >= 0) plugin.semantics.clefTranspose = -12;
if (absChild.abcelem.type.indexOf('+8') >= 0) plugin.semantics.clefTranspose = 12;
}
switch (absChild.type) {
case 'staff-extra key-signature':
// refresh key accidentals
this.accidentals = absChild.abcelem.accidentals;
plugin.semantics.accidentals = this.accidentals;
break;
case 'bar':
plugin.semantics.measureAccidentals = {};
var lastBar = false;
if (ii === source.children.length - 1) {
// used for final line bar drawing
// for multi tabs / multi staves
lastBar = true;
}
var cloned = cloneAbsoluteAndRelatives(absChild, plugin);
if (cloned.abcelem.barNumber) {
delete cloned.abcelem.barNumber;
for (var bn = 0; bn < cloned.children.length; bn++) {
if (cloned.children[bn].type === "barNumber") {
cloned.children.splice(bn, 1);
break;
}
}
}
cloned.abcelem.lastBar = lastBar;
dest.children.push(cloned);
tabVoice.push({
el_type: absChild.abcelem.el_type,
type: absChild.abcelem.type,
endChar: absChild.abcelem.endChar,
startChar: absChild.abcelem.startChar,
abselem: cloned
});
break;
case 'rest':
var restGraces = graceInRest(absChild);
if (restGraces) {
// to number conversion
tabPos = convertToNumber(plugin, null, restGraces);
if (tabPos.error) return;
// build relative for grace
defGrace = {
el_type: "note",
startChar: absChild.abcelem.startChar,
endChar: absChild.abcelem.endChar,
notes: [],
grace: true
};
buildGraceRelativesForRest(plugin, abs, absChild, tabPos.graces, tabVoice);
}
break;
case 'note':
var abs = cloneAbsolute(absChild);
abs.x = absChild.heads[0].x + absChild.heads[0].w / 2; // center the number
abs.lyricDim = lyricsDim(absChild);
var pitches = absChild.abcelem.pitches;
var graceNotes = absChild.abcelem.gracenotes;
abs.type = 'tabNumber';
// to number conversion
tabPos = convertToNumber(plugin, pitches, graceNotes);
if (tabPos.error) return;
if (tabPos.graces) {
// add graces to last note in notes
var posNote = tabPos.notes.length - 1;
tabPos.notes[posNote].graces = tabPos.graces;
}
// build relative
defNote = {
el_type: "note",
startChar: absChild.abcelem.startChar,
endChar: absChild.abcelem.endChar,
notes: []
};
for (var ll = 0; ll < tabPos.notes.length; ll++) {
var curNote = tabPos.notes[ll];
if (curNote.graces) {
for (var mm = 0; mm < curNote.graces.length; mm++) {
var defGrace = {
el_type: "note",
startChar: absChild.abcelem.startChar,
endChar: absChild.abcelem.endChar,
notes: [],
grace: true
};
var graceX = getXGrace(absChild, mm);
var curGrace = curNote.graces[mm];
var tabGraceRelative = buildRelativeTabNote(plugin, graceX, defGrace, curGrace, true);
abs.children.push(tabGraceRelative);
tabVoice.push(defGrace);
}
}
var tabNoteRelative = buildRelativeTabNote(plugin, abs.x + absChild.heads[ll].dx, defNote, curNote, false);
abs.children.push(tabNoteRelative);
}
if (defNote.notes.length > 0) {
defNote.abselem = abs;
tabVoice.push(defNote);
dest.children.push(abs);
}
break;
}
}
};
module.exports = TabAbsoluteElements;
/***/ }),
/***/ "./src/tablatures/render/tab-renderer.js":
/*!***********************************************!*\
!*** ./src/tablatures/render/tab-renderer.js ***!
\***********************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/* eslint-disable no-debugger */
var VoiceElement = __webpack_require__(/*! ../../write/creation/elements/voice-element */ "./src/write/creation/elements/voice-element.js");
var TabAbsoluteElements = __webpack_require__(/*! ./tab-absolute-elements */ "./src/tablatures/render/tab-absolute-elements.js");
var spacing = __webpack_require__(/*! ../../write/helpers/spacing */ "./src/write/helpers/spacing.js");
function initSpecialY() {
return {
tempoHeightAbove: 0,
partHeightAbove: 0,
volumeHeightAbove: 0,
dynamicHeightAbove: 0,
endingHeightAbove: 0,
chordHeightAbove: 0,
lyricHeightAbove: 0,
lyricHeightBelow: 0,
chordHeightBelow: 0,
volumeHeightBelow: 0,
dynamicHeightBelow: 0
};
}
function getLyricHeight(voice) {
var maxLyricHeight = 0;
for (var ii = 0; ii < voice.children.length; ii++) {
var curAbs = voice.children[ii];
if (curAbs.specialY) {
if (curAbs.specialY.lyricHeightBelow > maxLyricHeight) {
maxLyricHeight = curAbs.specialY.lyricHeightBelow;
}
}
}
return maxLyricHeight; // add spacing
}
function buildTabName(plugin, renderer, dest) {
var stringSemantics = plugin.semantics;
var textSize = renderer.controller.getTextSize;
var tabName = stringSemantics.tabInfos(plugin);
var suppress = stringSemantics.suppress(plugin);
var doDraw = true;
if (suppress) {
doDraw = false;
}
if (doDraw) {
var size = textSize.calc(tabName, 'tablabelfont', 'text instrumentname');
dest.tabNameInfos = {
textSize: {
height: size.height,
width: size.width
},
name: tabName
};
return size.height;
}
return 0;
}
function islastTabInStaff(index, staffGroup) {
if (staffGroup[index].isTabStaff) {
if (index === staffGroup.length - 1) return true;
if (staffGroup[index + 1].isTabStaff) {
return false;
} else {
return true;
}
}
return false;
}
function getStaffNumbers(staffs) {
var nbStaffs = 0;
for (var ii = 0; ii < staffs.length; ii++) {
if (!staffs[ii].isTabStaff) {
nbStaffs++;
}
}
return nbStaffs;
}
function getParentStaffIndex(staffs, index) {
for (var ii = index; ii >= 0; ii--) {
if (!staffs[ii].isTabStaff) {
return ii;
}
}
return -1;
}
function linkStaffAndTabs(staffs) {
for (var ii = 0; ii < staffs.length; ii++) {
if (staffs[ii].isTabStaff) {
// link to parent staff
var parentIndex = getParentStaffIndex(staffs, ii);
staffs[ii].hasStaff = staffs[parentIndex];
if (!staffs[parentIndex].hasTab) staffs[parentIndex].hasTab = [];
staffs[parentIndex].hasTab.push(staffs[ii]);
}
}
}
function isMultiVoiceSingleStaff(staffs, parent) {
if (getStaffNumbers(staffs) === 1) {
if (parent.voices.length > 1) return true;
}
return false;
}
function getNextTabPos(tabIndex, staffGroup) {
var startIndex = 0;
var handledVoices = 0;
var inProgress = true;
var nbVoices = 0;
while (inProgress) {
//for (var ii = 0; ii < staffGroup.length; ii++) {
if (!staffGroup[startIndex]) return -1;
if (!staffGroup[startIndex].isTabStaff) {
nbVoices = staffGroup[startIndex].voices.length; // get number of staff voices
}
if (staffGroup[startIndex].isTabStaff) {
handledVoices++;
if (islastTabInStaff(startIndex, staffGroup)) {
if (handledVoices < nbVoices) return startIndex + 1;
}
} else {
handledVoices = 0;
if (startIndex >= tabIndex) {
if (startIndex + 1 == staffGroup.length) return startIndex + 1;
if (!staffGroup[startIndex + 1].isTabStaff) return startIndex + 1;
}
}
startIndex++;
// out of space case
if (startIndex > staffGroup.length) return -1;
}
}
function getLastStaff(staffs, lastTab) {
for (var ii = lastTab; ii >= 0; ii--) {
if (!staffs[ii].isTabStaff) {
return staffs[ii];
}
}
return null;
}
function checkVoiceKeySig(voices, ii) {
var curVoice = voices[ii];
// on multivoice multistaff only the first voice has key signature
// folling consecutive do not have one => we should provide the first voice key sig back then
var elem0 = curVoice.children[0].abcelem;
if (elem0.el_type === 'clef') return null;
if (ii == 0) {
// not found => clef=none case
return 'none';
}
return voices[ii - 1].children[0];
}
function tabRenderer(plugin, renderer, line, staffIndex) {
//console.log("RENDER tabRenderer")
var absolutes = new TabAbsoluteElements();
var tabStaff = {
clef: {
type: 'TAB'
}
};
var tabSize = plugin.linePitch * plugin.nbLines;
var staffs = line.staff;
if (staffs) {
// give up on staffline=0 in key
var firstStaff = staffs[0];
if (firstStaff) {
if (firstStaff.clef) {
if (firstStaff.clef.stafflines == 0) {
plugin.setError("No tablatures when stafflines=0");
return;
}
}
}
staffs.splice(staffs.length, 0, tabStaff);
}
var staffGroup = line.staffGroup;
var voices = staffGroup.voices;
var firstVoice = voices[0];
// take lyrics into account if any
var lyricsHeight = getLyricHeight(firstVoice);
var padd = 3;
var prevIndex = staffIndex;
var previousStaff = staffGroup.staffs[prevIndex];
var tabTop = tabSize + padd - previousStaff.bottom - lyricsHeight;
if (previousStaff.isTabStaff) {
tabTop = previousStaff.top;
}
var staffGroupInfos = {
bottom: -1,
isTabStaff: true,
specialY: initSpecialY(),
lines: plugin.nbLines,
linePitch: plugin.linePitch,
dy: 0.15,
top: tabTop
};
var nextTabPos = getNextTabPos(staffIndex, staffGroup.staffs);
if (nextTabPos === -1) return;
staffGroupInfos.parentIndex = nextTabPos - 1;
staffGroup.staffs.splice(nextTabPos, 0, staffGroupInfos);
// staffGroup.staffs.push(staffGroupInfos);
staffGroup.height += tabSize + padd;
var parentStaff = getLastStaff(staffGroup.staffs, nextTabPos);
var nbVoices = 1;
if (isMultiVoiceSingleStaff(staffGroup.staffs, parentStaff)) {
nbVoices = parentStaff.voices.length;
}
// build from staff
tabStaff.voices = [];
for (var ii = 0; ii < nbVoices; ii++) {
var tabVoice = new VoiceElement(0, 0);
if (ii > 0) tabVoice.duplicate = true;
var nameHeight = buildTabName(plugin, renderer, tabVoice) / spacing.STEP;
nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
// This was pushing down the top staff by the tab label height
//staffGroup.staffs[staffIndex].top += nameHeight;
staffGroup.staffs[staffIndex].top += 1;
staffGroup.height += nameHeight;
tabVoice.staff = staffGroupInfos;
var tabVoiceIndex = voices.length;
voices.splice(voices.length, 0, tabVoice);
var keySig = checkVoiceKeySig(voices, ii + staffIndex);
tabStaff.voices[ii] = [];
absolutes.build(plugin, voices, tabStaff.voices[ii], ii, staffIndex, keySig, tabVoiceIndex);
}
linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
}
module.exports = tabRenderer;
/***/ }),
/***/ "./src/write/creation/abstract-engraver.js":
/*!*************************************************!*\
!*** ./src/write/creation/abstract-engraver.js ***!
\*************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_abstract_engraver.js: Creates a data structure suitable for printing a line of abc
var AbsoluteElement = __webpack_require__(/*! ./elements/absolute-element */ "./src/write/creation/elements/absolute-element.js");
var BeamElem = __webpack_require__(/*! ./elements/beam-element */ "./src/write/creation/elements/beam-element.js");
var BraceElem = __webpack_require__(/*! ./elements/brace-element */ "./src/write/creation/elements/brace-element.js");
var createClef = __webpack_require__(/*! ./create-clef */ "./src/write/creation/create-clef.js");
var createKeySignature = __webpack_require__(/*! ./create-key-signature */ "./src/write/creation/create-key-signature.js");
var createNoteHead = __webpack_require__(/*! ./create-note-head */ "./src/write/creation/create-note-head.js");
var createTimeSignature = __webpack_require__(/*! ./create-time-signature */ "./src/write/creation/create-time-signature.js");
var Decoration = __webpack_require__(/*! ./decoration */ "./src/write/creation/decoration.js");
var EndingElem = __webpack_require__(/*! ./elements/ending-element */ "./src/write/creation/elements/ending-element.js");
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var StaffGroupElement = __webpack_require__(/*! ./elements/staff-group-element */ "./src/write/creation/elements/staff-group-element.js");
var TempoElement = __webpack_require__(/*! ./elements/tempo-element */ "./src/write/creation/elements/tempo-element.js");
var TieElem = __webpack_require__(/*! ./elements/tie-element */ "./src/write/creation/elements/tie-element.js");
var TripletElem = __webpack_require__(/*! ./elements/triplet-element */ "./src/write/creation/elements/triplet-element.js");
var VoiceElement = __webpack_require__(/*! ./elements/voice-element */ "./src/write/creation/elements/voice-element.js");
var addChord = __webpack_require__(/*! ./add-chord */ "./src/write/creation/add-chord.js");
var pitchesToPerc = __webpack_require__(/*! ../../synth/pitches-to-perc */ "./src/synth/pitches-to-perc.js");
var parseCommon = __webpack_require__(/*! ../../parse/abc_common */ "./src/parse/abc_common.js");
var getDuration = function getDuration(elem) {
var d = 0;
if (elem.duration) {
d = elem.duration;
}
return d;
};
var hint = false;
var chartable = {
rest: {
0: "rests.whole",
1: "rests.half",
2: "rests.quarter",
3: "rests.8th",
4: "rests.16th",
5: "rests.32nd",
6: "rests.64th",
7: "rests.128th",
"multi": "rests.multimeasure"
},
note: {
"-1": "noteheads.dbl",
0: "noteheads.whole",
1: "noteheads.half",
2: "noteheads.quarter",
3: "noteheads.quarter",
4: "noteheads.quarter",
5: "noteheads.quarter",
6: "noteheads.quarter",
7: "noteheads.quarter",
'nostem': "noteheads.quarter"
},
rhythm: {
"-1": "noteheads.slash.whole",
0: "noteheads.slash.whole",
1: "noteheads.slash.whole",
2: "noteheads.slash.quarter",
3: "noteheads.slash.quarter",
4: "noteheads.slash.quarter",
5: "noteheads.slash.quarter",
6: "noteheads.slash.quarter",
7: "noteheads.slash.quarter",
nostem: "noteheads.slash.nostem"
},
x: {
"-1": "noteheads.indeterminate",
0: "noteheads.indeterminate",
1: "noteheads.indeterminate",
2: "noteheads.indeterminate",
3: "noteheads.indeterminate",
4: "noteheads.indeterminate",
5: "noteheads.indeterminate",
6: "noteheads.indeterminate",
7: "noteheads.indeterminate",
nostem: "noteheads.indeterminate"
},
harmonic: {
"-1": "noteheads.harmonic.quarter",
0: "noteheads.harmonic.quarter",
1: "noteheads.harmonic.quarter",
2: "noteheads.harmonic.quarter",
3: "noteheads.harmonic.quarter",
4: "noteheads.harmonic.quarter",
5: "noteheads.harmonic.quarter",
6: "noteheads.harmonic.quarter",
7: "noteheads.harmonic.quarter",
nostem: "noteheads.harmonic.quarter"
},
triangle: {
"-1": "noteheads.triangle.quarter",
0: "noteheads.triangle.quarter",
1: "noteheads.triangle.quarter",
2: "noteheads.triangle.quarter",
3: "noteheads.triangle.quarter",
4: "noteheads.triangle.quarter",
5: "noteheads.triangle.quarter",
6: "noteheads.triangle.quarter",
7: "noteheads.triangle.quarter",
nostem: "noteheads.triangle.quarter"
},
uflags: {
3: "flags.u8th",
4: "flags.u16th",
5: "flags.u32nd",
6: "flags.u64th"
},
dflags: {
3: "flags.d8th",
4: "flags.d16th",
5: "flags.d32nd",
6: "flags.d64th"
}
};
var AbstractEngraver = function AbstractEngraver(getTextSize, tuneNumber, options) {
this.decoration = new Decoration();
this.getTextSize = getTextSize;
this.tuneNumber = tuneNumber;
this.isBagpipes = options.bagpipes;
this.flatBeams = options.flatbeams;
this.graceSlurs = options.graceSlurs;
this.percmap = options.percmap;
this.initialClef = options.initialClef;
this.jazzchords = !!options.jazzchords;
this.accentAbove = !!options.accentAbove;
this.germanAlphabet = !!options.germanAlphabet;
this.reset();
};
AbstractEngraver.prototype.reset = function () {
this.slurs = {};
this.ties = [];
this.voiceScale = 1;
this.voiceColor = undefined;
this.slursbyvoice = {};
this.tiesbyvoice = {};
this.endingsbyvoice = {};
this.scaleByVoice = {};
this.colorByVoice = {};
this.tripletmultiplier = 1;
this.abcline = undefined;
this.accidentalSlot = undefined;
this.accidentalshiftx = undefined;
this.dotshiftx = undefined;
this.hasVocals = false;
this.minY = undefined;
this.partstartelem = undefined;
this.startlimitelem = undefined;
this.stemdir = undefined;
};
AbstractEngraver.prototype.setStemHeight = function (heightInPixels) {
this.stemHeight = Math.round(heightInPixels * 10 / spacing.STEP) / 10;
};
AbstractEngraver.prototype.getCurrentVoiceId = function (s, v) {
return "s" + s + "v" + v;
};
AbstractEngraver.prototype.pushCrossLineElems = function (s, v) {
this.slursbyvoice[this.getCurrentVoiceId(s, v)] = this.slurs;
this.tiesbyvoice[this.getCurrentVoiceId(s, v)] = this.ties;
this.endingsbyvoice[this.getCurrentVoiceId(s, v)] = this.partstartelem;
this.scaleByVoice[this.getCurrentVoiceId(s, v)] = this.voiceScale;
if (this.voiceColor) this.colorByVoice[this.getCurrentVoiceId(s, v)] = this.voiceColor;
};
AbstractEngraver.prototype.popCrossLineElems = function (s, v) {
this.slurs = this.slursbyvoice[this.getCurrentVoiceId(s, v)] || {};
this.ties = this.tiesbyvoice[this.getCurrentVoiceId(s, v)] || [];
this.partstartelem = this.endingsbyvoice[this.getCurrentVoiceId(s, v)];
this.voiceScale = this.scaleByVoice[this.getCurrentVoiceId(s, v)];
if (this.voiceScale === undefined) this.voiceScale = 1;
this.voiceColor = this.colorByVoice[this.getCurrentVoiceId(s, v)];
};
AbstractEngraver.prototype.containsLyrics = function (staves) {
for (var i = 0; i < staves.length; i++) {
for (var j = 0; j < staves[i].voices.length; j++) {
for (var k = 0; k < staves[i].voices[j].length; k++) {
var el = staves[i].voices[j][k];
if (el.lyric) {
// We just want to see if there are vocals below the music to know where to put the dynamics.
if (!el.positioning || el.positioning.vocalPosition === 'below') this.hasVocals = true;
return;
}
}
}
}
};
AbstractEngraver.prototype.createABCLine = function (staffs, tempo, l) {
this.minY = 2; // PER: This will be the lowest that any note reaches. It will be used to set the dynamics row.
// See if there are any lyrics on this line.
this.containsLyrics(staffs);
var staffgroup = new StaffGroupElement(this.getTextSize);
this.tempoSet = false;
for (var s = 0; s < staffs.length; s++) {
if (hint) this.restoreState();
hint = false;
this.createABCStaff(staffgroup, staffs[s], tempo, s, l);
}
return staffgroup;
};
AbstractEngraver.prototype.createABCStaff = function (staffgroup, abcstaff, tempo, s, l) {
// If the tempo is passed in, then the first element should get the tempo attached to it.
staffgroup.getTextSize.updateFonts(abcstaff);
for (var v = 0; v < abcstaff.voices.length; v++) {
var voice = new VoiceElement(v, abcstaff.voices.length);
if (v === 0) {
voice.barfrom = abcstaff.connectBarLines === "start" || abcstaff.connectBarLines === "continue";
voice.barto = abcstaff.connectBarLines === "continue" || abcstaff.connectBarLines === "end";
} else {
voice.duplicate = true; // bar lines and other duplicate info need not be created
}
if (abcstaff.title && abcstaff.title[v]) {
voice.header = abcstaff.title[v].replace(/\\n/g, "\n");
voice.headerPosition = 6 + staffgroup.getTextSize.baselineToCenter(voice.header, "voicefont", 'staff-extra voice-name', v, abcstaff.voices.length) / spacing.STEP;
}
if (abcstaff.clef && abcstaff.clef.type === "perc") voice.isPercussion = true;
var clef = (!this.initialClef || l === 0) && createClef(abcstaff.clef, this.tuneNumber);
if (clef) {
if (v === 0 && abcstaff.barNumber) {
this.addMeasureNumber(abcstaff.barNumber, clef);
}
voice.addChild(clef);
this.startlimitelem = clef; // limit ties here
}
var keySig = createKeySignature(abcstaff.key, this.tuneNumber);
if (keySig) {
voice.addChild(keySig);
this.startlimitelem = keySig; // limit ties here
}
if (abcstaff.meter) {
if (abcstaff.meter.type === 'specified') {
this.measureLength = abcstaff.meter.value[0].num / abcstaff.meter.value[0].den;
} else this.measureLength = 1;
var ts = createTimeSignature(abcstaff.meter, this.tuneNumber);
voice.addChild(ts);
this.startlimitelem = ts; // limit ties here
}
if (voice.duplicate) voice.children = []; // we shouldn't reprint the above if we're reusing the same staff. We just created them to get the right spacing.
var staffLines = abcstaff.clef.stafflines || abcstaff.clef.stafflines === 0 ? abcstaff.clef.stafflines : 5;
staffgroup.addVoice(voice, s, staffLines);
var isSingleLineStaff = staffLines === 1;
this.createABCVoice(abcstaff.voices[v], tempo, s, v, isSingleLineStaff, voice);
staffgroup.setStaffLimits(voice);
if (v === 0) {
// only do brace and bracket processing on the first voice, otherwise it would be done twice.
if (abcstaff.brace === "start" || !staffgroup.brace && abcstaff.brace) {
if (!staffgroup.brace) staffgroup.brace = [];
staffgroup.brace.push(new BraceElem(voice, "brace"));
} else if (abcstaff.brace === "end" && staffgroup.brace) {
staffgroup.brace[staffgroup.brace.length - 1].setBottomStaff(voice);
} else if (abcstaff.brace === "continue" && staffgroup.brace) {
staffgroup.brace[staffgroup.brace.length - 1].continuing(voice);
}
if (abcstaff.bracket === "start" || !staffgroup.bracket && abcstaff.bracket) {
if (!staffgroup.bracket) staffgroup.bracket = [];
staffgroup.bracket.push(new BraceElem(voice, "bracket"));
} else if (abcstaff.bracket === "end" && staffgroup.bracket) {
staffgroup.bracket[staffgroup.bracket.length - 1].setBottomStaff(voice);
} else if (abcstaff.bracket === "continue" && staffgroup.bracket) {
staffgroup.bracket[staffgroup.bracket.length - 1].continuing(voice);
}
}
}
};
function getBeamGroup(abcline, pos) {
// If there are notes beamed together, they are handled as a group, so find all of them here.
var elem = abcline[pos];
if (elem.el_type !== 'note' || !elem.startBeam || elem.endBeam) return {
count: 1,
elem: elem
};
var group = [];
while (pos < abcline.length && abcline[pos].el_type === 'note') {
group.push(abcline[pos]);
if (abcline[pos].endBeam) break;
pos++;
}
return {
count: group.length,
elem: group
};
}
AbstractEngraver.prototype.createABCVoice = function (abcline, tempo, s, v, isSingleLineStaff, voice) {
this.popCrossLineElems(s, v);
this.stemdir = this.isBagpipes ? "down" : null;
this.abcline = abcline;
if (this.partstartelem) {
this.partstartelem = new EndingElem("", null, null);
voice.addOther(this.partstartelem);
}
var voiceNumber = voice.voicetotal < 2 ? -1 : voice.voicenumber;
for (var slur in this.slurs) {
if (this.slurs.hasOwnProperty(slur)) {
// this is already a slur element, but it was created for the last line, so recreate it.
this.slurs[slur] = new TieElem({
force: this.slurs[slur].force,
voiceNumber: voiceNumber,
stemDir: this.slurs[slur].stemDir,
style: this.slurs[slur].dotted
});
if (hint) this.slurs[slur].setHint();
voice.addOther(this.slurs[slur]);
}
}
for (var i = 0; i < this.ties.length; i++) {
// this is already a tie element, but it was created for the last line, so recreate it.
this.ties[i] = new TieElem({
force: this.ties[i].force,
stemDir: this.ties[i].stemDir,
voiceNumber: voiceNumber,
style: this.ties[i].dotted
});
if (hint) this.ties[i].setHint();
voice.addOther(this.ties[i]);
}
for (var j = 0; j < this.abcline.length; j++) {
setAveragePitch(this.abcline[j]);
this.minY = Math.min(this.abcline[j].minpitch, this.minY);
}
var isFirstStaff = s === 0;
var pos = 0;
while (pos < this.abcline.length) {
var ret = getBeamGroup(this.abcline, pos);
var abselems = this.createABCElement(isFirstStaff, isSingleLineStaff, voice, ret.elem);
if (abselems) {
for (i = 0; i < abselems.length; i++) {
if (!this.tempoSet && tempo && !tempo.suppress) {
this.tempoSet = true;
var tempoElement = new AbsoluteElement(tempo, 0, 0, "tempo", this.tuneNumber, {});
tempoElement.addFixedX(new TempoElement(tempo, this.tuneNumber, createNoteHead));
voice.addChild(tempoElement);
}
voice.addChild(abselems[i]);
}
}
pos += ret.count;
}
this.pushCrossLineElems(s, v);
};
AbstractEngraver.prototype.saveState = function () {
this.tiesSave = parseCommon.cloneArray(this.ties);
this.slursSave = parseCommon.cloneHashOfHash(this.slurs);
this.slursbyvoiceSave = parseCommon.cloneHashOfHash(this.slursbyvoice);
this.tiesbyvoiceSave = parseCommon.cloneHashOfArrayOfHash(this.tiesbyvoice);
};
AbstractEngraver.prototype.restoreState = function () {
this.ties = parseCommon.cloneArray(this.tiesSave);
this.slurs = parseCommon.cloneHashOfHash(this.slursSave);
this.slursbyvoice = parseCommon.cloneHashOfHash(this.slursbyvoiceSave);
this.tiesbyvoice = parseCommon.cloneHashOfArrayOfHash(this.tiesbyvoiceSave);
};
// function writeMeasureWidth(voice) {
// var width = 0;
// for (var i = voice.children.length-1; i >= 0; i--) {
// var elem = voice.children[i];
// if (elem.abcelem.el_type === 'bar')
// break;
// width += elem.w;
// }
// return new RelativeElement(width.toFixed(2), -70, 0, undefined, {type:"debug"});
// }
// return an array of AbsoluteElement
AbstractEngraver.prototype.createABCElement = function (isFirstStaff, isSingleLineStaff, voice, elem) {
var elemset = [];
switch (elem.el_type) {
case undefined:
// it is undefined if we were passed an array in - an array means a set of notes that should be beamed together.
elemset = this.createBeam(isSingleLineStaff, voice, elem);
break;
case "note":
elemset[0] = this.createNote(elem, false, isSingleLineStaff, voice);
if (this.triplet && this.triplet.isClosed()) {
voice.addOther(this.triplet);
this.triplet = null;
this.tripletmultiplier = 1;
}
break;
case "bar":
elemset[0] = this.createBarLine(voice, elem, isFirstStaff);
if (voice.duplicate && elemset.length > 0) elemset[0].invisible = true;
// elemset[0].addChild(writeMeasureWidth(voice));
break;
case "meter":
elemset[0] = createTimeSignature(elem, this.tuneNumber);
this.startlimitelem = elemset[0]; // limit ties here
if (voice.duplicate && elemset.length > 0) elemset[0].invisible = true;
break;
case "clef":
elemset[0] = createClef(elem, this.tuneNumber);
if (!elemset[0]) return null;
if (voice.duplicate && elemset.length > 0) elemset[0].invisible = true;
break;
case "key":
var absKey = createKeySignature(elem, this.tuneNumber);
if (absKey) {
elemset[0] = absKey;
this.startlimitelem = elemset[0]; // limit ties here
}
if (voice.duplicate && elemset.length > 0) elemset[0].invisible = true;
break;
case "stem":
this.stemdir = elem.direction === "auto" ? undefined : elem.direction;
break;
case "part":
var abselem = new AbsoluteElement(elem, 0, 0, 'part', this.tuneNumber);
var dim = this.getTextSize.calc(elem.title, 'partsfont', "part");
abselem.addFixedX(new RelativeElement(elem.title, 0, 0, undefined, {
type: "part",
height: dim.height / spacing.STEP
}));
elemset[0] = abselem;
break;
case "tempo":
var abselem3 = new AbsoluteElement(elem, 0, 0, 'tempo', this.tuneNumber);
abselem3.addFixedX(new TempoElement(elem, this.tuneNumber, createNoteHead));
elemset[0] = abselem3;
break;
case "style":
if (elem.head === "normal") delete this.style;else this.style = elem.head;
break;
case "hint":
hint = true;
this.saveState();
break;
case "midi":
// This has no effect on the visible music, so just skip it.
break;
case "scale":
this.voiceScale = elem.size;
break;
case "color":
this.voiceColor = elem.color;
voice.color = this.voiceColor;
break;
default:
var abselem2 = new AbsoluteElement(elem, 0, 0, 'unsupported', this.tuneNumber);
abselem2.addFixed(new RelativeElement("element type " + elem.el_type, 0, 0, undefined, {
type: "debug"
}));
elemset[0] = abselem2;
}
return elemset;
};
function setAveragePitch(elem) {
if (elem.pitches) {
sortPitch(elem);
var sum = 0;
for (var p = 0; p < elem.pitches.length; p++) {
sum += elem.pitches[p].verticalPos;
}
elem.averagepitch = sum / elem.pitches.length;
elem.minpitch = elem.pitches[0].verticalPos;
elem.maxpitch = elem.pitches[elem.pitches.length - 1].verticalPos;
}
}
AbstractEngraver.prototype.createBeam = function (isSingleLineStaff, voice, elems) {
var abselemset = [];
var beamelem = new BeamElem(this.stemHeight * this.voiceScale, this.stemdir, this.flatBeams, elems[0]);
if (hint) beamelem.setHint();
for (var i = 0; i < elems.length; i++) {
// Do a first pass to figure out the stem direction before creating the notes, so that staccatos and other decorations can be placed correctly.
beamelem.runningDirection(elems[i]);
}
beamelem.setStemDirection();
var tempStemDir = this.stemdir;
this.stemdir = beamelem.stemsUp ? 'up' : 'down';
for (i = 0; i < elems.length; i++) {
var elem = elems[i];
var abselem = this.createNote(elem, true, isSingleLineStaff, voice);
abselemset.push(abselem);
beamelem.add(abselem);
if (this.triplet && this.triplet.isClosed()) {
voice.addOther(this.triplet);
this.triplet = null;
this.tripletmultiplier = 1;
}
}
beamelem.calcDir();
voice.addBeam(beamelem);
this.stemdir = tempStemDir;
return abselemset;
};
var sortPitch = function sortPitch(elem) {
var sorted;
do {
sorted = true;
for (var p = 0; p < elem.pitches.length - 1; p++) {
if (elem.pitches[p].pitch > elem.pitches[p + 1].pitch) {
sorted = false;
var tmp = elem.pitches[p];
elem.pitches[p] = elem.pitches[p + 1];
elem.pitches[p + 1] = tmp;
}
}
} while (!sorted);
};
var ledgerLines = function ledgerLines(abselem, minPitch, maxPitch, isRest, symbolWidth, additionalLedgers, dir, dx, scale) {
for (var i = maxPitch; i > 11; i--) {
if (i % 2 === 0 && !isRest) {
abselem.addFixed(new RelativeElement(null, dx, (symbolWidth + 4) * scale, i, {
type: "ledger"
}));
}
}
for (i = minPitch; i < 1; i++) {
if (i % 2 === 0 && !isRest) {
abselem.addFixed(new RelativeElement(null, dx, (symbolWidth + 4) * scale, i, {
type: "ledger"
}));
}
}
for (i = 0; i < additionalLedgers.length; i++) {
// PER: draw additional ledgers
var ofs = symbolWidth;
if (dir === 'down') ofs = -ofs;
abselem.addFixed(new RelativeElement(null, ofs + dx, (symbolWidth + 4) * scale, additionalLedgers[i], {
type: "ledger"
}));
}
};
AbstractEngraver.prototype.addGraceNotes = function (elem, voice, abselem, notehead, stemHeight, isBagpipes, roomtaken) {
var gracescale = 3 / 5;
var graceScaleStem = 3.5 / 5; // TODO-PER: empirically found constant.
stemHeight = Math.round(stemHeight * graceScaleStem);
var gracebeam = null;
var flag;
if (elem.gracenotes.length > 1) {
gracebeam = new BeamElem(stemHeight, "grace", isBagpipes);
if (hint) gracebeam.setHint();
gracebeam.mainNote = abselem; // this gives us a reference back to the note this is attached to so that the stems can be attached somewhere.
}
var i;
var graceoffsets = [];
for (i = elem.gracenotes.length - 1; i >= 0; i--) {
// figure out where to place each gracenote
roomtaken += 10;
graceoffsets[i] = roomtaken;
if (elem.gracenotes[i].accidental) {
roomtaken += 7;
}
}
for (i = 0; i < elem.gracenotes.length; i++) {
var gracepitch = elem.gracenotes[i].verticalPos;
flag = gracebeam ? null : chartable.uflags[isBagpipes ? 5 : 3];
var accidentalSlot = [];
var ret = createNoteHead(abselem, "noteheads.quarter", elem.gracenotes[i], {
dir: "up",
headx: -graceoffsets[i],
extrax: -graceoffsets[i],
flag: flag,
scale: gracescale * this.voiceScale,
accidentalSlot: accidentalSlot
});
ret.notehead.highestVert = ret.notehead.pitch + stemHeight;
var grace = ret.notehead;
this.addSlursAndTies(abselem, elem.gracenotes[i], grace, voice, "up", true);
abselem.addExtra(grace);
// PER: added acciaccatura slash
if (elem.gracenotes[i].acciaccatura) {
var pos = elem.gracenotes[i].verticalPos + 7 * gracescale; // the same formula that determines the flag position.
var dAcciaccatura = gracebeam ? 5 : 6; // just an offset to make it line up correctly.
abselem.addRight(new RelativeElement("flags.ugrace", -graceoffsets[i] + dAcciaccatura, 0, pos, {
scalex: gracescale,
scaley: gracescale
}));
}
if (gracebeam) {
// give the beam the necessary info
var graceDuration = elem.gracenotes[i].duration / 2;
if (isBagpipes) graceDuration /= 2;
var pseudoabselem = {
heads: [grace],
abcelem: {
averagepitch: gracepitch,
minpitch: gracepitch,
maxpitch: gracepitch,
duration: graceDuration
}
};
gracebeam.add(pseudoabselem);
} else {
// draw the stem
var p1 = gracepitch + 1 / 3 * gracescale;
var p2 = gracepitch + 7 * gracescale;
var dx = grace.dx + grace.w;
var width = -0.6;
abselem.addExtra(new RelativeElement(null, dx, 0, p1, {
"type": "stem",
"pitch2": p2,
linewidth: width
}));
}
ledgerLines(abselem, gracepitch, gracepitch, false, glyphs.getSymbolWidth("noteheads.quarter"), [], true, grace.dx - 1, 0.6);
// if this is the first grace note, we might want to start a slur.
// there is a slur if graceSlurs is specifically set.
// there is no slur if it is bagpipes.
// there is not a slur if the element is a spacer or invisible rest.
var isInvisibleRest = elem.rest && (elem.rest.type === "spacer" || elem.rest.type === "invisible");
if (i === 0 && !isBagpipes && this.graceSlurs && !isInvisibleRest) {
// This is the overall slur that is under the grace notes.
voice.addOther(new TieElem({
anchor1: grace,
anchor2: notehead,
isGrace: true
}));
}
}
if (gracebeam) {
gracebeam.calcDir();
voice.addBeam(gracebeam);
}
return roomtaken;
};
function addRestToAbsElement(abselem, elem, duration, dot, isMultiVoice, stemdir, isSingleLineStaff, durlog, voiceScale) {
var c;
var restpitch = 7;
var noteHead;
var roomTaken;
var roomTakenRight;
if (isMultiVoice) {
if (stemdir === "down") restpitch = 3;
if (stemdir === "up") restpitch = 11;
}
// There is special placement for the percussion staff. If there is one staff line, then move the rest position.
if (isSingleLineStaff) {
// The half and whole rests are attached to different lines normally, so we need to tweak their position to get them to both be attached to the same one.
if (duration < 0.5) restpitch = 7;else if (duration < 1) restpitch = 7; // half rest
else restpitch = 5; // whole rest
}
switch (elem.rest.type) {
case "whole":
c = chartable.rest[0];
elem.averagepitch = restpitch;
elem.minpitch = restpitch;
elem.maxpitch = restpitch;
dot = 0;
break;
case "rest":
if (elem.style === "rhythm")
// special case for rhythm: rests are a handy way to express the rhythm.
c = chartable.rhythm[-durlog];else c = chartable.rest[-durlog];
elem.averagepitch = restpitch;
elem.minpitch = restpitch;
elem.maxpitch = restpitch;
break;
case "invisible":
case "invisible-multimeasure":
case "spacer":
c = "";
elem.averagepitch = restpitch;
elem.minpitch = restpitch;
elem.maxpitch = restpitch;
break;
case "multimeasure":
c = chartable.rest['multi'];
elem.averagepitch = restpitch;
elem.minpitch = restpitch;
elem.maxpitch = restpitch;
dot = 0;
var mmWidth = glyphs.getSymbolWidth(c);
abselem.addHead(new RelativeElement(c, mmWidth, mmWidth * 2, 7));
var numMeasures = new RelativeElement("" + elem.rest.text, mmWidth, mmWidth, 16, {
type: "multimeasure-text"
});
abselem.addExtra(numMeasures);
}
if (elem.rest.type.indexOf("multimeasure") < 0 && elem.rest.type !== "invisible") {
var ret = createNoteHead(abselem, c, {
verticalPos: restpitch
}, {
dot: dot,
scale: voiceScale
});
noteHead = ret.notehead;
if (noteHead) {
abselem.addHead(noteHead);
roomTaken = ret.accidentalshiftx;
roomTakenRight = ret.dotshiftx;
}
}
return {
noteHead: noteHead,
roomTaken: roomTaken,
roomTakenRight: roomTakenRight
};
}
function addIfNotExist(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (JSON.stringify(arr[i]) === JSON.stringify(item)) return;
}
arr.push(item);
}
AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, stemdir, style, zeroDuration, durlog, nostem, voice) {
var dotshiftx = 0; // room taken by chords with displaced noteheads which cause dots to shift
var noteHead;
var roomTaken = 0;
var roomTakenRight = 0;
var min;
var i;
var additionalLedgers = [];
// The accidentalSlot will hold a list of all the accidentals on this chord. Each element is a vertical place,
// and contains a pitch, which is the last pitch that contains an accidental in that slot. The slots are numbered
// from closest to the note to farther left. We only need to know the last accidental we placed because
// we know that the pitches are sorted by now.
var accidentalSlot = [];
var symbolWidth = 0;
var dir = elem.averagepitch >= 6 ? "down" : "up";
if (stemdir) dir = stemdir;
style = elem.style ? elem.style : style; // get the style of note head.
if (!style || style === "normal") style = "note";
var noteSymbol;
if (zeroDuration) noteSymbol = chartable[style].nostem;else noteSymbol = chartable[style][-durlog];
if (!noteSymbol) console.log("noteSymbol:", style, durlog, zeroDuration);
// determine elements of chords which should be shifted
var p;
for (p = dir === "down" ? elem.pitches.length - 2 : 1; dir === "down" ? p >= 0 : p < elem.pitches.length; p = dir === "down" ? p - 1 : p + 1) {
var prev = elem.pitches[dir === "down" ? p + 1 : p - 1];
var curr = elem.pitches[p];
var delta = dir === "down" ? prev.pitch - curr.pitch : curr.pitch - prev.pitch;
if (delta <= 1 && !prev.printer_shift) {
curr.printer_shift = delta ? "different" : "same";
if (curr.verticalPos > 11 || curr.verticalPos < 1) {
// PER: add extra ledger line
additionalLedgers.push(curr.verticalPos - curr.verticalPos % 2);
}
if (dir === "down") {
roomTaken = glyphs.getSymbolWidth(noteSymbol) + 2;
} else {
dotshiftx = glyphs.getSymbolWidth(noteSymbol) + 2;
}
}
}
var pp = elem.pitches.length;
for (p = 0; p < elem.pitches.length; p++) {
if (!nostem) {
var flag;
if (dir === "down" && p !== 0 || dir === "up" && p !== pp - 1) {
// not the stemmed elem of the chord
flag = null;
} else {
flag = chartable[dir === "down" ? "dflags" : "uflags"][-durlog];
}
}
var c;
if (elem.pitches[p].style) {
// There is a style for the whole group of pitches, but there could also be an override for a particular pitch.
c = chartable[elem.pitches[p].style][-durlog];
} else if (voice.isPercussion && this.percmap) {
c = noteSymbol;
var percHead = this.percmap[pitchesToPerc(elem.pitches[p])];
if (percHead && percHead.noteHead) {
if (chartable[percHead.noteHead]) c = chartable[percHead.noteHead][-durlog];
}
} else c = noteSymbol;
// The highest position for the sake of placing slurs is itself if the slur is internal. It is the highest position possible if the slur is for the whole chord.
// If the note is the only one in the chord, then any slur it has counts as if it were on the whole chord.
elem.pitches[p].highestVert = elem.pitches[p].verticalPos;
var isTopWhenStemIsDown = (stemdir === "up" || dir === "up") && p === 0;
var isBottomWhenStemIsUp = (stemdir === "down" || dir === "down") && p === pp - 1;
if (isTopWhenStemIsDown || isBottomWhenStemIsUp) {
// place to put slurs if not already on pitches
if (elem.startSlur || pp === 1) {
elem.pitches[p].highestVert = elem.pitches[pp - 1].verticalPos;
if (getDuration(elem) < 1 && (stemdir === "up" || dir === "up")) elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
}
if (elem.startSlur) {
if (!elem.pitches[p].startSlur) elem.pitches[p].startSlur = []; //TODO possibly redundant, provided array is not optional
for (i = 0; i < elem.startSlur.length; i++) {
addIfNotExist(elem.pitches[p].startSlur, elem.startSlur[i]);
}
}
if (elem.endSlur) {
elem.pitches[p].highestVert = elem.pitches[pp - 1].verticalPos;
if (getDuration(elem) < 1 && (stemdir === "up" || dir === "up")) elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
if (!elem.pitches[p].endSlur) elem.pitches[p].endSlur = []; //TODO possibly redundant, provided array is not optional
for (i = 0; i < elem.endSlur.length; i++) {
addIfNotExist(elem.pitches[p].endSlur, elem.endSlur[i]);
}
}
}
var hasStem = !nostem && durlog <= -1;
var ret = createNoteHead(abselem, c, elem.pitches[p], {
dir: dir,
extrax: -roomTaken,
flag: flag,
dot: dot,
dotshiftx: dotshiftx,
scale: this.voiceScale,
accidentalSlot: accidentalSlot,
shouldExtendStem: !stemdir,
printAccidentals: !voice.isPercussion
});
symbolWidth = Math.max(glyphs.getSymbolWidth(c), symbolWidth);
abselem.extraw -= ret.extraLeft;
noteHead = ret.notehead;
if (noteHead) {
this.addSlursAndTies(abselem, elem.pitches[p], noteHead, voice, hasStem ? dir : null, false);
if (elem.gracenotes && elem.gracenotes.length > 0) noteHead.bottom = noteHead.bottom - 1; // If there is a tie to the grace notes, leave a little more room for the note to avoid collisions.
abselem.addHead(noteHead);
}
roomTaken += ret.accidentalshiftx;
roomTakenRight = Math.max(roomTakenRight, ret.dotshiftx);
}
// draw stem from the furthest note to a pitch above/below the stemmed note
if (hasStem) {
var stemHeight = Math.round(70 * this.voiceScale) / 10;
var p1 = dir === "down" ? elem.minpitch - stemHeight : elem.minpitch + 1 / 3;
// PER added stemdir test to make the line meet the note.
if (p1 > 6 && !stemdir) p1 = 6;
var p2 = dir === "down" ? elem.maxpitch - 1 / 3 : elem.maxpitch + stemHeight;
// PER added stemdir test to make the line meet the note.
if (p2 < 6 && !stemdir) p2 = 6;
var dx = dir === "down" || abselem.heads.length === 0 ? 0 : abselem.heads[0].w;
var width = dir === "down" ? 1 : -1;
// TODO-PER-HACK: One type of note head has a different placement of the stem. This should be more generically calculated:
if (noteHead && noteHead.c === 'noteheads.slash.quarter') {
if (dir === 'down') p2 -= 1;else p1 += 1;
}
if (noteHead && noteHead.c === 'noteheads.triangle.quarter') {
if (dir === 'down') p2 -= 0.7;else p1 -= 1.2;
}
abselem.addRight(new RelativeElement(null, dx, 0, p1, {
"type": "stem",
"pitch2": p2,
linewidth: width,
bottom: p1 - 1
}));
//var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
min = Math.min(p1, p2);
}
return {
noteHead: noteHead,
roomTaken: roomTaken,
roomTakenRight: roomTakenRight,
min: min,
additionalLedgers: additionalLedgers,
dir: dir,
symbolWidth: symbolWidth
};
};
AbstractEngraver.prototype.addLyric = function (abselem, elem) {
var lyricStr = "";
elem.lyric.forEach(function (ly) {
var div = ly.divider === ' ' ? "" : ly.divider;
lyricStr += ly.syllable + div + "\n";
});
var lyricDim = this.getTextSize.calc(lyricStr, 'vocalfont', "lyric");
var position = elem.positioning ? elem.positioning.vocalPosition : 'below';
abselem.addCentered(new RelativeElement(lyricStr, 0, lyricDim.width, undefined, {
type: "lyric",
position: position,
height: lyricDim.height / spacing.STEP,
dim: this.getTextSize.attr('vocalfont', "lyric")
}));
};
AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaff, voice) {
//stem presence: true for drawing stemless notehead
var notehead = null;
var roomtaken = 0; // room needed to the left of the note
var roomtakenright = 0; // room needed to the right of the note
var symbolWidth = 0;
var additionalLedgers = []; // PER: handle the case of [bc'], where the b doesn't have a ledger line
var dir;
var duration = getDuration(elem);
var zeroDuration = false;
if (duration === 0) {
zeroDuration = true;
duration = 0.25;
nostem = true;
} //PER: zero duration will draw a quarter note head.
var durlog = Math.floor(Math.log(duration) / Math.log(2)); //TODO use getDurlog
var dot = 0;
for (var tot = Math.pow(2, durlog), inc = tot / 2; tot < duration; dot++, tot += inc, inc /= 2) {
;
}
if (elem.startTriplet) {
this.tripletmultiplier = elem.tripletMultiplier;
}
var durationForSpacing = duration * this.tripletmultiplier;
if (elem.rest && elem.rest.type === 'multimeasure') durationForSpacing = 1;
if (elem.rest && elem.rest.type === 'invisible-multimeasure') durationForSpacing = this.measureLength * elem.rest.text;
var absType = elem.rest ? "rest" : "note";
var abselem = new AbsoluteElement(elem, durationForSpacing, 1, absType, this.tuneNumber, {
durationClassOveride: elem.duration * this.tripletmultiplier
});
if (hint) abselem.setHint();
if (elem.rest) {
if (this.measureLength === duration && elem.rest.type !== 'invisible' && elem.rest.type !== 'spacer' && elem.rest.type.indexOf('multimeasure') < 0) elem.rest.type = 'whole'; // If the rest is exactly a measure, always use a whole rest
var ret1 = addRestToAbsElement(abselem, elem, duration, dot, voice.voicetotal > 1, this.stemdir, isSingleLineStaff, durlog, this.voiceScale);
notehead = ret1.noteHead;
roomtaken = ret1.roomTaken;
roomtakenright = ret1.roomTakenRight;
} else {
var ret2 = this.addNoteToAbcElement(abselem, elem, dot, this.stemdir, this.style, zeroDuration, durlog, nostem, voice);
if (ret2.min !== undefined) this.minY = Math.min(ret2.min, this.minY);
notehead = ret2.noteHead;
roomtaken = ret2.roomTaken;
roomtakenright = ret2.roomTakenRight;
additionalLedgers = ret2.additionalLedgers;
dir = ret2.dir;
symbolWidth = ret2.symbolWidth;
}
if (elem.lyric !== undefined) {
this.addLyric(abselem, elem);
}
if (elem.gracenotes !== undefined) {
roomtaken += this.addGraceNotes(elem, voice, abselem, notehead, this.stemHeight * this.voiceScale, this.isBagpipes, roomtaken);
}
if (elem.decoration) {
// TODO-PER: nostem is true if this is beamed. In that case we don't know where to place the decoration yet so just make a guess. This should be refactored to not place decorations until after the beams are determined.
// This should probably be combined with moveDecorations()
var bottom = nostem && dir !== 'up' ? Math.min(-3, abselem.bottom - 6) : abselem.bottom;
this.decoration.createDecoration(voice, elem.decoration, abselem.top, notehead ? notehead.w : 0, abselem, roomtaken, dir, bottom, elem.positioning, this.hasVocals, this.accentAbove);
}
if (elem.barNumber) {
abselem.addFixed(new RelativeElement(elem.barNumber, -10, 0, 0, {
type: "barNumber"
}));
}
// ledger lines
ledgerLines(abselem, elem.minpitch, elem.maxpitch, elem.rest, symbolWidth, additionalLedgers, dir, -2, 1);
if (elem.chord !== undefined) {
var ret3 = addChord(this.getTextSize, abselem, elem, roomtaken, roomtakenright, symbolWidth, this.jazzchords, this.germanAlphabet);
roomtaken = ret3.roomTaken;
roomtakenright = ret3.roomTakenRight;
}
if (elem.startTriplet) {
this.triplet = new TripletElem(elem.startTriplet, notehead, {
flatBeams: this.flatBeams
}); // above is opposite from case of slurs
}
if (elem.endTriplet && this.triplet) {
this.triplet.setCloseAnchor(notehead);
}
if (this.triplet && !elem.startTriplet && !elem.endTriplet && !(elem.rest && elem.rest.type === "spacer")) {
this.triplet.middleNote(notehead);
}
return abselem;
};
AbstractEngraver.prototype.addSlursAndTies = function (abselem, pitchelem, notehead, voice, dir, isGrace) {
if (pitchelem.endTie) {
if (this.ties.length > 0) {
// If there are multiple open ties, find the one that applies by matching the pitch, if possible.
var found = false;
for (var j = 0; j < this.ties.length; j++) {
if (this.ties[j].anchor1 && this.ties[j].anchor1.pitch === notehead.pitch) {
this.ties[j].setEndAnchor(notehead);
voice.setRange(this.ties[j]);
this.ties.splice(j, 1);
found = true;
break;
}
}
if (!found) {
this.ties[0].setEndAnchor(notehead);
voice.setRange(this.ties[0]);
this.ties.splice(0, 1);
}
}
}
var voiceNumber = voice.voicetotal < 2 ? -1 : voice.voicenumber;
if (pitchelem.startTie) {
var tie = new TieElem({
anchor1: notehead,
force: this.stemdir === "down" || this.stemdir === "up",
stemDir: this.stemdir,
isGrace: isGrace,
voiceNumber: voiceNumber,
style: pitchelem.startTie.style
});
if (hint) tie.setHint();
this.ties[this.ties.length] = tie;
voice.addOther(tie);
// HACK-PER: For the animation, we need to know if a note is tied to the next one, so here's a flag.
// Unfortunately, only some of the notes in the current event might be tied, but this will consider it
// tied if any one of them is. That will work for most cases.
abselem.startTie = true;
}
var slur;
var slurid;
if (pitchelem.endSlur) {
for (var i = 0; i < pitchelem.endSlur.length; i++) {
slurid = pitchelem.endSlur[i];
if (this.slurs[slurid]) {
slur = this.slurs[slurid];
slur.setEndAnchor(notehead);
voice.setRange(slur);
delete this.slurs[slurid];
} else {
slur = new TieElem({
anchor2: notehead,
stemDir: this.stemdir,
voiceNumber: voiceNumber
});
if (hint) slur.setHint();
voice.addOther(slur);
}
if (this.startlimitelem) {
slur.setStartX(this.startlimitelem);
}
}
} else if (!isGrace) {
for (var s in this.slurs) {
if (this.slurs.hasOwnProperty(s)) {
this.slurs[s].addInternalNote(notehead);
}
}
}
if (pitchelem.startSlur) {
for (i = 0; i < pitchelem.startSlur.length; i++) {
slurid = pitchelem.startSlur[i].label;
slur = new TieElem({
anchor1: notehead,
stemDir: this.stemdir,
voiceNumber: voiceNumber,
style: pitchelem.startSlur[i].style
});
if (hint) slur.setHint();
this.slurs[slurid] = slur;
voice.addOther(slur);
}
}
};
AbstractEngraver.prototype.addMeasureNumber = function (number, abselem) {
var measureNumDim = this.getTextSize.calc(number, "measurefont", 'bar-number');
var dx = 0;
if (abselem.isClef)
// If this is a clef rather than bar line, then the number shouldn't be centered because it could overlap the left side. This is an easy way to let it be centered but move it over, too.
dx += measureNumDim.width / 2;
var vert = measureNumDim.width > 10 && abselem.abcelem.type === "treble" ? 13 : 11;
abselem.addFixed(new RelativeElement(number, dx, measureNumDim.width, vert + measureNumDim.height / spacing.STEP, {
type: "barNumber",
dim: this.getTextSize.attr("measurefont", 'bar-number')
}));
};
AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff) {
// bar_thin, bar_thin_thick, bar_thin_thin, bar_thick_thin, bar_right_repeat, bar_left_repeat, bar_double_repeat
var abselem = new AbsoluteElement(elem, 0, 10, 'bar', this.tuneNumber);
var anchor = null; // place to attach part lines
var dx = 0;
if (elem.barNumber) {
this.addMeasureNumber(elem.barNumber, abselem);
}
var firstdots = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat";
var firstthin = elem.type !== "bar_left_repeat" && elem.type !== "bar_thick_thin" && elem.type !== "bar_invisible";
var thick = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat" || elem.type === "bar_left_repeat" || elem.type === "bar_thin_thick" || elem.type === "bar_thick_thin";
var secondthin = elem.type === "bar_left_repeat" || elem.type === "bar_thick_thin" || elem.type === "bar_thin_thin" || elem.type === "bar_dbl_repeat";
var seconddots = elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat";
// limit positioning of slurs
if (firstdots || seconddots) {
for (var slur in this.slurs) {
if (this.slurs.hasOwnProperty(slur)) {
this.slurs[slur].setEndX(abselem);
}
}
this.startlimitelem = abselem;
}
if (firstdots) {
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 7));
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 5));
dx += 6; //2 hardcoded, twice;
}
if (firstthin) {
anchor = new RelativeElement(null, dx, 1, 2, {
"type": "bar",
"pitch2": 10,
linewidth: 0.6
});
abselem.addRight(anchor);
}
if (elem.type === "bar_invisible") {
anchor = new RelativeElement(null, dx, 1, 2, {
"type": "none",
"pitch2": 10,
linewidth: 0.6
});
abselem.addRight(anchor);
}
if (elem.decoration) {
this.decoration.createDecoration(voice, elem.decoration, 12, thick ? 3 : 1, abselem, 0, "down", 2, elem.positioning, this.hasVocals, this.accentAbove);
}
if (thick) {
dx += 4; //3 hardcoded;
anchor = new RelativeElement(null, dx, 4, 2, {
"type": "bar",
"pitch2": 10,
linewidth: 4
});
abselem.addRight(anchor);
dx += 5;
}
// if (this.partstartelem && (thick || (firstthin && secondthin))) { // means end of nth part
// this.partstartelem.anchor2=anchor;
// this.partstartelem = null;
// }
if (this.partstartelem && elem.endEnding) {
this.partstartelem.anchor2 = anchor;
this.partstartelem = null;
}
if (secondthin) {
dx += 3; //3 hardcoded;
anchor = new RelativeElement(null, dx, 1, 2, {
"type": "bar",
"pitch2": 10,
linewidth: 0.6
});
abselem.addRight(anchor); // 3 is hardcoded
}
if (seconddots) {
dx += 3; //3 hardcoded;
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 7));
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 5));
} // 2 is hardcoded
if (elem.startEnding && isFirstStaff) {
// only put the first & second ending marks on the first staff
var textWidth = this.getTextSize.calc(elem.startEnding, "repeatfont", '').width;
abselem.minspacing += textWidth + 10; // Give plenty of room for the ending number.
this.partstartelem = new EndingElem(elem.startEnding, anchor, null);
voice.addOther(this.partstartelem);
}
// Add a little space to the left of the bar line so that nothing can crowd it.
abselem.extraw -= 5;
if (elem.chord !== undefined) {
var ret3 = addChord(this.getTextSize, abselem, elem, 0, 0, 0, false, this.germanAlphabet);
}
return abselem;
};
module.exports = AbstractEngraver;
/***/ }),
/***/ "./src/write/creation/add-chord.js":
/*!*****************************************!*\
!*** ./src/write/creation/add-chord.js ***!
\*****************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var translateChord = __webpack_require__(/*! ./translate-chord */ "./src/write/creation/translate-chord.js");
var addChord = function addChord(getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet) {
for (var i = 0; i < elem.chord.length; i++) {
var pos = elem.chord[i].position;
var rel_position = elem.chord[i].rel_position;
var isAnnotation = pos === "left" || pos === "right" || pos === "below" || pos === "above" || !!rel_position;
var font;
var klass;
if (isAnnotation) {
font = 'annotationfont';
klass = "abcjs-annotation";
} else {
font = 'gchordfont';
klass = "abcjs-chord";
}
var attr = getTextSize.attr(font, klass);
var name = elem.chord[i].name;
var ret;
//console.log("chord",name)
if (typeof name === "string") {
ret = chordString(name, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
roomTaken = ret.roomTaken;
roomTakenRight = ret.roomTakenRight;
} else {
for (var j = 0; j < name.length; j++) {
ret = chordString(name[j].text, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet);
roomTaken = ret.roomTaken;
roomTakenRight = ret.roomTakenRight;
}
}
}
return {
roomTaken: roomTaken,
roomTakenRight: roomTakenRight
};
};
function chordString(chordString, pos, rel_position, isAnnotation, font, klass, attr, getTextSize, abselem, elem, roomTaken, roomTakenRight, noteheadWidth, jazzchords, germanAlphabet) {
var chords = chordString.split("\n");
for (var j = chords.length - 1; j >= 0; j--) {
// parse these in opposite order because we place them from bottom to top.
var chord = chords[j];
var x = 0;
var y;
if (!isAnnotation) chord = translateChord(chord, jazzchords, germanAlphabet);
var dim = getTextSize.calc(chord, font, klass);
var chordWidth = dim.width;
var chordHeight = dim.height / spacing.STEP;
switch (pos) {
case "left":
roomTaken += chordWidth + 7;
x = -roomTaken; // TODO-PER: This is just a guess from trial and error
y = elem.averagepitch;
abselem.addExtra(new RelativeElement(chord, x, chordWidth + 4, y, {
type: "text",
height: chordHeight,
dim: attr,
position: "left"
}));
break;
case "right":
roomTakenRight += 4;
x = roomTakenRight; // TODO-PER: This is just a guess from trial and error
y = elem.averagepitch;
abselem.addRight(new RelativeElement(chord, x, chordWidth + 4, y, {
type: "text",
height: chordHeight,
dim: attr,
position: "right"
}));
break;
case "below":
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
type: "text",
position: "below",
height: chordHeight,
dim: attr,
realWidth: chordWidth
}));
break;
case "above":
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
abselem.addRight(new RelativeElement(chord, 0, 0, undefined, {
type: "text",
position: "above",
height: chordHeight,
dim: attr,
realWidth: chordWidth
}));
break;
default:
if (rel_position) {
var relPositionY = rel_position.y + 3 * spacing.STEP; // TODO-PER: this is a fudge factor to make it line up with abcm2ps
abselem.addRight(new RelativeElement(chord, x + rel_position.x, 0, elem.minpitch + relPositionY / spacing.STEP, {
position: "relative",
type: "text",
height: chordHeight,
dim: attr
}));
} else {
// setting the y-coordinate to undefined for now: it will be overwritten later on, after we figure out what the highest element on the line is.
var pos2 = 'above';
if (elem.positioning && elem.positioning.chordPosition) pos2 = elem.positioning.chordPosition;
if (pos2 !== 'hidden') {
abselem.addCentered(new RelativeElement(chord, noteheadWidth / 2, chordWidth, undefined, {
type: "chord",
position: pos2,
height: chordHeight,
dim: attr,
realWidth: chordWidth
}));
}
}
}
}
return {
roomTaken: roomTaken,
roomTakenRight: roomTakenRight
};
}
module.exports = addChord;
/***/ }),
/***/ "./src/write/creation/add-text-if.js":
/*!*******************************************!*\
!*** ./src/write/creation/add-text-if.js ***!
\*******************************************/
/***/ (function(module) {
function addTextIf(rows, params, getTextSize) {
if (!params.text) return;
if (!params.marginLeft) params.marginLeft = 0;
if (!params.klass) params.klass = '';
if (!params.anchor) params.anchor = 'start';
if (!params.info) params.info = {
startChar: -2,
endChar: -2
};
if (params.marginTop) rows.push({
move: params.marginTop
});
var attr = {
left: params.marginLeft,
text: params.text,
font: params.font,
anchor: params.anchor,
startChar: params.info.startChar,
endChar: params.info.endChar,
'dominant-baseline': params['dominant-baseline']
};
if (params.absElemType) attr.absElemType = params.absElemType;
if (!params.inGroup && params.klass) attr.klass = params.klass;
if (params.name) attr.name = params.name;
rows.push(attr);
// If there are blank lines they won't be counted by getTextSize, so just get the height of one line and multiply
var size = getTextSize.calc("A", params.font, params.klass);
var numLines = params.text.split("\n").length;
if (params.text[params.text.length - 1] === '\n') numLines--; // If there is a new line at the end of the string, then an extra line will be counted.
if (!params.noMove) {
var h = size.height * 1.1 * numLines;
rows.push({
move: Math.round(h)
});
if (params.marginBottom) rows.push({
move: params.marginBottom
});
}
}
module.exports = addTextIf;
/***/ }),
/***/ "./src/write/creation/calc-height.js":
/*!*******************************************!*\
!*** ./src/write/creation/calc-height.js ***!
\*******************************************/
/***/ (function(module) {
var calcHeight = function calcHeight(staffGroup) {
// the height is calculated here in a parallel way to the drawing below in hopes that both of these functions will be modified together.
// TODO-PER: also add the space between staves. (That's systemStaffSeparation, which is the minimum distance between the staff LINES.)
var height = 0;
for (var i = 0; i < staffGroup.voices.length; i++) {
var staff = staffGroup.voices[i].staff;
if (!staffGroup.voices[i].duplicate) {
height += staff.top;
//if (staff.bottom < 0)
height += -staff.bottom;
}
}
return height;
};
module.exports = calcHeight;
/***/ }),
/***/ "./src/write/creation/create-clef.js":
/*!*******************************************!*\
!*** ./src/write/creation/create-clef.js ***!
\*******************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_create_clef.js
var AbsoluteElement = __webpack_require__(/*! ./elements/absolute-element */ "./src/write/creation/elements/absolute-element.js");
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var createClef = function createClef(elem, tuneNumber) {
var clef;
var octave = 0;
elem.el_type = "clef";
var abselem = new AbsoluteElement(elem, 0, 10, 'staff-extra clef', tuneNumber);
abselem.isClef = true;
switch (elem.type) {
case "treble":
clef = "clefs.G";
break;
case "tenor":
clef = "clefs.C";
break;
case "alto":
clef = "clefs.C";
break;
case "bass":
clef = "clefs.F";
break;
case 'treble+8':
clef = "clefs.G";
octave = 1;
break;
case 'tenor+8':
clef = "clefs.C";
octave = 1;
break;
case 'bass+8':
clef = "clefs.F";
octave = 1;
break;
case 'alto+8':
clef = "clefs.C";
octave = 1;
break;
case 'treble-8':
clef = "clefs.G";
octave = -1;
break;
case 'tenor-8':
clef = "clefs.C";
octave = -1;
break;
case 'bass-8':
clef = "clefs.F";
octave = -1;
break;
case 'alto-8':
clef = "clefs.C";
octave = -1;
break;
case 'none':
return null;
case 'perc':
clef = "clefs.perc";
break;
default:
abselem.addFixed(new RelativeElement("clef=" + elem.type, 0, 0, undefined, {
type: "debug"
}));
}
// if (elem.verticalPos) {
// pitch = elem.verticalPos;
// }
var dx = 5;
if (clef) {
var height = glyphs.symbolHeightInPitches(clef);
var ofs = clefOffsets(clef);
abselem.addRight(new RelativeElement(clef, dx, glyphs.getSymbolWidth(clef), elem.clefPos, {
top: height + elem.clefPos + ofs,
bottom: elem.clefPos + ofs
}));
if (octave !== 0) {
var scale = 2 / 3;
var adjustspacing = (glyphs.getSymbolWidth(clef) - glyphs.getSymbolWidth("8") * scale) / 2;
var pitch = octave > 0 ? abselem.top + 3 : abselem.bottom - 1;
var top = octave > 0 ? abselem.top + 3 : abselem.bottom - 3;
var bottom = top - 2;
if (elem.type === "bass-8") {
// The placement for bass octave is a little different. It should hug the clef.
pitch = 3;
adjustspacing = 0;
}
abselem.addRight(new RelativeElement("8", dx + adjustspacing, glyphs.getSymbolWidth("8") * scale, pitch, {
scalex: scale,
scaley: scale,
top: top,
bottom: bottom
}));
//abselem.top += 2;
}
}
return abselem;
};
function clefOffsets(clef) {
switch (clef) {
case "clefs.G":
return -5;
case "clefs.C":
return -4;
case "clefs.F":
return -4;
case "clefs.perc":
return -2;
default:
return 0;
}
}
module.exports = createClef;
/***/ }),
/***/ "./src/write/creation/create-key-signature.js":
/*!****************************************************!*\
!*** ./src/write/creation/create-key-signature.js ***!
\****************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_create_key_signature.js
var AbsoluteElement = __webpack_require__(/*! ./elements/absolute-element */ "./src/write/creation/elements/absolute-element.js");
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var createKeySignature = function createKeySignature(elem, tuneNumber) {
elem.el_type = "keySignature";
if (!elem.accidentals || elem.accidentals.length === 0) return null;
var abselem = new AbsoluteElement(elem, 0, 10, 'staff-extra key-signature', tuneNumber);
abselem.isKeySig = true;
var dx = 0;
elem.accidentals.forEach(function (acc) {
var symbol;
var fudge = 0;
switch (acc.acc) {
case "sharp":
symbol = "accidentals.sharp";
fudge = -3;
break;
case "natural":
symbol = "accidentals.nat";
break;
case "flat":
symbol = "accidentals.flat";
fudge = -1.2;
break;
case "quartersharp":
symbol = "accidentals.halfsharp";
fudge = -2.5;
break;
case "quarterflat":
symbol = "accidentals.halfflat";
fudge = -1.2;
break;
default:
symbol = "accidentals.flat";
}
abselem.addRight(new RelativeElement(symbol, dx, glyphs.getSymbolWidth(symbol), acc.verticalPos, {
thickness: glyphs.symbolHeightInPitches(symbol),
top: acc.verticalPos + glyphs.symbolHeightInPitches(symbol) + fudge,
bottom: acc.verticalPos + fudge
}));
dx += glyphs.getSymbolWidth(symbol) + 2;
}, this);
return abselem;
};
module.exports = createKeySignature;
/***/ }),
/***/ "./src/write/creation/create-note-head.js":
/*!************************************************!*\
!*** ./src/write/creation/create-note-head.js ***!
\************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
if (!options) options = {};
var dir = options.dir !== undefined ? options.dir : null;
var headx = options.headx !== undefined ? options.headx : 0;
var extrax = options.extrax !== undefined ? options.extrax : 0;
var flag = options.flag !== undefined ? options.flag : null;
var dot = options.dot !== undefined ? options.dot : 0;
var dotshiftx = options.dotshiftx !== undefined ? options.dotshiftx : 0;
var scale = options.scale !== undefined ? options.scale : 1;
var accidentalSlot = options.accidentalSlot !== undefined ? options.accidentalSlot : [];
var shouldExtendStem = options.shouldExtendStem !== undefined ? options.shouldExtendStem : false;
var printAccidentals = options.printAccidentals !== undefined ? options.printAccidentals : true;
// TODO scale the dot as well
var pitch = pitchelem.verticalPos;
var notehead;
var accidentalshiftx = 0;
var newDotShiftX = 0;
var extraLeft = 0;
if (c === undefined) abselem.addFixed(new RelativeElement("pitch is undefined", 0, 0, 0, {
type: "debug"
}));else if (c === "") {
notehead = new RelativeElement(null, 0, 0, pitch);
} else {
var shiftheadx = headx;
if (pitchelem.printer_shift) {
var adjust = pitchelem.printer_shift === "same" ? 1 : 0;
shiftheadx = dir === "down" ? -glyphs.getSymbolWidth(c) * scale + adjust : glyphs.getSymbolWidth(c) * scale - adjust;
}
var opts = {
scalex: scale,
scaley: scale,
thickness: glyphs.symbolHeightInPitches(c) * scale,
name: pitchelem.name
};
notehead = new RelativeElement(c, shiftheadx, glyphs.getSymbolWidth(c) * scale, pitch, opts);
notehead.stemDir = dir;
if (flag) {
var pos = pitch + (dir === "down" ? -7 : 7) * scale;
// if this is a regular note, (not grace or tempo indicator) then the stem will have been stretched to the middle line if it is far from the center.
if (shouldExtendStem) {
if (dir === "down" && pos > 6) pos = 6;
if (dir === "up" && pos < 6) pos = 6;
}
//if (scale===1 && (dir==="down")?(pos>6):(pos<6)) pos=6;
var xdelta = dir === "down" ? headx : headx + notehead.w - 0.6;
abselem.addRight(new RelativeElement(flag, xdelta, glyphs.getSymbolWidth(flag) * scale, pos, {
scalex: scale,
scaley: scale
}));
}
newDotShiftX = notehead.w + dotshiftx - 2 + 5 * dot;
for (; dot > 0; dot--) {
var dotadjusty = 1 - Math.abs(pitch) % 2; //PER: take abs value of the pitch. And the shift still happens on ledger lines.
abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty));
}
}
if (notehead) notehead.highestVert = pitchelem.highestVert;
if (printAccidentals && pitchelem.accidental) {
var symb;
switch (pitchelem.accidental) {
case "quartersharp":
symb = "accidentals.halfsharp";
break;
case "dblsharp":
symb = "accidentals.dblsharp";
break;
case "sharp":
symb = "accidentals.sharp";
break;
case "quarterflat":
symb = "accidentals.halfflat";
break;
case "flat":
symb = "accidentals.flat";
break;
case "dblflat":
symb = "accidentals.dblflat";
break;
case "natural":
symb = "accidentals.nat";
}
// if a note is at least a sixth away, it can share a slot with another accidental
var accSlotFound = false;
var accPlace = extrax;
for (var j = 0; j < accidentalSlot.length; j++) {
if (pitch - accidentalSlot[j][0] >= 6) {
accidentalSlot[j][0] = pitch;
accPlace = accidentalSlot[j][1];
accSlotFound = true;
break;
}
}
if (accSlotFound === false) {
accPlace -= glyphs.getSymbolWidth(symb) * scale + 2;
accidentalSlot.push([pitch, accPlace]);
accidentalshiftx = glyphs.getSymbolWidth(symb) * scale + 2;
}
var h = glyphs.symbolHeightInPitches(symb);
abselem.addExtra(new RelativeElement(symb, accPlace, glyphs.getSymbolWidth(symb), pitch, {
scalex: scale,
scaley: scale,
top: pitch + h / 2,
bottom: pitch - h / 2
}));
extraLeft = glyphs.getSymbolWidth(symb) / 2; // TODO-PER: We need a little extra width if there is an accidental, but I'm not sure why it isn't the full width of the accidental.
}
return {
notehead: notehead,
accidentalshiftx: accidentalshiftx,
dotshiftx: newDotShiftX,
extraLeft: extraLeft
};
};
module.exports = createNoteHead;
/***/ }),
/***/ "./src/write/creation/create-time-signature.js":
/*!*****************************************************!*\
!*** ./src/write/creation/create-time-signature.js ***!
\*****************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_create_time_signature.js
var AbsoluteElement = __webpack_require__(/*! ./elements/absolute-element */ "./src/write/creation/elements/absolute-element.js");
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var createTimeSignature = function createTimeSignature(elem, tuneNumber) {
elem.el_type = "timeSignature";
var abselem = new AbsoluteElement(elem, 0, 10, 'staff-extra time-signature', tuneNumber);
if (elem.type === "specified") {
var x = 0;
for (var i = 0; i < elem.value.length; i++) {
if (i !== 0) {
abselem.addRight(new RelativeElement('+', x + 1, glyphs.getSymbolWidth("+"), 6, {
thickness: glyphs.symbolHeightInPitches("+")
}));
x += glyphs.getSymbolWidth("+") + 2;
}
if (elem.value[i].den) {
var numWidth = 0;
for (var i2 = 0; i2 < elem.value[i].num.length; i2++) {
numWidth += glyphs.getSymbolWidth(elem.value[i].num[i2]);
}
var denWidth = 0;
for (i2 = 0; i2 < elem.value[i].num.length; i2++) {
denWidth += glyphs.getSymbolWidth(elem.value[i].den[i2]);
}
var maxWidth = Math.max(numWidth, denWidth);
abselem.addRight(new RelativeElement(elem.value[i].num, x + (maxWidth - numWidth) / 2, numWidth, 8, {
thickness: glyphs.symbolHeightInPitches(elem.value[i].num[0])
}));
abselem.addRight(new RelativeElement(elem.value[i].den, x + (maxWidth - denWidth) / 2, denWidth, 4, {
thickness: glyphs.symbolHeightInPitches(elem.value[i].den[0])
}));
x += maxWidth;
} else {
var thisWidth = 0;
for (var i3 = 0; i3 < elem.value[i].num.length; i3++) {
thisWidth += glyphs.getSymbolWidth(elem.value[i].num[i3]);
}
abselem.addRight(new RelativeElement(elem.value[i].num, x, thisWidth, 6, {
thickness: glyphs.symbolHeightInPitches(elem.value[i].num[0])
}));
x += thisWidth;
}
}
} else if (elem.type === "common_time") {
abselem.addRight(new RelativeElement("timesig.common", 0, glyphs.getSymbolWidth("timesig.common"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.common")
}));
} else if (elem.type === "cut_time") {
abselem.addRight(new RelativeElement("timesig.cut", 0, glyphs.getSymbolWidth("timesig.cut"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.cut")
}));
} else if (elem.type === "tempus_imperfectum") {
abselem.addRight(new RelativeElement("timesig.imperfectum", 0, glyphs.getSymbolWidth("timesig.imperfectum"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.imperfectum")
}));
} else if (elem.type === "tempus_imperfectum_prolatio") {
abselem.addRight(new RelativeElement("timesig.imperfectum2", 0, glyphs.getSymbolWidth("timesig.imperfectum2"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.imperfectum2")
}));
} else if (elem.type === "tempus_perfectum") {
abselem.addRight(new RelativeElement("timesig.perfectum", 0, glyphs.getSymbolWidth("timesig.perfectum"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.perfectum")
}));
} else if (elem.type === "tempus_perfectum_prolatio") {
abselem.addRight(new RelativeElement("timesig.perfectum2", 0, glyphs.getSymbolWidth("timesig.perfectum2"), 6, {
thickness: glyphs.symbolHeightInPitches("timesig.perfectum2")
}));
} else {
console.log("time signature:", elem);
}
return abselem;
};
module.exports = createTimeSignature;
/***/ }),
/***/ "./src/write/creation/decoration.js":
/*!******************************************!*\
!*** ./src/write/creation/decoration.js ***!
\******************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_decoration.js: Creates a data structure suitable for printing a line of abc
var DynamicDecoration = __webpack_require__(/*! ./elements/dynamic-decoration */ "./src/write/creation/elements/dynamic-decoration.js");
var CrescendoElem = __webpack_require__(/*! ./elements/crescendo-element */ "./src/write/creation/elements/crescendo-element.js");
var GlissandoElem = __webpack_require__(/*! ./elements/glissando-element */ "./src/write/creation/elements/glissando-element.js");
var glyphs = __webpack_require__(/*! ./glyphs */ "./src/write/creation/glyphs.js");
var RelativeElement = __webpack_require__(/*! ./elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var TieElem = __webpack_require__(/*! ./elements/tie-element */ "./src/write/creation/elements/tie-element.js");
var Decoration = function Decoration() {
this.startDiminuendoX = undefined;
this.startCrescendoX = undefined;
this.minTop = 12; // TODO-PER: this is assuming a 5-line staff. Pass that info in.
this.minBottom = 0;
};
var closeDecoration = function closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove) {
var yPos;
for (var i = 0; i < decoration.length; i++) {
if (decoration[i] === "staccato" || decoration[i] === "tenuto" || decoration[i] === "accent" && !accentAbove) {
var symbol = "scripts." + decoration[i];
if (decoration[i] === "accent") symbol = "scripts.sforzato";
if (yPos === undefined) yPos = dir === "down" ? pitch + 2 : minPitch - 2;else yPos = dir === "down" ? yPos + 2 : yPos - 2;
if (decoration[i] === "accent") {
// Always place the accent three pitches away, no matter whether that is a line or space.
if (dir === "up") yPos--;else yPos++;
} else {
// don't place on a stave line. The stave lines are 2,4,6,8,10
switch (yPos) {
case 2:
case 4:
case 6:
case 8:
case 10:
if (dir === "up") yPos--;else yPos++;
break;
}
}
if (pitch > 9) yPos++; // take up some room of those that are above
var deltaX = width / 2;
if (glyphs.getSymbolAlign(symbol) !== "center") {
deltaX -= glyphs.getSymbolWidth(symbol) / 2;
}
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), yPos));
}
if (decoration[i] === "slide" && abselem.heads[0]) {
var yPos2 = abselem.heads[0].pitch;
yPos2 -= 2; // TODO-PER: not sure what this fudge factor is.
var blank1 = new RelativeElement("", -roomtaken - 15, 0, yPos2 - 1);
var blank2 = new RelativeElement("", -roomtaken - 5, 0, yPos2 + 1);
abselem.addFixedX(blank1);
abselem.addFixedX(blank2);
voice.addOther(new TieElem({
anchor1: blank1,
anchor2: blank2,
fixedY: true
}));
}
}
if (yPos === undefined) yPos = pitch;
return {
above: yPos,
below: abselem.bottom
};
};
var volumeDecoration = function volumeDecoration(voice, decoration, abselem, positioning) {
for (var i = 0; i < decoration.length; i++) {
switch (decoration[i]) {
case "p":
case "mp":
case "pp":
case "ppp":
case "pppp":
case "f":
case "ff":
case "fff":
case "ffff":
case "sfz":
case "mf":
var elem = new DynamicDecoration(abselem, decoration[i], positioning);
voice.addOther(elem);
}
}
};
var compoundDecoration = function compoundDecoration(decoration, pitch, width, abselem, dir) {
function highestPitch() {
if (abselem.heads.length === 0) return 10; // TODO-PER: I don't know if this can happen, but we'll return the top of the staff if so.
var pitch = abselem.heads[0].pitch;
for (var i = 1; i < abselem.heads.length; i++) {
pitch = Math.max(pitch, abselem.heads[i].pitch);
}
return pitch;
}
function lowestPitch() {
if (abselem.heads.length === 0) return 2; // TODO-PER: I don't know if this can happen, but we'll return the bottom of the staff if so.
var pitch = abselem.heads[0].pitch;
for (var i = 1; i < abselem.heads.length; i++) {
pitch = Math.min(pitch, abselem.heads[i].pitch);
}
return pitch;
}
function compoundDecoration(symbol, count) {
var placement = dir === 'down' ? lowestPitch() + 1 : highestPitch() + 9;
if (dir !== 'down' && count === 1) placement--;
var deltaX = width / 2;
deltaX += dir === 'down' ? -5 : 3;
for (var i = 0; i < count; i++) {
placement -= 1;
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), placement));
}
}
for (var i = 0; i < decoration.length; i++) {
switch (decoration[i]) {
case "/":
compoundDecoration("flags.ugrace", 1);
break;
case "//":
compoundDecoration("flags.ugrace", 2);
break;
case "///":
compoundDecoration("flags.ugrace", 3);
break;
case "////":
compoundDecoration("flags.ugrace", 4);
break;
}
}
};
var stackedDecoration = function stackedDecoration(decoration, width, abselem, yPos, positioning, minTop, minBottom, accentAbove) {
function incrementPlacement(placement, height) {
if (placement === 'above') yPos.above += height;else yPos.below -= height;
}
function getPlacement(placement) {
var y;
if (placement === 'above') {
y = yPos.above;
if (y < minTop) y = minTop;
} else {
y = yPos.below;
if (y > minBottom) y = minBottom;
}
return y;
}
function textDecoration(text, placement, anchor) {
var y = getPlacement(placement);
var textFudge = 2;
var textHeight = 5;
// TODO-PER: Get the height of the current font and use that for the thickness.
abselem.addFixedX(new RelativeElement(text, width / 2, 0, y + textFudge, {
type: "decoration",
klass: 'ornament',
thickness: 3,
anchor: anchor
}));
incrementPlacement(placement, textHeight);
}
function symbolDecoration(symbol, placement) {
var deltaX = width / 2;
if (glyphs.getSymbolAlign(symbol) !== "center") {
deltaX -= glyphs.getSymbolWidth(symbol) / 2;
}
var height = glyphs.symbolHeightInPitches(symbol) + 1; // adding a little padding so nothing touches.
var y = getPlacement(placement);
y = placement === 'above' ? y + height / 2 : y - height / 2; // Center the element vertically.
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), y, {
klass: 'ornament',
thickness: glyphs.symbolHeightInPitches(symbol),
position: placement
}));
incrementPlacement(placement, height);
}
var symbolList = {
"+": "scripts.stopped",
"open": "scripts.open",
"snap": "scripts.snap",
"wedge": "scripts.wedge",
"thumb": "scripts.thumb",
"shortphrase": "scripts.shortphrase",
"mediumphrase": "scripts.mediumphrase",
"longphrase": "scripts.longphrase",
"trill": "scripts.trill",
"roll": "scripts.roll",
"irishroll": "scripts.roll",
"marcato": "scripts.umarcato",
"dmarcato": "scripts.dmarcato",
"umarcato": "scripts.umarcato",
"turn": "scripts.turn",
"uppermordent": "scripts.prall",
"pralltriller": "scripts.prall",
"mordent": "scripts.mordent",
"lowermordent": "scripts.mordent",
"downbow": "scripts.downbow",
"upbow": "scripts.upbow",
"fermata": "scripts.ufermata",
"invertedfermata": "scripts.dfermata",
"breath": ",",
"coda": "scripts.coda",
"segno": "scripts.segno"
};
var hasOne = false;
for (var i = 0; i < decoration.length; i++) {
switch (decoration[i]) {
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "D.C.":
case "D.S.":
textDecoration(decoration[i], positioning, 'middle');
hasOne = true;
break;
case "D.C.alcoda":
textDecoration("D.C. al coda", positioning, 'end');
hasOne = true;
break;
case "D.C.alfine":
textDecoration("D.C. al fine", positioning, 'end');
hasOne = true;
break;
case "D.S.alcoda":
textDecoration("D.S. al coda", positioning, 'end');
hasOne = true;
break;
case "D.S.alfine":
textDecoration("D.S. al fine", positioning, 'end');
hasOne = true;
break;
case "fine":
textDecoration("FINE", positioning, 'middle');
hasOne = true;
break;
case "+":
case "open":
case "snap":
case "wedge":
case "thumb":
case "shortphrase":
case "mediumphrase":
case "longphrase":
case "trill":
case "roll":
case "irishroll":
case "marcato":
case "dmarcato":
case "turn":
case "uppermordent":
case "pralltriller":
case "mordent":
case "lowermordent":
case "downbow":
case "upbow":
case "fermata":
case "breath":
case "umarcato":
case "coda":
case "segno":
symbolDecoration(symbolList[decoration[i]], positioning);
hasOne = true;
break;
case "invertedfermata":
symbolDecoration(symbolList[decoration[i]], 'below');
hasOne = true;
break;
case "mark":
abselem.klass = "mark";
break;
case "accent":
if (accentAbove) {
symbolDecoration("scripts.sforzato", positioning);
hasOne = true;
}
break;
}
}
return hasOne;
};
function leftDecoration(decoration, abselem, roomtaken) {
for (var i = 0; i < decoration.length; i++) {
switch (decoration[i]) {
case "arpeggio":
// The arpeggio symbol is the height of a note (that is, two Y units). This stacks as many as we need to go from the
// top note to the bottom note. The arpeggio should also be a little taller than the stacked notes, so there is an extra
// one drawn and it is offset by half of a note height (that is, one Y unit).
for (var j = abselem.abcelem.minpitch - 1; j <= abselem.abcelem.maxpitch; j += 2) {
abselem.addExtra(new RelativeElement("scripts.arpeggio", -glyphs.getSymbolWidth("scripts.arpeggio") * 2 - roomtaken, 0, j + 2, {
klass: 'ornament',
thickness: glyphs.symbolHeightInPitches("scripts.arpeggio")
}));
}
break;
}
}
}
Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) {
var diminuendo;
var crescendo;
var glissando;
for (var i = 0; i < decoration.length; i++) {
switch (decoration[i]) {
case "diminuendo(":
this.startDiminuendoX = abselem;
diminuendo = undefined;
break;
case "diminuendo)":
diminuendo = {
start: this.startDiminuendoX,
stop: abselem
};
this.startDiminuendoX = undefined;
break;
case "crescendo(":
this.startCrescendoX = abselem;
crescendo = undefined;
break;
case "crescendo)":
crescendo = {
start: this.startCrescendoX,
stop: abselem
};
this.startCrescendoX = undefined;
break;
case '~(':
case "glissando(":
this.startGlissandoX = abselem;
glissando = undefined;
break;
case '~)':
case "glissando)":
glissando = {
start: this.startGlissandoX,
stop: abselem
};
this.startGlissandoX = undefined;
break;
}
}
if (diminuendo) {
voice.addOther(new CrescendoElem(diminuendo.start, diminuendo.stop, ">", positioning));
}
if (crescendo) {
voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning));
}
if (glissando) {
voice.addOther(new GlissandoElem(glissando.start, glissando.stop));
}
};
Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals, accentAbove) {
if (!positioning) positioning = {
ornamentPosition: 'above',
volumePosition: hasVocals ? 'above' : 'below',
dynamicPosition: hasVocals ? 'above' : 'below'
};
// These decorations don't affect the placement of other decorations
volumeDecoration(voice, decoration, abselem, positioning.volumePosition);
this.dynamicDecoration(voice, decoration, abselem, positioning.dynamicPosition);
compoundDecoration(decoration, pitch, width, abselem, dir);
// treat staccato, accent, and tenuto first (may need to shift other markers)
var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove);
// yPos is an object containing 'above' and 'below'. That is the placement of the next symbol on either side.
yPos.above = Math.max(yPos.above, this.minTop);
yPos.below = Math.min(yPos.below, minPitch);
var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, minPitch, accentAbove);
//if (hasOne) {
// abselem.top = Math.max(yPos.above + 3, abselem.top); // TODO-PER: Not sure why we need this fudge factor.
//}
leftDecoration(decoration, abselem, roomtaken);
};
module.exports = Decoration;
/***/ }),
/***/ "./src/write/creation/elements/absolute-element.js":
/*!*********************************************************!*\
!*** ./src/write/creation/elements/absolute-element.js ***!
\*********************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_absolute_element.js: Definition of the AbsoluteElement class.
var highlight = __webpack_require__(/*! ../../interactive/highlight */ "./src/write/interactive/highlight.js");
var unhighlight = __webpack_require__(/*! ../../interactive/unhighlight */ "./src/write/interactive/unhighlight.js");
// Everything that is placed in the SVG is first created as an absolute element. This is one unit of graphic information.
// That is, it embodies a concept: a clef, a time signature, a bar line,etc. or most complexly:
// a note with its accidental, grace note, chord symbol, trill, stem, eighth flags, etc.
// In the largest sense, these are placed on the page at a particular place that is determined during the layout phase.
// This object doesn't contain any of the drawing information, though. That information is contained in an array of
// RelativeElements as the "children" of this class.
// During the layout phase, the width of all the children is calculated and the X coordinate of the absolute element is set.
//
// So, after the AbsoluteElement is placed, then its children can be placed relative to that. There are different types of
// relative elements that are placed with different rules:
// 1) Fixed - these elements don't move relative to the absolute element's coordinates. These are things like the notehead,
// any ledger lines, accidentals, etc.
// 2) Slotted - these elements can move vertically and don't get Y coordinates until after the absolute element is placed.
// These are things like the chord symbol, many decorations, the lyrics, etc.
//
// Relative elements are also classified by how they are related. This could be:
// 1) Increases the absolute element's width to the left. This doesn't change the center point of
// the absolute element, so adding a sharp to the note won't move it to the right. However, if the elements
// are close together then this enforces a minimum distance.
// 2) Has no effect on the width. Annotations and the tempo act like this. No matter how long they are the width doesn't change.
// 3) Increases the absolute element's width to the right. This doesn't change the center point,
// but it will increase the minimum distance.
// 4) Sets the width on both sides. This is the note heads. They are centered on both sides of the absolute element's X coordinate.
// duration - actual musical duration - different from notehead duration in triplets. refer to abcelem to get the notehead duration
// minspacing - spacing which must be taken on top of the width defined by the duration
// type is a meta-type for the element. It is not necessary for drawing, but it is useful to make semantic sense of the element. For instance, it can be used in the element's class name.
var AbsoluteElement = function AbsoluteElement(abcelem, duration, minspacing, type, tuneNumber, options) {
// console.log("Absolute:",abcelem, duration, minspacing, type, tuneNumber, options);
if (!options) options = {};
this.tuneNumber = tuneNumber;
this.abcelem = abcelem;
this.duration = duration;
this.durationClass = options.durationClassOveride ? options.durationClassOveride : this.duration;
this.minspacing = minspacing || 0;
this.x = 0;
this.children = [];
this.heads = [];
this.extra = [];
this.extraw = 0;
this.w = 0;
this.right = [];
this.invisible = false;
this.bottom = undefined;
this.top = undefined;
this.type = type;
// The following are the dimensions of the fixed part of the element.
// That is, the chord text will be a different height depending on lot of factors, but the 8th flag will always be in the same place.
this.fixed = {
w: 0,
t: undefined,
b: undefined
}; // there is no x-coord here, because that is set later.
// these are the heights of all of the vertical elements that can't be placed until the end of the line.
// the vertical order of elements that are above is: tempo, part, volume/dynamic, ending/chord, lyric
// the vertical order of elements that are below is: lyric, chord, volume/dynamic
this.specialY = {
tempoHeightAbove: 0,
partHeightAbove: 0,
volumeHeightAbove: 0,
dynamicHeightAbove: 0,
endingHeightAbove: 0,
chordHeightAbove: 0,
lyricHeightAbove: 0,
lyricHeightBelow: 0,
chordHeightBelow: 0,
volumeHeightBelow: 0,
dynamicHeightBelow: 0
};
};
AbsoluteElement.prototype.getFixedCoords = function () {
return {
x: this.x,
w: this.fixed.w,
t: this.fixed.t,
b: this.fixed.b
};
};
AbsoluteElement.prototype.addExtra = function (extra) {
// used for accidentals, multi-measure rest text,
// left-side decorations, gracenote heads,
// left annotations, gracenote stems.
// if (!(extra.c && extra.c.indexOf("accidentals") >= 0) &&
// !(extra.c && extra.c.indexOf("arpeggio") >= 0) &&
// extra.type !== "multimeasure-text" &&
// !(extra.c === "noteheads.quarter" && (extra.scalex === 0.6 || extra.scalex === 0.36)) &&
// !(extra.type === "stem" && extra.linewidth === -0.6) &&
// extra.position !== "left"
// )
// console.log("extra", extra);
this.fixed.w = Math.max(this.fixed.w, extra.dx + extra.w);
if (this.fixed.t === undefined) this.fixed.t = extra.top;else this.fixed.t = Math.max(this.fixed.t, extra.top);
if (this.fixed.b === undefined) this.fixed.b = extra.bottom;else this.fixed.b = Math.min(this.fixed.b, extra.bottom);
if (extra.dx < this.extraw) this.extraw = extra.dx;
this.extra[this.extra.length] = extra;
this._addChild(extra);
};
AbsoluteElement.prototype.addHead = function (head) {
if (head.dx < this.extraw) this.extraw = head.dx;
this.heads[this.heads.length] = head;
this.addRight(head);
};
AbsoluteElement.prototype.addRight = function (right) {
// // used for clefs, note heads, bar lines, stems, key-signature accidentals, non-beamed flags, dots
// if (!(right.c && right.c.indexOf("clefs") >= 0) &&
// !(right.c && right.c.indexOf("noteheads") >= 0) &&
// !(right.c && right.c.indexOf("flags") >= 0) &&
// !(right.c && right.c.indexOf("rests") >= 0) &&
// !(right.c && right.c.indexOf("dots.dot") >= 0) &&
// right.type !== "stem" &&
// right.type !== "bar" &&
// right.type !== "none" && // used when an invisible anchor is needed.
// !(this.type.indexOf("clef") >= -1 && right.c === "8") &&
// this.type.indexOf("key-signature") === -1 &&
// this.type.indexOf("time-signature") === -1 &&
// !(this.abcelem && this.abcelem.rest && this.abcelem.rest.type === "spacer") &&
// !(this.abcelem && this.abcelem.rest && this.abcelem.rest.type === "invisible") &&
// !(right.type === "text" && right.position === "relative") &&
// !(right.type === "text" && right.position === "right") &&
// !(right.type === "text" && right.position === "above") &&
// !(right.type === "text" && right.position === "below")
// )
// console.log("right", right);
// These are the elements that are the fixed part.
this.fixed.w = Math.max(this.fixed.w, right.dx + right.w);
if (right.top !== undefined) {
if (this.fixed.t === undefined) this.fixed.t = right.top;else this.fixed.t = Math.max(this.fixed.t, right.top);
}
if (right.bottom !== undefined) {
if (this.fixed.b === undefined) this.fixed.b = right.bottom;else this.fixed.b = Math.min(this.fixed.b, right.bottom);
}
// if (isNaN(this.fixed.t) || isNaN(this.fixed.b))
// debugger;
if (right.dx + right.w > this.w) this.w = right.dx + right.w;
this.right[this.right.length] = right;
this._addChild(right);
};
AbsoluteElement.prototype.addFixed = function (elem) {
// used for elements that can't move relative to other elements after they have been placed.
// used for ledger lines, bar numbers, debug msgs, clef, key sigs, time sigs
this._addChild(elem);
};
AbsoluteElement.prototype.addFixedX = function (elem) {
// used for elements that can't move horizontally relative to other elements after they have been placed.
// used for parts, tempo, decorations
this._addChild(elem);
};
AbsoluteElement.prototype.addCentered = function (elem) {
// // used for chord labels, lyrics
// if (!(elem.type === "chord" && elem.position === "above") &&
// !(elem.type === "chord" && elem.position === "below") &&
// elem.type !== 'lyric'
// )
// console.log("centered", elem);
var half = elem.w / 2;
if (-half < this.extraw) this.extraw = -half;
this.extra[this.extra.length] = elem;
if (elem.dx + half > this.w) this.w = elem.dx + half;
this.right[this.right.length] = elem;
this._addChild(elem);
};
AbsoluteElement.prototype.setLimit = function (member, child) {
if (!child[member]) return;
if (!this.specialY[member]) this.specialY[member] = child[member];else this.specialY[member] = Math.max(this.specialY[member], child[member]);
};
AbsoluteElement.prototype._addChild = function (child) {
// console.log("Relative:",child);
child.parent = this;
this.children[this.children.length] = child;
this.pushTop(child.top);
this.pushBottom(child.bottom);
this.setLimit('tempoHeightAbove', child);
this.setLimit('partHeightAbove', child);
this.setLimit('volumeHeightAbove', child);
this.setLimit('dynamicHeightAbove', child);
this.setLimit('endingHeightAbove', child);
this.setLimit('chordHeightAbove', child);
this.setLimit('lyricHeightAbove', child);
this.setLimit('lyricHeightBelow', child);
this.setLimit('chordHeightBelow', child);
this.setLimit('volumeHeightBelow', child);
this.setLimit('dynamicHeightBelow', child);
};
AbsoluteElement.prototype.pushTop = function (top) {
if (top !== undefined) {
if (this.top === undefined) this.top = top;else this.top = Math.max(top, this.top);
}
};
AbsoluteElement.prototype.pushBottom = function (bottom) {
if (bottom !== undefined) {
if (this.bottom === undefined) this.bottom = bottom;else this.bottom = Math.min(bottom, this.bottom);
}
};
AbsoluteElement.prototype.setX = function (x) {
this.x = x;
for (var i = 0; i < this.children.length; i++) {
this.children[i].setX(x);
}
};
AbsoluteElement.prototype.center = function (before, after) {
// Used to center whole rests
var midpoint = (after.x - before.x) / 2 + before.x;
this.x = midpoint - this.w / 2;
for (var k = 0; k < this.children.length; k++) {
this.children[k].setX(this.x);
}
};
AbsoluteElement.prototype.setHint = function () {
this.hint = true;
};
AbsoluteElement.prototype.highlight = function (klass, color) {
highlight.bind(this)(klass, color);
};
AbsoluteElement.prototype.unhighlight = function (klass, color) {
unhighlight.bind(this)(klass, color);
};
module.exports = AbsoluteElement;
/***/ }),
/***/ "./src/write/creation/elements/beam-element.js":
/*!*****************************************************!*\
!*** ./src/write/creation/elements/beam-element.js ***!
\*****************************************************/
/***/ (function(module) {
// abc_beam_element.js: Definition of the BeamElem class.
// Most elements on the page are related to a particular absolute element -- notes, rests, bars, etc. Beams, however, span multiple elements.
// This means that beams can't be laid out until the absolute elements are placed. There is the further complication that the stems for beamed
// notes can't be laid out until the beams are because we don't know how long they will be until we know the slope of the beam and the horizontal
// spacing of the absolute elements.
//
// So, when a beam is detected, a BeamElem is created, then all notes belonging to that beam are added to it. These notes are not given stems at that time.
// Then, after the horizontal layout is complete, all of the BeamElem are iterated to set the beam position, then all of the notes that are beamed are given
// stems. After that, we are ready for the drawing step.
// There are three phases: the setup phase, when new elements are being discovered, the layout phase, when everything is calculated, and the drawing phase,
// when the object is not changed, but is used to put the elements on the page.
//
// Setup phase
//
var BeamElem = function BeamElem(stemHeight, type, flat, firstElement) {
// type is "grace", "up", "down", or undefined. flat is used to force flat beams, as it commonly found in the grace notes of bagpipe music.
this.type = "BeamElem";
this.isflat = !!flat;
this.isgrace = !!(type && type === "grace");
this.forceup = !!(this.isgrace || type && type === "up");
this.forcedown = !!(type && type === "down");
this.elems = []; // all the AbsoluteElements that this beam touches. It may include embedded rests.
this.total = 0;
this.average = 6; // use middle line as start for average.
this.allrests = true;
this.stemHeight = stemHeight;
this.beams = []; // During the layout phase, this will become a list of the beams that need to be drawn.
if (firstElement && firstElement.duration) {
this.duration = firstElement.duration;
if (firstElement.startTriplet) {
this.duration *= firstElement.tripletMultiplier;
}
this.duration = Math.round(this.duration * 1000) / 1000;
} else this.duration = 0;
};
BeamElem.prototype.setHint = function () {
this.hint = true;
};
BeamElem.prototype.runningDirection = function (abcelem) {
var pitch = abcelem.averagepitch;
if (pitch === undefined) return; // don't include elements like spacers in beams
this.total = Math.round(this.total + pitch);
if (!this.count) this.count = 0;
this.count++;
};
BeamElem.prototype.add = function (abselem) {
var pitch = abselem.abcelem.averagepitch;
if (pitch === undefined) return; // don't include elements like spacers in beams
if (!abselem.abcelem.rest) this.allrests = false;
abselem.beam = this;
this.elems.push(abselem);
this.total = Math.round(this.total + pitch);
if (this.min === undefined || abselem.abcelem.minpitch < this.min) {
this.min = abselem.abcelem.minpitch;
}
if (this.max === undefined || abselem.abcelem.maxpitch > this.max) {
this.max = abselem.abcelem.maxpitch;
}
};
BeamElem.prototype.addBeam = function (beam) {
this.beams.push(beam);
};
BeamElem.prototype.setStemDirection = function () {
// Have to figure this out before the notes are placed because placing the notes also places the decorations.
this.average = calcAverage(this.total, this.count);
if (this.forceup) {
this.stemsUp = true;
} else if (this.forcedown) {
this.stemsUp = false;
} else {
var middleLine = 6; // hardcoded 6 is B
this.stemsUp = this.average < middleLine; // true is up, false is down;
}
delete this.count;
this.total = 0;
};
BeamElem.prototype.calcDir = function () {
this.average = calcAverage(this.total, this.elems.length);
if (this.forceup) {
this.stemsUp = true;
} else if (this.forcedown) {
this.stemsUp = false;
} else {
var middleLine = 6; // hardcoded 6 is B
this.stemsUp = this.average < middleLine; // true is up, false is down;
}
var dir = this.stemsUp ? 'up' : 'down';
for (var i = 0; i < this.elems.length; i++) {
for (var j = 0; j < this.elems[i].heads.length; j++) {
this.elems[i].heads[j].stemDir = dir;
}
}
};
function calcAverage(total, numElements) {
if (!numElements) return 0;
return total / numElements;
}
module.exports = BeamElem;
/***/ }),
/***/ "./src/write/creation/elements/bottom-text.js":
/*!****************************************************!*\
!*** ./src/write/creation/elements/bottom-text.js ***!
\****************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
function BottomText(metaText, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
this.rows = [];
if (metaText.unalignedWords && metaText.unalignedWords.length > 0) this.unalignedWords(metaText.unalignedWords, paddingLeft, spacing, shouldAddClasses, getTextSize);
this.extraText(metaText, paddingLeft, spacing, shouldAddClasses, getTextSize);
if (metaText.footer && isPrint) this.footer(metaText.footer, width, paddingLeft, getTextSize);
}
BottomText.prototype.unalignedWords = function (unalignedWords, marginLeft, spacing, shouldAddClasses, getTextSize) {
var klass = shouldAddClasses ? 'abcjs-unaligned-words' : '';
var defFont = 'wordsfont';
var space = getTextSize.calc("i", defFont, klass);
this.rows.push({
move: spacing.words
});
addMultiLine(this.rows, '', unalignedWords, marginLeft, defFont, "unalignedWords", "unalignedWords", klass, "unalignedWords", spacing, shouldAddClasses, getTextSize);
this.rows.push({
move: space.height
});
};
function addSingleLine(rows, preface, text, marginLeft, klass, shouldAddClasses, getTextSize) {
if (text) {
if (preface) {
if (typeof text === 'string') text = preface + text;else text = [{
text: preface
}].concat(text);
}
klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
richText(rows, text, 'historyfont', klass, "description", marginLeft, {
absElemType: "extraText",
anchor: 'start'
}, getTextSize);
}
}
function addMultiLine(rows, preface, content, marginLeft, defFont, absElemType, groupName, klass, name, spacing, shouldAddClasses, getTextSize) {
if (content) {
klass = shouldAddClasses ? 'abcjs-extra-text ' + klass : '';
var size = getTextSize.calc("A", defFont, klass);
if (typeof content === 'string') {
if (preface) content = preface + "\n" + content;
addTextIf(rows, {
marginLeft: marginLeft,
text: content,
font: defFont,
absElemType: "extraText",
name: name,
'dominant-baseline': 'middle',
klass: klass
}, getTextSize);
//rows.push({move: size.height*3/4})
} else {
rows.push({
startGroup: groupName,
klass: klass,
name: name
});
rows.push({
move: spacing.info
});
if (preface) {
addTextIf(rows, {
marginLeft: marginLeft,
text: preface,
font: defFont,
absElemType: "extraText",
name: name,
'dominant-baseline': 'middle'
}, getTextSize);
rows.push({
move: size.height * 3 / 4
});
}
for (var j = 0; j < content.length; j++) {
richText(rows, content[j], defFont, '', name, marginLeft, {
anchor: 'start'
}, getTextSize);
// TODO-PER: Hack! the string and rich lines should have used up the same amount of space without this.
if (j < content.length - 1 && typeof content[j] === 'string' && typeof content[j + 1] !== 'string') rows.push({
move: size.height * 3 / 4
});
}
rows.push({
endGroup: groupName,
absElemType: absElemType,
startChar: -1,
endChar: -1,
name: name
});
rows.push({
move: size.height
});
}
}
}
BottomText.prototype.extraText = function (metaText, marginLeft, spacing, shouldAddClasses, getTextSize) {
addSingleLine(this.rows, "Book: ", metaText.book, marginLeft, 'abcjs-book', shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Source: ", metaText.source, marginLeft, 'abcjs-source', shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Discography: ", metaText.discography, marginLeft, 'abcjs-discography', shouldAddClasses, getTextSize);
addMultiLine(this.rows, 'Notes:', metaText.notes, marginLeft, 'historyfont', "extraText", "notes", 'abcjs-notes', "description", spacing, shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Transcription: ", metaText.transcription, marginLeft, 'abcjs-transcription', shouldAddClasses, getTextSize);
addMultiLine(this.rows, "History:", metaText.history, marginLeft, 'historyfont', "extraText", "history", 'abcjs-history', "description", spacing, shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Copyright: ", metaText['abc-copyright'], marginLeft, 'abcjs-copyright', shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Creator: ", metaText['abc-creator'], marginLeft, 'abcjs-creator', shouldAddClasses, getTextSize);
addSingleLine(this.rows, "Edited By: ", metaText['abc-edited-by'], marginLeft, 'abcjs-edited-by', shouldAddClasses, getTextSize);
};
BottomText.prototype.footer = function (footer, width, paddingLeft, getTextSize) {
var klass = 'header meta-bottom';
var font = "footerfont";
this.rows.push({
startGroup: "footer",
klass: klass
});
// Note: whether there is a footer or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
addTextIf(this.rows, {
marginLeft: paddingLeft,
text: footer.left,
font: font,
klass: klass,
name: "footer"
}, getTextSize);
addTextIf(this.rows, {
marginLeft: paddingLeft + width / 2,
text: footer.center,
font: font,
klass: klass,
anchor: 'middle',
name: "footer"
}, getTextSize);
addTextIf(this.rows, {
marginLeft: paddingLeft + width,
text: footer.right,
font: font,
klass: klass,
anchor: 'end',
name: "footer"
}, getTextSize);
};
module.exports = BottomText;
/***/ }),
/***/ "./src/write/creation/elements/brace-element.js":
/*!******************************************************!*\
!*** ./src/write/creation/elements/brace-element.js ***!
\******************************************************/
/***/ (function(module) {
// abc_brace_element.js: Definition of the BraceElement class.
var BraceElem = function BraceElem(voice, type) {
this.startVoice = voice;
this.type = type;
};
BraceElem.prototype.setBottomStaff = function (voice) {
this.endVoice = voice;
// If only the start brace has a name then the name belongs to the brace instead of the staff.
if (this.startVoice.header && !this.endVoice.header) {
this.header = this.startVoice.header;
delete this.startVoice.header;
}
};
BraceElem.prototype.continuing = function (voice) {
// If the final staff isn't present, then use the last one we saw.
this.lastContinuedVoice = voice;
};
BraceElem.prototype.getWidth = function () {
return 10; // TODO-PER: right now the drawing function doesn't vary the width at all. If it does in the future then this will change.
};
BraceElem.prototype.isStartVoice = function (voice) {
if (this.startVoice && this.startVoice.staff && this.startVoice.staff.voices.length > 0 && this.startVoice.staff.voices[0] === voice) return true;
return false;
};
module.exports = BraceElem;
/***/ }),
/***/ "./src/write/creation/elements/crescendo-element.js":
/*!**********************************************************!*\
!*** ./src/write/creation/elements/crescendo-element.js ***!
\**********************************************************/
/***/ (function(module) {
// abc_crescendo_element.js: Definition of the CrescendoElem class.
var CrescendoElem = function CrescendoElem(anchor1, anchor2, dir, positioning) {
this.type = "CrescendoElem";
this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
this.dir = dir; // either "<" or ">"
if (positioning === 'above') this.dynamicHeightAbove = 6;else this.dynamicHeightBelow = 6;
this.pitch = undefined; // This will be set later
};
module.exports = CrescendoElem;
/***/ }),
/***/ "./src/write/creation/elements/dynamic-decoration.js":
/*!***********************************************************!*\
!*** ./src/write/creation/elements/dynamic-decoration.js ***!
\***********************************************************/
/***/ (function(module) {
// abc_dynamic_decoration.js: Definition of the DynamicDecoration class.
var DynamicDecoration = function DynamicDecoration(anchor, dec, position) {
this.type = "DynamicDecoration";
this.anchor = anchor;
this.dec = dec;
if (position === 'below') this.volumeHeightBelow = 6;else this.volumeHeightAbove = 6;
this.pitch = undefined; // This will be set later
};
module.exports = DynamicDecoration;
/***/ }),
/***/ "./src/write/creation/elements/ending-element.js":
/*!*******************************************************!*\
!*** ./src/write/creation/elements/ending-element.js ***!
\*******************************************************/
/***/ (function(module) {
// abc_ending_element.js: Definition of the EndingElement class.
var EndingElem = function EndingElem(text, anchor1, anchor2) {
this.type = "EndingElem";
this.text = text; // text to be displayed top left
this.anchor1 = anchor1; // must have a .x property or be null (means starts at the "beginning" of the line - after keysig)
this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
this.endingHeightAbove = 5;
this.pitch = undefined; // This will be set later
};
module.exports = EndingElem;
/***/ }),
/***/ "./src/write/creation/elements/free-text.js":
/*!**************************************************!*\
!*** ./src/write/creation/elements/free-text.js ***!
\**************************************************/
/***/ (function(module) {
function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize) {
var text = info.text;
this.rows = [];
var size;
if (vskip) this.rows.push({
move: vskip
});
var hash = getFontAndAttr.calc('textfont', 'defined-text');
if (text === "") {
// we do want to print out blank lines if they have been specified.
this.rows.push({
move: hash.attr['font-size'] * 2
}); // move the distance of the line, plus the distance of the margin, which is also one line.
} else if (typeof text === 'string') {
this.rows.push({
move: hash.attr['font-size'] / 2
}); // TODO-PER: move down some - the y location should be the top of the text, but we output text specifying the center line.
this.rows.push({
left: paddingLeft,
text: text,
font: 'textfont',
klass: 'defined-text',
anchor: "start",
startChar: info.startChar,
endChar: info.endChar,
absElemType: "freeText",
name: "free-text"
});
size = getTextSize.calc(text, 'textfont', 'defined-text');
this.rows.push({
move: size.height
});
} else if (text) {
var maxHeight = 0;
var leftSide = paddingLeft;
var currentFont = 'textfont';
for (var i = 0; i < text.length; i++) {
if (text[i].font) {
currentFont = text[i].font;
} else currentFont = 'textfont';
this.rows.push({
left: leftSide,
text: text[i].text,
font: currentFont,
klass: 'defined-text',
anchor: 'start',
startChar: info.startChar,
endChar: info.endChar,
absElemType: "freeText",
name: "free-text"
});
size = getTextSize.calc(text[i].text, getFontAndAttr.calc(currentFont, 'defined-text').font, 'defined-text');
leftSide += size.width + size.height / 2; // add a little padding to the right side. The height of the font is probably a close enough approximation.
maxHeight = Math.max(maxHeight, size.height);
}
this.rows.push({
move: maxHeight
});
} else {
// The structure is wrong here: it requires an array to do centering, but it shouldn't have.
if (info.length === 1) {
var x = width / 2;
this.rows.push({
left: x,
text: info[0].text,
font: 'textfont',
klass: 'defined-text',
anchor: 'middle',
startChar: info.startChar,
endChar: info.endChar,
absElemType: "freeText",
name: "free-text"
});
size = getTextSize.calc(info[0].text, 'textfont', 'defined-text');
this.rows.push({
move: size.height
});
}
}
}
module.exports = FreeText;
/***/ }),
/***/ "./src/write/creation/elements/glissando-element.js":
/*!**********************************************************!*\
!*** ./src/write/creation/elements/glissando-element.js ***!
\**********************************************************/
/***/ (function(module) {
var GlissandoElem = function GlissandoElem(anchor1, anchor2) {
this.type = "GlissandoElem";
this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
};
module.exports = GlissandoElem;
/***/ }),
/***/ "./src/write/creation/elements/relative-element.js":
/*!*********************************************************!*\
!*** ./src/write/creation/elements/relative-element.js ***!
\*********************************************************/
/***/ (function(module) {
// abc_relative_element.js: Definition of the RelativeElement class.
var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
opt = opt || {};
this.x = 0;
this.c = c; // character or path or string
this.dx = dx; // relative x position
this.w = w; // minimum width taken up by this element (can include gratuitous space)
this.pitch = pitch; // relative y position by pitch
this.scalex = opt.scalex || 1; // should the character/path be scaled?
this.scaley = opt.scaley || 1; // should the character/path be scaled?
this.type = opt.type || "symbol"; // cheap types.
this.pitch2 = opt.pitch2;
this.linewidth = opt.linewidth;
this.klass = opt.klass;
this.anchor = opt.anchor ? opt.anchor : 'middle';
this.top = pitch;
if (this.pitch2 !== undefined && this.pitch2 > this.top) this.top = this.pitch2;
this.bottom = pitch;
if (this.pitch2 !== undefined && this.pitch2 < this.bottom) this.bottom = this.pitch2;
if (opt.thickness) {
this.top += opt.thickness / 2;
this.bottom -= opt.thickness / 2;
}
if (opt.stemHeight) {
if (opt.stemHeight > 0) this.top += opt.stemHeight;else this.bottom += opt.stemHeight;
}
if (opt.dim) this.dim = opt.dim;
if (opt.position) this.position = opt.position;
this.height = opt.height ? opt.height : 4; // The +1 is to give a little bit of padding.
if (opt.top) this.top = opt.top;
if (opt.bottom) this.bottom = opt.bottom;
if (opt.name) this.name = opt.name;else if (this.c) this.name = this.c;else this.name = this.type;
if (opt.realWidth) this.realWidth = opt.realWidth;else this.realWidth = this.w;
this.centerVertically = false;
switch (this.type) {
case "debug":
this.chordHeightAbove = this.height;
break;
case "lyric":
if (opt.position && opt.position === 'below') this.lyricHeightBelow = this.height;else this.lyricHeightAbove = this.height;
break;
case "chord":
if (opt.position && opt.position === 'below') this.chordHeightBelow = this.height;else this.chordHeightAbove = this.height;
break;
case "text":
if (this.pitch === undefined) {
if (opt.position && opt.position === 'below') this.chordHeightBelow = this.height;else this.chordHeightAbove = this.height;
} else this.centerVertically = true;
break;
case "part":
this.partHeightAbove = this.height;
break;
}
};
RelativeElement.prototype.getChordDim = function () {
if (this.type === "debug") return null;
if (!this.chordHeightAbove && !this.chordHeightBelow) return null;
// Chords are centered, annotations are left justified.
// NOTE: the font reports extra space to the left and right anyway, so there is a built in margin.
// We add a little margin so that items can't touch - we use half the font size as the margin, so that is 1/4 on each side.
// if there is only one character that we're printing, use half of that margin.
// var margin = this.dim.font.size/4;
// if (this.c.length === 1)
// margin = margin / 2;
var margin = 0;
var offset = this.type === "chord" ? this.realWidth / 2 : 0;
var left = this.x - offset - margin;
var right = left + this.realWidth + margin;
return {
left: left,
right: right
};
};
RelativeElement.prototype.invertLane = function (total) {
if (this.lane === undefined) this.lane = 0;
this.lane = total - this.lane - 1;
};
RelativeElement.prototype.putChordInLane = function (i) {
this.lane = i;
// Add some extra space to account for the character's descenders.
if (this.chordHeightAbove) this.chordHeightAbove = this.height * 1.25 * this.lane;else this.chordHeightBelow = this.height * 1.25 * this.lane;
};
RelativeElement.prototype.getLane = function () {
if (this.lane === undefined) return 0;
return this.lane;
};
RelativeElement.prototype.setX = function (x) {
this.x = x + this.dx;
};
module.exports = RelativeElement;
/***/ }),
/***/ "./src/write/creation/elements/rich-text.js":
/*!**************************************************!*\
!*** ./src/write/creation/elements/rich-text.js ***!
\**************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
function richText(rows, str, defFont, klass, name, paddingLeft, attr, getTextSize) {
var space = getTextSize.calc("i", defFont, klass);
if (str === '') {
rows.push({
move: space.height
});
} else {
if (typeof str === 'string') {
addTextIf(rows, {
marginLeft: paddingLeft,
text: str,
font: defFont,
klass: klass,
marginTop: attr.marginTop,
anchor: attr.anchor,
absElemType: attr.absElemType,
info: attr.info,
name: name
}, getTextSize);
return;
}
if (attr.marginTop) rows.push({
move: attr.marginTop
});
var largestY = 0;
var gap = 0;
var row = {
left: paddingLeft,
anchor: attr.anchor,
phrases: []
};
if (klass) row.klass = klass;
rows.push(row);
for (var k = 0; k < str.length; k++) {
var thisWord = str[k];
var font = thisWord.font ? thisWord.font : getTextSize.attr(defFont, klass).font;
var phrase = {
content: thisWord.text
};
if (font) phrase.attrs = {
"font-family": getTextSize.getFamily(font.face),
"font-size": font.size,
"font-weight": font.weight,
"font-style": font.style,
"font-decoration": font.decoration
};
//if (thisWord.text) {
row.phrases.push(phrase);
var size = getTextSize.calc(thisWord.text, font, klass);
largestY = Math.max(largestY, size.height);
if (thisWord.text[thisWord.text.length - 1] === ' ') {
gap = space.width;
}
}
rows.push({
move: largestY
});
}
}
module.exports = richText;
/***/ }),
/***/ "./src/write/creation/elements/separator.js":
/*!**************************************************!*\
!*** ./src/write/creation/elements/separator.js ***!
\**************************************************/
/***/ (function(module) {
function Separator(spaceAbove, lineLength, spaceBelow) {
this.rows = [];
if (spaceAbove) this.rows.push({
move: spaceAbove
});
this.rows.push({
separator: lineLength,
absElemType: "separator"
});
if (spaceBelow) this.rows.push({
move: spaceBelow
});
}
module.exports = Separator;
/***/ }),
/***/ "./src/write/creation/elements/staff-group-element.js":
/*!************************************************************!*\
!*** ./src/write/creation/elements/staff-group-element.js ***!
\************************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_staff_group_element.js: Definition of the StaffGroupElement class.
// StaffGroupElement contains all the elements that go together to make one line of music.
// That might be multiple staves that are tied together, and it might be multiple voices on one staff.
//
// Methods:
// constructor: some basic initialization
// addVoice(): Called once for each voice. May add a new staff if needed.
// finished(): Called only internally by layout()
// layout(): This does all the layout. It sets the following: spacingunits, startx, minspace, w, and the x-coordinate of each element in each voice.
// draw(): Calls the underlying methods on the voice objects to do the drawing. Sets y and height.
//
// Members:
// staffs: an array of all the staves in this group. Each staff contains the following elements:
// { top, bottom, highest, lowest, y }
// voices: array of VoiceElement objects. This is mostly passed in, but the VoiceElement objects are modified here.
//
// spacingunits: number of relative x-units in the line. Used by the calling function to pass back in as the "spacing" input parameter.
// TODO-PER: This should actually be passed back as a return value.
// minspace: smallest space between two notes. Used by the calling function to pass back in as the "spacing" input parameter.
// TODO-PER: This should actually be passed back as a return value.
// startx: The left edge, taking the margin and the optional voice name. Used by the draw() method.
// w: The width of the line. Used by calling function to pass back in as the "spacing" input parameter, and the draw() method.
// TODO-PER: This should actually be passed back as a return value. (TODO-PER: in pixels or spacing units?)
// y: The top of the staff group, in pixels. This is set in the draw method.
// TODO-PER: Where is that used? It looks like it might not be needed.
// height: Set in the draw() method to the height actually used. Used by the calling function to know where to start the next staff group.
// TODO-PER: This should actually be set in the layout method and passed back as a return value.
var calcHeight = __webpack_require__(/*! ../calc-height */ "./src/write/creation/calc-height.js");
var StaffGroupElement = function StaffGroupElement(getTextSize) {
this.getTextSize = getTextSize;
this.voices = [];
this.staffs = [];
this.brace = undefined; //tony
this.bracket = undefined;
};
StaffGroupElement.prototype.setLimit = function (member, voice) {
if (!voice.specialY[member]) return;
if (!voice.staff.specialY[member]) voice.staff.specialY[member] = voice.specialY[member];else voice.staff.specialY[member] = Math.max(voice.staff.specialY[member], voice.specialY[member]);
};
StaffGroupElement.prototype.addVoice = function (voice, staffnumber, stafflines) {
var voiceNum = this.voices.length;
this.voices[voiceNum] = voice;
if (this.staffs[staffnumber]) this.staffs[staffnumber].voices.push(voiceNum);else {
// TODO-PER: how does the min/max change when stafflines is not 5?
this.staffs[this.staffs.length] = {
top: 10,
bottom: 2,
lines: stafflines,
voices: [voiceNum],
specialY: {
tempoHeightAbove: 0,
partHeightAbove: 0,
volumeHeightAbove: 0,
dynamicHeightAbove: 0,
endingHeightAbove: 0,
chordHeightAbove: 0,
lyricHeightAbove: 0,
lyricHeightBelow: 0,
chordHeightBelow: 0,
volumeHeightBelow: 0,
dynamicHeightBelow: 0
}
};
}
voice.staff = this.staffs[staffnumber];
};
StaffGroupElement.prototype.setHeight = function () {
this.height = calcHeight(this);
};
StaffGroupElement.prototype.setWidth = function (width) {
this.w = width;
for (var i = 0; i < this.voices.length; i++) {
this.voices[i].setWidth(width);
}
};
StaffGroupElement.prototype.setStaffLimits = function (voice) {
voice.staff.top = Math.max(voice.staff.top, voice.top);
voice.staff.bottom = Math.min(voice.staff.bottom, voice.bottom);
this.setLimit('tempoHeightAbove', voice);
this.setLimit('partHeightAbove', voice);
this.setLimit('volumeHeightAbove', voice);
this.setLimit('dynamicHeightAbove', voice);
this.setLimit('endingHeightAbove', voice);
this.setLimit('chordHeightAbove', voice);
this.setLimit('lyricHeightAbove', voice);
this.setLimit('lyricHeightBelow', voice);
this.setLimit('chordHeightBelow', voice);
this.setLimit('volumeHeightBelow', voice);
this.setLimit('dynamicHeightBelow', voice);
};
module.exports = StaffGroupElement;
/***/ }),
/***/ "./src/write/creation/elements/subtitle.js":
/*!*************************************************!*\
!*** ./src/write/creation/elements/subtitle.js ***!
\*************************************************/
/***/ (function(module) {
function Subtitle(spaceAbove, formatting, info, center, paddingLeft, getTextSize) {
this.rows = [];
if (spaceAbove) this.rows.push({
move: spaceAbove
});
var tAnchor = formatting.titleleft ? 'start' : 'middle';
var tLeft = formatting.titleleft ? paddingLeft : center;
this.rows.push({
left: tLeft,
text: info.text,
font: 'subtitlefont',
klass: 'text subtitle',
anchor: tAnchor,
startChar: info.startChar,
endChar: info.endChar,
absElemType: "subtitle",
name: "subtitle"
});
var size = getTextSize.calc(info.text, 'subtitlefont', 'text subtitle');
this.rows.push({
move: size.height
});
}
module.exports = Subtitle;
/***/ }),
/***/ "./src/write/creation/elements/tempo-element.js":
/*!******************************************************!*\
!*** ./src/write/creation/elements/tempo-element.js ***!
\******************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_tempo_element.js: Definition of the TempoElement class.
var AbsoluteElement = __webpack_require__(/*! ./absolute-element */ "./src/write/creation/elements/absolute-element.js");
var RelativeElement = __webpack_require__(/*! ./relative-element */ "./src/write/creation/elements/relative-element.js");
var TempoElement = function TempoElement(tempo, tuneNumber, createNoteHead) {
this.type = "TempoElement";
this.tempo = tempo;
this.tempo.type = "tempo"; /// TODO-PER: this should be set earlier, in the parser, probably.
this.tuneNumber = tuneNumber;
// TODO: can these two properties be merged?
this.totalHeightInPitches = 6;
this.tempoHeightAbove = this.totalHeightInPitches;
this.pitch = undefined; // This will be set later
if (this.tempo.duration && !this.tempo.suppressBpm) {
this.note = this.createNote(createNoteHead, tempo, tuneNumber);
}
};
TempoElement.prototype.setX = function (x) {
this.x = x;
};
TempoElement.prototype.createNote = function (createNoteHead, tempo, tuneNumber) {
var temposcale = 0.75;
var duration = tempo.duration[0]; // TODO when multiple durations
var absElem = new AbsoluteElement(tempo, duration, 1, 'tempo', tuneNumber);
// There aren't an infinite number of note values, but we are passed a float, so just in case something is off upstream,
// merge all of the in between points.
var dot;
var flag;
var note;
if (duration <= 1 / 32) {
note = "noteheads.quarter";
flag = "flags.u32nd";
dot = 0;
} else if (duration <= 1 / 16) {
note = "noteheads.quarter";
flag = "flags.u16th";
dot = 0;
} else if (duration <= 3 / 32) {
note = "noteheads.quarter";
flag = "flags.u16nd";
dot = 1;
} else if (duration <= 1 / 8) {
note = "noteheads.quarter";
flag = "flags.u8th";
dot = 0;
} else if (duration <= 3 / 16) {
note = "noteheads.quarter";
flag = "flags.u8th";
dot = 1;
} else if (duration <= 1 / 4) {
note = "noteheads.quarter";
dot = 0;
} else if (duration <= 3 / 8) {
note = "noteheads.quarter";
dot = 1;
} else if (duration <= 1 / 2) {
note = "noteheads.half";
dot = 0;
} else if (duration <= 3 / 4) {
note = "noteheads.half";
dot = 1;
} else if (duration <= 1) {
note = "noteheads.whole";
dot = 0;
} else if (duration <= 1.5) {
note = "noteheads.whole";
dot = 1;
} else if (duration <= 2) {
note = "noteheads.dbl";
dot = 0;
} else {
note = "noteheads.dbl";
dot = 1;
}
var ret = createNoteHead(absElem, note, {
verticalPos: 0
},
// This is just temporary: we'll offset the vertical positioning when we get the actual vertical spot.
{
dir: "up",
flag: flag,
dot: dot,
scale: temposcale
});
var tempoNote = ret.notehead;
absElem.addHead(tempoNote);
var stem;
if (note !== "noteheads.whole" && note !== "noteheads.dbl") {
var p1 = 1 / 3 * temposcale;
var p2 = 5 * temposcale;
var dx = tempoNote.dx + tempoNote.w;
var width = -0.6;
stem = new RelativeElement(null, dx, 0, p1, {
"type": "stem",
"pitch2": p2,
linewidth: width
});
absElem.addRight(stem);
}
return absElem;
};
module.exports = TempoElement;
/***/ }),
/***/ "./src/write/creation/elements/tie-element.js":
/*!****************************************************!*\
!*** ./src/write/creation/elements/tie-element.js ***!
\****************************************************/
/***/ (function(module) {
// abc_tie_element.js: Definition of the TieElement class.
var TieElem = function TieElem(options) {
this.type = "TieElem";
// console.log("constructor", options.anchor1 ? options.anchor1.pitch : "N/A", options.anchor2 ? options.anchor2.pitch : "N/A", options.isTie, options.isGrace);
this.anchor1 = options.anchor1; // must have a .x and a .pitch, and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
this.anchor2 = options.anchor2; // must have a .x and a .pitch property or be null (means ends at the end of the line)
if (options.isGrace) this.isGrace = true;
if (options.fixedY) this.fixedY = true;
if (options.stemDir) this.stemDir = options.stemDir;
if (options.voiceNumber !== undefined) this.voiceNumber = options.voiceNumber;
if (options.style !== undefined) this.dotted = true;
this.internalNotes = [];
};
TieElem.prototype.addInternalNote = function (note) {
this.internalNotes.push(note);
};
TieElem.prototype.setEndAnchor = function (anchor2) {
// console.log("end", this.anchor1 ? this.anchor1.pitch : "N/A", anchor2 ? anchor2.pitch : "N/A", this.isTie, this.isGrace);
this.anchor2 = anchor2; // must have a .x and a .pitch property or be null (means ends at the end of the line)
// we don't really have enough info to know what the vertical extent is yet and we won't until drawing. This will just give it enough
// room on either side (we don't even know if the slur will be above yet). We need to set this so that we can make sure the voice has
// at least enough room that the line doesn't get cut off if the tie or slur is the lowest thing.
if (this.anchor1) {
this.top = Math.max(this.anchor1.pitch, this.anchor2.pitch) + 4;
this.bottom = Math.min(this.anchor1.pitch, this.anchor2.pitch) - 4;
} else {
this.top = this.anchor2.pitch + 4;
this.bottom = this.anchor2.pitch - 4;
}
};
// If we encounter a repeat sign, then we don't want to extend either a tie or a slur past it, so these are called to be a limit.
TieElem.prototype.setStartX = function (startLimitElem) {
this.startLimitX = startLimitElem;
};
TieElem.prototype.setEndX = function (endLimitElem) {
this.endLimitX = endLimitElem;
};
TieElem.prototype.setHint = function () {
this.hint = true;
};
TieElem.prototype.calcTieDirection = function () {
// The rules:
// 1) If it is in a grace note group, then the direction is always BELOW.
// 2) If it is in a single voice, then the direction is always OPPOSITE of the stem (or where the stem would have been in the case of whole notes.)
// 3) If the stem direction is forced (probably because there are two voices on the same line), then the direction is the SAME as the stem direction.
if (this.isGrace) this.above = false;else if (this.voiceNumber === 0) this.above = true;else if (this.voiceNumber > 0) this.above = false;else {
var referencePitch;
if (this.anchor1) referencePitch = this.anchor1.pitch;else if (this.anchor2) referencePitch = this.anchor2.pitch;else referencePitch = 14; // TODO-PER: this can't really happen normally. This would imply that a tie crossed over three lines, something like "C-\nz\nC"
// Put the arc in the opposite direction of the stem. That isn't always the pitch if one or both of the notes are beamed with something that affects its stem.
if (this.anchor1 && this.anchor1.stemDir === 'down' && this.anchor2 && this.anchor2.stemDir === "down") this.above = true;else if (this.anchor1 && this.anchor1.stemDir === 'up' && this.anchor2 && this.anchor2.stemDir === "up") this.above = false;else if (this.anchor1 && this.anchor2) this.above = referencePitch >= 6;else if (this.anchor1) this.above = this.anchor1.stemDir === "down";else if (this.anchor2) this.above = this.anchor2.stemDir === "down";else this.above = referencePitch >= 6;
}
};
// From "standard music notation practice" by Music Publishers’ Association:
// 1) Slurs are placed under the note heads if all stems go up.
// 2) Slurs are placed over the note heads if all stems go down.
// 3) If there are both up stems and down stems, prefer placing the slur over.
// 4) When the staff has opposite stemmed voices, all slurs should be on the stemmed side.
TieElem.prototype.calcSlurDirection = function () {
if (this.isGrace) this.above = false;else if (this.voiceNumber === 0) this.above = true;else if (this.voiceNumber > 0) this.above = false;else {
var hasDownStem = false;
if (this.anchor1 && this.anchor1.stemDir === "down") hasDownStem = true;
if (this.anchor2 && this.anchor2.stemDir === "down") hasDownStem = true;
for (var i = 0; i < this.internalNotes.length; i++) {
var n = this.internalNotes[i];
if (n.stemDir === "down") hasDownStem = true;
}
this.above = hasDownStem;
}
};
TieElem.prototype.calcX = function (lineStartX, lineEndX) {
if (this.anchor1) {
this.startX = this.anchor1.x; // The normal case where there is a starting element to attach to.
if (this.anchor1.scalex < 1)
// this is a grace note - don't offset the tie as much.
this.startX -= 3;
} else if (this.startLimitX) this.startX = this.startLimitX.x + this.startLimitX.w; // if there is no start element, but there is a repeat mark before the start of the line.
else {
if (this.anchor2) this.startX = this.anchor2.x - 20; // There is no element and no repeat mark: make a small arc
else this.startX = lineStartX; // Don't have any guidance, so extend to beginning of line
}
if (!this.anchor1 && this.dotted) this.startX -= 3; // The arc needs to be long enough to tell that it is dotted.
if (this.anchor2) this.endX = this.anchor2.x; // The normal case where there is a starting element to attach to.
else if (this.endLimitX) this.endX = this.endLimitX.x; // if there is no start element, but there is a repeat mark before the start of the line.
else this.endX = lineEndX; // There is no element and no repeat mark: extend to the beginning of the line.
};
TieElem.prototype.calcTieY = function () {
// If the tie comes from another line, then one or both anchors will be missing.
if (this.anchor1) this.startY = this.anchor1.pitch;else if (this.anchor2) this.startY = this.anchor2.pitch;else this.startY = this.above ? 14 : 0;
if (this.anchor2) this.endY = this.anchor2.pitch;else if (this.anchor1) this.endY = this.anchor1.pitch;else this.endY = this.above ? 14 : 0;
};
// From "standard music notation practice" by Music Publishers’ Association:
// 1) If the anchor note is down stem, the slur points to the note head.
// 2) If the anchor note is up stem, and the slur is over, then point to middle of stem.
TieElem.prototype.calcSlurY = function () {
if (this.anchor1 && this.anchor2) {
if (this.above && this.anchor1.stemDir === "up" && !this.fixedY) {
this.startY = (this.anchor1.highestVert + this.anchor1.pitch) / 2;
this.startX += this.anchor1.w / 2; // When going to the middle of the stem, bump the line to the right a little bit to make it look right.
} else this.startY = this.anchor1.pitch;
// If the closing note has an up stem, and it is beamed, and it isn't the first note in the beam, then the beam will get in the way.
var beamInterferes = this.anchor2.parent.beam && this.anchor2.parent.beam.stemsUp && this.anchor2.parent.beam.elems[0] !== this.anchor2.parent;
var midPoint = (this.anchor2.highestVert + this.anchor2.pitch) / 2;
if (this.above && this.anchor2.stemDir === "up" && !this.fixedY && !beamInterferes && midPoint < this.startY) {
this.endY = midPoint;
this.endX += Math.round(this.anchor2.w / 2); // When going to the middle of the stem, bump the line to the right a little bit to make it look right.
} else this.endY = this.above && beamInterferes ? this.anchor2.highestVert : this.anchor2.pitch;
if (this.anchor1.scalex === 1) {
// Need a way to tell if this is a grace note - if so then keep the slur as close as possible. TODO-PER-HACK: this should be more declaratively determined.
var hasBeam1 = !!this.anchor1.parent.beam;
var hasBeam2 = !!this.anchor2.parent.beam;
if (hasBeam1) {
var isLastInBeam = this.anchor1.parent === this.anchor1.parent.beam.elems[this.anchor1.parent.beam.elems.length - 1];
if (!isLastInBeam) {
if (this.above) this.startY = this.anchor1.parent.fixed.t;else this.startY = this.anchor1.parent.fixed.b;
}
}
if (hasBeam2) {
var isFirstInBeam = this.anchor2.parent === this.anchor2.parent.beam.elems[0];
if (!isFirstInBeam) {
if (this.above) this.endY = this.anchor2.parent.fixed.t;else this.endY = this.anchor2.parent.fixed.b;
}
}
}
} else if (this.anchor1) {
this.startY = this.endY = this.anchor1.pitch;
} else if (this.anchor2) {
this.startY = this.endY = this.anchor2.pitch;
} else {
// This is the case where the slur covers the entire line.
// TODO-PER: figure out where the real top and bottom of the line are.
this.startY = this.above ? 14 : 0;
this.endY = this.above ? 14 : 0;
}
};
TieElem.prototype.avoidCollisionAbove = function () {
// Double check that an interior note in the slur isn't so high that it interferes.
if (this.above) {
var maxInnerHeight = -50;
for (var i = 0; i < this.internalNotes.length; i++) {
if (this.internalNotes[i].highestVert > maxInnerHeight) maxInnerHeight = this.internalNotes[i].highestVert;
}
if (maxInnerHeight > this.startY && maxInnerHeight > this.endY) this.startY = this.endY = maxInnerHeight - 1;
}
};
TieElem.prototype.getYBounds = function () {
var lineStartX = 10; // TODO-PER: I'm not sure where to get this number from but it probably doesn't matter much
var lineEndX = 1000; // TODO-PER: I'm not sure where to get this number from but it probably doesn't matter much
if (this.isTie) {
this.calcTieDirection();
this.calcX(lineStartX, lineEndX);
this.calcTieY();
} else {
this.calcSlurDirection();
this.calcX(lineStartX, lineEndX);
this.calcSlurY();
}
var top;
var bottom;
// TODO-PER: It's hard to tell how far the arc is, so I'm just using 3 as the max
if (this.above) {
bottom = Math.min(this.startY, this.endY);
top = bottom + 3;
} else {
top = Math.min(this.startY, this.endY);
bottom = top - 3;
}
return [top, bottom];
};
module.exports = TieElem;
/***/ }),
/***/ "./src/write/creation/elements/top-text.js":
/*!*************************************************!*\
!*** ./src/write/creation/elements/top-text.js ***!
\*************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var addTextIf = __webpack_require__(/*! ../add-text-if */ "./src/write/creation/add-text-if.js");
var richText = __webpack_require__(/*! ./rich-text */ "./src/write/creation/elements/rich-text.js");
function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, paddingLeft, spacing, shouldAddClasses, getTextSize) {
this.rows = [];
if (metaText.header && isPrint) {
// Note: whether there is a header or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
// This text goes above the margin, so we'll temporarily move up.
var headerTextHeight = getTextSize.calc("X", "headerfont", 'abcjs-header abcjs-meta-top').height;
addTextIf(this.rows, {
marginLeft: paddingLeft,
text: metaText.header.left,
font: 'headerfont',
klass: 'header meta-top',
marginTop: -headerTextHeight,
info: metaTextInfo.header,
name: "header"
}, getTextSize);
addTextIf(this.rows, {
marginLeft: paddingLeft + width / 2,
text: metaText.header.center,
font: 'headerfont',
klass: 'header meta-top',
marginTop: -headerTextHeight,
anchor: 'middle',
info: metaTextInfo.header,
name: "header"
}, getTextSize);
addTextIf(this.rows, {
marginLeft: paddingLeft + width,
text: metaText.header.right,
font: 'headerfont',
klass: 'header meta-top',
marginTop: -headerTextHeight,
anchor: 'end',
info: metaTextInfo.header,
name: "header"
}, getTextSize);
// TopText.prototype.addTextIf = function (marginLeft, text, font, klass, marginTop, marginBottom, anchor, getTextSize, absElemType, noMove) {
}
if (isPrint) this.rows.push({
move: spacing.top
});
var tAnchor = formatting.titleleft ? 'start' : 'middle';
var tLeft = formatting.titleleft ? paddingLeft : paddingLeft + width / 2;
if (metaText.title) {
var klass = shouldAddClasses ? 'abcjs-title' : '';
richText(this.rows, metaText.title, "titlefont", klass, 'title', tLeft, {
marginTop: spacing.title,
anchor: tAnchor,
absElemType: "title",
info: metaTextInfo.title
}, getTextSize);
}
if (lines.length) {
var index = 0;
while (index < lines.length && lines[index].subtitle) {
var klass = shouldAddClasses ? 'abcjs-text abcjs-subtitle' : '';
richText(this.rows, lines[index].subtitle.text, "subtitlefont", klass, 'subtitle', tLeft, {
marginTop: spacing.subtitle,
anchor: tAnchor,
absElemType: "subtitle",
info: lines[index].subtitle
}, getTextSize);
index++;
}
}
if (metaText.rhythm || metaText.origin || metaText.composer) {
this.rows.push({
move: spacing.composer
});
if (metaText.rhythm && metaText.rhythm.length > 0) {
var noMove = !!(metaText.composer || metaText.origin);
var klass = shouldAddClasses ? 'abcjs-rhythm' : '';
addTextIf(this.rows, {
marginLeft: paddingLeft,
text: metaText.rhythm,
font: 'infofont',
klass: klass,
absElemType: "rhythm",
noMove: noMove,
info: metaTextInfo.rhythm,
name: "rhythm"
}, getTextSize);
}
var hasSimpleComposerLine = true;
if (metaText.composer && typeof metaText.composer !== 'string') hasSimpleComposerLine = false;
if (metaText.origin && typeof metaText.origin !== 'string') hasSimpleComposerLine = false;
var composerLine = metaText.composer ? metaText.composer : '';
if (metaText.origin) {
if (typeof composerLine === 'string' && typeof metaText.origin === 'string') composerLine += ' (' + metaText.origin + ')';else if (typeof composerLine === 'string' && typeof metaText.origin !== 'string') {
composerLine = [{
text: composerLine
}];
composerLine.push({
text: " ("
});
composerLine = composerLine.concat(metaText.origin);
composerLine.push({
text: ")"
});
} else {
composerLine.push({
text: " ("
});
composerLine = composerLine.concat(metaText.origin);
composerLine.push({
text: ")"
});
}
}
if (composerLine) {
var klass = shouldAddClasses ? 'abcjs-composer' : '';
richText(this.rows, composerLine, 'composerfont', klass, "composer", paddingLeft + width, {
anchor: "end",
absElemType: "composer",
info: metaTextInfo.composer,
ingroup: true
}, getTextSize);
}
}
if (metaText.author && metaText.author.length > 0) {
var klass = shouldAddClasses ? 'abcjs-author' : '';
richText(this.rows, metaText.author, 'composerfont', klass, "author", paddingLeft + width, {
anchor: "end",
absElemType: "author",
info: metaTextInfo.author
}, getTextSize);
}
if (metaText.partOrder && metaText.partOrder.length > 0) {
var klass = shouldAddClasses ? 'abcjs-part-order' : '';
richText(this.rows, metaText.partOrder, 'partsfont', klass, "part-order", paddingLeft, {
absElemType: "partOrder",
info: metaTextInfo.partOrder,
anchor: 'start'
}, getTextSize);
}
}
module.exports = TopText;
/***/ }),
/***/ "./src/write/creation/elements/triplet-element.js":
/*!********************************************************!*\
!*** ./src/write/creation/elements/triplet-element.js ***!
\********************************************************/
/***/ (function(module) {
// abc_triplet_element.js: Definition of the TripletElem class.
var TripletElem = function TripletElem(number, anchor1, options) {
this.type = "TripletElem";
this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after key signature)
this.number = number;
this.durationClass = ('d' + Math.round(anchor1.parent.durationClass * 1000) / 1000).replace(/\./, '-');
this.middleElems = []; // This is to calculate the highest interior pitch. It is used to make sure that the drawn bracket never crosses a really high middle note.
this.flatBeams = options.flatBeams;
};
TripletElem.prototype.isClosed = function () {
return !!this.anchor2;
};
TripletElem.prototype.middleNote = function (elem) {
this.middleElems.push(elem);
};
TripletElem.prototype.setCloseAnchor = function (anchor2) {
this.anchor2 = anchor2;
// TODO-PER: This used to be just for beamed triplets but it looks like bracketed triplets need extra room, too. The only one that doesn't is stem down and beamed
//if (this.anchor1.parent.beam)
if (!this.anchor1.parent.beam || this.anchor1.stemDir === 'up') this.endingHeightAbove = 4;
};
module.exports = TripletElem;
/***/ }),
/***/ "./src/write/creation/elements/voice-element.js":
/*!******************************************************!*\
!*** ./src/write/creation/elements/voice-element.js ***!
\******************************************************/
/***/ (function(module) {
// abc_voice_element.js: Definition of the VoiceElement class.
var VoiceElement = function VoiceElement(voicenumber, voicetotal) {
this.children = [];
this.beams = [];
this.otherchildren = []; // ties, slurs, triplets
this.w = 0;
this.duplicate = false;
this.voicenumber = voicenumber; //number of the voice on a given stave (not staffgroup)
this.voicetotal = voicetotal;
this.bottom = 7;
this.top = 7;
this.specialY = {
tempoHeightAbove: 0,
partHeightAbove: 0,
volumeHeightAbove: 0,
dynamicHeightAbove: 0,
endingHeightAbove: 0,
chordHeightAbove: 0,
lyricHeightAbove: 0,
lyricHeightBelow: 0,
chordHeightBelow: 0,
volumeHeightBelow: 0,
dynamicHeightBelow: 0
};
};
VoiceElement.prototype.addChild = function (absElem) {
// This is always passed an AbsoluteElement
if (absElem.type === 'bar') {
var firstItem = true;
for (var i = 0; firstItem && i < this.children.length; i++) {
if (this.children[i].type.indexOf("staff-extra") < 0 && this.children[i].type !== "tempo") firstItem = false;
}
if (!firstItem) {
this.beams.push("bar");
this.otherchildren.push("bar");
}
}
this.children[this.children.length] = absElem;
this.setRange(absElem);
};
VoiceElement.prototype.setLimit = function (member, child) {
// Sometimes we get an absolute element in here and sometimes we get some type of relative element.
// If there is a "specialY" element, then assume it is an absolute element. If that doesn't exist, look for the
// same members at the top level, because that's where they are in relative elements.
var specialY = child.specialY;
if (!specialY) specialY = child;
if (!specialY[member]) return;
if (!this.specialY[member]) this.specialY[member] = specialY[member];else this.specialY[member] = Math.max(this.specialY[member], specialY[member]);
};
VoiceElement.prototype.adjustRange = function (child) {
if (child.bottom !== undefined) this.bottom = Math.min(this.bottom, child.bottom);
if (child.top !== undefined) this.top = Math.max(this.top, child.top);
};
VoiceElement.prototype.setRange = function (child) {
this.adjustRange(child);
this.setLimit('tempoHeightAbove', child);
this.setLimit('partHeightAbove', child);
this.setLimit('volumeHeightAbove', child);
this.setLimit('dynamicHeightAbove', child);
this.setLimit('endingHeightAbove', child);
this.setLimit('chordHeightAbove', child);
this.setLimit('lyricHeightAbove', child);
this.setLimit('lyricHeightBelow', child);
this.setLimit('chordHeightBelow', child);
this.setLimit('volumeHeightBelow', child);
this.setLimit('dynamicHeightBelow', child);
};
VoiceElement.prototype.addOther = function (child) {
this.otherchildren.push(child);
this.setRange(child);
};
VoiceElement.prototype.addBeam = function (child) {
this.beams.push(child);
};
VoiceElement.prototype.setWidth = function (width) {
this.w = width;
};
module.exports = VoiceElement;
/***/ }),
/***/ "./src/write/creation/glyphs.js":
/*!**************************************!*\
!*** ./src/write/creation/glyphs.js ***!
\**************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
/**
* Glyphs and some methods to adjust for their x and y baseline
*/
var glyphs = {
'0': {
d: [['M', 4.83, -14.97], ['c', 0.33, -0.03, 1.11, 0.00, 1.47, 0.06], ['c', 1.68, 0.36, 2.97, 1.59, 3.78, 3.60], ['c', 1.20, 2.97, 0.81, 6.96, -0.90, 9.27], ['c', -0.78, 1.08, -1.71, 1.71, -2.91, 1.95], ['c', -0.45, 0.09, -1.32, 0.09, -1.77, 0.00], ['c', -0.81, -0.18, -1.47, -0.51, -2.07, -1.02], ['c', -2.34, -2.07, -3.15, -6.72, -1.74, -10.20], ['c', 0.87, -2.16, 2.28, -3.42, 4.14, -3.66], ['z'], ['m', 1.11, 0.87], ['c', -0.21, -0.06, -0.69, -0.09, -0.87, -0.06], ['c', -0.54, 0.12, -0.87, 0.42, -1.17, 0.99], ['c', -0.36, 0.66, -0.51, 1.56, -0.60, 3.00], ['c', -0.03, 0.75, -0.03, 4.59, 0.00, 5.31], ['c', 0.09, 1.50, 0.27, 2.40, 0.60, 3.06], ['c', 0.24, 0.48, 0.57, 0.78, 0.96, 0.90], ['c', 0.27, 0.09, 0.78, 0.09, 1.05, 0.00], ['c', 0.39, -0.12, 0.72, -0.42, 0.96, -0.90], ['c', 0.33, -0.66, 0.51, -1.56, 0.60, -3.06], ['c', 0.03, -0.72, 0.03, -4.56, 0.00, -5.31], ['c', -0.09, -1.47, -0.27, -2.37, -0.60, -3.03], ['c', -0.24, -0.48, -0.54, -0.78, -0.93, -0.90], ['z']],
w: 10.78,
h: 14.959
},
'1': {
d: [['M', 3.30, -15.06], ['c', 0.06, -0.06, 0.21, -0.03, 0.66, 0.15], ['c', 0.81, 0.39, 1.08, 0.39, 1.83, 0.03], ['c', 0.21, -0.09, 0.39, -0.15, 0.42, -0.15], ['c', 0.12, 0.00, 0.21, 0.09, 0.27, 0.21], ['c', 0.06, 0.12, 0.06, 0.33, 0.06, 5.94], ['c', 0.00, 3.93, 0.00, 5.85, 0.03, 6.03], ['c', 0.06, 0.36, 0.15, 0.69, 0.27, 0.96], ['c', 0.36, 0.75, 0.93, 1.17, 1.68, 1.26], ['c', 0.30, 0.03, 0.39, 0.09, 0.39, 0.30], ['c', 0.00, 0.15, -0.03, 0.18, -0.09, 0.24], ['c', -0.06, 0.06, -0.09, 0.06, -0.48, 0.06], ['c', -0.42, 0.00, -0.69, -0.03, -2.10, -0.24], ['c', -0.90, -0.15, -1.77, -0.15, -2.67, 0.00], ['c', -1.41, 0.21, -1.68, 0.24, -2.10, 0.24], ['c', -0.39, 0.00, -0.42, 0.00, -0.48, -0.06], ['c', -0.06, -0.06, -0.06, -0.09, -0.06, -0.24], ['c', 0.00, -0.21, 0.06, -0.27, 0.36, -0.30], ['c', 0.75, -0.09, 1.32, -0.51, 1.68, -1.26], ['c', 0.12, -0.27, 0.21, -0.60, 0.27, -0.96], ['c', 0.03, -0.18, 0.03, -1.59, 0.03, -4.29], ['c', 0.00, -3.87, 0.00, -4.05, -0.06, -4.14], ['c', -0.09, -0.15, -0.18, -0.24, -0.39, -0.24], ['c', -0.12, 0.00, -0.15, 0.03, -0.21, 0.06], ['c', -0.03, 0.06, -0.45, 0.99, -0.96, 2.13], ['c', -0.48, 1.14, -0.90, 2.10, -0.93, 2.16], ['c', -0.06, 0.15, -0.21, 0.24, -0.33, 0.24], ['c', -0.24, 0.00, -0.42, -0.18, -0.42, -0.39], ['c', 0.00, -0.06, 3.27, -7.62, 3.33, -7.74], ['z']],
w: 8.94,
h: 15.058
},
'2': {
d: [['M', 4.23, -14.97], ['c', 0.57, -0.06, 1.68, 0.00, 2.34, 0.18], ['c', 0.69, 0.18, 1.50, 0.54, 2.01, 0.90], ['c', 1.35, 0.96, 1.95, 2.25, 1.77, 3.81], ['c', -0.15, 1.35, -0.66, 2.34, -1.68, 3.15], ['c', -0.60, 0.48, -1.44, 0.93, -3.12, 1.65], ['c', -1.32, 0.57, -1.80, 0.81, -2.37, 1.14], ['c', -0.57, 0.33, -0.57, 0.33, -0.24, 0.27], ['c', 0.39, -0.09, 1.26, -0.09, 1.68, 0.00], ['c', 0.72, 0.15, 1.41, 0.45, 2.10, 0.90], ['c', 0.99, 0.63, 1.86, 0.87, 2.55, 0.75], ['c', 0.24, -0.06, 0.42, -0.15, 0.57, -0.30], ['c', 0.12, -0.09, 0.30, -0.42, 0.30, -0.51], ['c', 0.00, -0.09, 0.12, -0.21, 0.24, -0.24], ['c', 0.18, -0.03, 0.39, 0.12, 0.39, 0.30], ['c', 0.00, 0.12, -0.15, 0.57, -0.30, 0.87], ['c', -0.54, 1.02, -1.56, 1.74, -2.79, 2.01], ['c', -0.42, 0.09, -1.23, 0.09, -1.62, 0.03], ['c', -0.81, -0.18, -1.32, -0.45, -2.01, -1.11], ['c', -0.45, -0.45, -0.63, -0.57, -0.96, -0.69], ['c', -0.84, -0.27, -1.89, 0.12, -2.25, 0.90], ['c', -0.12, 0.21, -0.21, 0.54, -0.21, 0.72], ['c', 0.00, 0.12, -0.12, 0.21, -0.27, 0.24], ['c', -0.15, 0.00, -0.27, -0.03, -0.33, -0.15], ['c', -0.09, -0.21, 0.09, -1.08, 0.33, -1.71], ['c', 0.24, -0.66, 0.66, -1.26, 1.29, -1.89], ['c', 0.45, -0.45, 0.90, -0.81, 1.92, -1.56], ['c', 1.29, -0.93, 1.89, -1.44, 2.34, -1.98], ['c', 0.87, -1.05, 1.26, -2.19, 1.20, -3.63], ['c', -0.06, -1.29, -0.39, -2.31, -0.96, -2.91], ['c', -0.36, -0.33, -0.72, -0.51, -1.17, -0.54], ['c', -0.84, -0.03, -1.53, 0.42, -1.59, 1.05], ['c', -0.03, 0.33, 0.12, 0.60, 0.57, 1.14], ['c', 0.45, 0.54, 0.54, 0.87, 0.42, 1.41], ['c', -0.15, 0.63, -0.54, 1.11, -1.08, 1.38], ['c', -0.63, 0.33, -1.20, 0.33, -1.83, 0.00], ['c', -0.24, -0.12, -0.33, -0.18, -0.54, -0.39], ['c', -0.18, -0.18, -0.27, -0.30, -0.36, -0.51], ['c', -0.24, -0.45, -0.27, -0.84, -0.21, -1.38], ['c', 0.12, -0.75, 0.45, -1.41, 1.02, -1.98], ['c', 0.72, -0.72, 1.74, -1.17, 2.85, -1.32], ['z']],
w: 10.764,
h: 14.97
},
'3': {
d: [['M', 3.78, -14.97], ['c', 0.30, -0.03, 1.41, 0.00, 1.83, 0.06], ['c', 2.22, 0.30, 3.51, 1.32, 3.72, 2.91], ['c', 0.03, 0.33, 0.03, 1.26, -0.03, 1.65], ['c', -0.12, 0.84, -0.48, 1.47, -1.05, 1.77], ['c', -0.27, 0.15, -0.36, 0.24, -0.45, 0.39], ['c', -0.09, 0.21, -0.09, 0.36, 0.00, 0.57], ['c', 0.09, 0.15, 0.18, 0.24, 0.51, 0.39], ['c', 0.75, 0.42, 1.23, 1.14, 1.41, 2.13], ['c', 0.06, 0.42, 0.06, 1.35, 0.00, 1.71], ['c', -0.18, 0.81, -0.48, 1.38, -1.02, 1.95], ['c', -0.75, 0.72, -1.80, 1.20, -3.18, 1.38], ['c', -0.42, 0.06, -1.56, 0.06, -1.95, 0.00], ['c', -1.89, -0.33, -3.18, -1.29, -3.51, -2.64], ['c', -0.03, -0.12, -0.03, -0.33, -0.03, -0.60], ['c', 0.00, -0.36, 0.00, -0.42, 0.06, -0.63], ['c', 0.12, -0.30, 0.27, -0.51, 0.51, -0.75], ['c', 0.24, -0.24, 0.45, -0.39, 0.75, -0.51], ['c', 0.21, -0.06, 0.27, -0.06, 0.60, -0.06], ['c', 0.33, 0.00, 0.39, 0.00, 0.60, 0.06], ['c', 0.30, 0.12, 0.51, 0.27, 0.75, 0.51], ['c', 0.36, 0.33, 0.57, 0.75, 0.60, 1.20], ['c', 0.00, 0.21, 0.00, 0.27, -0.06, 0.42], ['c', -0.09, 0.18, -0.12, 0.24, -0.54, 0.54], ['c', -0.51, 0.36, -0.63, 0.54, -0.60, 0.87], ['c', 0.06, 0.54, 0.54, 0.90, 1.38, 0.99], ['c', 0.36, 0.06, 0.72, 0.03, 0.96, -0.06], ['c', 0.81, -0.27, 1.29, -1.23, 1.44, -2.79], ['c', 0.03, -0.45, 0.03, -1.95, -0.03, -2.37], ['c', -0.09, -0.75, -0.33, -1.23, -0.75, -1.44], ['c', -0.33, -0.18, -0.45, -0.18, -1.98, -0.18], ['c', -1.35, 0.00, -1.41, 0.00, -1.50, -0.06], ['c', -0.18, -0.12, -0.24, -0.39, -0.12, -0.60], ['c', 0.12, -0.15, 0.15, -0.15, 1.68, -0.15], ['c', 1.50, 0.00, 1.62, 0.00, 1.89, -0.15], ['c', 0.18, -0.09, 0.42, -0.36, 0.54, -0.57], ['c', 0.18, -0.42, 0.27, -0.90, 0.30, -1.95], ['c', 0.03, -1.20, -0.06, -1.80, -0.36, -2.37], ['c', -0.24, -0.48, -0.63, -0.81, -1.14, -0.96], ['c', -0.30, -0.06, -1.08, -0.06, -1.38, 0.03], ['c', -0.60, 0.15, -0.90, 0.42, -0.96, 0.84], ['c', -0.03, 0.30, 0.06, 0.45, 0.63, 0.84], ['c', 0.33, 0.24, 0.42, 0.39, 0.45, 0.63], ['c', 0.03, 0.72, -0.57, 1.50, -1.32, 1.65], ['c', -1.05, 0.27, -2.10, -0.57, -2.10, -1.65], ['c', 0.00, -0.45, 0.15, -0.96, 0.39, -1.38], ['c', 0.12, -0.21, 0.54, -0.63, 0.81, -0.81], ['c', 0.57, -0.42, 1.38, -0.69, 2.25, -0.81], ['z']],
w: 9.735,
h: 14.967
},
'4': {
d: [['M', 8.64, -14.94], ['c', 0.27, -0.09, 0.42, -0.12, 0.54, -0.03], ['c', 0.09, 0.06, 0.15, 0.21, 0.15, 0.30], ['c', -0.03, 0.06, -1.92, 2.31, -4.23, 5.04], ['c', -2.31, 2.73, -4.23, 4.98, -4.26, 5.01], ['c', -0.03, 0.06, 0.12, 0.06, 2.55, 0.06], ['l', 2.61, 0.00], ['l', 0.00, -2.37], ['c', 0.00, -2.19, 0.03, -2.37, 0.06, -2.46], ['c', 0.03, -0.06, 0.21, -0.18, 0.57, -0.42], ['c', 1.08, -0.72, 1.38, -1.08, 1.86, -2.16], ['c', 0.12, -0.30, 0.24, -0.54, 0.27, -0.57], ['c', 0.12, -0.12, 0.39, -0.06, 0.45, 0.12], ['c', 0.06, 0.09, 0.06, 0.57, 0.06, 3.96], ['l', 0.00, 3.90], ['l', 1.08, 0.00], ['c', 1.05, 0.00, 1.11, 0.00, 1.20, 0.06], ['c', 0.24, 0.15, 0.24, 0.54, 0.00, 0.69], ['c', -0.09, 0.06, -0.15, 0.06, -1.20, 0.06], ['l', -1.08, 0.00], ['l', 0.00, 0.33], ['c', 0.00, 0.57, 0.09, 1.11, 0.30, 1.53], ['c', 0.36, 0.75, 0.93, 1.17, 1.68, 1.26], ['c', 0.30, 0.03, 0.39, 0.09, 0.39, 0.30], ['c', 0.00, 0.15, -0.03, 0.18, -0.09, 0.24], ['c', -0.06, 0.06, -0.09, 0.06, -0.48, 0.06], ['c', -0.42, 0.00, -0.69, -0.03, -2.10, -0.24], ['c', -0.90, -0.15, -1.77, -0.15, -2.67, 0.00], ['c', -1.41, 0.21, -1.68, 0.24, -2.10, 0.24], ['c', -0.39, 0.00, -0.42, 0.00, -0.48, -0.06], ['c', -0.06, -0.06, -0.06, -0.09, -0.06, -0.24], ['c', 0.00, -0.21, 0.06, -0.27, 0.36, -0.30], ['c', 0.75, -0.09, 1.32, -0.51, 1.68, -1.26], ['c', 0.21, -0.42, 0.30, -0.96, 0.30, -1.53], ['l', 0.00, -0.33], ['l', -2.70, 0.00], ['c', -2.91, 0.00, -2.85, 0.00, -3.09, -0.15], ['c', -0.18, -0.12, -0.30, -0.39, -0.27, -0.54], ['c', 0.03, -0.06, 0.18, -0.24, 0.33, -0.45], ['c', 0.75, -0.90, 1.59, -2.07, 2.13, -3.03], ['c', 0.33, -0.54, 0.84, -1.62, 1.05, -2.16], ['c', 0.57, -1.41, 0.84, -2.64, 0.90, -4.05], ['c', 0.03, -0.63, 0.06, -0.72, 0.24, -0.81], ['l', 0.12, -0.06], ['l', 0.45, 0.12], ['c', 0.66, 0.18, 1.02, 0.24, 1.47, 0.27], ['c', 0.60, 0.03, 1.23, -0.09, 2.01, -0.33], ['z']],
w: 11.795,
h: 14.994
},
'5': {
d: [['M', 1.02, -14.94], ['c', 0.12, -0.09, 0.03, -0.09, 1.08, 0.06], ['c', 2.49, 0.36, 4.35, 0.36, 6.96, -0.06], ['c', 0.57, -0.09, 0.66, -0.06, 0.81, 0.06], ['c', 0.15, 0.18, 0.12, 0.24, -0.15, 0.51], ['c', -1.29, 1.26, -3.24, 2.04, -5.58, 2.31], ['c', -0.60, 0.09, -1.20, 0.12, -1.71, 0.12], ['c', -0.39, 0.00, -0.45, 0.00, -0.57, 0.06], ['c', -0.09, 0.06, -0.15, 0.12, -0.21, 0.21], ['l', -0.06, 0.12], ['l', 0.00, 1.65], ['l', 0.00, 1.65], ['l', 0.21, -0.21], ['c', 0.66, -0.57, 1.41, -0.96, 2.19, -1.14], ['c', 0.33, -0.06, 1.41, -0.06, 1.95, 0.00], ['c', 2.61, 0.36, 4.02, 1.74, 4.26, 4.14], ['c', 0.03, 0.45, 0.03, 1.08, -0.03, 1.44], ['c', -0.18, 1.02, -0.78, 2.01, -1.59, 2.70], ['c', -0.72, 0.57, -1.62, 1.02, -2.49, 1.20], ['c', -1.38, 0.27, -3.03, 0.06, -4.20, -0.54], ['c', -1.08, -0.54, -1.71, -1.32, -1.86, -2.28], ['c', -0.09, -0.69, 0.09, -1.29, 0.57, -1.74], ['c', 0.24, -0.24, 0.45, -0.39, 0.75, -0.51], ['c', 0.21, -0.06, 0.27, -0.06, 0.60, -0.06], ['c', 0.33, 0.00, 0.39, 0.00, 0.60, 0.06], ['c', 0.30, 0.12, 0.51, 0.27, 0.75, 0.51], ['c', 0.36, 0.33, 0.57, 0.75, 0.60, 1.20], ['c', 0.00, 0.21, 0.00, 0.27, -0.06, 0.42], ['c', -0.09, 0.18, -0.12, 0.24, -0.54, 0.54], ['c', -0.18, 0.12, -0.36, 0.30, -0.42, 0.33], ['c', -0.36, 0.42, -0.18, 0.99, 0.36, 1.26], ['c', 0.51, 0.27, 1.47, 0.36, 2.01, 0.27], ['c', 0.93, -0.21, 1.47, -1.17, 1.65, -2.91], ['c', 0.06, -0.45, 0.06, -1.89, 0.00, -2.31], ['c', -0.15, -1.20, -0.51, -2.10, -1.05, -2.55], ['c', -0.21, -0.18, -0.54, -0.36, -0.81, -0.39], ['c', -0.30, -0.06, -0.84, -0.03, -1.26, 0.06], ['c', -0.93, 0.18, -1.65, 0.60, -2.16, 1.20], ['c', -0.15, 0.21, -0.27, 0.30, -0.39, 0.30], ['c', -0.15, 0.00, -0.30, -0.09, -0.36, -0.18], ['c', -0.06, -0.09, -0.06, -0.15, -0.06, -3.66], ['c', 0.00, -3.39, 0.00, -3.57, 0.06, -3.66], ['c', 0.03, -0.06, 0.09, -0.15, 0.15, -0.18], ['z']],
w: 10.212,
h: 14.997
},
'6': {
d: [['M', 4.98, -14.97], ['c', 0.36, -0.03, 1.20, 0.00, 1.59, 0.06], ['c', 0.90, 0.15, 1.68, 0.51, 2.25, 1.05], ['c', 0.57, 0.51, 0.87, 1.23, 0.84, 1.98], ['c', -0.03, 0.51, -0.21, 0.90, -0.60, 1.26], ['c', -0.24, 0.24, -0.45, 0.39, -0.75, 0.51], ['c', -0.21, 0.06, -0.27, 0.06, -0.60, 0.06], ['c', -0.33, 0.00, -0.39, 0.00, -0.60, -0.06], ['c', -0.30, -0.12, -0.51, -0.27, -0.75, -0.51], ['c', -0.39, -0.36, -0.57, -0.78, -0.57, -1.26], ['c', 0.00, -0.27, 0.00, -0.30, 0.09, -0.42], ['c', 0.03, -0.09, 0.18, -0.21, 0.30, -0.30], ['c', 0.12, -0.09, 0.30, -0.21, 0.39, -0.27], ['c', 0.09, -0.06, 0.21, -0.18, 0.27, -0.24], ['c', 0.06, -0.12, 0.09, -0.15, 0.09, -0.33], ['c', 0.00, -0.18, -0.03, -0.24, -0.09, -0.36], ['c', -0.24, -0.39, -0.75, -0.60, -1.38, -0.57], ['c', -0.54, 0.03, -0.90, 0.18, -1.23, 0.48], ['c', -0.81, 0.72, -1.08, 2.16, -0.96, 5.37], ['l', 0.00, 0.63], ['l', 0.30, -0.12], ['c', 0.78, -0.27, 1.29, -0.33, 2.10, -0.27], ['c', 1.47, 0.12, 2.49, 0.54, 3.27, 1.29], ['c', 0.48, 0.51, 0.81, 1.11, 0.96, 1.89], ['c', 0.06, 0.27, 0.06, 0.42, 0.06, 0.93], ['c', 0.00, 0.54, 0.00, 0.69, -0.06, 0.96], ['c', -0.15, 0.78, -0.48, 1.38, -0.96, 1.89], ['c', -0.54, 0.51, -1.17, 0.87, -1.98, 1.08], ['c', -1.14, 0.30, -2.40, 0.33, -3.24, 0.03], ['c', -1.50, -0.48, -2.64, -1.89, -3.27, -4.02], ['c', -0.36, -1.23, -0.51, -2.82, -0.42, -4.08], ['c', 0.30, -3.66, 2.28, -6.30, 4.95, -6.66], ['z'], ['m', 0.66, 7.41], ['c', -0.27, -0.09, -0.81, -0.12, -1.08, -0.06], ['c', -0.72, 0.18, -1.08, 0.69, -1.23, 1.71], ['c', -0.06, 0.54, -0.06, 3.00, 0.00, 3.54], ['c', 0.18, 1.26, 0.72, 1.77, 1.80, 1.74], ['c', 0.39, -0.03, 0.63, -0.09, 0.90, -0.27], ['c', 0.66, -0.42, 0.90, -1.32, 0.90, -3.24], ['c', 0.00, -2.22, -0.36, -3.12, -1.29, -3.42], ['z']],
w: 9.956,
h: 14.982
},
'7': {
d: [['M', 0.21, -14.97], ['c', 0.21, -0.06, 0.45, 0.00, 0.54, 0.15], ['c', 0.06, 0.09, 0.06, 0.15, 0.06, 0.39], ['c', 0.00, 0.24, 0.00, 0.33, 0.06, 0.42], ['c', 0.06, 0.12, 0.21, 0.24, 0.27, 0.24], ['c', 0.03, 0.00, 0.12, -0.12, 0.24, -0.21], ['c', 0.96, -1.20, 2.58, -1.35, 3.99, -0.42], ['c', 0.15, 0.12, 0.42, 0.30, 0.54, 0.45], ['c', 0.48, 0.39, 0.81, 0.57, 1.29, 0.60], ['c', 0.69, 0.03, 1.50, -0.30, 2.13, -0.87], ['c', 0.09, -0.09, 0.27, -0.30, 0.39, -0.45], ['c', 0.12, -0.15, 0.24, -0.27, 0.30, -0.30], ['c', 0.18, -0.06, 0.39, 0.03, 0.51, 0.21], ['c', 0.06, 0.18, 0.06, 0.24, -0.27, 0.72], ['c', -0.18, 0.24, -0.54, 0.78, -0.78, 1.17], ['c', -2.37, 3.54, -3.54, 6.27, -3.87, 9.00], ['c', -0.03, 0.33, -0.03, 0.66, -0.03, 1.26], ['c', 0.00, 0.90, 0.00, 1.08, 0.15, 1.89], ['c', 0.06, 0.45, 0.06, 0.48, 0.03, 0.60], ['c', -0.06, 0.09, -0.21, 0.21, -0.30, 0.21], ['c', -0.03, 0.00, -0.27, -0.06, -0.54, -0.15], ['c', -0.84, -0.27, -1.11, -0.30, -1.65, -0.30], ['c', -0.57, 0.00, -0.84, 0.03, -1.56, 0.27], ['c', -0.60, 0.18, -0.69, 0.21, -0.81, 0.15], ['c', -0.12, -0.06, -0.21, -0.18, -0.21, -0.30], ['c', 0.00, -0.15, 0.60, -1.44, 1.20, -2.61], ['c', 1.14, -2.22, 2.73, -4.68, 5.10, -8.01], ['c', 0.21, -0.27, 0.36, -0.48, 0.33, -0.48], ['c', 0.00, 0.00, -0.12, 0.06, -0.27, 0.12], ['c', -0.54, 0.30, -0.99, 0.39, -1.56, 0.39], ['c', -0.75, 0.03, -1.20, -0.18, -1.83, -0.75], ['c', -0.99, -0.90, -1.83, -1.17, -2.31, -0.72], ['c', -0.18, 0.15, -0.36, 0.51, -0.45, 0.84], ['c', -0.06, 0.24, -0.06, 0.33, -0.09, 1.98], ['c', 0.00, 1.62, -0.03, 1.74, -0.06, 1.80], ['c', -0.15, 0.24, -0.54, 0.24, -0.69, 0.00], ['c', -0.06, -0.09, -0.06, -0.15, -0.06, -3.57], ['c', 0.00, -3.42, 0.00, -3.48, 0.06, -3.57], ['c', 0.03, -0.06, 0.09, -0.12, 0.15, -0.15], ['z']],
w: 10.561,
h: 15.093
},
'8': {
d: [['M', 4.98, -14.97], ['c', 0.33, -0.03, 1.02, -0.03, 1.32, 0.00], ['c', 1.32, 0.12, 2.49, 0.60, 3.21, 1.32], ['c', 0.39, 0.39, 0.66, 0.81, 0.78, 1.29], ['c', 0.09, 0.36, 0.09, 1.08, 0.00, 1.44], ['c', -0.21, 0.84, -0.66, 1.59, -1.59, 2.55], ['l', -0.30, 0.30], ['l', 0.27, 0.18], ['c', 1.47, 0.93, 2.31, 2.31, 2.25, 3.75], ['c', -0.03, 0.75, -0.24, 1.35, -0.63, 1.95], ['c', -0.45, 0.66, -1.02, 1.14, -1.83, 1.53], ['c', -1.80, 0.87, -4.20, 0.87, -6.00, 0.03], ['c', -1.62, -0.78, -2.52, -2.16, -2.46, -3.66], ['c', 0.06, -0.99, 0.54, -1.77, 1.80, -2.97], ['c', 0.54, -0.51, 0.54, -0.54, 0.48, -0.57], ['c', -0.39, -0.27, -0.96, -0.78, -1.20, -1.14], ['c', -0.75, -1.11, -0.87, -2.40, -0.30, -3.60], ['c', 0.69, -1.35, 2.25, -2.25, 4.20, -2.40], ['z'], ['m', 1.53, 0.69], ['c', -0.42, -0.09, -1.11, -0.12, -1.38, -0.06], ['c', -0.30, 0.06, -0.60, 0.18, -0.81, 0.30], ['c', -0.21, 0.12, -0.60, 0.51, -0.72, 0.72], ['c', -0.51, 0.87, -0.42, 1.89, 0.21, 2.52], ['c', 0.21, 0.21, 0.36, 0.30, 1.95, 1.23], ['c', 0.96, 0.54, 1.74, 0.99, 1.77, 1.02], ['c', 0.09, 0.00, 0.63, -0.60, 0.99, -1.11], ['c', 0.21, -0.36, 0.48, -0.87, 0.57, -1.23], ['c', 0.06, -0.24, 0.06, -0.36, 0.06, -0.72], ['c', 0.00, -0.45, -0.03, -0.66, -0.15, -0.99], ['c', -0.39, -0.81, -1.29, -1.44, -2.49, -1.68], ['z'], ['m', -1.44, 8.07], ['l', -1.89, -1.08], ['c', -0.03, 0.00, -0.18, 0.15, -0.39, 0.33], ['c', -1.20, 1.08, -1.65, 1.95, -1.59, 3.00], ['c', 0.09, 1.59, 1.35, 2.85, 3.21, 3.24], ['c', 0.33, 0.06, 0.45, 0.06, 0.93, 0.06], ['c', 0.63, 0.00, 0.81, -0.03, 1.29, -0.27], ['c', 0.90, -0.42, 1.47, -1.41, 1.41, -2.40], ['c', -0.06, -0.66, -0.39, -1.29, -0.90, -1.65], ['c', -0.12, -0.09, -1.05, -0.63, -2.07, -1.23], ['z']],
w: 10.926,
h: 14.989
},
'9': {
d: [['M', 4.23, -14.97], ['c', 0.42, -0.03, 1.29, 0.00, 1.62, 0.06], ['c', 0.51, 0.12, 0.93, 0.30, 1.38, 0.57], ['c', 1.53, 1.02, 2.52, 3.24, 2.73, 5.94], ['c', 0.18, 2.55, -0.48, 4.98, -1.83, 6.57], ['c', -1.05, 1.26, -2.40, 1.89, -3.93, 1.83], ['c', -1.23, -0.06, -2.31, -0.45, -3.03, -1.14], ['c', -0.57, -0.51, -0.87, -1.23, -0.84, -1.98], ['c', 0.03, -0.51, 0.21, -0.90, 0.60, -1.26], ['c', 0.24, -0.24, 0.45, -0.39, 0.75, -0.51], ['c', 0.21, -0.06, 0.27, -0.06, 0.60, -0.06], ['c', 0.33, 0.00, 0.39, 0.00, 0.60, 0.06], ['c', 0.30, 0.12, 0.51, 0.27, 0.75, 0.51], ['c', 0.39, 0.36, 0.57, 0.78, 0.57, 1.26], ['c', 0.00, 0.27, 0.00, 0.30, -0.09, 0.42], ['c', -0.03, 0.09, -0.18, 0.21, -0.30, 0.30], ['c', -0.12, 0.09, -0.30, 0.21, -0.39, 0.27], ['c', -0.09, 0.06, -0.21, 0.18, -0.27, 0.24], ['c', -0.06, 0.12, -0.06, 0.15, -0.06, 0.33], ['c', 0.00, 0.18, 0.00, 0.24, 0.06, 0.36], ['c', 0.24, 0.39, 0.75, 0.60, 1.38, 0.57], ['c', 0.54, -0.03, 0.90, -0.18, 1.23, -0.48], ['c', 0.81, -0.72, 1.08, -2.16, 0.96, -5.37], ['l', 0.00, -0.63], ['l', -0.30, 0.12], ['c', -0.78, 0.27, -1.29, 0.33, -2.10, 0.27], ['c', -1.47, -0.12, -2.49, -0.54, -3.27, -1.29], ['c', -0.48, -0.51, -0.81, -1.11, -0.96, -1.89], ['c', -0.06, -0.27, -0.06, -0.42, -0.06, -0.96], ['c', 0.00, -0.51, 0.00, -0.66, 0.06, -0.93], ['c', 0.15, -0.78, 0.48, -1.38, 0.96, -1.89], ['c', 0.15, -0.12, 0.33, -0.27, 0.42, -0.36], ['c', 0.69, -0.51, 1.62, -0.81, 2.76, -0.93], ['z'], ['m', 1.17, 0.66], ['c', -0.21, -0.06, -0.57, -0.06, -0.81, -0.03], ['c', -0.78, 0.12, -1.26, 0.69, -1.41, 1.74], ['c', -0.12, 0.63, -0.15, 1.95, -0.09, 2.79], ['c', 0.12, 1.71, 0.63, 2.40, 1.77, 2.46], ['c', 1.08, 0.03, 1.62, -0.48, 1.80, -1.74], ['c', 0.06, -0.54, 0.06, -3.00, 0.00, -3.54], ['c', -0.15, -1.05, -0.51, -1.53, -1.26, -1.68], ['z']],
w: 9.959,
h: 14.986
},
'rests.multimeasure': {
d: [['M', 0, -4], ['l', 0, 16], ['l', 1, 0], ['l', 0, -5], ['l', 40, 0], ['l', 0, 5], ['l', 1, 0], ['l', 0, -16], ['l', -1, 0], ['l', 0, 5], ['l', -40, 0], ['l', 0, -5], ['z']],
w: 42,
h: 18
},
'rests.whole': {
d: [['M', 0.06, 0.03], ['l', 0.09, -0.06], ['l', 5.46, 0.00], ['l', 5.49, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 2.19], ['l', 0.00, 2.19], ['l', -0.06, 0.09], ['l', -0.09, 0.06], ['l', -5.49, 0.00], ['l', -5.46, 0.00], ['l', -0.09, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -2.19], ['l', 0.00, -2.19], ['z']],
w: 11.25,
h: 4.68
},
'rests.half': {
d: [['M', 0.06, -4.62], ['l', 0.09, -0.06], ['l', 5.46, 0.00], ['l', 5.49, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 2.19], ['l', 0.00, 2.19], ['l', -0.06, 0.09], ['l', -0.09, 0.06], ['l', -5.49, 0.00], ['l', -5.46, 0.00], ['l', -0.09, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -2.19], ['l', 0.00, -2.19], ['z']],
w: 11.25,
h: 4.68
},
'rests.quarter': {
d: [['M', 1.89, -11.82], ['c', 0.12, -0.06, 0.24, -0.06, 0.36, -0.03], ['c', 0.09, 0.06, 4.74, 5.58, 4.86, 5.82], ['c', 0.21, 0.39, 0.15, 0.78, -0.15, 1.26], ['c', -0.24, 0.33, -0.72, 0.81, -1.62, 1.56], ['c', -0.45, 0.36, -0.87, 0.75, -0.96, 0.84], ['c', -0.93, 0.99, -1.14, 2.49, -0.60, 3.63], ['c', 0.18, 0.39, 0.27, 0.48, 1.32, 1.68], ['c', 1.92, 2.25, 1.83, 2.16, 1.83, 2.34], ['c', 0.00, 0.18, -0.18, 0.36, -0.36, 0.39], ['c', -0.15, 0.00, -0.27, -0.06, -0.48, -0.27], ['c', -0.75, -0.75, -2.46, -1.29, -3.39, -1.08], ['c', -0.45, 0.09, -0.69, 0.27, -0.90, 0.69], ['c', -0.12, 0.30, -0.21, 0.66, -0.24, 1.14], ['c', -0.03, 0.66, 0.09, 1.35, 0.30, 2.01], ['c', 0.15, 0.42, 0.24, 0.66, 0.45, 0.96], ['c', 0.18, 0.24, 0.18, 0.33, 0.03, 0.42], ['c', -0.12, 0.06, -0.18, 0.03, -0.45, -0.30], ['c', -1.08, -1.38, -2.07, -3.36, -2.40, -4.83], ['c', -0.27, -1.05, -0.15, -1.77, 0.27, -2.07], ['c', 0.21, -0.12, 0.42, -0.15, 0.87, -0.15], ['c', 0.87, 0.06, 2.10, 0.39, 3.30, 0.90], ['l', 0.39, 0.18], ['l', -1.65, -1.95], ['c', -2.52, -2.97, -2.61, -3.09, -2.70, -3.27], ['c', -0.09, -0.24, -0.12, -0.48, -0.03, -0.75], ['c', 0.15, -0.48, 0.57, -0.96, 1.83, -2.01], ['c', 0.45, -0.36, 0.84, -0.72, 0.93, -0.78], ['c', 0.69, -0.75, 1.02, -1.80, 0.90, -2.79], ['c', -0.06, -0.33, -0.21, -0.84, -0.39, -1.11], ['c', -0.09, -0.15, -0.45, -0.60, -0.81, -1.05], ['c', -0.36, -0.42, -0.69, -0.81, -0.72, -0.87], ['c', -0.09, -0.18, 0.00, -0.42, 0.21, -0.51], ['z']],
w: 7.888,
h: 21.435
},
'rests.8th': {
d: [['M', 1.68, -6.12], ['c', 0.66, -0.09, 1.23, 0.09, 1.68, 0.51], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.12, 0.27, 0.33, 0.45, 0.60, 0.48], ['c', 0.12, 0.00, 0.18, 0.00, 0.33, -0.09], ['c', 0.39, -0.18, 1.32, -1.29, 1.68, -1.98], ['c', 0.09, -0.21, 0.24, -0.30, 0.39, -0.30], ['c', 0.12, 0.00, 0.27, 0.09, 0.33, 0.18], ['c', 0.03, 0.06, -0.27, 1.11, -1.86, 6.42], ['c', -1.02, 3.48, -1.89, 6.39, -1.92, 6.42], ['c', 0.00, 0.03, -0.12, 0.12, -0.24, 0.15], ['c', -0.18, 0.09, -0.21, 0.09, -0.45, 0.09], ['c', -0.24, 0.00, -0.30, 0.00, -0.48, -0.06], ['c', -0.09, -0.06, -0.21, -0.12, -0.21, -0.15], ['c', -0.06, -0.03, 0.15, -0.57, 1.68, -4.92], ['c', 0.96, -2.67, 1.74, -4.89, 1.71, -4.89], ['l', -0.51, 0.15], ['c', -1.08, 0.36, -1.74, 0.48, -2.55, 0.48], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.33, -0.45, 0.84, -0.81, 1.38, -0.90], ['z']],
w: 7.534,
h: 13.883
},
'rests.16th': {
d: [['M', 3.33, -6.12], ['c', 0.66, -0.09, 1.23, 0.09, 1.68, 0.51], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.15, 0.39, 0.57, 0.57, 0.87, 0.42], ['c', 0.39, -0.18, 1.20, -1.23, 1.62, -2.07], ['c', 0.06, -0.15, 0.24, -0.24, 0.36, -0.24], ['c', 0.12, 0.00, 0.27, 0.09, 0.33, 0.18], ['c', 0.03, 0.06, -0.45, 1.86, -2.67, 10.17], ['c', -1.50, 5.55, -2.73, 10.14, -2.76, 10.17], ['c', -0.03, 0.03, -0.12, 0.12, -0.24, 0.15], ['c', -0.18, 0.09, -0.21, 0.09, -0.45, 0.09], ['c', -0.24, 0.00, -0.30, 0.00, -0.48, -0.06], ['c', -0.09, -0.06, -0.21, -0.12, -0.21, -0.15], ['c', -0.06, -0.03, 0.12, -0.57, 1.44, -4.92], ['c', 0.81, -2.67, 1.47, -4.86, 1.47, -4.89], ['c', -0.03, 0.00, -0.27, 0.06, -0.54, 0.15], ['c', -1.08, 0.36, -1.77, 0.48, -2.58, 0.48], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.72, -1.05, 2.22, -1.23, 3.06, -0.42], ['c', 0.30, 0.33, 0.42, 0.60, 0.60, 1.38], ['c', 0.09, 0.45, 0.21, 0.78, 0.33, 0.90], ['c', 0.09, 0.09, 0.27, 0.18, 0.45, 0.21], ['c', 0.12, 0.00, 0.18, 0.00, 0.33, -0.09], ['c', 0.33, -0.15, 1.02, -0.93, 1.41, -1.59], ['c', 0.12, -0.21, 0.18, -0.39, 0.39, -1.08], ['c', 0.66, -2.10, 1.17, -3.84, 1.17, -3.87], ['c', 0.00, 0.00, -0.21, 0.06, -0.42, 0.15], ['c', -0.51, 0.15, -1.20, 0.33, -1.68, 0.42], ['c', -0.33, 0.06, -0.51, 0.06, -0.96, 0.06], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.33, -0.45, 0.84, -0.81, 1.38, -0.90], ['z']],
w: 9.724,
h: 21.383
},
'rests.32nd': {
d: [['M', 4.23, -13.62], ['c', 0.66, -0.09, 1.23, 0.09, 1.68, 0.51], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.12, 0.27, 0.33, 0.45, 0.60, 0.48], ['c', 0.12, 0.00, 0.18, 0.00, 0.27, -0.06], ['c', 0.33, -0.21, 0.99, -1.11, 1.44, -1.98], ['c', 0.09, -0.24, 0.21, -0.33, 0.39, -0.33], ['c', 0.12, 0.00, 0.27, 0.09, 0.33, 0.18], ['c', 0.03, 0.06, -0.57, 2.67, -3.21, 13.89], ['c', -1.80, 7.62, -3.30, 13.89, -3.30, 13.92], ['c', -0.03, 0.06, -0.12, 0.12, -0.24, 0.18], ['c', -0.21, 0.09, -0.24, 0.09, -0.48, 0.09], ['c', -0.24, 0.00, -0.30, 0.00, -0.48, -0.06], ['c', -0.09, -0.06, -0.21, -0.12, -0.21, -0.15], ['c', -0.06, -0.03, 0.09, -0.57, 1.23, -4.92], ['c', 0.69, -2.67, 1.26, -4.86, 1.29, -4.89], ['c', 0.00, -0.03, -0.12, -0.03, -0.48, 0.12], ['c', -1.17, 0.39, -2.22, 0.57, -3.00, 0.54], ['c', -0.42, -0.03, -0.75, -0.12, -1.11, -0.30], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.72, -1.05, 2.22, -1.23, 3.06, -0.42], ['c', 0.30, 0.33, 0.42, 0.60, 0.60, 1.38], ['c', 0.09, 0.45, 0.21, 0.78, 0.33, 0.90], ['c', 0.12, 0.09, 0.30, 0.18, 0.48, 0.21], ['c', 0.12, 0.00, 0.18, 0.00, 0.30, -0.09], ['c', 0.42, -0.21, 1.29, -1.29, 1.56, -1.89], ['c', 0.03, -0.12, 1.23, -4.59, 1.23, -4.65], ['c', 0.00, -0.03, -0.18, 0.03, -0.39, 0.12], ['c', -0.63, 0.18, -1.20, 0.36, -1.74, 0.45], ['c', -0.39, 0.06, -0.54, 0.06, -1.02, 0.06], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.72, -1.05, 2.22, -1.23, 3.06, -0.42], ['c', 0.30, 0.33, 0.42, 0.60, 0.60, 1.38], ['c', 0.09, 0.45, 0.21, 0.78, 0.33, 0.90], ['c', 0.18, 0.18, 0.51, 0.27, 0.72, 0.15], ['c', 0.30, -0.12, 0.69, -0.57, 1.08, -1.17], ['c', 0.42, -0.60, 0.39, -0.51, 1.05, -3.03], ['c', 0.33, -1.26, 0.60, -2.31, 0.60, -2.34], ['c', 0.00, 0.00, -0.21, 0.03, -0.45, 0.12], ['c', -0.57, 0.18, -1.14, 0.33, -1.62, 0.42], ['c', -0.33, 0.06, -0.51, 0.06, -0.96, 0.06], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.33, -0.45, 0.84, -0.81, 1.38, -0.90], ['z']],
w: 11.373,
h: 28.883
},
'rests.64th': {
d: [['M', 5.13, -13.62], ['c', 0.66, -0.09, 1.23, 0.09, 1.68, 0.51], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.15, 0.63, 0.21, 0.81, 0.33, 0.96], ['c', 0.18, 0.21, 0.54, 0.30, 0.75, 0.18], ['c', 0.24, -0.12, 0.63, -0.66, 1.08, -1.56], ['c', 0.33, -0.66, 0.39, -0.72, 0.60, -0.72], ['c', 0.12, 0.00, 0.27, 0.09, 0.33, 0.18], ['c', 0.03, 0.06, -0.69, 3.66, -3.54, 17.64], ['c', -1.95, 9.66, -3.57, 17.61, -3.57, 17.64], ['c', -0.03, 0.06, -0.12, 0.12, -0.24, 0.18], ['c', -0.21, 0.09, -0.24, 0.09, -0.48, 0.09], ['c', -0.24, 0.00, -0.30, 0.00, -0.48, -0.06], ['c', -0.09, -0.06, -0.21, -0.12, -0.21, -0.15], ['c', -0.06, -0.03, 0.06, -0.57, 1.05, -4.95], ['c', 0.60, -2.70, 1.08, -4.89, 1.08, -4.92], ['c', 0.00, 0.00, -0.24, 0.06, -0.51, 0.15], ['c', -0.66, 0.24, -1.20, 0.36, -1.77, 0.48], ['c', -0.42, 0.06, -0.57, 0.06, -1.05, 0.06], ['c', -0.69, 0.00, -0.87, -0.03, -1.35, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.72, -1.05, 2.22, -1.23, 3.06, -0.42], ['c', 0.30, 0.33, 0.42, 0.60, 0.60, 1.38], ['c', 0.09, 0.45, 0.21, 0.78, 0.33, 0.90], ['c', 0.09, 0.09, 0.27, 0.18, 0.45, 0.21], ['c', 0.21, 0.03, 0.39, -0.09, 0.72, -0.42], ['c', 0.45, -0.45, 1.02, -1.26, 1.17, -1.65], ['c', 0.03, -0.09, 0.27, -1.14, 0.54, -2.34], ['c', 0.27, -1.20, 0.48, -2.19, 0.51, -2.22], ['c', 0.00, -0.03, -0.09, -0.03, -0.48, 0.12], ['c', -1.17, 0.39, -2.22, 0.57, -3.00, 0.54], ['c', -0.42, -0.03, -0.75, -0.12, -1.11, -0.30], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.15, 0.39, 0.57, 0.57, 0.90, 0.42], ['c', 0.36, -0.18, 1.20, -1.26, 1.47, -1.89], ['c', 0.03, -0.09, 0.30, -1.20, 0.57, -2.43], ['l', 0.51, -2.28], ['l', -0.54, 0.18], ['c', -1.11, 0.36, -1.80, 0.48, -2.61, 0.48], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.15, 0.63, 0.21, 0.81, 0.33, 0.96], ['c', 0.21, 0.21, 0.54, 0.30, 0.75, 0.18], ['c', 0.36, -0.18, 0.93, -0.93, 1.29, -1.68], ['c', 0.12, -0.24, 0.18, -0.48, 0.63, -2.55], ['l', 0.51, -2.31], ['c', 0.00, -0.03, -0.18, 0.03, -0.39, 0.12], ['c', -1.14, 0.36, -2.10, 0.54, -2.82, 0.51], ['c', -0.42, -0.03, -0.75, -0.12, -1.11, -0.30], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.33, -0.45, 0.84, -0.81, 1.38, -0.90], ['z']],
w: 12.453,
h: 36.383
},
'rests.128th': {
d: [['M', 6.03, -21.12], ['c', 0.66, -0.09, 1.23, 0.09, 1.68, 0.51], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.12, 0.27, 0.33, 0.45, 0.60, 0.48], ['c', 0.21, 0.00, 0.33, -0.06, 0.54, -0.36], ['c', 0.15, -0.21, 0.54, -0.93, 0.78, -1.47], ['c', 0.15, -0.33, 0.18, -0.39, 0.30, -0.48], ['c', 0.18, -0.09, 0.45, 0.00, 0.51, 0.15], ['c', 0.03, 0.09, -7.11, 42.75, -7.17, 42.84], ['c', -0.03, 0.03, -0.15, 0.09, -0.24, 0.15], ['c', -0.18, 0.06, -0.24, 0.06, -0.45, 0.06], ['c', -0.24, 0.00, -0.30, 0.00, -0.48, -0.06], ['c', -0.09, -0.06, -0.21, -0.12, -0.21, -0.15], ['c', -0.06, -0.03, 0.03, -0.57, 0.84, -4.98], ['c', 0.51, -2.70, 0.93, -4.92, 0.90, -4.92], ['c', 0.00, 0.00, -0.15, 0.06, -0.36, 0.12], ['c', -0.78, 0.27, -1.62, 0.48, -2.31, 0.57], ['c', -0.15, 0.03, -0.54, 0.03, -0.81, 0.03], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.12, 0.27, 0.33, 0.45, 0.63, 0.48], ['c', 0.12, 0.00, 0.18, 0.00, 0.30, -0.09], ['c', 0.42, -0.21, 1.14, -1.11, 1.50, -1.83], ['c', 0.12, -0.27, 0.12, -0.27, 0.54, -2.52], ['c', 0.24, -1.23, 0.42, -2.25, 0.39, -2.25], ['c', 0.00, 0.00, -0.24, 0.06, -0.51, 0.18], ['c', -1.26, 0.39, -2.25, 0.57, -3.06, 0.54], ['c', -0.42, -0.03, -0.75, -0.12, -1.11, -0.30], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.15, 0.63, 0.21, 0.81, 0.33, 0.96], ['c', 0.18, 0.21, 0.51, 0.30, 0.75, 0.18], ['c', 0.36, -0.15, 1.05, -0.99, 1.41, -1.77], ['l', 0.15, -0.30], ['l', 0.42, -2.25], ['c', 0.21, -1.26, 0.42, -2.28, 0.39, -2.28], ['l', -0.51, 0.15], ['c', -1.11, 0.39, -1.89, 0.51, -2.70, 0.51], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.15, 0.63, 0.21, 0.81, 0.33, 0.96], ['c', 0.18, 0.18, 0.48, 0.27, 0.72, 0.21], ['c', 0.33, -0.12, 1.14, -1.26, 1.41, -1.95], ['c', 0.00, -0.09, 0.21, -1.11, 0.45, -2.34], ['c', 0.21, -1.20, 0.39, -2.22, 0.39, -2.28], ['c', 0.03, -0.03, 0.00, -0.03, -0.45, 0.12], ['c', -0.57, 0.18, -1.20, 0.33, -1.71, 0.42], ['c', -0.30, 0.06, -0.51, 0.06, -0.93, 0.06], ['c', -0.66, 0.00, -0.84, -0.03, -1.32, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.36, -0.54, 0.96, -0.87, 1.65, -0.93], ['c', 0.54, -0.03, 1.02, 0.15, 1.41, 0.54], ['c', 0.27, 0.30, 0.39, 0.54, 0.57, 1.26], ['c', 0.09, 0.33, 0.18, 0.66, 0.21, 0.72], ['c', 0.12, 0.27, 0.33, 0.45, 0.60, 0.48], ['c', 0.18, 0.00, 0.36, -0.09, 0.57, -0.33], ['c', 0.33, -0.36, 0.78, -1.14, 0.93, -1.56], ['c', 0.03, -0.12, 0.24, -1.20, 0.45, -2.40], ['c', 0.24, -1.20, 0.42, -2.22, 0.42, -2.28], ['c', 0.03, -0.03, 0.00, -0.03, -0.39, 0.09], ['c', -1.05, 0.36, -1.80, 0.48, -2.58, 0.48], ['c', -0.63, 0.00, -0.84, -0.03, -1.29, -0.27], ['c', -1.32, -0.63, -1.77, -2.16, -1.02, -3.30], ['c', 0.33, -0.45, 0.84, -0.81, 1.38, -0.90], ['z']],
w: 12.992,
h: 43.883
},
'accidentals.sharp': {
d: [['M', 5.73, -11.19], ['c', 0.21, -0.12, 0.54, -0.03, 0.66, 0.24], ['c', 0.06, 0.12, 0.06, 0.21, 0.06, 2.31], ['c', 0.00, 1.23, 0.00, 2.22, 0.03, 2.22], ['c', 0.00, 0.00, 0.27, -0.12, 0.60, -0.24], ['c', 0.69, -0.27, 0.78, -0.30, 0.96, -0.15], ['c', 0.21, 0.15, 0.21, 0.18, 0.21, 1.38], ['c', 0.00, 1.02, 0.00, 1.11, -0.06, 1.20], ['c', -0.03, 0.06, -0.09, 0.12, -0.12, 0.15], ['c', -0.06, 0.03, -0.42, 0.21, -0.84, 0.36], ['l', -0.75, 0.33], ['l', -0.03, 2.43], ['c', 0.00, 1.32, 0.00, 2.43, 0.03, 2.43], ['c', 0.00, 0.00, 0.27, -0.12, 0.60, -0.24], ['c', 0.69, -0.27, 0.78, -0.30, 0.96, -0.15], ['c', 0.21, 0.15, 0.21, 0.18, 0.21, 1.38], ['c', 0.00, 1.02, 0.00, 1.11, -0.06, 1.20], ['c', -0.03, 0.06, -0.09, 0.12, -0.12, 0.15], ['c', -0.06, 0.03, -0.42, 0.21, -0.84, 0.36], ['l', -0.75, 0.33], ['l', -0.03, 2.52], ['c', 0.00, 2.28, -0.03, 2.55, -0.06, 2.64], ['c', -0.21, 0.36, -0.72, 0.36, -0.93, 0.00], ['c', -0.03, -0.09, -0.06, -0.33, -0.06, -2.43], ['l', 0.00, -2.31], ['l', -1.29, 0.51], ['l', -1.26, 0.51], ['l', 0.00, 2.43], ['c', 0.00, 2.58, 0.00, 2.52, -0.15, 2.67], ['c', -0.06, 0.09, -0.27, 0.18, -0.36, 0.18], ['c', -0.12, 0.00, -0.33, -0.09, -0.39, -0.18], ['c', -0.15, -0.15, -0.15, -0.09, -0.15, -2.43], ['c', 0.00, -1.23, 0.00, -2.22, -0.03, -2.22], ['c', 0.00, 0.00, -0.27, 0.12, -0.60, 0.24], ['c', -0.69, 0.27, -0.78, 0.30, -0.96, 0.15], ['c', -0.21, -0.15, -0.21, -0.18, -0.21, -1.38], ['c', 0.00, -1.02, 0.00, -1.11, 0.06, -1.20], ['c', 0.03, -0.06, 0.09, -0.12, 0.12, -0.15], ['c', 0.06, -0.03, 0.42, -0.21, 0.84, -0.36], ['l', 0.78, -0.33], ['l', 0.00, -2.43], ['c', 0.00, -1.32, 0.00, -2.43, -0.03, -2.43], ['c', 0.00, 0.00, -0.27, 0.12, -0.60, 0.24], ['c', -0.69, 0.27, -0.78, 0.30, -0.96, 0.15], ['c', -0.21, -0.15, -0.21, -0.18, -0.21, -1.38], ['c', 0.00, -1.02, 0.00, -1.11, 0.06, -1.20], ['c', 0.03, -0.06, 0.09, -0.12, 0.12, -0.15], ['c', 0.06, -0.03, 0.42, -0.21, 0.84, -0.36], ['l', 0.78, -0.33], ['l', 0.00, -2.52], ['c', 0.00, -2.28, 0.03, -2.55, 0.06, -2.64], ['c', 0.21, -0.36, 0.72, -0.36, 0.93, 0.00], ['c', 0.03, 0.09, 0.06, 0.33, 0.06, 2.43], ['l', 0.03, 2.31], ['l', 1.26, -0.51], ['l', 1.26, -0.51], ['l', 0.00, -2.43], ['c', 0.00, -2.28, 0.00, -2.43, 0.06, -2.55], ['c', 0.06, -0.12, 0.12, -0.18, 0.27, -0.24], ['z'], ['m', -0.33, 10.65], ['l', 0.00, -2.43], ['l', -1.29, 0.51], ['l', -1.26, 0.51], ['l', 0.00, 2.46], ['l', 0.00, 2.43], ['l', 0.09, -0.03], ['c', 0.06, -0.03, 0.63, -0.27, 1.29, -0.51], ['l', 1.17, -0.48], ['l', 0.00, -2.46], ['z']],
w: 8.25,
h: 22.462
},
'accidentals.halfsharp': {
d: [['M', 2.43, -10.05], ['c', 0.21, -0.12, 0.54, -0.03, 0.66, 0.24], ['c', 0.06, 0.12, 0.06, 0.21, 0.06, 2.01], ['c', 0.00, 1.05, 0.00, 1.89, 0.03, 1.89], ['l', 0.72, -0.48], ['c', 0.69, -0.48, 0.69, -0.51, 0.87, -0.51], ['c', 0.15, 0.00, 0.18, 0.03, 0.27, 0.09], ['c', 0.21, 0.15, 0.21, 0.18, 0.21, 1.41], ['c', 0.00, 1.11, -0.03, 1.14, -0.09, 1.23], ['c', -0.03, 0.03, -0.48, 0.39, -1.02, 0.75], ['l', -0.99, 0.66], ['l', 0.00, 2.37], ['c', 0.00, 1.32, 0.00, 2.37, 0.03, 2.37], ['l', 0.72, -0.48], ['c', 0.69, -0.48, 0.69, -0.51, 0.87, -0.51], ['c', 0.15, 0.00, 0.18, 0.03, 0.27, 0.09], ['c', 0.21, 0.15, 0.21, 0.18, 0.21, 1.41], ['c', 0.00, 1.11, -0.03, 1.14, -0.09, 1.23], ['c', -0.03, 0.03, -0.48, 0.39, -1.02, 0.75], ['l', -0.99, 0.66], ['l', 0.00, 2.25], ['c', 0.00, 1.95, 0.00, 2.28, -0.06, 2.37], ['c', -0.06, 0.12, -0.12, 0.21, -0.24, 0.27], ['c', -0.27, 0.12, -0.54, 0.03, -0.69, -0.24], ['c', -0.06, -0.12, -0.06, -0.21, -0.06, -2.01], ['c', 0.00, -1.05, 0.00, -1.89, -0.03, -1.89], ['l', -0.72, 0.48], ['c', -0.69, 0.48, -0.69, 0.48, -0.87, 0.48], ['c', -0.15, 0.00, -0.18, 0.00, -0.27, -0.06], ['c', -0.21, -0.15, -0.21, -0.18, -0.21, -1.41], ['c', 0.00, -1.11, 0.03, -1.14, 0.09, -1.23], ['c', 0.03, -0.03, 0.48, -0.39, 1.02, -0.75], ['l', 0.99, -0.66], ['l', 0.00, -2.37], ['c', 0.00, -1.32, 0.00, -2.37, -0.03, -2.37], ['l', -0.72, 0.48], ['c', -0.69, 0.48, -0.69, 0.48, -0.87, 0.48], ['c', -0.15, 0.00, -0.18, 0.00, -0.27, -0.06], ['c', -0.21, -0.15, -0.21, -0.18, -0.21, -1.41], ['c', 0.00, -1.11, 0.03, -1.14, 0.09, -1.23], ['c', 0.03, -0.03, 0.48, -0.39, 1.02, -0.75], ['l', 0.99, -0.66], ['l', 0.00, -2.25], ['c', 0.00, -2.13, 0.00, -2.28, 0.06, -2.40], ['c', 0.06, -0.12, 0.12, -0.18, 0.27, -0.24], ['z']],
w: 5.25,
h: 20.174
},
'accidentals.nat': {
d: [['M', 0.21, -11.40], ['c', 0.24, -0.06, 0.78, 0.00, 0.99, 0.15], ['c', 0.03, 0.03, 0.03, 0.48, 0.00, 2.61], ['c', -0.03, 1.44, -0.03, 2.61, -0.03, 2.61], ['c', 0.00, 0.03, 0.75, -0.09, 1.68, -0.24], ['c', 0.96, -0.18, 1.71, -0.27, 1.74, -0.27], ['c', 0.15, 0.03, 0.27, 0.15, 0.36, 0.30], ['l', 0.06, 0.12], ['l', 0.09, 8.67], ['c', 0.09, 6.96, 0.12, 8.67, 0.09, 8.67], ['c', -0.03, 0.03, -0.12, 0.06, -0.21, 0.09], ['c', -0.24, 0.09, -0.72, 0.09, -0.96, 0.00], ['c', -0.09, -0.03, -0.18, -0.06, -0.21, -0.09], ['c', -0.03, -0.03, -0.03, -0.48, 0.00, -2.61], ['c', 0.03, -1.44, 0.03, -2.61, 0.03, -2.61], ['c', 0.00, -0.03, -0.75, 0.09, -1.68, 0.24], ['c', -0.96, 0.18, -1.71, 0.27, -1.74, 0.27], ['c', -0.15, -0.03, -0.27, -0.15, -0.36, -0.30], ['l', -0.06, -0.15], ['l', -0.09, -7.53], ['c', -0.06, -4.14, -0.09, -8.04, -0.12, -8.67], ['l', 0.00, -1.11], ['l', 0.15, -0.06], ['c', 0.09, -0.03, 0.21, -0.06, 0.27, -0.09], ['z'], ['m', 3.75, 8.40], ['c', 0.00, -0.33, 0.00, -0.42, -0.03, -0.42], ['c', -0.12, 0.00, -2.79, 0.45, -2.79, 0.48], ['c', -0.03, 0.00, -0.09, 6.30, -0.09, 6.33], ['c', 0.03, 0.00, 2.79, -0.45, 2.82, -0.48], ['c', 0.00, 0.00, 0.09, -4.53, 0.09, -5.91], ['z']],
w: 5.4,
h: 22.8
},
'accidentals.flat': {
d: [['M', -0.36, -14.07], ['c', 0.33, -0.06, 0.87, 0.00, 1.08, 0.15], ['c', 0.06, 0.03, 0.06, 0.36, -0.03, 5.25], ['c', -0.06, 2.85, -0.09, 5.19, -0.09, 5.19], ['c', 0.00, 0.03, 0.12, -0.03, 0.24, -0.12], ['c', 0.63, -0.42, 1.41, -0.66, 2.19, -0.72], ['c', 0.81, -0.03, 1.47, 0.21, 2.04, 0.78], ['c', 0.57, 0.54, 0.87, 1.26, 0.93, 2.04], ['c', 0.03, 0.57, -0.09, 1.08, -0.36, 1.62], ['c', -0.42, 0.81, -1.02, 1.38, -2.82, 2.61], ['c', -1.14, 0.78, -1.44, 1.02, -1.80, 1.44], ['c', -0.18, 0.18, -0.39, 0.39, -0.45, 0.42], ['c', -0.27, 0.18, -0.57, 0.15, -0.81, -0.06], ['c', -0.06, -0.09, -0.12, -0.18, -0.15, -0.27], ['c', -0.03, -0.06, -0.09, -3.27, -0.18, -8.34], ['c', -0.09, -4.53, -0.15, -8.58, -0.18, -9.03], ['l', 0.00, -0.78], ['l', 0.12, -0.06], ['c', 0.06, -0.03, 0.18, -0.09, 0.27, -0.12], ['z'], ['m', 3.18, 11.01], ['c', -0.21, -0.12, -0.54, -0.15, -0.81, -0.06], ['c', -0.54, 0.15, -0.99, 0.63, -1.17, 1.26], ['c', -0.06, 0.30, -0.12, 2.88, -0.06, 3.87], ['c', 0.03, 0.42, 0.03, 0.81, 0.06, 0.90], ['l', 0.03, 0.12], ['l', 0.45, -0.39], ['c', 0.63, -0.54, 1.26, -1.17, 1.56, -1.59], ['c', 0.30, -0.42, 0.60, -0.99, 0.72, -1.41], ['c', 0.18, -0.69, 0.09, -1.47, -0.18, -2.07], ['c', -0.15, -0.30, -0.33, -0.51, -0.60, -0.63], ['z']],
w: 6.75,
h: 18.801
},
'accidentals.halfflat': {
d: [['M', 4.83, -14.07], ['c', 0.33, -0.06, 0.87, 0.00, 1.08, 0.15], ['c', 0.06, 0.03, 0.06, 0.60, -0.12, 9.06], ['c', -0.09, 5.55, -0.15, 9.06, -0.18, 9.12], ['c', -0.03, 0.09, -0.09, 0.18, -0.15, 0.27], ['c', -0.24, 0.21, -0.54, 0.24, -0.81, 0.06], ['c', -0.06, -0.03, -0.27, -0.24, -0.45, -0.42], ['c', -0.36, -0.42, -0.66, -0.66, -1.80, -1.44], ['c', -1.23, -0.84, -1.83, -1.32, -2.25, -1.77], ['c', -0.66, -0.78, -0.96, -1.56, -0.93, -2.46], ['c', 0.09, -1.41, 1.11, -2.58, 2.40, -2.79], ['c', 0.30, -0.06, 0.84, -0.03, 1.23, 0.06], ['c', 0.54, 0.12, 1.08, 0.33, 1.53, 0.63], ['c', 0.12, 0.09, 0.24, 0.15, 0.24, 0.12], ['c', 0.00, 0.00, -0.12, -8.37, -0.18, -9.75], ['l', 0.00, -0.66], ['l', 0.12, -0.06], ['c', 0.06, -0.03, 0.18, -0.09, 0.27, -0.12], ['z'], ['m', -1.65, 10.95], ['c', -0.60, -0.18, -1.08, 0.09, -1.38, 0.69], ['c', -0.27, 0.60, -0.36, 1.38, -0.18, 2.07], ['c', 0.12, 0.42, 0.42, 0.99, 0.72, 1.41], ['c', 0.30, 0.42, 0.93, 1.05, 1.56, 1.59], ['l', 0.48, 0.39], ['l', 0.00, -0.12], ['c', 0.03, -0.09, 0.03, -0.48, 0.06, -0.90], ['c', 0.03, -0.57, 0.03, -1.08, 0.00, -2.22], ['c', -0.03, -1.62, -0.03, -1.62, -0.24, -2.07], ['c', -0.21, -0.42, -0.60, -0.75, -1.02, -0.84], ['z']],
w: 6.728,
h: 18.801
},
'accidentals.dblflat': {
d: [['M', -0.36, -14.07], ['c', 0.33, -0.06, 0.87, 0.00, 1.08, 0.15], ['c', 0.06, 0.03, 0.06, 0.36, -0.03, 5.25], ['c', -0.06, 2.85, -0.09, 5.19, -0.09, 5.19], ['c', 0.00, 0.03, 0.12, -0.03, 0.24, -0.12], ['c', 0.63, -0.42, 1.41, -0.66, 2.19, -0.72], ['c', 0.81, -0.03, 1.47, 0.21, 2.04, 0.78], ['c', 0.57, 0.54, 0.87, 1.26, 0.93, 2.04], ['c', 0.03, 0.57, -0.09, 1.08, -0.36, 1.62], ['c', -0.42, 0.81, -1.02, 1.38, -2.82, 2.61], ['c', -1.14, 0.78, -1.44, 1.02, -1.80, 1.44], ['c', -0.18, 0.18, -0.39, 0.39, -0.45, 0.42], ['c', -0.27, 0.18, -0.57, 0.15, -0.81, -0.06], ['c', -0.06, -0.09, -0.12, -0.18, -0.15, -0.27], ['c', -0.03, -0.06, -0.09, -3.27, -0.18, -8.34], ['c', -0.09, -4.53, -0.15, -8.58, -0.18, -9.03], ['l', 0.00, -0.78], ['l', 0.12, -0.06], ['c', 0.06, -0.03, 0.18, -0.09, 0.27, -0.12], ['z'], ['m', 3.18, 11.01], ['c', -0.21, -0.12, -0.54, -0.15, -0.81, -0.06], ['c', -0.54, 0.15, -0.99, 0.63, -1.17, 1.26], ['c', -0.06, 0.30, -0.12, 2.88, -0.06, 3.87], ['c', 0.03, 0.42, 0.03, 0.81, 0.06, 0.90], ['l', 0.03, 0.12], ['l', 0.45, -0.39], ['c', 0.63, -0.54, 1.26, -1.17, 1.56, -1.59], ['c', 0.30, -0.42, 0.60, -0.99, 0.72, -1.41], ['c', 0.18, -0.69, 0.09, -1.47, -0.18, -2.07], ['c', -0.15, -0.30, -0.33, -0.51, -0.60, -0.63], ['z'], ['m', 3, -11], ['c', 0.33, -0.06, 0.87, 0.00, 1.08, 0.15], ['c', 0.06, 0.03, 0.06, 0.36, -0.03, 5.25], ['c', -0.06, 2.85, -0.09, 5.19, -0.09, 5.19], ['c', 0.00, 0.03, 0.12, -0.03, 0.24, -0.12], ['c', 0.63, -0.42, 1.41, -0.66, 2.19, -0.72], ['c', 0.81, -0.03, 1.47, 0.21, 2.04, 0.78], ['c', 0.57, 0.54, 0.87, 1.26, 0.93, 2.04], ['c', 0.03, 0.57, -0.09, 1.08, -0.36, 1.62], ['c', -0.42, 0.81, -1.02, 1.38, -2.82, 2.61], ['c', -1.14, 0.78, -1.44, 1.02, -1.80, 1.44], ['c', -0.18, 0.18, -0.39, 0.39, -0.45, 0.42], ['c', -0.27, 0.18, -0.57, 0.15, -0.81, -0.06], ['c', -0.06, -0.09, -0.12, -0.18, -0.15, -0.27], ['c', -0.03, -0.06, -0.09, -3.27, -0.18, -8.34], ['c', -0.09, -4.53, -0.15, -8.58, -0.18, -9.03], ['l', 0.00, -0.78], ['l', 0.12, -0.06], ['c', 0.06, -0.03, 0.18, -0.09, 0.27, -0.12], ['z'], ['m', 3.18, 11.01], ['c', -0.21, -0.12, -0.54, -0.15, -0.81, -0.06], ['c', -0.54, 0.15, -0.99, 0.63, -1.17, 1.26], ['c', -0.06, 0.30, -0.12, 2.88, -0.06, 3.87], ['c', 0.03, 0.42, 0.03, 0.81, 0.06, 0.90], ['l', 0.03, 0.12], ['l', 0.45, -0.39], ['c', 0.63, -0.54, 1.26, -1.17, 1.56, -1.59], ['c', 0.30, -0.42, 0.60, -0.99, 0.72, -1.41], ['c', 0.18, -0.69, 0.09, -1.47, -0.18, -2.07], ['c', -0.15, -0.30, -0.33, -0.51, -0.60, -0.63], ['z']],
w: 12.1,
h: 18.804
},
'accidentals.dblsharp': {
d: [['M', -0.18, -3.96], ['c', 0.06, -0.03, 0.12, -0.06, 0.15, -0.06], ['c', 0.09, 0.00, 2.76, 0.27, 2.79, 0.30], ['c', 0.12, 0.03, 0.15, 0.12, 0.15, 0.51], ['c', 0.06, 0.96, 0.24, 1.59, 0.57, 2.10], ['c', 0.06, 0.09, 0.15, 0.21, 0.18, 0.24], ['l', 0.09, 0.06], ['l', 0.09, -0.06], ['c', 0.03, -0.03, 0.12, -0.15, 0.18, -0.24], ['c', 0.33, -0.51, 0.51, -1.14, 0.57, -2.10], ['c', 0.00, -0.39, 0.03, -0.45, 0.12, -0.51], ['c', 0.03, 0.00, 0.66, -0.09, 1.44, -0.15], ['c', 1.47, -0.15, 1.50, -0.15, 1.56, -0.03], ['c', 0.03, 0.06, 0.00, 0.42, -0.09, 1.44], ['c', -0.09, 0.72, -0.15, 1.35, -0.15, 1.38], ['c', 0.00, 0.03, -0.03, 0.09, -0.06, 0.12], ['c', -0.06, 0.06, -0.12, 0.09, -0.51, 0.09], ['c', -1.08, 0.06, -1.80, 0.30, -2.28, 0.75], ['l', -0.12, 0.09], ['l', 0.09, 0.09], ['c', 0.12, 0.15, 0.39, 0.33, 0.63, 0.45], ['c', 0.42, 0.18, 0.96, 0.27, 1.68, 0.33], ['c', 0.39, 0.00, 0.45, 0.03, 0.51, 0.09], ['c', 0.03, 0.03, 0.06, 0.09, 0.06, 0.12], ['c', 0.00, 0.03, 0.06, 0.66, 0.15, 1.38], ['c', 0.09, 1.02, 0.12, 1.38, 0.09, 1.44], ['c', -0.06, 0.12, -0.09, 0.12, -1.56, -0.03], ['c', -0.78, -0.06, -1.41, -0.15, -1.44, -0.15], ['c', -0.09, -0.06, -0.12, -0.12, -0.12, -0.54], ['c', -0.06, -0.93, -0.24, -1.56, -0.57, -2.07], ['c', -0.06, -0.09, -0.15, -0.21, -0.18, -0.24], ['l', -0.09, -0.06], ['l', -0.09, 0.06], ['c', -0.03, 0.03, -0.12, 0.15, -0.18, 0.24], ['c', -0.33, 0.51, -0.51, 1.14, -0.57, 2.07], ['c', 0.00, 0.42, -0.03, 0.48, -0.12, 0.54], ['c', -0.03, 0.00, -0.66, 0.09, -1.44, 0.15], ['c', -1.47, 0.15, -1.50, 0.15, -1.56, 0.03], ['c', -0.03, -0.06, 0.00, -0.42, 0.09, -1.44], ['c', 0.09, -0.72, 0.15, -1.35, 0.15, -1.38], ['c', 0.00, -0.03, 0.03, -0.09, 0.06, -0.12], ['c', 0.06, -0.06, 0.12, -0.09, 0.51, -0.09], ['c', 0.72, -0.06, 1.26, -0.15, 1.68, -0.33], ['c', 0.24, -0.12, 0.51, -0.30, 0.63, -0.45], ['l', 0.09, -0.09], ['l', -0.12, -0.09], ['c', -0.48, -0.45, -1.20, -0.69, -2.28, -0.75], ['c', -0.39, 0.00, -0.45, -0.03, -0.51, -0.09], ['c', -0.03, -0.03, -0.06, -0.09, -0.06, -0.12], ['c', 0.00, -0.03, -0.06, -0.63, -0.12, -1.38], ['c', -0.09, -0.72, -0.15, -1.35, -0.15, -1.38], ['z']],
w: 7.95,
h: 7.977
},
'dots.dot': {
d: [['M', 1.32, -1.68], ['c', 0.09, -0.03, 0.27, -0.06, 0.39, -0.06], ['c', 0.96, 0.00, 1.74, 0.78, 1.74, 1.71], ['c', 0.00, 0.96, -0.78, 1.74, -1.71, 1.74], ['c', -0.96, 0.00, -1.74, -0.78, -1.74, -1.71], ['c', 0.00, -0.78, 0.54, -1.50, 1.32, -1.68], ['z']],
w: 3.45,
h: 3.45
},
'noteheads.dbl': {
d: [['M', -0.69, -4.02], ['c', 0.18, -0.09, 0.36, -0.09, 0.54, 0.00], ['c', 0.18, 0.09, 0.24, 0.15, 0.33, 0.30], ['c', 0.06, 0.15, 0.06, 0.18, 0.06, 1.41], ['l', 0.00, 1.23], ['l', 0.12, -0.18], ['c', 0.72, -1.26, 2.64, -2.31, 4.86, -2.64], ['c', 0.81, -0.15, 1.11, -0.15, 2.13, -0.15], ['c', 0.99, 0.00, 1.29, 0.00, 2.10, 0.15], ['c', 0.75, 0.12, 1.38, 0.27, 2.04, 0.54], ['c', 1.35, 0.51, 2.34, 1.26, 2.82, 2.10], ['l', 0.12, 0.18], ['l', 0.00, -1.23], ['c', 0.00, -1.20, 0.00, -1.26, 0.06, -1.38], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['c', 0.18, -0.09, 0.36, -0.09, 0.54, 0.00], ['c', 0.18, 0.09, 0.24, 0.15, 0.33, 0.30], ['l', 0.06, 0.15], ['l', 0.00, 3.54], ['l', 0.00, 3.54], ['l', -0.06, 0.15], ['c', -0.09, 0.18, -0.15, 0.24, -0.33, 0.33], ['c', -0.18, 0.09, -0.36, 0.09, -0.54, 0.00], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['c', -0.06, -0.12, -0.06, -0.18, -0.06, -1.38], ['l', 0.00, -1.23], ['l', -0.12, 0.18], ['c', -0.48, 0.84, -1.47, 1.59, -2.82, 2.10], ['c', -0.84, 0.33, -1.71, 0.54, -2.85, 0.66], ['c', -0.45, 0.06, -2.16, 0.06, -2.61, 0.00], ['c', -1.14, -0.12, -2.01, -0.33, -2.85, -0.66], ['c', -1.35, -0.51, -2.34, -1.26, -2.82, -2.10], ['l', -0.12, -0.18], ['l', 0.00, 1.23], ['c', 0.00, 1.23, 0.00, 1.26, -0.06, 1.38], ['c', -0.09, 0.18, -0.15, 0.24, -0.33, 0.33], ['c', -0.18, 0.09, -0.36, 0.09, -0.54, 0.00], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['l', -0.06, -0.15], ['l', 0.00, -3.54], ['c', 0.00, -3.48, 0.00, -3.54, 0.06, -3.66], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['z'], ['m', 7.71, 0.63], ['c', -0.36, -0.06, -0.90, -0.06, -1.14, 0.00], ['c', -0.30, 0.03, -0.66, 0.24, -0.87, 0.42], ['c', -0.60, 0.54, -0.90, 1.62, -0.75, 2.82], ['c', 0.12, 0.93, 0.51, 1.68, 1.11, 2.31], ['c', 0.75, 0.72, 1.83, 1.20, 2.85, 1.26], ['c', 1.05, 0.06, 1.83, -0.54, 2.10, -1.65], ['c', 0.21, -0.90, 0.12, -1.95, -0.24, -2.82], ['c', -0.36, -0.81, -1.08, -1.53, -1.95, -1.95], ['c', -0.30, -0.15, -0.78, -0.30, -1.11, -0.39], ['z']],
w: 16.83,
h: 8.145
},
'noteheads.whole': {
d: [['M', 6.51, -4.05], ['c', 0.51, -0.03, 2.01, 0.00, 2.52, 0.03], ['c', 1.41, 0.18, 2.64, 0.51, 3.72, 1.08], ['c', 1.20, 0.63, 1.95, 1.41, 2.19, 2.31], ['c', 0.09, 0.33, 0.09, 0.90, 0.00, 1.23], ['c', -0.24, 0.90, -0.99, 1.68, -2.19, 2.31], ['c', -1.08, 0.57, -2.28, 0.90, -3.75, 1.08], ['c', -0.66, 0.06, -2.31, 0.06, -2.97, 0.00], ['c', -1.47, -0.18, -2.67, -0.51, -3.75, -1.08], ['c', -1.20, -0.63, -1.95, -1.41, -2.19, -2.31], ['c', -0.09, -0.33, -0.09, -0.90, 0.00, -1.23], ['c', 0.24, -0.90, 0.99, -1.68, 2.19, -2.31], ['c', 1.20, -0.63, 2.61, -0.99, 4.23, -1.11], ['z'], ['m', 0.57, 0.66], ['c', -0.87, -0.15, -1.53, 0.00, -2.04, 0.51], ['c', -0.15, 0.15, -0.24, 0.27, -0.33, 0.48], ['c', -0.24, 0.51, -0.36, 1.08, -0.33, 1.77], ['c', 0.03, 0.69, 0.18, 1.26, 0.42, 1.77], ['c', 0.60, 1.17, 1.74, 1.98, 3.18, 2.22], ['c', 1.11, 0.21, 1.95, -0.15, 2.34, -0.99], ['c', 0.24, -0.51, 0.36, -1.08, 0.33, -1.80], ['c', -0.06, -1.11, -0.45, -2.04, -1.17, -2.76], ['c', -0.63, -0.63, -1.47, -1.05, -2.40, -1.20], ['z']],
w: 14.985,
h: 8.097
},
'noteheads.half': {
d: [['M', 7.44, -4.05], ['c', 0.06, -0.03, 0.27, -0.03, 0.48, -0.03], ['c', 1.05, 0.00, 1.71, 0.24, 2.10, 0.81], ['c', 0.42, 0.60, 0.45, 1.35, 0.18, 2.40], ['c', -0.42, 1.59, -1.14, 2.73, -2.16, 3.39], ['c', -1.41, 0.93, -3.18, 1.44, -5.40, 1.53], ['c', -1.17, 0.03, -1.89, -0.21, -2.28, -0.81], ['c', -0.42, -0.60, -0.45, -1.35, -0.18, -2.40], ['c', 0.42, -1.59, 1.14, -2.73, 2.16, -3.39], ['c', 0.63, -0.42, 1.23, -0.72, 1.98, -0.96], ['c', 0.90, -0.30, 1.65, -0.42, 3.12, -0.54], ['z'], ['m', 1.29, 0.87], ['c', -0.27, -0.09, -0.63, -0.12, -0.90, -0.03], ['c', -0.72, 0.24, -1.53, 0.69, -3.27, 1.80], ['c', -2.34, 1.50, -3.30, 2.25, -3.57, 2.79], ['c', -0.36, 0.72, -0.06, 1.50, 0.66, 1.77], ['c', 0.24, 0.12, 0.69, 0.09, 0.99, 0.00], ['c', 0.84, -0.30, 1.92, -0.93, 4.14, -2.37], ['c', 1.62, -1.08, 2.37, -1.71, 2.61, -2.19], ['c', 0.36, -0.72, 0.06, -1.50, -0.66, -1.77], ['z']],
w: 10.37,
h: 8.132
},
'noteheads.quarter': {
d: [['M', 6.09, -4.05], ['c', 0.36, -0.03, 1.20, 0.00, 1.53, 0.06], ['c', 1.17, 0.24, 1.89, 0.84, 2.16, 1.83], ['c', 0.06, 0.18, 0.06, 0.30, 0.06, 0.66], ['c', 0.00, 0.45, 0.00, 0.63, -0.15, 1.08], ['c', -0.66, 2.04, -3.06, 3.93, -5.52, 4.38], ['c', -0.54, 0.09, -1.44, 0.09, -1.83, 0.03], ['c', -1.23, -0.27, -1.98, -0.87, -2.25, -1.86], ['c', -0.06, -0.18, -0.06, -0.30, -0.06, -0.66], ['c', 0.00, -0.45, 0.00, -0.63, 0.15, -1.08], ['c', 0.24, -0.78, 0.75, -1.53, 1.44, -2.22], ['c', 1.20, -1.20, 2.85, -2.01, 4.47, -2.22], ['z']],
w: 9.81,
h: 8.094
},
'noteheads.slash.nostem': {
d: [['M', 9.30, -7.77], ['c', 0.06, -0.06, 0.18, -0.06, 1.71, -0.06], ['l', 1.65, 0.00], ['l', 0.09, 0.09], ['c', 0.06, 0.06, 0.06, 0.09, 0.06, 0.15], ['c', -0.03, 0.12, -9.21, 15.24, -9.30, 15.33], ['c', -0.06, 0.06, -0.18, 0.06, -1.71, 0.06], ['l', -1.65, 0.00], ['l', -0.09, -0.09], ['c', -0.06, -0.06, -0.06, -0.09, -0.06, -0.15], ['c', 0.03, -0.12, 9.21, -15.24, 9.30, -15.33], ['z']],
w: 12.81,
h: 15.63
},
'noteheads.indeterminate': {
d: [['M', 0.78, -4.05], ['c', 0.12, -0.03, 0.24, -0.03, 0.36, 0.03], ['c', 0.03, 0.03, 0.93, 0.72, 1.95, 1.56], ['l', 1.86, 1.50], ['l', 1.86, -1.50], ['c', 1.02, -0.84, 1.92, -1.53, 1.95, -1.56], ['c', 0.21, -0.12, 0.33, -0.09, 0.75, 0.24], ['c', 0.30, 0.27, 0.36, 0.36, 0.36, 0.54], ['c', 0.00, 0.03, -0.03, 0.12, -0.06, 0.18], ['c', -0.03, 0.06, -0.90, 0.75, -1.89, 1.56], ['l', -1.80, 1.47], ['c', 0.00, 0.03, 0.81, 0.69, 1.80, 1.50], ['c', 0.99, 0.81, 1.86, 1.50, 1.89, 1.56], ['c', 0.03, 0.06, 0.06, 0.15, 0.06, 0.18], ['c', 0.00, 0.18, -0.06, 0.27, -0.36, 0.54], ['c', -0.42, 0.33, -0.54, 0.36, -0.75, 0.24], ['c', -0.03, -0.03, -0.93, -0.72, -1.95, -1.56], ['l', -1.86, -1.50], ['l', -1.86, 1.50], ['c', -1.02, 0.84, -1.92, 1.53, -1.95, 1.56], ['c', -0.21, 0.12, -0.33, 0.09, -0.75, -0.24], ['c', -0.30, -0.27, -0.36, -0.36, -0.36, -0.54], ['c', 0.00, -0.03, 0.03, -0.12, 0.06, -0.18], ['c', 0.03, -0.06, 0.90, -0.75, 1.89, -1.56], ['l', 1.80, -1.47], ['c', 0.00, -0.03, -0.81, -0.69, -1.80, -1.50], ['c', -0.99, -0.81, -1.86, -1.50, -1.89, -1.56], ['c', -0.06, -0.12, -0.09, -0.21, -0.03, -0.36], ['c', 0.03, -0.09, 0.57, -0.57, 0.72, -0.63], ['z']],
w: 9.843,
h: 8.139
},
'scripts.ufermata': {
d: [['M', -0.75, -10.77], ['c', 0.12, 0.00, 0.45, -0.03, 0.69, -0.03], ['c', 2.91, -0.03, 5.55, 1.53, 7.41, 4.35], ['c', 1.17, 1.71, 1.95, 3.72, 2.43, 6.03], ['c', 0.12, 0.51, 0.12, 0.57, 0.03, 0.69], ['c', -0.12, 0.21, -0.48, 0.27, -0.69, 0.12], ['c', -0.12, -0.09, -0.18, -0.24, -0.27, -0.69], ['c', -0.78, -3.63, -3.42, -6.54, -6.78, -7.38], ['c', -0.78, -0.21, -1.20, -0.24, -2.07, -0.24], ['c', -0.63, 0.00, -0.84, 0.00, -1.20, 0.06], ['c', -1.83, 0.27, -3.42, 1.08, -4.80, 2.37], ['c', -1.41, 1.35, -2.40, 3.21, -2.85, 5.19], ['c', -0.09, 0.45, -0.15, 0.60, -0.27, 0.69], ['c', -0.21, 0.15, -0.57, 0.09, -0.69, -0.12], ['c', -0.09, -0.12, -0.09, -0.18, 0.03, -0.69], ['c', 0.33, -1.62, 0.78, -3.00, 1.47, -4.38], ['c', 1.77, -3.54, 4.44, -5.67, 7.56, -5.97], ['z'], ['m', 0.33, 7.47], ['c', 1.38, -0.30, 2.58, 0.90, 2.31, 2.25], ['c', -0.15, 0.72, -0.78, 1.35, -1.47, 1.50], ['c', -1.38, 0.27, -2.58, -0.93, -2.31, -2.31], ['c', 0.15, -0.69, 0.78, -1.29, 1.47, -1.44], ['z']],
w: 19.748,
h: 11.289
},
'scripts.dfermata': {
d: [['M', -9.63, -0.42], ['c', 0.15, -0.09, 0.36, -0.06, 0.51, 0.03], ['c', 0.12, 0.09, 0.18, 0.24, 0.27, 0.66], ['c', 0.78, 3.66, 3.42, 6.57, 6.78, 7.41], ['c', 0.78, 0.21, 1.20, 0.24, 2.07, 0.24], ['c', 0.63, 0.00, 0.84, 0.00, 1.20, -0.06], ['c', 1.83, -0.27, 3.42, -1.08, 4.80, -2.37], ['c', 1.41, -1.35, 2.40, -3.21, 2.85, -5.22], ['c', 0.09, -0.42, 0.15, -0.57, 0.27, -0.66], ['c', 0.21, -0.15, 0.57, -0.09, 0.69, 0.12], ['c', 0.09, 0.12, 0.09, 0.18, -0.03, 0.69], ['c', -0.33, 1.62, -0.78, 3.00, -1.47, 4.38], ['c', -1.92, 3.84, -4.89, 6.00, -8.31, 6.00], ['c', -3.42, 0.00, -6.39, -2.16, -8.31, -6.00], ['c', -0.48, -0.96, -0.84, -1.92, -1.14, -2.97], ['c', -0.18, -0.69, -0.42, -1.74, -0.42, -1.92], ['c', 0.00, -0.12, 0.09, -0.27, 0.24, -0.33], ['z'], ['m', 9.21, 0.00], ['c', 1.20, -0.27, 2.34, 0.63, 2.34, 1.86], ['c', 0.00, 0.90, -0.66, 1.68, -1.50, 1.89], ['c', -1.38, 0.27, -2.58, -0.93, -2.31, -2.31], ['c', 0.15, -0.69, 0.78, -1.29, 1.47, -1.44], ['z']],
w: 19.744,
h: 11.274
},
'scripts.sforzato': {
d: [['M', -6.45, -3.69], ['c', 0.06, -0.03, 0.15, -0.06, 0.18, -0.06], ['c', 0.06, 0.00, 2.85, 0.72, 6.24, 1.59], ['l', 6.33, 1.65], ['c', 0.33, 0.06, 0.45, 0.21, 0.45, 0.51], ['c', 0.00, 0.30, -0.12, 0.45, -0.45, 0.51], ['l', -6.33, 1.65], ['c', -3.39, 0.87, -6.18, 1.59, -6.21, 1.59], ['c', -0.21, 0.00, -0.48, -0.24, -0.51, -0.45], ['c', 0.00, -0.15, 0.06, -0.36, 0.18, -0.45], ['c', 0.09, -0.06, 0.87, -0.27, 3.84, -1.05], ['c', 2.04, -0.54, 3.84, -0.99, 4.02, -1.02], ['c', 0.15, -0.06, 1.14, -0.24, 2.22, -0.42], ['c', 1.05, -0.18, 1.92, -0.36, 1.92, -0.36], ['c', 0.00, 0.00, -0.87, -0.18, -1.92, -0.36], ['c', -1.08, -0.18, -2.07, -0.36, -2.22, -0.42], ['c', -0.18, -0.03, -1.98, -0.48, -4.02, -1.02], ['c', -2.97, -0.78, -3.75, -0.99, -3.84, -1.05], ['c', -0.12, -0.09, -0.18, -0.30, -0.18, -0.45], ['c', 0.03, -0.15, 0.15, -0.30, 0.30, -0.39], ['z']],
w: 13.5,
h: 7.5
},
'scripts.staccato': {
d: [['M', -0.36, -1.47], ['c', 0.93, -0.21, 1.86, 0.51, 1.86, 1.47], ['c', 0.00, 0.93, -0.87, 1.65, -1.80, 1.47], ['c', -0.54, -0.12, -1.02, -0.57, -1.14, -1.08], ['c', -0.21, -0.81, 0.27, -1.65, 1.08, -1.86], ['z']],
w: 2.989,
h: 3.004
},
'scripts.tenuto': {
d: [['M', -4.20, -0.48], ['l', 0.12, -0.06], ['l', 4.08, 0.00], ['l', 4.08, 0.00], ['l', 0.12, 0.06], ['c', 0.39, 0.21, 0.39, 0.75, 0.00, 0.96], ['l', -0.12, 0.06], ['l', -4.08, 0.00], ['l', -4.08, 0.00], ['l', -0.12, -0.06], ['c', -0.39, -0.21, -0.39, -0.75, 0.00, -0.96], ['z']],
w: 8.985,
h: 1.08
},
'scripts.umarcato': {
d: [['M', -0.15, -8.19], ['c', 0.15, -0.12, 0.36, -0.03, 0.45, 0.15], ['c', 0.21, 0.42, 3.45, 7.65, 3.45, 7.71], ['c', 0.00, 0.12, -0.12, 0.27, -0.21, 0.30], ['c', -0.03, 0.03, -0.51, 0.03, -1.14, 0.03], ['c', -1.05, 0.00, -1.08, 0.00, -1.17, -0.06], ['c', -0.09, -0.06, -0.24, -0.36, -1.17, -2.40], ['c', -0.57, -1.29, -1.05, -2.34, -1.08, -2.34], ['c', 0.00, -0.03, -0.51, 1.02, -1.08, 2.34], ['c', -0.93, 2.07, -1.08, 2.34, -1.14, 2.40], ['c', -0.06, 0.03, -0.15, 0.06, -0.18, 0.06], ['c', -0.15, 0.00, -0.33, -0.18, -0.33, -0.33], ['c', 0.00, -0.06, 3.24, -7.32, 3.45, -7.71], ['c', 0.03, -0.06, 0.09, -0.15, 0.15, -0.15], ['z']],
w: 7.5,
h: 8.245
},
'scripts.dmarcato': {
d: [['M', -3.57, 0.03], ['c', 0.03, 0.00, 0.57, -0.03, 1.17, -0.03], ['c', 1.05, 0.00, 1.08, 0.00, 1.17, 0.06], ['c', 0.09, 0.06, 0.24, 0.36, 1.17, 2.40], ['c', 0.57, 1.29, 1.05, 2.34, 1.08, 2.34], ['c', 0.00, 0.03, 0.51, -1.02, 1.08, -2.34], ['c', 0.93, -2.07, 1.08, -2.34, 1.14, -2.40], ['c', 0.06, -0.03, 0.15, -0.06, 0.18, -0.06], ['c', 0.15, 0.00, 0.33, 0.18, 0.33, 0.33], ['c', 0.00, 0.09, -3.45, 7.74, -3.54, 7.83], ['c', -0.12, 0.12, -0.30, 0.12, -0.42, 0.00], ['c', -0.09, -0.09, -3.54, -7.74, -3.54, -7.83], ['c', 0.00, -0.09, 0.12, -0.27, 0.18, -0.30], ['z']],
w: 7.5,
h: 8.25
},
'scripts.stopped': {
d: [['M', -0.27, -4.08], ['c', 0.18, -0.09, 0.36, -0.09, 0.54, 0.00], ['c', 0.18, 0.09, 0.24, 0.15, 0.33, 0.30], ['l', 0.06, 0.15], ['l', 0.00, 1.50], ['l', 0.00, 1.47], ['l', 1.47, 0.00], ['l', 1.50, 0.00], ['l', 0.15, 0.06], ['c', 0.15, 0.09, 0.21, 0.15, 0.30, 0.33], ['c', 0.09, 0.18, 0.09, 0.36, 0.00, 0.54], ['c', -0.09, 0.18, -0.15, 0.24, -0.33, 0.33], ['c', -0.12, 0.06, -0.18, 0.06, -1.62, 0.06], ['l', -1.47, 0.00], ['l', 0.00, 1.47], ['l', 0.00, 1.47], ['l', -0.06, 0.15], ['c', -0.09, 0.18, -0.15, 0.24, -0.33, 0.33], ['c', -0.18, 0.09, -0.36, 0.09, -0.54, 0.00], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['l', -0.06, -0.15], ['l', 0.00, -1.47], ['l', 0.00, -1.47], ['l', -1.47, 0.00], ['c', -1.44, 0.00, -1.50, 0.00, -1.62, -0.06], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['c', -0.09, -0.18, -0.09, -0.36, 0.00, -0.54], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['l', 0.15, -0.06], ['l', 1.47, 0.00], ['l', 1.47, 0.00], ['l', 0.00, -1.47], ['c', 0.00, -1.44, 0.00, -1.50, 0.06, -1.62], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['z']],
w: 8.295,
h: 8.295
},
'scripts.upbow': {
d: [['M', -4.65, -15.54], ['c', 0.12, -0.09, 0.36, -0.06, 0.48, 0.03], ['c', 0.03, 0.03, 0.09, 0.09, 0.12, 0.15], ['c', 0.03, 0.06, 0.66, 2.13, 1.41, 4.62], ['c', 1.35, 4.41, 1.38, 4.56, 2.01, 6.96], ['l', 0.63, 2.46], ['l', 0.63, -2.46], ['c', 0.63, -2.40, 0.66, -2.55, 2.01, -6.96], ['c', 0.75, -2.49, 1.38, -4.56, 1.41, -4.62], ['c', 0.06, -0.15, 0.18, -0.21, 0.36, -0.24], ['c', 0.15, 0.00, 0.30, 0.06, 0.39, 0.18], ['c', 0.15, 0.21, 0.24, -0.18, -2.10, 7.56], ['c', -1.20, 3.96, -2.22, 7.32, -2.25, 7.41], ['c', 0.00, 0.12, -0.06, 0.27, -0.09, 0.30], ['c', -0.12, 0.21, -0.60, 0.21, -0.72, 0.00], ['c', -0.03, -0.03, -0.09, -0.18, -0.09, -0.30], ['c', -0.03, -0.09, -1.05, -3.45, -2.25, -7.41], ['c', -2.34, -7.74, -2.25, -7.35, -2.10, -7.56], ['c', 0.03, -0.03, 0.09, -0.09, 0.15, -0.12], ['z']],
w: 9.73,
h: 15.608
},
'scripts.downbow': {
d: [['M', -5.55, -9.93], ['l', 0.09, -0.06], ['l', 5.46, 0.00], ['l', 5.46, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 4.77], ['c', 0.00, 5.28, 0.00, 4.89, -0.18, 5.01], ['c', -0.18, 0.12, -0.42, 0.06, -0.54, -0.12], ['c', -0.06, -0.09, -0.06, -0.18, -0.06, -2.97], ['l', 0.00, -2.85], ['l', -4.83, 0.00], ['l', -4.83, 0.00], ['l', 0.00, 2.85], ['c', 0.00, 2.79, 0.00, 2.88, -0.06, 2.97], ['c', -0.15, 0.24, -0.51, 0.24, -0.66, 0.00], ['c', -0.06, -0.09, -0.06, -0.21, -0.06, -4.89], ['l', 0.00, -4.77], ['z']],
w: 11.22,
h: 9.992
},
'scripts.turn': {
d: [['M', -4.77, -3.90], ['c', 0.36, -0.06, 1.05, -0.06, 1.44, 0.03], ['c', 0.78, 0.15, 1.50, 0.51, 2.34, 1.14], ['c', 0.60, 0.45, 1.05, 0.87, 2.22, 2.01], ['c', 1.11, 1.08, 1.62, 1.50, 2.22, 1.86], ['c', 0.60, 0.36, 1.32, 0.57, 1.92, 0.57], ['c', 0.90, 0.00, 1.71, -0.57, 1.89, -1.35], ['c', 0.24, -0.93, -0.39, -1.89, -1.35, -2.10], ['l', -0.15, -0.06], ['l', -0.09, 0.15], ['c', -0.03, 0.09, -0.15, 0.24, -0.24, 0.33], ['c', -0.72, 0.72, -2.04, 0.54, -2.49, -0.36], ['c', -0.48, -0.93, 0.03, -1.86, 1.17, -2.19], ['c', 0.30, -0.09, 1.02, -0.09, 1.35, 0.00], ['c', 0.99, 0.27, 1.74, 0.87, 2.25, 1.83], ['c', 0.69, 1.41, 0.63, 3.00, -0.21, 4.26], ['c', -0.21, 0.30, -0.69, 0.81, -0.99, 1.02], ['c', -0.30, 0.21, -0.84, 0.45, -1.17, 0.54], ['c', -1.23, 0.36, -2.49, 0.15, -3.72, -0.60], ['c', -0.75, -0.48, -1.41, -1.02, -2.85, -2.46], ['c', -1.11, -1.08, -1.62, -1.50, -2.22, -1.86], ['c', -0.60, -0.36, -1.32, -0.57, -1.92, -0.57], ['c', -0.90, 0.00, -1.71, 0.57, -1.89, 1.35], ['c', -0.24, 0.93, 0.39, 1.89, 1.35, 2.10], ['l', 0.15, 0.06], ['l', 0.09, -0.15], ['c', 0.03, -0.09, 0.15, -0.24, 0.24, -0.33], ['c', 0.72, -0.72, 2.04, -0.54, 2.49, 0.36], ['c', 0.48, 0.93, -0.03, 1.86, -1.17, 2.19], ['c', -0.30, 0.09, -1.02, 0.09, -1.35, 0.00], ['c', -0.99, -0.27, -1.74, -0.87, -2.25, -1.83], ['c', -0.69, -1.41, -0.63, -3.00, 0.21, -4.26], ['c', 0.21, -0.30, 0.69, -0.81, 0.99, -1.02], ['c', 0.48, -0.33, 1.11, -0.57, 1.74, -0.66], ['z']],
w: 16.366,
h: 7.893
},
'scripts.trill': {
d: [['M', -0.51, -16.02], ['c', 0.12, -0.09, 0.21, -0.18, 0.21, -0.18], ['l', -0.81, 4.02], ['l', -0.81, 4.02], ['c', 0.03, 0.00, 0.51, -0.27, 1.08, -0.60], ['c', 0.60, -0.30, 1.14, -0.63, 1.26, -0.66], ['c', 1.14, -0.54, 2.31, -0.60, 3.09, -0.18], ['c', 0.27, 0.15, 0.54, 0.36, 0.60, 0.51], ['l', 0.06, 0.12], ['l', 0.21, -0.21], ['c', 0.90, -0.81, 2.22, -0.99, 3.12, -0.42], ['c', 0.60, 0.42, 0.90, 1.14, 0.78, 2.07], ['c', -0.15, 1.29, -1.05, 2.31, -1.95, 2.25], ['c', -0.48, -0.03, -0.78, -0.30, -0.96, -0.81], ['c', -0.09, -0.27, -0.09, -0.90, -0.03, -1.20], ['c', 0.21, -0.75, 0.81, -1.23, 1.59, -1.32], ['l', 0.24, -0.03], ['l', -0.09, -0.12], ['c', -0.51, -0.66, -1.62, -0.63, -2.31, 0.03], ['c', -0.39, 0.42, -0.30, 0.09, -1.23, 4.77], ['l', -0.81, 4.14], ['c', -0.03, 0.00, -0.12, -0.03, -0.21, -0.09], ['c', -0.33, -0.15, -0.54, -0.18, -0.99, -0.18], ['c', -0.42, 0.00, -0.66, 0.03, -1.05, 0.18], ['c', -0.12, 0.06, -0.21, 0.09, -0.21, 0.09], ['c', 0.00, -0.03, 0.36, -1.86, 0.81, -4.11], ['c', 0.90, -4.47, 0.87, -4.26, 0.69, -4.53], ['c', -0.21, -0.36, -0.66, -0.51, -1.17, -0.36], ['c', -0.15, 0.06, -2.22, 1.14, -2.58, 1.38], ['c', -0.12, 0.09, -0.12, 0.09, -0.21, 0.60], ['l', -0.09, 0.51], ['l', 0.21, 0.24], ['c', 0.63, 0.75, 1.02, 1.47, 1.20, 2.19], ['c', 0.06, 0.27, 0.06, 0.36, 0.06, 0.81], ['c', 0.00, 0.42, 0.00, 0.54, -0.06, 0.78], ['c', -0.15, 0.54, -0.33, 0.93, -0.63, 1.35], ['c', -0.18, 0.24, -0.57, 0.63, -0.81, 0.78], ['c', -0.24, 0.15, -0.63, 0.36, -0.84, 0.42], ['c', -0.27, 0.06, -0.66, 0.06, -0.87, 0.03], ['c', -0.81, -0.18, -1.32, -1.05, -1.38, -2.46], ['c', -0.03, -0.60, 0.03, -0.99, 0.33, -2.46], ['c', 0.21, -1.08, 0.24, -1.32, 0.21, -1.29], ['c', -1.20, 0.48, -2.40, 0.75, -3.21, 0.72], ['c', -0.69, -0.06, -1.17, -0.30, -1.41, -0.72], ['c', -0.39, -0.75, -0.12, -1.80, 0.66, -2.46], ['c', 0.24, -0.18, 0.69, -0.42, 1.02, -0.51], ['c', 0.69, -0.18, 1.53, -0.15, 2.31, 0.09], ['c', 0.30, 0.09, 0.75, 0.30, 0.99, 0.45], ['c', 0.12, 0.09, 0.15, 0.09, 0.15, 0.03], ['c', 0.03, -0.03, 0.33, -1.59, 0.72, -3.45], ['c', 0.36, -1.86, 0.66, -3.42, 0.69, -3.45], ['c', 0.00, -0.03, 0.03, -0.03, 0.21, 0.03], ['c', 0.21, 0.06, 0.27, 0.06, 0.48, 0.06], ['c', 0.42, -0.03, 0.78, -0.18, 1.26, -0.48], ['c', 0.15, -0.12, 0.36, -0.27, 0.48, -0.39], ['z'], ['m', -5.73, 7.68], ['c', -0.27, -0.03, -0.96, -0.06, -1.20, -0.03], ['c', -0.81, 0.12, -1.35, 0.57, -1.50, 1.20], ['c', -0.18, 0.66, 0.12, 1.14, 0.75, 1.29], ['c', 0.66, 0.12, 1.92, -0.12, 3.18, -0.66], ['l', 0.33, -0.15], ['l', 0.09, -0.39], ['c', 0.06, -0.21, 0.09, -0.42, 0.09, -0.45], ['c', 0.00, -0.03, -0.45, -0.30, -0.75, -0.45], ['c', -0.27, -0.15, -0.66, -0.27, -0.99, -0.36], ['z'], ['m', 4.29, 3.63], ['c', -0.24, -0.39, -0.51, -0.75, -0.51, -0.69], ['c', -0.06, 0.12, -0.39, 1.92, -0.45, 2.28], ['c', -0.09, 0.54, -0.12, 1.14, -0.06, 1.38], ['c', 0.06, 0.42, 0.21, 0.60, 0.51, 0.57], ['c', 0.39, -0.06, 0.75, -0.48, 0.93, -1.14], ['c', 0.09, -0.33, 0.09, -1.05, 0.00, -1.38], ['c', -0.09, -0.39, -0.24, -0.69, -0.42, -1.02], ['z']],
w: 17.963,
h: 16.49
},
'scripts.segno': {
d: [['M', -3.72, -11.22], ['c', 0.78, -0.09, 1.59, 0.03, 2.31, 0.42], ['c', 1.20, 0.60, 2.01, 1.71, 2.31, 3.09], ['c', 0.09, 0.42, 0.09, 1.20, 0.03, 1.50], ['c', -0.15, 0.45, -0.39, 0.81, -0.66, 0.93], ['c', -0.33, 0.18, -0.84, 0.21, -1.23, 0.15], ['c', -0.81, -0.18, -1.32, -0.93, -1.26, -1.89], ['c', 0.03, -0.36, 0.09, -0.57, 0.24, -0.90], ['c', 0.15, -0.33, 0.45, -0.60, 0.72, -0.75], ['c', 0.12, -0.06, 0.18, -0.09, 0.18, -0.12], ['c', 0.00, -0.03, -0.03, -0.15, -0.09, -0.24], ['c', -0.18, -0.45, -0.54, -0.87, -0.96, -1.08], ['c', -1.11, -0.57, -2.34, -0.18, -2.88, 0.90], ['c', -0.24, 0.51, -0.33, 1.11, -0.24, 1.83], ['c', 0.27, 1.92, 1.50, 3.54, 3.93, 5.13], ['c', 0.48, 0.33, 1.26, 0.78, 1.29, 0.78], ['c', 0.03, 0.00, 1.35, -2.19, 2.94, -4.89], ['l', 2.88, -4.89], ['l', 0.84, 0.00], ['l', 0.87, 0.00], ['l', -0.03, 0.06], ['c', -0.15, 0.21, -6.15, 10.41, -6.15, 10.44], ['c', 0.00, 0.00, 0.21, 0.15, 0.48, 0.27], ['c', 2.61, 1.47, 4.35, 3.03, 5.13, 4.65], ['c', 1.14, 2.34, 0.51, 5.07, -1.44, 6.39], ['c', -0.66, 0.42, -1.32, 0.63, -2.13, 0.69], ['c', -2.01, 0.09, -3.81, -1.41, -4.26, -3.54], ['c', -0.09, -0.42, -0.09, -1.20, -0.03, -1.50], ['c', 0.15, -0.45, 0.39, -0.81, 0.66, -0.93], ['c', 0.33, -0.18, 0.84, -0.21, 1.23, -0.15], ['c', 0.81, 0.18, 1.32, 0.93, 1.26, 1.89], ['c', -0.03, 0.36, -0.09, 0.57, -0.24, 0.90], ['c', -0.15, 0.33, -0.45, 0.60, -0.72, 0.75], ['c', -0.12, 0.06, -0.18, 0.09, -0.18, 0.12], ['c', 0.00, 0.03, 0.03, 0.15, 0.09, 0.24], ['c', 0.18, 0.45, 0.54, 0.87, 0.96, 1.08], ['c', 1.11, 0.57, 2.34, 0.18, 2.88, -0.90], ['c', 0.24, -0.51, 0.33, -1.11, 0.24, -1.83], ['c', -0.27, -1.92, -1.50, -3.54, -3.93, -5.13], ['c', -0.48, -0.33, -1.26, -0.78, -1.29, -0.78], ['c', -0.03, 0.00, -1.35, 2.19, -2.91, 4.89], ['l', -2.88, 4.89], ['l', -0.87, 0.00], ['l', -0.87, 0.00], ['l', 0.03, -0.06], ['c', 0.15, -0.21, 6.15, -10.41, 6.15, -10.44], ['c', 0.00, 0.00, -0.21, -0.15, -0.48, -0.30], ['c', -2.61, -1.44, -4.35, -3.00, -5.13, -4.62], ['c', -0.90, -1.89, -0.72, -4.02, 0.48, -5.52], ['c', 0.69, -0.84, 1.68, -1.41, 2.73, -1.53], ['z'], ['m', 8.76, 9.09], ['c', 0.03, -0.03, 0.15, -0.03, 0.27, -0.03], ['c', 0.33, 0.03, 0.57, 0.18, 0.72, 0.48], ['c', 0.09, 0.18, 0.09, 0.57, 0.00, 0.75], ['c', -0.09, 0.18, -0.21, 0.30, -0.36, 0.39], ['c', -0.15, 0.06, -0.21, 0.06, -0.39, 0.06], ['c', -0.21, 0.00, -0.27, 0.00, -0.39, -0.06], ['c', -0.30, -0.15, -0.48, -0.45, -0.48, -0.75], ['c', 0.00, -0.39, 0.24, -0.72, 0.63, -0.84], ['z'], ['m', -10.53, 2.61], ['c', 0.03, -0.03, 0.15, -0.03, 0.27, -0.03], ['c', 0.33, 0.03, 0.57, 0.18, 0.72, 0.48], ['c', 0.09, 0.18, 0.09, 0.57, 0.00, 0.75], ['c', -0.09, 0.18, -0.21, 0.30, -0.36, 0.39], ['c', -0.15, 0.06, -0.21, 0.06, -0.39, 0.06], ['c', -0.21, 0.00, -0.27, 0.00, -0.39, -0.06], ['c', -0.30, -0.15, -0.48, -0.45, -0.48, -0.75], ['c', 0.00, -0.39, 0.24, -0.72, 0.63, -0.84], ['z']],
w: 15,
h: 22.504
},
'scripts.coda': {
d: [['M', -0.21, -10.47], ['c', 0.18, -0.12, 0.42, -0.06, 0.54, 0.12], ['c', 0.06, 0.09, 0.06, 0.18, 0.06, 1.50], ['l', 0.00, 1.38], ['l', 0.18, 0.00], ['c', 0.39, 0.06, 0.96, 0.24, 1.38, 0.48], ['c', 1.68, 0.93, 2.82, 3.24, 3.03, 6.12], ['c', 0.03, 0.24, 0.03, 0.45, 0.03, 0.45], ['c', 0.00, 0.03, 0.60, 0.03, 1.35, 0.03], ['c', 1.50, 0.00, 1.47, 0.00, 1.59, 0.18], ['c', 0.09, 0.12, 0.09, 0.30, 0.00, 0.42], ['c', -0.12, 0.18, -0.09, 0.18, -1.59, 0.18], ['c', -0.75, 0.00, -1.35, 0.00, -1.35, 0.03], ['c', 0.00, 0.00, 0.00, 0.21, -0.03, 0.42], ['c', -0.24, 3.15, -1.53, 5.58, -3.45, 6.36], ['c', -0.27, 0.12, -0.72, 0.24, -0.96, 0.27], ['l', -0.18, 0.00], ['l', 0.00, 1.38], ['c', 0.00, 1.32, 0.00, 1.41, -0.06, 1.50], ['c', -0.15, 0.24, -0.51, 0.24, -0.66, 0.00], ['c', -0.06, -0.09, -0.06, -0.18, -0.06, -1.50], ['l', 0.00, -1.38], ['l', -0.18, 0.00], ['c', -0.39, -0.06, -0.96, -0.24, -1.38, -0.48], ['c', -1.68, -0.93, -2.82, -3.24, -3.03, -6.15], ['c', -0.03, -0.21, -0.03, -0.42, -0.03, -0.42], ['c', 0.00, -0.03, -0.60, -0.03, -1.35, -0.03], ['c', -1.50, 0.00, -1.47, 0.00, -1.59, -0.18], ['c', -0.09, -0.12, -0.09, -0.30, 0.00, -0.42], ['c', 0.12, -0.18, 0.09, -0.18, 1.59, -0.18], ['c', 0.75, 0.00, 1.35, 0.00, 1.35, -0.03], ['c', 0.00, 0.00, 0.00, -0.21, 0.03, -0.45], ['c', 0.24, -3.12, 1.53, -5.55, 3.45, -6.33], ['c', 0.27, -0.12, 0.72, -0.24, 0.96, -0.27], ['l', 0.18, 0.00], ['l', 0.00, -1.38], ['c', 0.00, -1.53, 0.00, -1.50, 0.18, -1.62], ['z'], ['m', -0.18, 6.93], ['c', 0.00, -2.97, 0.00, -3.15, -0.06, -3.15], ['c', -0.09, 0.00, -0.51, 0.15, -0.66, 0.21], ['c', -0.87, 0.51, -1.38, 1.62, -1.56, 3.51], ['c', -0.06, 0.54, -0.12, 1.59, -0.12, 2.16], ['l', 0.00, 0.42], ['l', 1.20, 0.00], ['l', 1.20, 0.00], ['l', 0.00, -3.15], ['z'], ['m', 1.17, -3.06], ['c', -0.09, -0.03, -0.21, -0.06, -0.27, -0.09], ['l', -0.12, 0.00], ['l', 0.00, 3.15], ['l', 0.00, 3.15], ['l', 1.20, 0.00], ['l', 1.20, 0.00], ['l', 0.00, -0.81], ['c', -0.06, -2.40, -0.33, -3.69, -0.93, -4.59], ['c', -0.27, -0.39, -0.66, -0.69, -1.08, -0.81], ['z'], ['m', -1.17, 10.14], ['l', 0.00, -3.15], ['l', -1.20, 0.00], ['l', -1.20, 0.00], ['l', 0.00, 0.81], ['c', 0.03, 0.96, 0.06, 1.47, 0.15, 2.13], ['c', 0.24, 2.04, 0.96, 3.12, 2.13, 3.36], ['l', 0.12, 0.00], ['l', 0.00, -3.15], ['z'], ['m', 3.18, -2.34], ['l', 0.00, -0.81], ['l', -1.20, 0.00], ['l', -1.20, 0.00], ['l', 0.00, 3.15], ['l', 0.00, 3.15], ['l', 0.12, 0.00], ['c', 1.17, -0.24, 1.89, -1.32, 2.13, -3.36], ['c', 0.09, -0.66, 0.12, -1.17, 0.15, -2.13], ['z']],
w: 16.035,
h: 21.062
},
'scripts.comma': {
d: [['M', 1.14, -4.62], ['c', 0.30, -0.12, 0.69, -0.03, 0.93, 0.15], ['c', 0.12, 0.12, 0.36, 0.45, 0.51, 0.78], ['c', 0.90, 1.77, 0.54, 4.05, -1.08, 6.75], ['c', -0.36, 0.63, -0.87, 1.38, -0.96, 1.44], ['c', -0.18, 0.12, -0.42, 0.06, -0.54, -0.12], ['c', -0.09, -0.18, -0.09, -0.30, 0.12, -0.60], ['c', 0.96, -1.44, 1.44, -2.97, 1.38, -4.35], ['c', -0.06, -0.93, -0.30, -1.68, -0.78, -2.46], ['c', -0.27, -0.39, -0.33, -0.63, -0.24, -0.96], ['c', 0.09, -0.27, 0.36, -0.54, 0.66, -0.63], ['z']],
w: 3.042,
h: 9.237
},
'scripts.roll': {
d: [['M', 1.95, -6.00], ['c', 0.21, -0.09, 0.36, -0.09, 0.57, 0.00], ['c', 0.39, 0.15, 0.63, 0.39, 1.47, 1.35], ['c', 0.66, 0.75, 0.78, 0.87, 1.08, 1.05], ['c', 0.75, 0.45, 1.65, 0.42, 2.40, -0.06], ['c', 0.12, -0.09, 0.27, -0.27, 0.54, -0.60], ['c', 0.42, -0.54, 0.51, -0.63, 0.69, -0.63], ['c', 0.09, 0.00, 0.30, 0.12, 0.36, 0.21], ['c', 0.09, 0.12, 0.12, 0.30, 0.03, 0.42], ['c', -0.06, 0.12, -3.15, 3.90, -3.30, 4.08], ['c', -0.06, 0.06, -0.18, 0.12, -0.27, 0.18], ['c', -0.27, 0.12, -0.60, 0.06, -0.99, -0.27], ['c', -0.27, -0.21, -0.42, -0.39, -1.08, -1.14], ['c', -0.63, -0.72, -0.81, -0.90, -1.17, -1.08], ['c', -0.36, -0.18, -0.57, -0.21, -0.99, -0.21], ['c', -0.39, 0.00, -0.63, 0.03, -0.93, 0.18], ['c', -0.36, 0.15, -0.51, 0.27, -0.90, 0.81], ['c', -0.24, 0.27, -0.45, 0.51, -0.48, 0.54], ['c', -0.12, 0.09, -0.27, 0.06, -0.39, 0.00], ['c', -0.24, -0.15, -0.33, -0.39, -0.21, -0.60], ['c', 0.09, -0.12, 3.18, -3.87, 3.33, -4.02], ['c', 0.06, -0.06, 0.18, -0.15, 0.24, -0.21], ['z']],
w: 10.817,
h: 6.125
},
'scripts.prall': {
d: [['M', -4.38, -3.69], ['c', 0.06, -0.03, 0.18, -0.06, 0.24, -0.06], ['c', 0.30, 0.00, 0.27, -0.03, 1.89, 1.95], ['l', 1.53, 1.83], ['c', 0.03, 0.00, 0.57, -0.84, 1.23, -1.83], ['c', 1.14, -1.68, 1.23, -1.83, 1.35, -1.89], ['c', 0.06, -0.03, 0.18, -0.06, 0.24, -0.06], ['c', 0.30, 0.00, 0.27, -0.03, 1.89, 1.95], ['l', 1.53, 1.83], ['l', 0.48, -0.69], ['c', 0.51, -0.78, 0.54, -0.84, 0.69, -0.90], ['c', 0.42, -0.18, 0.87, 0.15, 0.81, 0.60], ['c', -0.03, 0.12, -0.30, 0.51, -1.50, 2.37], ['c', -1.38, 2.07, -1.50, 2.22, -1.62, 2.28], ['c', -0.06, 0.03, -0.18, 0.06, -0.24, 0.06], ['c', -0.30, 0.00, -0.27, 0.03, -1.89, -1.95], ['l', -1.53, -1.83], ['c', -0.03, 0.00, -0.57, 0.84, -1.23, 1.83], ['c', -1.14, 1.68, -1.23, 1.83, -1.35, 1.89], ['c', -0.06, 0.03, -0.18, 0.06, -0.24, 0.06], ['c', -0.30, 0.00, -0.27, 0.03, -1.89, -1.95], ['l', -1.53, -1.83], ['l', -0.48, 0.69], ['c', -0.51, 0.78, -0.54, 0.84, -0.69, 0.90], ['c', -0.42, 0.18, -0.87, -0.15, -0.81, -0.60], ['c', 0.03, -0.12, 0.30, -0.51, 1.50, -2.37], ['c', 1.38, -2.07, 1.50, -2.22, 1.62, -2.28], ['z']],
w: 15.011,
h: 7.5
},
'scripts.arpeggio': {
d: [['M', 1.5, 0], ['c', 1.5, 2, 1.5, 3, 1.5, 3], ['s', 0, 1, -2, 1.5], ['s', -0.5, 3, 1, 5.5], ['l', 1.5, 0], ['s', -1.75, -2, -1.9, -3.25], ['s', 2.15, -0.6, 2.95, -1.6], ['s', 0.45, -1, 0.5, -1.25], ['s', 0, -1, -2, -3.9], ['l', -1.5, 0], ['z']],
w: 5,
h: 10
},
'scripts.mordent': {
d: [['M', -0.21, -4.95], ['c', 0.27, -0.15, 0.63, 0.00, 0.75, 0.27], ['c', 0.06, 0.12, 0.06, 0.24, 0.06, 1.44], ['l', 0.00, 1.29], ['l', 0.57, -0.84], ['c', 0.51, -0.75, 0.57, -0.84, 0.69, -0.90], ['c', 0.06, -0.03, 0.18, -0.06, 0.24, -0.06], ['c', 0.30, 0.00, 0.27, -0.03, 1.89, 1.95], ['l', 1.53, 1.83], ['l', 0.48, -0.69], ['c', 0.51, -0.78, 0.54, -0.84, 0.69, -0.90], ['c', 0.42, -0.18, 0.87, 0.15, 0.81, 0.60], ['c', -0.03, 0.12, -0.30, 0.51, -1.50, 2.37], ['c', -1.38, 2.07, -1.50, 2.22, -1.62, 2.28], ['c', -0.06, 0.03, -0.18, 0.06, -0.24, 0.06], ['c', -0.30, 0.00, -0.27, 0.03, -1.83, -1.89], ['c', -0.81, -0.99, -1.50, -1.80, -1.53, -1.86], ['c', -0.06, -0.03, -0.06, -0.03, -0.12, 0.03], ['c', -0.06, 0.06, -0.06, 0.15, -0.06, 2.28], ['c', 0.00, 1.95, 0.00, 2.25, -0.06, 2.34], ['c', -0.18, 0.45, -0.81, 0.48, -1.05, 0.03], ['c', -0.03, -0.06, -0.06, -0.24, -0.06, -1.41], ['l', 0.00, -1.35], ['l', -0.57, 0.84], ['c', -0.54, 0.78, -0.60, 0.87, -0.72, 0.93], ['c', -0.06, 0.03, -0.18, 0.06, -0.24, 0.06], ['c', -0.30, 0.00, -0.27, 0.03, -1.89, -1.95], ['l', -1.53, -1.83], ['l', -0.48, 0.69], ['c', -0.51, 0.78, -0.54, 0.84, -0.69, 0.90], ['c', -0.42, 0.18, -0.87, -0.15, -0.81, -0.60], ['c', 0.03, -0.12, 0.30, -0.51, 1.50, -2.37], ['c', 1.38, -2.07, 1.50, -2.22, 1.62, -2.28], ['c', 0.06, -0.03, 0.18, -0.06, 0.24, -0.06], ['c', 0.30, 0.00, 0.27, -0.03, 1.89, 1.95], ['l', 1.53, 1.83], ['c', 0.03, 0.00, 0.06, -0.06, 0.09, -0.09], ['c', 0.06, -0.12, 0.06, -0.15, 0.06, -2.28], ['c', 0.00, -1.92, 0.00, -2.22, 0.06, -2.31], ['c', 0.06, -0.15, 0.15, -0.24, 0.30, -0.30], ['z']],
w: 15.011,
h: 10.012
},
'flags.u8th': {
d: [['M', -0.42, 3.75], ['l', 0.00, -3.75], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 0.18], ['c', 0.00, 0.30, 0.06, 0.84, 0.12, 1.23], ['c', 0.24, 1.53, 0.90, 3.12, 2.13, 5.16], ['l', 0.99, 1.59], ['c', 0.87, 1.44, 1.38, 2.34, 1.77, 3.09], ['c', 0.81, 1.68, 1.20, 3.06, 1.26, 4.53], ['c', 0.03, 1.53, -0.21, 3.27, -0.75, 5.01], ['c', -0.21, 0.69, -0.51, 1.50, -0.60, 1.59], ['c', -0.09, 0.12, -0.27, 0.21, -0.42, 0.21], ['c', -0.15, 0.00, -0.42, -0.12, -0.51, -0.21], ['c', -0.15, -0.18, -0.18, -0.42, -0.09, -0.66], ['c', 0.15, -0.33, 0.45, -1.20, 0.57, -1.62], ['c', 0.42, -1.38, 0.60, -2.58, 0.60, -3.90], ['c', 0.00, -0.66, 0.00, -0.81, -0.06, -1.11], ['c', -0.39, -2.07, -1.80, -4.26, -4.59, -7.14], ['l', -0.42, -0.45], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -3.75], ['z']],
w: 6.692,
h: 22.59
},
'flags.u16th': {
d: [['M', -0.42, 7.50], ['l', 0.00, -7.50], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 0.39], ['c', 0.06, 1.08, 0.39, 2.19, 0.99, 3.39], ['c', 0.45, 0.90, 0.87, 1.59, 1.95, 3.12], ['c', 1.29, 1.86, 1.77, 2.64, 2.22, 3.57], ['c', 0.45, 0.93, 0.72, 1.80, 0.87, 2.64], ['c', 0.06, 0.51, 0.06, 1.50, 0.00, 1.92], ['c', -0.12, 0.60, -0.30, 1.20, -0.54, 1.71], ['l', -0.09, 0.24], ['l', 0.18, 0.45], ['c', 0.51, 1.20, 0.72, 2.22, 0.69, 3.42], ['c', -0.06, 1.53, -0.39, 3.03, -0.99, 4.53], ['c', -0.30, 0.75, -0.36, 0.81, -0.57, 0.90], ['c', -0.15, 0.09, -0.33, 0.06, -0.48, 0.00], ['c', -0.18, -0.09, -0.27, -0.18, -0.33, -0.33], ['c', -0.09, -0.18, -0.06, -0.30, 0.12, -0.75], ['c', 0.66, -1.41, 1.02, -2.88, 1.08, -4.32], ['c', 0.00, -0.60, -0.03, -1.05, -0.18, -1.59], ['c', -0.30, -1.20, -0.99, -2.40, -2.25, -3.87], ['c', -0.42, -0.48, -1.53, -1.62, -2.19, -2.22], ['l', -0.45, -0.42], ['l', -0.03, 1.11], ['l', 0.00, 1.11], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -7.50], ['z'], ['m', 1.65, 0.09], ['c', -0.30, -0.30, -0.69, -0.72, -0.90, -0.87], ['l', -0.33, -0.33], ['l', 0.00, 0.15], ['c', 0.00, 0.30, 0.06, 0.81, 0.15, 1.26], ['c', 0.27, 1.29, 0.87, 2.61, 2.04, 4.29], ['c', 0.15, 0.24, 0.60, 0.87, 0.96, 1.38], ['l', 1.08, 1.53], ['l', 0.42, 0.63], ['c', 0.03, 0.00, 0.12, -0.36, 0.21, -0.72], ['c', 0.06, -0.33, 0.06, -1.20, 0.00, -1.62], ['c', -0.33, -1.71, -1.44, -3.48, -3.63, -5.70], ['z']],
w: 6.693,
h: 26.337
},
'flags.u32nd': {
d: [['M', -0.42, 11.25], ['l', 0.00, -11.25], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 0.36], ['c', 0.09, 1.68, 0.69, 3.27, 2.07, 5.46], ['l', 0.87, 1.35], ['c', 1.02, 1.62, 1.47, 2.37, 1.86, 3.18], ['c', 0.48, 1.02, 0.78, 1.92, 0.93, 2.88], ['c', 0.06, 0.48, 0.06, 1.50, 0.00, 1.89], ['c', -0.09, 0.42, -0.21, 0.87, -0.36, 1.26], ['l', -0.12, 0.30], ['l', 0.15, 0.39], ['c', 0.69, 1.56, 0.84, 2.88, 0.54, 4.38], ['c', -0.09, 0.45, -0.27, 1.08, -0.45, 1.47], ['l', -0.12, 0.24], ['l', 0.18, 0.36], ['c', 0.33, 0.72, 0.57, 1.56, 0.69, 2.34], ['c', 0.12, 1.02, -0.06, 2.52, -0.42, 3.84], ['c', -0.27, 0.93, -0.75, 2.13, -0.93, 2.31], ['c', -0.18, 0.15, -0.45, 0.18, -0.66, 0.09], ['c', -0.18, -0.09, -0.27, -0.18, -0.33, -0.33], ['c', -0.09, -0.18, -0.06, -0.30, 0.06, -0.60], ['c', 0.21, -0.36, 0.42, -0.90, 0.57, -1.38], ['c', 0.51, -1.41, 0.69, -3.06, 0.48, -4.08], ['c', -0.15, -0.81, -0.57, -1.68, -1.20, -2.55], ['c', -0.72, -0.99, -1.83, -2.13, -3.30, -3.33], ['l', -0.48, -0.42], ['l', -0.03, 1.53], ['l', 0.00, 1.56], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -11.25], ['z'], ['m', 1.26, -3.96], ['c', -0.27, -0.30, -0.54, -0.60, -0.66, -0.72], ['l', -0.18, -0.21], ['l', 0.00, 0.42], ['c', 0.06, 0.87, 0.24, 1.74, 0.66, 2.67], ['c', 0.36, 0.87, 0.96, 1.86, 1.92, 3.18], ['c', 0.21, 0.33, 0.63, 0.87, 0.87, 1.23], ['c', 0.27, 0.39, 0.60, 0.84, 0.75, 1.08], ['l', 0.27, 0.39], ['l', 0.03, -0.12], ['c', 0.12, -0.45, 0.15, -1.05, 0.09, -1.59], ['c', -0.27, -1.86, -1.38, -3.78, -3.75, -6.33], ['z'], ['m', -0.27, 6.09], ['c', -0.27, -0.21, -0.48, -0.42, -0.51, -0.45], ['c', -0.06, -0.03, -0.06, -0.03, -0.06, 0.21], ['c', 0.00, 0.90, 0.30, 2.04, 0.81, 3.09], ['c', 0.48, 1.02, 0.96, 1.77, 2.37, 3.63], ['c', 0.60, 0.78, 1.05, 1.44, 1.29, 1.77], ['c', 0.06, 0.12, 0.15, 0.21, 0.15, 0.18], ['c', 0.03, -0.03, 0.18, -0.57, 0.24, -0.87], ['c', 0.06, -0.45, 0.06, -1.32, -0.03, -1.74], ['c', -0.09, -0.48, -0.24, -0.90, -0.51, -1.44], ['c', -0.66, -1.35, -1.83, -2.70, -3.75, -4.38], ['z']],
w: 6.697,
h: 32.145
},
'flags.u64th': {
d: [['M', -0.42, 15.00], ['l', 0.00, -15.00], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 0.36], ['c', 0.06, 1.20, 0.39, 2.37, 1.02, 3.66], ['c', 0.39, 0.81, 0.84, 1.56, 1.80, 3.09], ['c', 0.81, 1.26, 1.05, 1.68, 1.35, 2.22], ['c', 0.87, 1.50, 1.35, 2.79, 1.56, 4.08], ['c', 0.06, 0.54, 0.06, 1.56, -0.03, 2.04], ['c', -0.09, 0.48, -0.21, 0.99, -0.36, 1.35], ['l', -0.12, 0.27], ['l', 0.12, 0.27], ['c', 0.09, 0.15, 0.21, 0.45, 0.27, 0.66], ['c', 0.69, 1.89, 0.63, 3.66, -0.18, 5.46], ['l', -0.18, 0.39], ['l', 0.15, 0.33], ['c', 0.30, 0.66, 0.51, 1.44, 0.63, 2.10], ['c', 0.06, 0.48, 0.06, 1.35, 0.00, 1.71], ['c', -0.15, 0.57, -0.42, 1.20, -0.78, 1.68], ['l', -0.21, 0.27], ['l', 0.18, 0.33], ['c', 0.57, 1.05, 0.93, 2.13, 1.02, 3.18], ['c', 0.06, 0.72, 0.00, 1.83, -0.21, 2.79], ['c', -0.18, 1.02, -0.63, 2.34, -1.02, 3.09], ['c', -0.15, 0.33, -0.48, 0.45, -0.78, 0.30], ['c', -0.18, -0.09, -0.27, -0.18, -0.33, -0.33], ['c', -0.09, -0.18, -0.06, -0.30, 0.03, -0.54], ['c', 0.75, -1.50, 1.23, -3.45, 1.17, -4.89], ['c', -0.06, -1.02, -0.42, -2.01, -1.17, -3.15], ['c', -0.48, -0.72, -1.02, -1.35, -1.89, -2.22], ['c', -0.57, -0.57, -1.56, -1.50, -1.92, -1.77], ['l', -0.12, -0.09], ['l', 0.00, 1.68], ['l', 0.00, 1.68], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -15.00], ['z'], ['m', 0.93, -8.07], ['c', -0.27, -0.30, -0.48, -0.54, -0.51, -0.54], ['c', 0.00, 0.00, 0.00, 0.69, 0.03, 1.02], ['c', 0.15, 1.47, 0.75, 2.94, 2.04, 4.83], ['l', 1.08, 1.53], ['c', 0.39, 0.57, 0.84, 1.20, 0.99, 1.44], ['c', 0.15, 0.24, 0.30, 0.45, 0.30, 0.45], ['c', 0.00, 0.00, 0.03, -0.09, 0.06, -0.21], ['c', 0.36, -1.59, -0.15, -3.33, -1.47, -5.40], ['c', -0.63, -0.93, -1.35, -1.83, -2.52, -3.12], ['z'], ['m', 0.06, 6.72], ['c', -0.24, -0.21, -0.48, -0.42, -0.51, -0.45], ['l', -0.06, -0.06], ['l', 0.00, 0.33], ['c', 0.00, 1.20, 0.30, 2.34, 0.93, 3.60], ['c', 0.45, 0.90, 0.96, 1.68, 2.25, 3.51], ['c', 0.39, 0.54, 0.84, 1.17, 1.02, 1.44], ['c', 0.21, 0.33, 0.33, 0.51, 0.33, 0.48], ['c', 0.06, -0.09, 0.21, -0.63, 0.30, -0.99], ['c', 0.06, -0.33, 0.06, -0.45, 0.06, -0.96], ['c', 0.00, -0.60, -0.03, -0.84, -0.18, -1.35], ['c', -0.30, -1.08, -1.02, -2.28, -2.13, -3.57], ['c', -0.39, -0.45, -1.44, -1.47, -2.01, -1.98], ['z'], ['m', 0.00, 6.72], ['c', -0.24, -0.21, -0.48, -0.39, -0.51, -0.42], ['l', -0.06, -0.06], ['l', 0.00, 0.33], ['c', 0.00, 1.41, 0.45, 2.82, 1.38, 4.35], ['c', 0.42, 0.72, 0.72, 1.14, 1.86, 2.73], ['c', 0.36, 0.45, 0.75, 0.99, 0.87, 1.20], ['c', 0.15, 0.21, 0.30, 0.36, 0.30, 0.36], ['c', 0.06, 0.00, 0.30, -0.48, 0.39, -0.75], ['c', 0.09, -0.36, 0.12, -0.63, 0.12, -1.05], ['c', -0.06, -1.05, -0.45, -2.04, -1.20, -3.18], ['c', -0.57, -0.87, -1.11, -1.53, -2.07, -2.49], ['c', -0.36, -0.33, -0.84, -0.78, -1.08, -1.02], ['z']],
w: 6.682,
h: 39.694
},
'flags.d8th': {
d: [['M', 5.67, -21.63], ['c', 0.24, -0.12, 0.54, -0.06, 0.69, 0.15], ['c', 0.06, 0.06, 0.21, 0.36, 0.39, 0.66], ['c', 0.84, 1.77, 1.26, 3.36, 1.32, 5.10], ['c', 0.03, 1.29, -0.21, 2.37, -0.81, 3.63], ['c', -0.60, 1.23, -1.26, 2.13, -3.21, 4.38], ['c', -1.35, 1.53, -1.86, 2.19, -2.40, 2.97], ['c', -0.63, 0.93, -1.11, 1.92, -1.38, 2.79], ['c', -0.15, 0.54, -0.27, 1.35, -0.27, 1.80], ['l', 0.00, 0.15], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -3.75], ['l', 0.00, -3.75], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.48, -0.30], ['c', 1.83, -1.11, 3.12, -2.10, 4.17, -3.12], ['c', 0.78, -0.81, 1.32, -1.53, 1.71, -2.31], ['c', 0.45, -0.93, 0.60, -1.74, 0.51, -2.88], ['c', -0.12, -1.56, -0.63, -3.18, -1.47, -4.68], ['c', -0.12, -0.21, -0.15, -0.33, -0.06, -0.51], ['c', 0.06, -0.15, 0.15, -0.24, 0.33, -0.33], ['z']],
w: 8.492,
h: 21.691
},
'flags.ugrace': {
d: [['M', 6.03, 6.93], ['c', 0.15, -0.09, 0.33, -0.06, 0.51, 0.00], ['c', 0.15, 0.09, 0.21, 0.15, 0.30, 0.33], ['c', 0.09, 0.18, 0.06, 0.39, -0.03, 0.54], ['c', -0.06, 0.15, -10.89, 8.88, -11.07, 8.97], ['c', -0.15, 0.09, -0.33, 0.06, -0.48, 0.00], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['c', -0.09, -0.18, -0.06, -0.39, 0.03, -0.54], ['c', 0.06, -0.15, 10.89, -8.88, 11.07, -8.97], ['z']],
w: 12.019,
h: 9.954
},
'flags.dgrace': {
d: [['M', -6.06, -15.93], ['c', 0.18, -0.09, 0.33, -0.12, 0.48, -0.06], ['c', 0.18, 0.09, 14.01, 8.04, 14.10, 8.10], ['c', 0.12, 0.12, 0.18, 0.33, 0.18, 0.51], ['c', -0.03, 0.21, -0.15, 0.39, -0.36, 0.48], ['c', -0.18, 0.09, -0.33, 0.12, -0.48, 0.06], ['c', -0.18, -0.09, -14.01, -8.04, -14.10, -8.10], ['c', -0.12, -0.12, -0.18, -0.33, -0.18, -0.51], ['c', 0.03, -0.21, 0.15, -0.39, 0.36, -0.48], ['z']],
w: 15.12,
h: 9.212
},
'flags.d16th': {
d: [['M', 6.84, -22.53], ['c', 0.27, -0.12, 0.57, -0.06, 0.72, 0.15], ['c', 0.15, 0.15, 0.33, 0.87, 0.45, 1.56], ['c', 0.06, 0.33, 0.06, 1.35, 0.00, 1.65], ['c', -0.06, 0.33, -0.15, 0.78, -0.27, 1.11], ['c', -0.12, 0.33, -0.45, 0.96, -0.66, 1.32], ['l', -0.18, 0.27], ['l', 0.09, 0.18], ['c', 0.48, 1.02, 0.72, 2.25, 0.69, 3.30], ['c', -0.06, 1.23, -0.42, 2.28, -1.26, 3.45], ['c', -0.57, 0.87, -0.99, 1.32, -3.00, 3.39], ['c', -1.56, 1.56, -2.22, 2.40, -2.76, 3.45], ['c', -0.42, 0.84, -0.66, 1.80, -0.66, 2.55], ['l', 0.00, 0.15], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -7.50], ['l', 0.00, -7.50], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 1.14], ['l', 0.00, 1.11], ['l', 0.27, -0.15], ['c', 1.11, -0.57, 1.77, -0.99, 2.52, -1.47], ['c', 2.37, -1.56, 3.69, -3.15, 4.05, -4.83], ['c', 0.03, -0.18, 0.03, -0.39, 0.03, -0.78], ['c', 0.00, -0.60, -0.03, -0.93, -0.24, -1.50], ['c', -0.06, -0.18, -0.12, -0.39, -0.15, -0.45], ['c', -0.03, -0.24, 0.12, -0.48, 0.36, -0.60], ['z'], ['m', -0.63, 7.50], ['c', -0.06, -0.18, -0.15, -0.36, -0.15, -0.36], ['c', -0.03, 0.00, -0.03, 0.03, -0.06, 0.06], ['c', -0.06, 0.12, -0.96, 1.02, -1.95, 1.98], ['c', -0.63, 0.57, -1.26, 1.17, -1.44, 1.35], ['c', -1.53, 1.62, -2.28, 2.85, -2.55, 4.32], ['c', -0.03, 0.18, -0.03, 0.54, -0.06, 0.99], ['l', 0.00, 0.69], ['l', 0.18, -0.09], ['c', 0.93, -0.54, 2.10, -1.29, 2.82, -1.83], ['c', 0.69, -0.51, 1.02, -0.81, 1.53, -1.29], ['c', 1.86, -1.89, 2.37, -3.66, 1.68, -5.82], ['z']],
w: 8.475,
h: 22.591
},
'flags.d32nd': {
d: [['M', 6.84, -29.13], ['c', 0.27, -0.12, 0.57, -0.06, 0.72, 0.15], ['c', 0.12, 0.12, 0.27, 0.63, 0.36, 1.11], ['c', 0.33, 1.59, 0.06, 3.06, -0.81, 4.47], ['l', -0.18, 0.27], ['l', 0.09, 0.15], ['c', 0.12, 0.24, 0.33, 0.69, 0.45, 1.05], ['c', 0.63, 1.83, 0.45, 3.57, -0.57, 5.22], ['l', -0.18, 0.30], ['l', 0.15, 0.27], ['c', 0.42, 0.87, 0.60, 1.71, 0.57, 2.61], ['c', -0.06, 1.29, -0.48, 2.46, -1.35, 3.78], ['c', -0.54, 0.81, -0.93, 1.29, -2.46, 3.00], ['c', -0.51, 0.54, -1.05, 1.17, -1.26, 1.41], ['c', -1.56, 1.86, -2.25, 3.36, -2.37, 5.01], ['l', 0.00, 0.33], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -11.25], ['l', 0.00, -11.25], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 1.35], ['l', 0.03, 1.35], ['l', 0.78, -0.39], ['c', 1.38, -0.69, 2.34, -1.26, 3.24, -1.92], ['c', 1.38, -1.02, 2.28, -2.13, 2.64, -3.21], ['c', 0.15, -0.48, 0.18, -0.72, 0.18, -1.29], ['c', 0.00, -0.57, -0.06, -0.90, -0.24, -1.47], ['c', -0.06, -0.18, -0.12, -0.39, -0.15, -0.45], ['c', -0.03, -0.24, 0.12, -0.48, 0.36, -0.60], ['z'], ['m', -0.63, 7.20], ['c', -0.09, -0.18, -0.12, -0.21, -0.12, -0.15], ['c', -0.03, 0.09, -1.02, 1.08, -2.04, 2.04], ['c', -1.17, 1.08, -1.65, 1.56, -2.07, 2.04], ['c', -0.84, 0.96, -1.38, 1.86, -1.68, 2.76], ['c', -0.21, 0.57, -0.27, 0.99, -0.30, 1.65], ['l', 0.00, 0.54], ['l', 0.66, -0.33], ['c', 3.57, -1.86, 5.49, -3.69, 5.94, -5.70], ['c', 0.06, -0.39, 0.06, -1.20, -0.03, -1.65], ['c', -0.06, -0.39, -0.24, -0.90, -0.36, -1.20], ['z'], ['m', -0.06, 7.20], ['c', -0.06, -0.15, -0.12, -0.33, -0.15, -0.45], ['l', -0.06, -0.18], ['l', -0.18, 0.21], ['l', -1.83, 1.83], ['c', -0.87, 0.90, -1.77, 1.80, -1.95, 2.01], ['c', -1.08, 1.29, -1.62, 2.31, -1.89, 3.51], ['c', -0.06, 0.30, -0.06, 0.51, -0.09, 0.93], ['l', 0.00, 0.57], ['l', 0.09, -0.06], ['c', 0.75, -0.45, 1.89, -1.26, 2.52, -1.74], ['c', 0.81, -0.66, 1.74, -1.53, 2.22, -2.16], ['c', 1.26, -1.53, 1.68, -3.06, 1.32, -4.47], ['z']],
w: 8.385,
h: 29.191
},
'flags.d64th': {
d: [['M', 7.08, -32.88], ['c', 0.30, -0.12, 0.66, -0.03, 0.78, 0.24], ['c', 0.18, 0.33, 0.27, 2.10, 0.15, 2.64], ['c', -0.09, 0.39, -0.21, 0.78, -0.39, 1.08], ['l', -0.15, 0.30], ['l', 0.09, 0.27], ['c', 0.03, 0.12, 0.09, 0.45, 0.12, 0.69], ['c', 0.27, 1.44, 0.18, 2.55, -0.30, 3.60], ['l', -0.12, 0.33], ['l', 0.06, 0.42], ['c', 0.27, 1.35, 0.33, 2.82, 0.21, 3.63], ['c', -0.12, 0.60, -0.30, 1.23, -0.57, 1.80], ['l', -0.15, 0.27], ['l', 0.03, 0.42], ['c', 0.06, 1.02, 0.06, 2.70, 0.03, 3.06], ['c', -0.15, 1.47, -0.66, 2.76, -1.74, 4.41], ['c', -0.45, 0.69, -0.75, 1.11, -1.74, 2.37], ['c', -1.05, 1.38, -1.50, 1.98, -1.95, 2.73], ['c', -0.93, 1.50, -1.38, 2.82, -1.44, 4.20], ['l', 0.00, 0.42], ['l', -0.21, 0.00], ['l', -0.21, 0.00], ['l', 0.00, -15.00], ['l', 0.00, -15.00], ['l', 0.21, 0.00], ['l', 0.21, 0.00], ['l', 0.00, 1.86], ['l', 0.00, 1.89], ['c', 0.00, 0.00, 0.21, -0.03, 0.45, -0.09], ['c', 2.22, -0.39, 4.08, -1.11, 5.19, -2.01], ['c', 0.63, -0.54, 1.02, -1.14, 1.20, -1.80], ['c', 0.06, -0.30, 0.06, -1.14, -0.03, -1.65], ['c', -0.03, -0.18, -0.06, -0.39, -0.09, -0.48], ['c', -0.03, -0.24, 0.12, -0.48, 0.36, -0.60], ['z'], ['m', -0.45, 6.15], ['c', -0.03, -0.18, -0.06, -0.42, -0.06, -0.54], ['l', -0.03, -0.18], ['l', -0.33, 0.30], ['c', -0.42, 0.36, -0.87, 0.72, -1.68, 1.29], ['c', -1.98, 1.38, -2.25, 1.59, -2.85, 2.16], ['c', -0.75, 0.69, -1.23, 1.44, -1.47, 2.19], ['c', -0.15, 0.45, -0.18, 0.63, -0.21, 1.35], ['l', 0.00, 0.66], ['l', 0.39, -0.18], ['c', 1.83, -0.90, 3.45, -1.95, 4.47, -2.91], ['c', 0.93, -0.90, 1.53, -1.83, 1.74, -2.82], ['c', 0.06, -0.33, 0.06, -0.87, 0.03, -1.32], ['z'], ['m', -0.27, 4.86], ['c', -0.03, -0.21, -0.06, -0.36, -0.06, -0.36], ['c', 0.00, -0.03, -0.12, 0.09, -0.24, 0.24], ['c', -0.39, 0.48, -0.99, 1.08, -2.16, 2.19], ['c', -1.47, 1.38, -1.92, 1.83, -2.46, 2.49], ['c', -0.66, 0.87, -1.08, 1.74, -1.29, 2.58], ['c', -0.09, 0.42, -0.15, 0.87, -0.15, 1.44], ['l', 0.00, 0.54], ['l', 0.48, -0.33], ['c', 1.50, -1.02, 2.58, -1.89, 3.51, -2.82], ['c', 1.47, -1.47, 2.25, -2.85, 2.40, -4.26], ['c', 0.03, -0.39, 0.03, -1.17, -0.03, -1.71], ['z'], ['m', -0.66, 7.68], ['c', 0.03, -0.15, 0.03, -0.60, 0.03, -0.99], ['l', 0.00, -0.72], ['l', -0.27, 0.33], ['l', -1.74, 1.98], ['c', -1.77, 1.92, -2.43, 2.76, -2.97, 3.90], ['c', -0.51, 1.02, -0.72, 1.77, -0.75, 2.91], ['c', 0.00, 0.63, 0.00, 0.63, 0.06, 0.60], ['c', 0.03, -0.03, 0.30, -0.27, 0.63, -0.54], ['c', 0.66, -0.60, 1.86, -1.80, 2.31, -2.31], ['c', 1.65, -1.89, 2.52, -3.54, 2.70, -5.16], ['z']],
w: 8.485,
h: 32.932
},
'clefs.C': {
d: [['M', 0.06, -14.94], ['l', 0.09, -0.06], ['l', 1.92, 0.00], ['l', 1.92, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 14.85], ['l', 0.00, 14.82], ['l', -0.06, 0.09], ['l', -0.09, 0.06], ['l', -1.92, 0.00], ['l', -1.92, 0.00], ['l', -0.09, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -14.82], ['l', 0.00, -14.85], ['z'], ['m', 5.37, 0.00], ['c', 0.09, -0.06, 0.09, -0.06, 0.57, -0.06], ['c', 0.45, 0.00, 0.45, 0.00, 0.54, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 7.14], ['l', 0.00, 7.11], ['l', 0.09, -0.06], ['c', 0.18, -0.18, 0.72, -0.84, 0.96, -1.20], ['c', 0.30, -0.45, 0.66, -1.17, 0.84, -1.65], ['c', 0.36, -0.90, 0.57, -1.83, 0.60, -2.79], ['c', 0.03, -0.48, 0.03, -0.54, 0.09, -0.63], ['c', 0.12, -0.18, 0.36, -0.21, 0.54, -0.12], ['c', 0.18, 0.09, 0.21, 0.15, 0.24, 0.66], ['c', 0.06, 0.87, 0.21, 1.56, 0.57, 2.22], ['c', 0.51, 1.02, 1.26, 1.68, 2.22, 1.92], ['c', 0.21, 0.06, 0.33, 0.06, 0.78, 0.06], ['c', 0.45, 0.00, 0.57, 0.00, 0.84, -0.06], ['c', 0.45, -0.12, 0.81, -0.33, 1.08, -0.60], ['c', 0.57, -0.57, 0.87, -1.41, 0.99, -2.88], ['c', 0.06, -0.54, 0.06, -3.00, 0.00, -3.57], ['c', -0.21, -2.58, -0.84, -3.87, -2.16, -4.50], ['c', -0.48, -0.21, -1.17, -0.36, -1.77, -0.36], ['c', -0.69, 0.00, -1.29, 0.27, -1.50, 0.72], ['c', -0.06, 0.15, -0.06, 0.21, -0.06, 0.42], ['c', 0.00, 0.24, 0.00, 0.30, 0.06, 0.45], ['c', 0.12, 0.24, 0.24, 0.39, 0.63, 0.66], ['c', 0.42, 0.30, 0.57, 0.48, 0.69, 0.72], ['c', 0.06, 0.15, 0.06, 0.21, 0.06, 0.48], ['c', 0.00, 0.39, -0.03, 0.63, -0.21, 0.96], ['c', -0.30, 0.60, -0.87, 1.08, -1.50, 1.26], ['c', -0.27, 0.06, -0.87, 0.06, -1.14, 0.00], ['c', -0.78, -0.24, -1.44, -0.87, -1.65, -1.68], ['c', -0.12, -0.42, -0.09, -1.17, 0.09, -1.71], ['c', 0.51, -1.65, 1.98, -2.82, 3.81, -3.09], ['c', 0.84, -0.09, 2.46, 0.03, 3.51, 0.27], ['c', 2.22, 0.57, 3.69, 1.80, 4.44, 3.75], ['c', 0.36, 0.93, 0.57, 2.13, 0.57, 3.36], ['c', 0.00, 1.44, -0.48, 2.73, -1.38, 3.81], ['c', -1.26, 1.50, -3.27, 2.43, -5.28, 2.43], ['c', -0.48, 0.00, -0.51, 0.00, -0.75, -0.09], ['c', -0.15, -0.03, -0.48, -0.21, -0.78, -0.36], ['c', -0.69, -0.36, -0.87, -0.42, -1.26, -0.42], ['c', -0.27, 0.00, -0.30, 0.00, -0.51, 0.09], ['c', -0.57, 0.30, -0.81, 0.90, -0.81, 2.10], ['c', 0.00, 1.23, 0.24, 1.83, 0.81, 2.13], ['c', 0.21, 0.09, 0.24, 0.09, 0.51, 0.09], ['c', 0.39, 0.00, 0.57, -0.06, 1.26, -0.42], ['c', 0.30, -0.15, 0.63, -0.33, 0.78, -0.36], ['c', 0.24, -0.09, 0.27, -0.09, 0.75, -0.09], ['c', 2.01, 0.00, 4.02, 0.93, 5.28, 2.40], ['c', 0.90, 1.11, 1.38, 2.40, 1.38, 3.84], ['c', 0.00, 1.50, -0.30, 2.88, -0.84, 3.96], ['c', -0.78, 1.59, -2.19, 2.64, -4.17, 3.15], ['c', -1.05, 0.24, -2.67, 0.36, -3.51, 0.27], ['c', -1.83, -0.27, -3.30, -1.44, -3.81, -3.09], ['c', -0.18, -0.54, -0.21, -1.29, -0.09, -1.74], ['c', 0.15, -0.60, 0.63, -1.20, 1.23, -1.47], ['c', 0.36, -0.18, 0.57, -0.21, 0.99, -0.21], ['c', 0.42, 0.00, 0.63, 0.03, 1.02, 0.21], ['c', 0.42, 0.21, 0.84, 0.63, 1.05, 1.05], ['c', 0.18, 0.36, 0.21, 0.60, 0.21, 0.96], ['c', 0.00, 0.30, 0.00, 0.36, -0.06, 0.51], ['c', -0.12, 0.24, -0.27, 0.42, -0.69, 0.72], ['c', -0.57, 0.42, -0.69, 0.63, -0.69, 1.08], ['c', 0.00, 0.24, 0.00, 0.30, 0.06, 0.45], ['c', 0.12, 0.21, 0.30, 0.39, 0.57, 0.54], ['c', 0.42, 0.18, 0.87, 0.21, 1.53, 0.15], ['c', 1.08, -0.15, 1.80, -0.57, 2.34, -1.32], ['c', 0.54, -0.75, 0.84, -1.83, 0.99, -3.51], ['c', 0.06, -0.57, 0.06, -3.03, 0.00, -3.57], ['c', -0.12, -1.47, -0.42, -2.31, -0.99, -2.88], ['c', -0.27, -0.27, -0.63, -0.48, -1.08, -0.60], ['c', -0.27, -0.06, -0.39, -0.06, -0.84, -0.06], ['c', -0.45, 0.00, -0.57, 0.00, -0.78, 0.06], ['c', -1.14, 0.27, -2.01, 1.17, -2.46, 2.49], ['c', -0.21, 0.57, -0.30, 0.99, -0.33, 1.65], ['c', -0.03, 0.51, -0.06, 0.57, -0.24, 0.66], ['c', -0.12, 0.06, -0.27, 0.06, -0.39, 0.00], ['c', -0.21, -0.09, -0.21, -0.15, -0.24, -0.75], ['c', -0.09, -1.92, -0.78, -3.72, -2.01, -5.19], ['c', -0.18, -0.21, -0.36, -0.42, -0.39, -0.45], ['l', -0.09, -0.06], ['l', 0.00, 7.11], ['l', 0.00, 7.14], ['l', -0.06, 0.09], ['c', -0.09, 0.06, -0.09, 0.06, -0.54, 0.06], ['c', -0.48, 0.00, -0.48, 0.00, -0.57, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -14.82], ['l', 0.00, -14.85], ['z']],
w: 20.31,
h: 29.97
},
'clefs.F': {
d: [['M', 6.30, -7.80], ['c', 0.36, -0.03, 1.65, 0.00, 2.13, 0.03], ['c', 3.60, 0.42, 6.03, 2.10, 6.93, 4.86], ['c', 0.27, 0.84, 0.36, 1.50, 0.36, 2.58], ['c', 0.00, 0.90, -0.03, 1.35, -0.18, 2.16], ['c', -0.78, 3.78, -3.54, 7.08, -8.37, 9.96], ['c', -1.74, 1.05, -3.87, 2.13, -6.18, 3.12], ['c', -0.39, 0.18, -0.75, 0.33, -0.81, 0.36], ['c', -0.06, 0.03, -0.15, 0.06, -0.18, 0.06], ['c', -0.15, 0.00, -0.33, -0.18, -0.33, -0.33], ['c', 0.00, -0.15, 0.06, -0.21, 0.51, -0.48], ['c', 3.00, -1.77, 5.13, -3.21, 6.84, -4.74], ['c', 0.51, -0.45, 1.59, -1.50, 1.95, -1.95], ['c', 1.89, -2.19, 2.88, -4.32, 3.15, -6.78], ['c', 0.06, -0.42, 0.06, -1.77, 0.00, -2.19], ['c', -0.24, -2.01, -0.93, -3.63, -2.04, -4.71], ['c', -0.63, -0.63, -1.29, -1.02, -2.07, -1.20], ['c', -1.62, -0.39, -3.36, 0.15, -4.56, 1.44], ['c', -0.54, 0.60, -1.05, 1.47, -1.32, 2.22], ['l', -0.09, 0.21], ['l', 0.24, -0.12], ['c', 0.39, -0.21, 0.63, -0.24, 1.11, -0.24], ['c', 0.30, 0.00, 0.45, 0.00, 0.66, 0.06], ['c', 1.92, 0.48, 2.85, 2.55, 1.95, 4.38], ['c', -0.45, 0.99, -1.41, 1.62, -2.46, 1.71], ['c', -1.47, 0.09, -2.91, -0.87, -3.39, -2.25], ['c', -0.18, -0.57, -0.21, -1.32, -0.03, -2.28], ['c', 0.39, -2.25, 1.83, -4.20, 3.81, -5.19], ['c', 0.69, -0.36, 1.59, -0.60, 2.37, -0.69], ['z'], ['m', 11.58, 2.52], ['c', 0.84, -0.21, 1.71, 0.30, 1.89, 1.14], ['c', 0.30, 1.17, -0.72, 2.19, -1.89, 1.89], ['c', -0.99, -0.21, -1.50, -1.32, -1.02, -2.25], ['c', 0.18, -0.39, 0.60, -0.69, 1.02, -0.78], ['z'], ['m', 0.00, 7.50], ['c', 0.84, -0.21, 1.71, 0.30, 1.89, 1.14], ['c', 0.21, 0.87, -0.30, 1.71, -1.14, 1.89], ['c', -0.87, 0.21, -1.71, -0.30, -1.89, -1.14], ['c', -0.21, -0.84, 0.30, -1.71, 1.14, -1.89], ['z']],
w: 20.153,
h: 23.142
},
'clefs.G': {
d: [['M', 9.69, -37.41], ['c', 0.09, -0.09, 0.24, -0.06, 0.36, 0.00], ['c', 0.12, 0.09, 0.57, 0.60, 0.96, 1.11], ['c', 1.77, 2.34, 3.21, 5.85, 3.57, 8.73], ['c', 0.21, 1.56, 0.03, 3.27, -0.45, 4.86], ['c', -0.69, 2.31, -1.92, 4.47, -4.23, 7.44], ['c', -0.30, 0.39, -0.57, 0.72, -0.60, 0.75], ['c', -0.03, 0.06, 0.00, 0.15, 0.18, 0.78], ['c', 0.54, 1.68, 1.38, 4.44, 1.68, 5.49], ['l', 0.09, 0.42], ['l', 0.39, 0.00], ['c', 1.47, 0.09, 2.76, 0.51, 3.96, 1.29], ['c', 1.83, 1.23, 3.06, 3.21, 3.39, 5.52], ['c', 0.09, 0.45, 0.12, 1.29, 0.06, 1.74], ['c', -0.09, 1.02, -0.33, 1.83, -0.75, 2.73], ['c', -0.84, 1.71, -2.28, 3.06, -4.02, 3.72], ['l', -0.33, 0.12], ['l', 0.03, 1.26], ['c', 0.00, 1.74, -0.06, 3.63, -0.21, 4.62], ['c', -0.45, 3.06, -2.19, 5.49, -4.47, 6.21], ['c', -0.57, 0.18, -0.90, 0.21, -1.59, 0.21], ['c', -0.69, 0.00, -1.02, -0.03, -1.65, -0.21], ['c', -1.14, -0.27, -2.13, -0.84, -2.94, -1.65], ['c', -0.99, -0.99, -1.56, -2.16, -1.71, -3.54], ['c', -0.09, -0.81, 0.06, -1.53, 0.45, -2.13], ['c', 0.63, -0.99, 1.83, -1.56, 3.00, -1.53], ['c', 1.50, 0.09, 2.64, 1.32, 2.73, 2.94], ['c', 0.06, 1.47, -0.93, 2.70, -2.37, 2.97], ['c', -0.45, 0.06, -0.84, 0.03, -1.29, -0.09], ['l', -0.21, -0.09], ['l', 0.09, 0.12], ['c', 0.39, 0.54, 0.78, 0.93, 1.32, 1.26], ['c', 1.35, 0.87, 3.06, 1.02, 4.35, 0.36], ['c', 1.44, -0.72, 2.52, -2.28, 2.97, -4.35], ['c', 0.15, -0.66, 0.24, -1.50, 0.30, -3.03], ['c', 0.03, -0.84, 0.03, -2.94, 0.00, -3.00], ['c', -0.03, 0.00, -0.18, 0.00, -0.36, 0.03], ['c', -0.66, 0.12, -0.99, 0.12, -1.83, 0.12], ['c', -1.05, 0.00, -1.71, -0.06, -2.61, -0.30], ['c', -4.02, -0.99, -7.11, -4.35, -7.80, -8.46], ['c', -0.12, -0.66, -0.12, -0.99, -0.12, -1.83], ['c', 0.00, -0.84, 0.00, -1.14, 0.15, -1.92], ['c', 0.36, -2.28, 1.41, -4.62, 3.30, -7.29], ['l', 2.79, -3.60], ['c', 0.54, -0.66, 0.96, -1.20, 0.96, -1.23], ['c', 0.00, -0.03, -0.09, -0.33, -0.18, -0.69], ['c', -0.96, -3.21, -1.41, -5.28, -1.59, -7.68], ['c', -0.12, -1.38, -0.15, -3.09, -0.06, -3.96], ['c', 0.33, -2.67, 1.38, -5.07, 3.12, -7.08], ['c', 0.36, -0.42, 0.99, -1.05, 1.17, -1.14], ['z'], ['m', 2.01, 4.71], ['c', -0.15, -0.30, -0.30, -0.54, -0.30, -0.54], ['c', -0.03, 0.00, -0.18, 0.09, -0.30, 0.21], ['c', -2.40, 1.74, -3.87, 4.20, -4.26, 7.11], ['c', -0.06, 0.54, -0.06, 1.41, -0.03, 1.89], ['c', 0.09, 1.29, 0.48, 3.12, 1.08, 5.22], ['c', 0.15, 0.42, 0.24, 0.78, 0.24, 0.81], ['c', 0.00, 0.03, 0.84, -1.11, 1.23, -1.68], ['c', 1.89, -2.73, 2.88, -5.07, 3.15, -7.53], ['c', 0.09, -0.57, 0.12, -1.74, 0.06, -2.37], ['c', -0.09, -1.23, -0.27, -1.92, -0.87, -3.12], ['z'], ['m', -2.94, 20.70], ['c', -0.21, -0.72, -0.39, -1.32, -0.42, -1.32], ['c', 0.00, 0.00, -1.20, 1.47, -1.86, 2.37], ['c', -2.79, 3.63, -4.02, 6.30, -4.35, 9.30], ['c', -0.03, 0.21, -0.03, 0.69, -0.03, 1.08], ['c', 0.00, 0.69, 0.00, 0.75, 0.06, 1.11], ['c', 0.12, 0.54, 0.27, 0.99, 0.51, 1.47], ['c', 0.69, 1.38, 1.83, 2.55, 3.42, 3.42], ['c', 0.96, 0.54, 2.07, 0.90, 3.21, 1.08], ['c', 0.78, 0.12, 2.04, 0.12, 2.94, -0.03], ['c', 0.51, -0.06, 0.45, -0.03, 0.42, -0.30], ['c', -0.24, -3.33, -0.72, -6.33, -1.62, -10.08], ['c', -0.09, -0.39, -0.18, -0.75, -0.18, -0.78], ['c', -0.03, -0.03, -0.42, 0.00, -0.81, 0.09], ['c', -0.90, 0.18, -1.65, 0.57, -2.22, 1.14], ['c', -0.72, 0.72, -1.08, 1.65, -1.05, 2.64], ['c', 0.06, 0.96, 0.48, 1.83, 1.23, 2.58], ['c', 0.36, 0.36, 0.72, 0.63, 1.17, 0.90], ['c', 0.33, 0.18, 0.36, 0.21, 0.42, 0.33], ['c', 0.18, 0.42, -0.18, 0.90, -0.60, 0.87], ['c', -0.18, -0.03, -0.84, -0.36, -1.26, -0.63], ['c', -0.78, -0.51, -1.38, -1.11, -1.86, -1.83], ['c', -1.77, -2.70, -0.99, -6.42, 1.71, -8.19], ['c', 0.30, -0.21, 0.81, -0.48, 1.17, -0.63], ['c', 0.30, -0.09, 1.02, -0.30, 1.14, -0.30], ['c', 0.06, 0.00, 0.09, 0.00, 0.09, -0.03], ['c', 0.03, -0.03, -0.51, -1.92, -1.23, -4.26], ['z'], ['m', 3.78, 7.41], ['c', -0.18, -0.03, -0.36, -0.06, -0.39, -0.06], ['c', -0.03, 0.00, 0.00, 0.21, 0.18, 1.02], ['c', 0.75, 3.18, 1.26, 6.30, 1.50, 9.09], ['c', 0.06, 0.72, 0.00, 0.69, 0.51, 0.42], ['c', 0.78, -0.36, 1.44, -0.96, 1.98, -1.77], ['c', 1.08, -1.62, 1.20, -3.69, 0.30, -5.55], ['c', -0.81, -1.62, -2.31, -2.79, -4.08, -3.15], ['z']],
w: 19.051,
h: 57.057
},
'clefs.perc': {
d: [['M', 5.07, -7.44], ['l', 0.09, -0.06], ['l', 1.53, 0.00], ['l', 1.53, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 7.35], ['l', 0.00, 7.32], ['l', -0.06, 0.09], ['l', -0.09, 0.06], ['l', -1.53, 0.00], ['l', -1.53, 0.00], ['l', -0.09, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -7.32], ['l', 0.00, -7.35], ['z'], ['m', 6.63, 0.00], ['l', 0.09, -0.06], ['l', 1.53, 0.00], ['l', 1.53, 0.00], ['l', 0.09, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 7.35], ['l', 0.00, 7.32], ['l', -0.06, 0.09], ['l', -0.09, 0.06], ['l', -1.53, 0.00], ['l', -1.53, 0.00], ['l', -0.09, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -7.32], ['l', 0.00, -7.35], ['z']],
w: 21,
h: 14.97
},
'tab.big': {
d: [['M', 20.16, -21.66], ['c', 0.24, -0.09, 0.66, 0.09, 0.78, 0.36], ['c', 0.09, 0.21, 0.09, 0.24, -0.18, 0.54], ['c', -0.78, 0.81, -1.86, 1.44, -2.94, 1.71], ['c', -0.87, 0.24, -1.71, 0.24, -2.55, 0.03], ['l', -0.06, -0.03], ['l', -0.18, 0.99], ['c', -0.33, 1.98, -0.75, 4.26, -0.96, 5.04], ['c', -0.42, 1.65, -1.26, 3.18, -2.28, 4.14], ['c', -0.57, 0.57, -1.17, 0.90, -1.86, 1.08], ['c', -0.18, 0.06, -0.33, 0.06, -0.66, 0.06], ['c', -0.54, 0.00, -0.78, -0.03, -1.23, -0.27], ['c', -0.39, -0.18, -0.66, -0.39, -1.38, -0.99], ['c', -0.30, -0.24, -0.66, -0.51, -0.75, -0.57], ['c', -0.21, -0.15, -0.27, -0.24, -0.24, -0.45], ['c', 0.06, -0.27, 0.36, -0.60, 0.60, -0.66], ['c', 0.18, -0.03, 0.33, 0.06, 0.90, 0.57], ['c', 0.48, 0.42, 0.72, 0.57, 0.93, 0.69], ['c', 0.66, 0.33, 1.38, 0.21, 1.95, -0.36], ['c', 0.63, -0.60, 1.05, -1.62, 1.23, -3.00], ['c', 0.03, -0.18, 0.09, -0.66, 0.09, -1.11], ['c', 0.09, -1.56, 0.33, -3.81, 0.57, -5.49], ['c', 0.06, -0.33, 0.09, -0.63, 0.09, -0.63], ['c', -0.03, -0.03, -0.81, -0.12, -1.02, -0.12], ['c', -0.57, 0.00, -1.32, 0.12, -1.80, 0.33], ['c', -0.87, 0.30, -1.35, 0.78, -1.50, 1.41], ['c', -0.18, 0.63, 0.09, 1.26, 0.66, 1.65], ['c', 0.12, 0.06, 0.15, 0.12, 0.18, 0.24], ['c', 0.09, 0.27, 0.06, 0.57, -0.09, 0.75], ['c', -0.03, 0.06, -0.12, 0.09, -0.27, 0.15], ['c', -0.72, 0.21, -1.44, 0.15, -2.10, -0.18], ['c', -0.54, -0.27, -0.96, -0.66, -1.20, -1.14], ['c', -0.39, -0.75, -0.33, -1.74, 0.15, -2.52], ['c', 0.27, -0.42, 0.84, -0.93, 1.41, -1.23], ['c', 1.17, -0.57, 2.88, -0.90, 4.80, -0.90], ['c', 0.69, 0.00, 0.78, 0.00, 1.08, 0.06], ['c', 0.45, 0.09, 1.11, 0.30, 2.07, 0.60], ['c', 1.47, 0.48, 1.83, 0.57, 2.55, 0.54], ['c', 1.02, -0.06, 2.04, -0.45, 2.94, -1.11], ['c', 0.12, -0.09, 0.24, -0.18, 0.27, -0.18], ['z'], ['m', -5.88, 13.05], ['c', 0.21, -0.03, 0.81, 0.00, 1.08, 0.06], ['c', 0.48, 0.12, 0.90, 0.42, 0.99, 0.69], ['c', 0.03, 0.09, 0.03, 0.15, 0.00, 0.27], ['c', 0.00, 0.09, -0.03, 0.57, -0.06, 1.08], ['c', -0.09, 2.19, -0.24, 5.76, -0.39, 8.28], ['c', -0.06, 1.53, -0.06, 1.77, 0.03, 2.01], ['c', 0.09, 0.18, 0.15, 0.24, 0.30, 0.30], ['c', 0.24, 0.12, 0.54, 0.06, 1.23, -0.27], ['c', 0.57, -0.27, 0.66, -0.30, 0.75, -0.24], ['c', 0.09, 0.06, 0.18, 0.30, 0.18, 0.45], ['c', 0.00, 0.33, -0.15, 0.51, -0.45, 0.63], ['c', -0.12, 0.03, -0.39, 0.15, -0.60, 0.27], ['c', -1.17, 0.60, -1.38, 0.69, -1.80, 0.72], ['c', -0.45, 0.03, -0.78, -0.09, -1.08, -0.39], ['c', -0.39, -0.42, -0.66, -1.20, -1.02, -3.12], ['c', -0.24, -1.23, -0.36, -2.07, -0.54, -3.75], ['l', 0.00, -0.18], ['l', -0.36, 0.45], ['c', -0.60, 0.75, -1.32, 1.59, -1.95, 2.25], ['c', -0.15, 0.18, -0.27, 0.30, -0.27, 0.33], ['c', 0.00, 0.00, 0.06, 0.09, 0.15, 0.18], ['c', 0.24, 0.33, 0.60, 0.57, 1.05, 0.69], ['c', 0.18, 0.06, 0.30, 0.06, 0.69, 0.06], ['l', 0.48, 0.03], ['l', 0.06, 0.12], ['c', 0.15, 0.27, 0.03, 0.72, -0.21, 0.90], ['c', -0.18, 0.12, -0.93, 0.27, -1.41, 0.27], ['c', -0.84, 0.00, -1.59, -0.30, -1.98, -0.84], ['l', -0.12, -0.15], ['l', -0.45, 0.42], ['c', -0.99, 0.87, -1.53, 1.32, -2.16, 1.74], ['c', -0.78, 0.51, -1.50, 0.84, -2.10, 0.93], ['c', -0.69, 0.12, -1.20, 0.03, -1.95, -0.42], ['c', -0.21, -0.12, -0.51, -0.27, -0.66, -0.36], ['c', -0.24, -0.12, -0.30, -0.18, -0.33, -0.24], ['c', -0.12, -0.27, 0.15, -0.78, 0.45, -0.93], ['c', 0.24, -0.12, 0.33, -0.09, 0.90, 0.18], ['c', 0.60, 0.30, 0.84, 0.39, 1.20, 0.36], ['c', 0.87, -0.09, 1.77, -0.69, 3.24, -2.31], ['c', 2.67, -2.85, 4.59, -5.94, 5.70, -9.15], ['c', 0.15, -0.45, 0.24, -0.63, 0.42, -0.81], ['c', 0.21, -0.24, 0.60, -0.45, 0.99, -0.51], ['z'], ['m', -3.99, 16.05], ['c', 0.18, 0.00, 0.69, -0.03, 1.17, 0.00], ['c', 3.27, 0.03, 5.37, 0.75, 6.00, 2.07], ['c', 0.45, 0.99, 0.12, 2.40, -0.81, 3.42], ['c', -0.24, 0.27, -0.57, 0.57, -0.84, 0.75], ['c', -0.09, 0.06, -0.18, 0.09, -0.18, 0.12], ['c', 0.00, 0.00, 0.18, 0.03, 0.42, 0.09], ['c', 1.23, 0.30, 2.01, 0.81, 2.37, 1.59], ['c', 0.27, 0.54, 0.30, 1.32, 0.09, 2.10], ['c', -0.12, 0.36, -0.45, 1.05, -0.69, 1.35], ['c', -0.87, 1.17, -2.10, 1.92, -3.54, 2.25], ['c', -0.36, 0.06, -0.48, 0.06, -0.96, 0.06], ['c', -0.45, 0.00, -0.66, 0.00, -0.84, -0.03], ['c', -0.84, -0.18, -1.47, -0.51, -2.07, -1.11], ['c', -0.33, -0.33, -0.45, -0.51, -0.45, -0.63], ['c', 0.00, -0.06, 0.03, -0.15, 0.06, -0.24], ['c', 0.18, -0.33, 0.69, -0.60, 0.93, -0.48], ['c', 0.03, 0.03, 0.15, 0.12, 0.27, 0.24], ['c', 0.39, 0.42, 0.99, 0.57, 1.62, 0.45], ['c', 1.05, -0.21, 1.98, -1.02, 2.31, -2.01], ['c', 0.48, -1.53, -0.48, -2.55, -2.58, -2.67], ['c', -0.21, 0.00, -0.36, -0.03, -0.42, -0.06], ['c', -0.15, -0.09, -0.21, -0.51, -0.06, -0.78], ['c', 0.12, -0.27, 0.24, -0.33, 0.60, -0.36], ['c', 0.57, -0.06, 1.11, -0.42, 1.50, -0.99], ['c', 0.48, -0.72, 0.54, -1.59, 0.18, -2.31], ['c', -0.12, -0.21, -0.45, -0.54, -0.69, -0.69], ['c', -0.33, -0.21, -0.93, -0.45, -1.35, -0.51], ['l', -0.12, -0.03], ['l', -0.06, 0.48], ['c', -0.54, 2.94, -1.14, 6.24, -1.29, 6.75], ['c', -0.33, 1.35, -0.93, 2.61, -1.65, 3.60], ['c', -0.30, 0.36, -0.81, 0.90, -1.14, 1.14], ['c', -0.30, 0.24, -0.84, 0.48, -1.14, 0.57], ['c', -0.33, 0.09, -0.96, 0.09, -1.26, 0.03], ['c', -0.45, -0.12, -0.87, -0.39, -1.53, -0.96], ['c', -0.24, -0.15, -0.51, -0.39, -0.63, -0.48], ['c', -0.30, -0.21, -0.33, -0.33, -0.21, -0.63], ['c', 0.12, -0.18, 0.27, -0.36, 0.42, -0.45], ['c', 0.27, -0.12, 0.36, -0.09, 0.87, 0.33], ['c', 0.78, 0.60, 1.08, 0.75, 1.65, 0.72], ['c', 0.45, -0.03, 0.81, -0.21, 1.17, -0.54], ['c', 0.87, -0.90, 1.38, -2.85, 1.38, -5.37], ['c', 0.00, -0.60, 0.03, -1.11, 0.12, -2.04], ['c', 0.06, -0.69, 0.24, -2.01, 0.33, -2.58], ['c', 0.06, -0.24, 0.06, -0.42, 0.06, -0.42], ['c', 0.00, 0.00, -0.12, 0.03, -0.21, 0.09], ['c', -1.44, 0.57, -2.16, 1.65, -1.74, 2.55], ['c', 0.09, 0.15, 0.18, 0.24, 0.27, 0.33], ['c', 0.24, 0.21, 0.30, 0.27, 0.33, 0.39], ['c', 0.06, 0.24, 0.00, 0.63, -0.15, 0.78], ['c', -0.09, 0.12, -0.54, 0.21, -0.96, 0.24], ['c', -1.02, 0.03, -2.01, -0.48, -2.43, -1.32], ['c', -0.21, -0.45, -0.27, -0.90, -0.15, -1.44], ['c', 0.06, -0.27, 0.21, -0.66, 0.39, -0.93], ['c', 0.87, -1.29, 3.00, -2.22, 5.64, -2.43], ['z']],
w: 19.643,
h: 43.325
},
'tab.tiny': {
d: [['M', 16.02, -17.25], ['c', 0.12, -0.09, 0.15, -0.09, 0.27, -0.09], ['c', 0.21, 0.03, 0.51, 0.30, 0.51, 0.45], ['c', 0.00, 0.06, -0.12, 0.18, -0.30, 0.36], ['c', -1.11, 1.08, -2.55, 1.59, -3.84, 1.41], ['c', -0.15, -0.03, -0.33, -0.06, -0.39, -0.09], ['c', -0.06, -0.03, -0.09, -0.03, -0.12, -0.03], ['c', 0.00, 0.00, -0.06, 0.42, -0.15, 0.93], ['c', -0.33, 2.01, -0.66, 3.69, -0.84, 4.26], ['c', -0.42, 1.41, -1.23, 2.67, -2.16, 3.33], ['c', -0.27, 0.18, -0.75, 0.42, -0.99, 0.48], ['c', -0.30, 0.09, -0.72, 0.09, -1.02, 0.06], ['c', -0.45, -0.09, -0.84, -0.33, -1.53, -0.90], ['c', -0.21, -0.18, -0.51, -0.39, -0.63, -0.48], ['c', -0.27, -0.21, -0.30, -0.24, -0.30, -0.36], ['c', 0.00, -0.12, 0.09, -0.36, 0.18, -0.45], ['c', 0.09, -0.09, 0.27, -0.18, 0.36, -0.18], ['c', 0.12, 0.00, 0.30, 0.12, 0.66, 0.45], ['c', 0.57, 0.51, 0.87, 0.69, 1.23, 0.72], ['c', 0.93, 0.06, 1.68, -0.78, 1.98, -2.37], ['c', 0.09, -0.39, 0.15, -0.75, 0.18, -1.53], ['c', 0.06, -0.99, 0.24, -2.79, 0.42, -4.05], ['c', 0.03, -0.30, 0.06, -0.57, 0.06, -0.60], ['c', 0.00, -0.06, -0.03, -0.09, -0.15, -0.12], ['c', -0.90, -0.18, -2.13, 0.06, -2.76, 0.57], ['c', -0.36, 0.30, -0.51, 0.60, -0.51, 1.02], ['c', 0.00, 0.45, 0.15, 0.75, 0.48, 0.99], ['c', 0.06, 0.06, 0.15, 0.18, 0.18, 0.24], ['c', 0.12, 0.24, 0.03, 0.63, -0.15, 0.69], ['c', -0.24, 0.12, -0.60, 0.15, -0.90, 0.15], ['c', -0.36, -0.03, -0.57, -0.09, -0.87, -0.24], ['c', -0.78, -0.36, -1.23, -1.11, -1.20, -1.92], ['c', 0.12, -1.53, 1.74, -2.49, 4.62, -2.70], ['c', 1.20, -0.09, 1.47, -0.03, 3.33, 0.57], ['c', 0.90, 0.30, 1.14, 0.36, 1.56, 0.39], ['c', 0.45, 0.00, 0.93, -0.06, 1.38, -0.21], ['c', 0.51, -0.18, 0.81, -0.33, 1.41, -0.75], ['z'], ['m', -4.68, 10.38], ['c', 0.39, -0.06, 0.84, 0.00, 1.20, 0.15], ['c', 0.24, 0.12, 0.36, 0.21, 0.45, 0.36], ['l', 0.09, 0.09], ['l', -0.06, 1.41], ['c', -0.09, 2.19, -0.18, 3.96, -0.27, 5.49], ['c', -0.03, 0.78, -0.06, 1.59, -0.06, 1.86], ['c', 0.00, 0.42, 0.00, 0.48, 0.06, 0.57], ['c', 0.06, 0.18, 0.18, 0.24, 0.36, 0.27], ['c', 0.18, 0.00, 0.39, -0.06, 0.84, -0.27], ['c', 0.45, -0.21, 0.54, -0.24, 0.63, -0.18], ['c', 0.12, 0.12, 0.15, 0.54, 0.03, 0.69], ['c', -0.03, 0.03, -0.15, 0.12, -0.27, 0.18], ['c', -0.15, 0.03, -0.30, 0.12, -0.36, 0.15], ['c', -0.87, 0.45, -1.02, 0.51, -1.26, 0.57], ['c', -0.33, 0.09, -0.60, 0.06, -0.84, -0.06], ['c', -0.42, -0.18, -0.63, -0.60, -0.87, -1.44], ['c', -0.30, -1.23, -0.57, -2.97, -0.66, -4.08], ['c', 0.00, -0.18, -0.03, -0.30, -0.03, -0.33], ['l', -0.06, 0.06], ['c', -0.18, 0.27, -1.11, 1.38, -1.68, 2.01], ['l', -0.33, 0.33], ['l', 0.06, 0.09], ['c', 0.06, 0.15, 0.27, 0.33, 0.48, 0.42], ['c', 0.27, 0.18, 0.51, 0.24, 0.96, 0.27], ['l', 0.39, 0.00], ['l', 0.03, 0.12], ['c', 0.12, 0.21, 0.03, 0.57, -0.15, 0.69], ['c', -0.03, 0.03, -0.21, 0.09, -0.36, 0.15], ['c', -0.27, 0.06, -0.39, 0.06, -0.75, 0.06], ['c', -0.48, 0.00, -0.75, -0.03, -1.08, -0.21], ['c', -0.21, -0.12, -0.51, -0.36, -0.57, -0.48], ['l', -0.03, -0.09], ['l', -0.39, 0.36], ['c', -1.47, 1.35, -2.49, 1.98, -3.42, 2.13], ['c', -0.54, 0.09, -0.96, -0.03, -1.62, -0.39], ['c', -0.21, -0.15, -0.45, -0.27, -0.54, -0.30], ['c', -0.18, -0.09, -0.21, -0.21, -0.12, -0.45], ['c', 0.06, -0.27, 0.33, -0.48, 0.54, -0.48], ['c', 0.03, 0.00, 0.27, 0.09, 0.48, 0.21], ['c', 0.48, 0.24, 0.69, 0.27, 0.99, 0.27], ['c', 0.60, -0.06, 1.17, -0.42, 2.10, -1.35], ['c', 2.22, -2.22, 4.02, -4.98, 4.95, -7.59], ['c', 0.21, -0.57, 0.30, -0.78, 0.48, -0.93], ['c', 0.15, -0.15, 0.42, -0.27, 0.66, -0.33], ['z'], ['m', -3.06, 12.84], ['c', 0.27, -0.03, 1.68, 0.00, 2.01, 0.03], ['c', 1.92, 0.18, 3.15, 0.69, 3.63, 1.50], ['c', 0.18, 0.33, 0.24, 0.51, 0.21, 0.93], ['c', 0.00, 0.45, -0.06, 0.72, -0.24, 1.11], ['c', -0.24, 0.51, -0.69, 1.02, -1.17, 1.35], ['c', -0.21, 0.15, -0.21, 0.15, -0.12, 0.18], ['c', 0.72, 0.15, 1.11, 0.30, 1.50, 0.57], ['c', 0.39, 0.24, 0.63, 0.57, 0.75, 0.96], ['c', 0.09, 0.30, 0.09, 0.96, 0.00, 1.29], ['c', -0.15, 0.57, -0.39, 1.05, -0.78, 1.50], ['c', -0.66, 0.75, -1.62, 1.32, -2.61, 1.53], ['c', -0.27, 0.06, -0.42, 0.06, -0.84, 0.06], ['c', -0.48, 0.00, -0.57, 0.00, -0.81, -0.06], ['c', -0.60, -0.18, -1.05, -0.42, -1.47, -0.81], ['c', -0.36, -0.39, -0.42, -0.51, -0.30, -0.75], ['c', 0.12, -0.21, 0.39, -0.39, 0.60, -0.39], ['c', 0.09, 0.00, 0.15, 0.03, 0.33, 0.18], ['c', 0.12, 0.12, 0.27, 0.24, 0.36, 0.27], ['c', 0.96, 0.48, 2.46, -0.33, 2.82, -1.50], ['c', 0.24, -0.81, -0.03, -1.44, -0.69, -1.77], ['c', -0.39, -0.21, -1.02, -0.33, -1.53, -0.33], ['c', -0.18, 0.00, -0.21, 0.00, -0.27, -0.09], ['c', -0.06, -0.09, -0.06, -0.30, -0.03, -0.48], ['c', 0.06, -0.18, 0.18, -0.36, 0.33, -0.36], ['c', 0.39, -0.06, 0.51, -0.09, 0.72, -0.18], ['c', 0.69, -0.36, 1.11, -1.23, 0.99, -2.01], ['c', -0.09, -0.51, -0.42, -0.90, -0.93, -1.17], ['c', -0.24, -0.12, -0.60, -0.27, -0.87, -0.30], ['c', -0.09, -0.03, -0.09, -0.03, -0.12, 0.12], ['c', 0.00, 0.09, -0.21, 1.11, -0.42, 2.25], ['c', -0.66, 3.75, -0.72, 3.99, -1.26, 5.07], ['c', -0.90, 1.89, -2.25, 2.85, -3.48, 2.61], ['c', -0.39, -0.09, -0.69, -0.27, -1.38, -0.84], ['c', -0.63, -0.51, -0.63, -0.48, -0.63, -0.60], ['c', 0.00, -0.18, 0.18, -0.48, 0.39, -0.57], ['c', 0.21, -0.12, 0.30, -0.09, 0.81, 0.33], ['c', 0.15, 0.15, 0.39, 0.30, 0.54, 0.36], ['c', 0.18, 0.12, 0.27, 0.12, 0.48, 0.15], ['c', 0.99, 0.06, 1.71, -0.78, 2.04, -2.46], ['c', 0.12, -0.66, 0.18, -1.14, 0.21, -2.22], ['c', 0.03, -1.23, 0.12, -2.25, 0.36, -3.63], ['c', 0.03, -0.24, 0.06, -0.45, 0.06, -0.48], ['c', -0.06, -0.03, -0.66, 0.27, -0.90, 0.42], ['c', -0.06, 0.06, -0.21, 0.18, -0.33, 0.30], ['c', -0.57, 0.57, -0.60, 1.35, -0.06, 1.74], ['c', 0.18, 0.12, 0.24, 0.24, 0.21, 0.51], ['c', -0.03, 0.30, -0.15, 0.42, -0.57, 0.48], ['c', -1.11, 0.24, -2.22, -0.42, -2.43, -1.38], ['c', -0.09, -0.45, 0.03, -1.02, 0.30, -1.47], ['c', 0.18, -0.24, 0.60, -0.63, 0.90, -0.84], ['c', 0.90, -0.60, 2.28, -1.02, 3.69, -1.11], ['z']],
w: 15.709,
h: 34.656
},
'timesig.common': {
d: [['M', 6.66, -7.83], ['c', 0.72, -0.06, 1.41, -0.03, 1.98, 0.09], ['c', 1.20, 0.27, 2.34, 0.96, 3.09, 1.92], ['c', 0.63, 0.81, 1.08, 1.86, 1.14, 2.73], ['c', 0.06, 1.02, -0.51, 1.92, -1.44, 2.22], ['c', -0.24, 0.09, -0.30, 0.09, -0.63, 0.09], ['c', -0.33, 0.00, -0.42, 0.00, -0.63, -0.06], ['c', -0.66, -0.24, -1.14, -0.63, -1.41, -1.20], ['c', -0.15, -0.30, -0.21, -0.51, -0.24, -0.90], ['c', -0.06, -1.08, 0.57, -2.04, 1.56, -2.37], ['c', 0.18, -0.06, 0.27, -0.06, 0.63, -0.06], ['l', 0.45, 0.00], ['c', 0.06, 0.03, 0.09, 0.03, 0.09, 0.00], ['c', 0.00, 0.00, -0.09, -0.12, -0.24, -0.27], ['c', -1.02, -1.11, -2.55, -1.68, -4.08, -1.50], ['c', -1.29, 0.15, -2.04, 0.69, -2.40, 1.74], ['c', -0.36, 0.93, -0.42, 1.89, -0.42, 5.37], ['c', 0.00, 2.97, 0.06, 3.96, 0.24, 4.77], ['c', 0.24, 1.08, 0.63, 1.68, 1.41, 2.07], ['c', 0.81, 0.39, 2.16, 0.45, 3.18, 0.09], ['c', 1.29, -0.45, 2.37, -1.53, 3.03, -2.97], ['c', 0.15, -0.33, 0.33, -0.87, 0.39, -1.17], ['c', 0.09, -0.24, 0.15, -0.36, 0.30, -0.39], ['c', 0.21, -0.03, 0.42, 0.15, 0.39, 0.36], ['c', -0.06, 0.39, -0.42, 1.38, -0.69, 1.89], ['c', -0.96, 1.80, -2.49, 2.94, -4.23, 3.18], ['c', -0.99, 0.12, -2.58, -0.06, -3.63, -0.45], ['c', -0.96, -0.36, -1.71, -0.84, -2.40, -1.50], ['c', -1.11, -1.11, -1.80, -2.61, -2.04, -4.56], ['c', -0.06, -0.60, -0.06, -2.01, 0.00, -2.61], ['c', 0.24, -1.95, 0.90, -3.45, 2.01, -4.56], ['c', 0.69, -0.66, 1.44, -1.11, 2.37, -1.47], ['c', 0.63, -0.24, 1.47, -0.42, 2.22, -0.48], ['z']],
w: 13.038,
h: 15.689
},
'timesig.cut': {
d: [['M', 6.24, -10.44], ['c', 0.09, -0.06, 0.09, -0.06, 0.48, -0.06], ['c', 0.36, 0.00, 0.36, 0.00, 0.45, 0.06], ['l', 0.06, 0.09], ['l', 0.00, 1.23], ['l', 0.00, 1.26], ['l', 0.27, 0.00], ['c', 1.26, 0.00, 2.49, 0.45, 3.48, 1.29], ['c', 1.05, 0.87, 1.80, 2.28, 1.89, 3.48], ['c', 0.06, 1.02, -0.51, 1.92, -1.44, 2.22], ['c', -0.24, 0.09, -0.30, 0.09, -0.63, 0.09], ['c', -0.33, 0.00, -0.42, 0.00, -0.63, -0.06], ['c', -0.66, -0.24, -1.14, -0.63, -1.41, -1.20], ['c', -0.15, -0.30, -0.21, -0.51, -0.24, -0.90], ['c', -0.06, -1.08, 0.57, -2.04, 1.56, -2.37], ['c', 0.18, -0.06, 0.27, -0.06, 0.63, -0.06], ['l', 0.45, 0.00], ['c', 0.06, 0.03, 0.09, 0.03, 0.09, 0.00], ['c', 0.00, -0.03, -0.45, -0.51, -0.66, -0.69], ['c', -0.87, -0.69, -1.83, -1.05, -2.94, -1.11], ['l', -0.42, 0.00], ['l', 0.00, 7.17], ['l', 0.00, 7.14], ['l', 0.42, 0.00], ['c', 0.69, -0.03, 1.23, -0.18, 1.86, -0.51], ['c', 1.05, -0.51, 1.89, -1.47, 2.46, -2.70], ['c', 0.15, -0.33, 0.33, -0.87, 0.39, -1.17], ['c', 0.09, -0.24, 0.15, -0.36, 0.30, -0.39], ['c', 0.21, -0.03, 0.42, 0.15, 0.39, 0.36], ['c', -0.03, 0.24, -0.21, 0.78, -0.39, 1.20], ['c', -0.96, 2.37, -2.94, 3.90, -5.13, 3.90], ['l', -0.30, 0.00], ['l', 0.00, 1.26], ['l', 0.00, 1.23], ['l', -0.06, 0.09], ['c', -0.09, 0.06, -0.09, 0.06, -0.45, 0.06], ['c', -0.39, 0.00, -0.39, 0.00, -0.48, -0.06], ['l', -0.06, -0.09], ['l', 0.00, -1.29], ['l', 0.00, -1.29], ['l', -0.21, -0.03], ['c', -1.23, -0.21, -2.31, -0.63, -3.21, -1.29], ['c', -0.15, -0.09, -0.45, -0.36, -0.66, -0.57], ['c', -1.11, -1.11, -1.80, -2.61, -2.04, -4.56], ['c', -0.06, -0.60, -0.06, -2.01, 0.00, -2.61], ['c', 0.24, -1.95, 0.93, -3.45, 2.04, -4.59], ['c', 0.42, -0.39, 0.78, -0.66, 1.26, -0.93], ['c', 0.75, -0.45, 1.65, -0.75, 2.61, -0.90], ['l', 0.21, -0.03], ['l', 0.00, -1.29], ['l', 0.00, -1.29], ['z'], ['m', -0.06, 10.44], ['c', 0.00, -5.58, 0.00, -6.99, -0.03, -6.99], ['c', -0.15, 0.00, -0.63, 0.27, -0.87, 0.45], ['c', -0.45, 0.36, -0.75, 0.93, -0.93, 1.77], ['c', -0.18, 0.81, -0.24, 1.80, -0.24, 4.74], ['c', 0.00, 2.97, 0.06, 3.96, 0.24, 4.77], ['c', 0.24, 1.08, 0.66, 1.68, 1.41, 2.07], ['c', 0.12, 0.06, 0.30, 0.12, 0.33, 0.15], ['l', 0.09, 0.00], ['l', 0.00, -6.96], ['z']],
w: 13.038,
h: 20.97
},
'timesig.imperfectum': {
d: [['M', 13, -5], ['a', 8, 8, 0, 1, 0, 0, 10]],
w: 13.038,
h: 20.97
},
'timesig.imperfectum2': {
d: [['M', 13, -5], ['a', 8, 8, 0, 1, 0, 0, 10]],
w: 13.038,
h: 20.97
},
'timesig.perfectum': {
d: [['M', 13, -5], ['a', 8, 8, 0, 1, 0, 0, 10]],
w: 13.038,
h: 20.97
},
'timesig.perfectum2': {
d: [['M', 13, -5], ['a', 8, 8, 0, 1, 0, 0, 10]],
w: 13.038,
h: 20.97
},
'f': {
d: [['M', 9.93, -14.28], ['c', 1.53, -0.18, 2.88, 0.45, 3.12, 1.50], ['c', 0.12, 0.51, 0.00, 1.32, -0.27, 1.86], ['c', -0.15, 0.30, -0.42, 0.57, -0.63, 0.69], ['c', -0.69, 0.36, -1.56, 0.03, -1.83, -0.69], ['c', -0.09, -0.24, -0.09, -0.69, 0.00, -0.87], ['c', 0.06, -0.12, 0.21, -0.24, 0.45, -0.42], ['c', 0.42, -0.24, 0.57, -0.45, 0.60, -0.72], ['c', 0.03, -0.33, -0.09, -0.39, -0.63, -0.42], ['c', -0.30, 0.00, -0.45, 0.00, -0.60, 0.03], ['c', -0.81, 0.21, -1.35, 0.93, -1.74, 2.46], ['c', -0.06, 0.27, -0.48, 2.25, -0.48, 2.31], ['c', 0.00, 0.03, 0.39, 0.03, 0.90, 0.03], ['c', 0.72, 0.00, 0.90, 0.00, 0.99, 0.06], ['c', 0.42, 0.15, 0.45, 0.72, 0.03, 0.90], ['c', -0.12, 0.06, -0.24, 0.06, -1.17, 0.06], ['l', -1.05, 0.00], ['l', -0.78, 2.55], ['c', -0.45, 1.41, -0.87, 2.79, -0.96, 3.06], ['c', -0.87, 2.37, -2.37, 4.74, -3.78, 5.91], ['c', -1.05, 0.90, -2.04, 1.23, -3.09, 1.08], ['c', -1.11, -0.18, -1.89, -0.78, -2.04, -1.59], ['c', -0.12, -0.66, 0.15, -1.71, 0.54, -2.19], ['c', 0.69, -0.75, 1.86, -0.54, 2.22, 0.39], ['c', 0.06, 0.15, 0.09, 0.27, 0.09, 0.48], ['c', 0.00, 0.24, -0.03, 0.27, -0.12, 0.42], ['c', -0.03, 0.09, -0.15, 0.18, -0.27, 0.27], ['c', -0.09, 0.06, -0.27, 0.21, -0.36, 0.27], ['c', -0.24, 0.18, -0.36, 0.36, -0.39, 0.60], ['c', -0.03, 0.33, 0.09, 0.39, 0.63, 0.42], ['c', 0.42, 0.00, 0.63, -0.03, 0.90, -0.15], ['c', 0.60, -0.30, 0.96, -0.96, 1.38, -2.64], ['c', 0.09, -0.42, 0.63, -2.55, 1.17, -4.77], ['l', 1.02, -4.08], ['c', 0.00, -0.03, -0.36, -0.03, -0.81, -0.03], ['c', -0.72, 0.00, -0.81, 0.00, -0.93, -0.06], ['c', -0.42, -0.18, -0.39, -0.75, 0.03, -0.90], ['c', 0.09, -0.06, 0.27, -0.06, 1.05, -0.06], ['l', 0.96, 0.00], ['l', 0.00, -0.09], ['c', 0.06, -0.18, 0.30, -0.72, 0.51, -1.17], ['c', 1.20, -2.46, 3.30, -4.23, 5.34, -4.50], ['z']],
w: 16.155,
h: 19.445
},
'm': {
d: [['M', 2.79, -8.91], ['c', 0.09, 0.00, 0.30, -0.03, 0.45, -0.03], ['c', 0.24, 0.03, 0.30, 0.03, 0.45, 0.12], ['c', 0.36, 0.15, 0.63, 0.54, 0.75, 1.02], ['l', 0.03, 0.21], ['l', 0.33, -0.30], ['c', 0.69, -0.69, 1.38, -1.02, 2.07, -1.02], ['c', 0.27, 0.00, 0.33, 0.00, 0.48, 0.06], ['c', 0.21, 0.09, 0.48, 0.36, 0.63, 0.60], ['c', 0.03, 0.09, 0.12, 0.27, 0.18, 0.42], ['c', 0.03, 0.15, 0.09, 0.27, 0.12, 0.27], ['c', 0.00, 0.00, 0.09, -0.09, 0.18, -0.21], ['c', 0.33, -0.39, 0.87, -0.81, 1.29, -0.99], ['c', 0.78, -0.33, 1.47, -0.21, 2.01, 0.33], ['c', 0.30, 0.33, 0.48, 0.69, 0.60, 1.14], ['c', 0.09, 0.42, 0.06, 0.54, -0.54, 3.06], ['c', -0.33, 1.29, -0.57, 2.40, -0.57, 2.43], ['c', 0.00, 0.12, 0.09, 0.21, 0.21, 0.21], ['c', 0.24, 0.00, 0.75, -0.30, 1.20, -0.72], ['c', 0.45, -0.39, 0.60, -0.45, 0.78, -0.27], ['c', 0.18, 0.18, 0.09, 0.36, -0.45, 0.87], ['c', -1.05, 0.96, -1.83, 1.47, -2.58, 1.71], ['c', -0.93, 0.33, -1.53, 0.21, -1.80, -0.33], ['c', -0.06, -0.15, -0.06, -0.21, -0.06, -0.45], ['c', 0.00, -0.24, 0.03, -0.48, 0.60, -2.82], ['c', 0.42, -1.71, 0.60, -2.64, 0.63, -2.79], ['c', 0.03, -0.57, -0.30, -0.75, -0.84, -0.48], ['c', -0.24, 0.12, -0.54, 0.39, -0.66, 0.63], ['c', -0.03, 0.09, -0.42, 1.38, -0.90, 3.00], ['c', -0.90, 3.15, -0.84, 3.00, -1.14, 3.15], ['l', -0.15, 0.09], ['l', -0.78, 0.00], ['c', -0.60, 0.00, -0.78, 0.00, -0.84, -0.06], ['c', -0.09, -0.03, -0.18, -0.18, -0.18, -0.27], ['c', 0.00, -0.03, 0.36, -1.38, 0.84, -2.97], ['c', 0.57, -2.04, 0.81, -2.97, 0.84, -3.12], ['c', 0.03, -0.54, -0.30, -0.72, -0.84, -0.45], ['c', -0.24, 0.12, -0.57, 0.42, -0.66, 0.63], ['c', -0.06, 0.09, -0.51, 1.44, -1.05, 2.97], ['c', -0.51, 1.56, -0.99, 2.85, -0.99, 2.91], ['c', -0.06, 0.12, -0.21, 0.24, -0.36, 0.30], ['c', -0.12, 0.06, -0.21, 0.06, -0.90, 0.06], ['c', -0.60, 0.00, -0.78, 0.00, -0.84, -0.06], ['c', -0.09, -0.03, -0.18, -0.18, -0.18, -0.27], ['c', 0.00, -0.03, 0.45, -1.38, 0.99, -2.97], ['c', 1.05, -3.18, 1.05, -3.18, 0.93, -3.45], ['c', -0.12, -0.27, -0.39, -0.30, -0.72, -0.15], ['c', -0.54, 0.27, -1.14, 1.17, -1.56, 2.40], ['c', -0.06, 0.15, -0.15, 0.30, -0.18, 0.36], ['c', -0.21, 0.21, -0.57, 0.27, -0.72, 0.09], ['c', -0.09, -0.09, -0.06, -0.21, 0.06, -0.63], ['c', 0.48, -1.26, 1.26, -2.46, 2.01, -3.21], ['c', 0.57, -0.54, 1.20, -0.87, 1.83, -1.02], ['z']],
w: 14.687,
h: 9.126
},
'p': {
d: [['M', 1.92, -8.70], ['c', 0.27, -0.09, 0.81, -0.06, 1.11, 0.03], ['c', 0.54, 0.18, 0.93, 0.51, 1.17, 0.99], ['c', 0.09, 0.15, 0.15, 0.33, 0.18, 0.36], ['l', 0.00, 0.12], ['l', 0.30, -0.27], ['c', 0.66, -0.60, 1.35, -1.02, 2.13, -1.20], ['c', 0.21, -0.06, 0.33, -0.06, 0.78, -0.06], ['c', 0.45, 0.00, 0.51, 0.00, 0.84, 0.09], ['c', 1.29, 0.33, 2.07, 1.32, 2.25, 2.79], ['c', 0.09, 0.81, -0.09, 2.01, -0.45, 2.79], ['c', -0.54, 1.26, -1.86, 2.55, -3.18, 3.03], ['c', -0.45, 0.18, -0.81, 0.24, -1.29, 0.24], ['c', -0.69, -0.03, -1.35, -0.18, -1.86, -0.45], ['c', -0.30, -0.15, -0.51, -0.18, -0.69, -0.09], ['c', -0.09, 0.03, -0.18, 0.09, -0.18, 0.12], ['c', -0.09, 0.12, -1.05, 2.94, -1.05, 3.06], ['c', 0.00, 0.24, 0.18, 0.48, 0.51, 0.63], ['c', 0.18, 0.06, 0.54, 0.15, 0.75, 0.15], ['c', 0.21, 0.00, 0.36, 0.06, 0.42, 0.18], ['c', 0.12, 0.18, 0.06, 0.42, -0.12, 0.54], ['c', -0.09, 0.03, -0.15, 0.03, -0.78, 0.00], ['c', -1.98, -0.15, -3.81, -0.15, -5.79, 0.00], ['c', -0.63, 0.03, -0.69, 0.03, -0.78, 0.00], ['c', -0.24, -0.15, -0.24, -0.57, 0.03, -0.66], ['c', 0.06, -0.03, 0.48, -0.09, 0.99, -0.12], ['c', 0.87, -0.06, 1.11, -0.09, 1.35, -0.21], ['c', 0.18, -0.06, 0.33, -0.18, 0.39, -0.30], ['c', 0.06, -0.12, 3.24, -9.42, 3.27, -9.60], ['c', 0.06, -0.33, 0.03, -0.57, -0.15, -0.69], ['c', -0.09, -0.06, -0.12, -0.06, -0.30, -0.06], ['c', -0.69, 0.06, -1.53, 1.02, -2.28, 2.61], ['c', -0.09, 0.21, -0.21, 0.45, -0.27, 0.51], ['c', -0.09, 0.12, -0.33, 0.24, -0.48, 0.24], ['c', -0.18, 0.00, -0.36, -0.15, -0.36, -0.30], ['c', 0.00, -0.24, 0.78, -1.83, 1.26, -2.55], ['c', 0.72, -1.11, 1.47, -1.74, 2.28, -1.92], ['z'], ['m', 5.37, 1.47], ['c', -0.27, -0.12, -0.75, -0.03, -1.14, 0.21], ['c', -0.75, 0.48, -1.47, 1.68, -1.89, 3.15], ['c', -0.45, 1.47, -0.42, 2.34, 0.00, 2.70], ['c', 0.45, 0.39, 1.26, 0.21, 1.83, -0.36], ['c', 0.51, -0.51, 0.99, -1.68, 1.38, -3.27], ['c', 0.30, -1.17, 0.33, -1.74, 0.15, -2.13], ['c', -0.09, -0.15, -0.15, -0.21, -0.33, -0.30], ['z']],
w: 14.689,
h: 13.127
},
'r': {
d: [['M', 6.33, -9.12], ['c', 0.27, -0.03, 0.93, 0.00, 1.20, 0.06], ['c', 0.84, 0.21, 1.23, 0.81, 1.02, 1.53], ['c', -0.24, 0.75, -0.90, 1.17, -1.56, 0.96], ['c', -0.33, -0.09, -0.51, -0.30, -0.66, -0.75], ['c', -0.03, -0.12, -0.09, -0.24, -0.12, -0.30], ['c', -0.09, -0.15, -0.30, -0.24, -0.48, -0.24], ['c', -0.57, 0.00, -1.38, 0.54, -1.65, 1.08], ['c', -0.06, 0.15, -0.33, 1.17, -0.90, 3.27], ['c', -0.57, 2.31, -0.81, 3.12, -0.87, 3.21], ['c', -0.03, 0.06, -0.12, 0.15, -0.18, 0.21], ['l', -0.12, 0.06], ['l', -0.81, 0.03], ['c', -0.69, 0.00, -0.81, 0.00, -0.90, -0.03], ['c', -0.09, -0.06, -0.18, -0.21, -0.18, -0.30], ['c', 0.00, -0.06, 0.39, -1.62, 0.90, -3.51], ['c', 0.84, -3.24, 0.87, -3.45, 0.87, -3.72], ['c', 0.00, -0.21, 0.00, -0.27, -0.03, -0.36], ['c', -0.12, -0.15, -0.21, -0.24, -0.42, -0.24], ['c', -0.24, 0.00, -0.45, 0.15, -0.78, 0.42], ['c', -0.33, 0.36, -0.45, 0.54, -0.72, 1.14], ['c', -0.03, 0.12, -0.21, 0.24, -0.36, 0.27], ['c', -0.12, 0.00, -0.15, 0.00, -0.24, -0.06], ['c', -0.18, -0.12, -0.18, -0.21, -0.06, -0.54], ['c', 0.21, -0.57, 0.42, -0.93, 0.78, -1.32], ['c', 0.54, -0.51, 1.20, -0.81, 1.95, -0.87], ['c', 0.81, -0.03, 1.53, 0.30, 1.92, 0.87], ['l', 0.12, 0.18], ['l', 0.09, -0.09], ['c', 0.57, -0.45, 1.41, -0.84, 2.19, -0.96], ['z']],
w: 9.41,
h: 9.132
},
's': {
d: [['M', 4.47, -8.73], ['c', 0.09, 0.00, 0.36, -0.03, 0.57, -0.03], ['c', 0.75, 0.03, 1.29, 0.24, 1.71, 0.63], ['c', 0.51, 0.54, 0.66, 1.26, 0.36, 1.83], ['c', -0.24, 0.42, -0.63, 0.57, -1.11, 0.42], ['c', -0.33, -0.09, -0.60, -0.36, -0.60, -0.57], ['c', 0.00, -0.03, 0.06, -0.21, 0.15, -0.39], ['c', 0.12, -0.21, 0.15, -0.33, 0.18, -0.48], ['c', 0.00, -0.24, -0.06, -0.48, -0.15, -0.60], ['c', -0.15, -0.21, -0.42, -0.24, -0.75, -0.15], ['c', -0.27, 0.06, -0.48, 0.18, -0.69, 0.36], ['c', -0.39, 0.39, -0.51, 0.96, -0.33, 1.38], ['c', 0.09, 0.21, 0.42, 0.51, 0.78, 0.72], ['c', 1.11, 0.69, 1.59, 1.11, 1.89, 1.68], ['c', 0.21, 0.39, 0.24, 0.78, 0.15, 1.29], ['c', -0.18, 1.20, -1.17, 2.16, -2.52, 2.52], ['c', -1.02, 0.24, -1.95, 0.12, -2.70, -0.42], ['c', -0.72, -0.51, -0.99, -1.47, -0.60, -2.19], ['c', 0.24, -0.48, 0.72, -0.63, 1.17, -0.42], ['c', 0.33, 0.18, 0.54, 0.45, 0.57, 0.81], ['c', 0.00, 0.21, -0.03, 0.30, -0.33, 0.51], ['c', -0.33, 0.24, -0.39, 0.42, -0.27, 0.69], ['c', 0.06, 0.15, 0.21, 0.27, 0.45, 0.33], ['c', 0.30, 0.09, 0.87, 0.09, 1.20, 0.00], ['c', 0.75, -0.21, 1.23, -0.72, 1.29, -1.35], ['c', 0.03, -0.42, -0.15, -0.81, -0.54, -1.20], ['c', -0.24, -0.24, -0.48, -0.42, -1.41, -1.02], ['c', -0.69, -0.42, -1.05, -0.93, -1.05, -1.47], ['c', 0.00, -0.39, 0.12, -0.87, 0.30, -1.23], ['c', 0.27, -0.57, 0.78, -1.05, 1.38, -1.35], ['c', 0.24, -0.12, 0.63, -0.27, 0.90, -0.30], ['z']],
w: 6.632,
h: 8.758
},
'z': {
d: [['M', 2.64, -7.95], ['c', 0.36, -0.09, 0.81, -0.03, 1.71, 0.27], ['c', 0.78, 0.21, 0.96, 0.27, 1.74, 0.30], ['c', 0.87, 0.06, 1.02, 0.03, 1.38, -0.21], ['c', 0.21, -0.15, 0.33, -0.15, 0.48, -0.06], ['c', 0.15, 0.09, 0.21, 0.30, 0.15, 0.45], ['c', -0.03, 0.06, -1.26, 1.26, -2.76, 2.67], ['l', -2.73, 2.55], ['l', 0.54, 0.03], ['c', 0.54, 0.03, 0.72, 0.03, 2.01, 0.15], ['c', 0.36, 0.03, 0.90, 0.06, 1.20, 0.09], ['c', 0.66, 0.00, 0.81, -0.03, 1.02, -0.24], ['c', 0.30, -0.30, 0.39, -0.72, 0.27, -1.23], ['c', -0.06, -0.27, -0.06, -0.27, -0.03, -0.39], ['c', 0.15, -0.30, 0.54, -0.27, 0.69, 0.03], ['c', 0.15, 0.33, 0.27, 1.02, 0.27, 1.50], ['c', 0.00, 1.47, -1.11, 2.70, -2.52, 2.79], ['c', -0.57, 0.03, -1.02, -0.09, -2.01, -0.51], ['c', -1.02, -0.42, -1.23, -0.48, -2.13, -0.54], ['c', -0.81, -0.06, -0.96, -0.03, -1.26, 0.18], ['c', -0.12, 0.06, -0.24, 0.12, -0.27, 0.12], ['c', -0.27, 0.00, -0.45, -0.30, -0.36, -0.51], ['c', 0.03, -0.06, 1.32, -1.32, 2.91, -2.79], ['l', 2.88, -2.73], ['c', -0.03, 0.00, -0.21, 0.03, -0.42, 0.06], ['c', -0.21, 0.03, -0.78, 0.09, -1.23, 0.12], ['c', -1.11, 0.12, -1.23, 0.15, -1.95, 0.27], ['c', -0.72, 0.15, -1.17, 0.18, -1.29, 0.09], ['c', -0.27, -0.18, -0.21, -0.75, 0.12, -1.26], ['c', 0.39, -0.60, 0.93, -1.02, 1.59, -1.20], ['z']],
w: 8.573,
h: 8.743
},
'+': {
d: [['M', 3.48, -9.3], ['c', 0.18, -0.09, 0.36, -0.09, 0.54, 0.00], ['c', 0.18, 0.09, 0.24, 0.15, 0.33, 0.30], ['l', 0.06, 0.15], ['l', 0.00, 1.29], ['l', 0.00, 1.29], ['l', 1.29, 0.00], ['c', 1.23, 0.00, 1.29, 0.00, 1.41, 0.06], ['c', 0.06, 0.03, 0.15, 0.09, 0.18, 0.12], ['c', 0.12, 0.09, 0.21, 0.33, 0.21, 0.48], ['c', 0.00, 0.15, -0.09, 0.39, -0.21, 0.48], ['c', -0.03, 0.03, -0.12, 0.09, -0.18, 0.12], ['c', -0.12, 0.06, -0.18, 0.06, -1.41, 0.06], ['l', -1.29, 0.00], ['l', 0.00, 1.29], ['c', 0.00, 1.23, 0.00, 1.29, -0.06, 1.41], ['c', -0.09, 0.18, -0.15, 0.24, -0.30, 0.33], ['c', -0.21, 0.09, -0.39, 0.09, -0.57, 0.00], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['c', -0.06, -0.12, -0.06, -0.18, -0.06, -1.41], ['l', 0.00, -1.29], ['l', -1.29, 0.00], ['c', -1.23, 0.00, -1.29, 0.00, -1.41, -0.06], ['c', -0.18, -0.09, -0.24, -0.15, -0.33, -0.33], ['c', -0.09, -0.18, -0.09, -0.36, 0.00, -0.54], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['l', 0.15, -0.06], ['l', 1.26, 0.00], ['l', 1.29, 0.00], ['l', 0.00, -1.29], ['c', 0.00, -1.23, 0.00, -1.29, 0.06, -1.41], ['c', 0.09, -0.18, 0.15, -0.24, 0.33, -0.33], ['z']],
w: 7.507,
h: 7.515
},
',': {
d: [['M', 1.85, -3.36], ['c', 0.57, -0.15, 1.17, 0.03, 1.59, 0.45], ['c', 0.45, 0.45, 0.60, 0.96, 0.51, 1.89], ['c', -0.09, 1.23, -0.42, 2.46, -0.99, 3.93], ['c', -0.30, 0.72, -0.72, 1.62, -0.78, 1.68], ['c', -0.18, 0.21, -0.51, 0.18, -0.66, -0.06], ['c', -0.03, -0.06, -0.06, -0.15, -0.06, -0.18], ['c', 0.00, -0.06, 0.12, -0.33, 0.24, -0.63], ['c', 0.84, -1.80, 1.02, -2.61, 0.69, -3.24], ['c', -0.12, -0.24, -0.27, -0.36, -0.75, -0.60], ['c', -0.36, -0.15, -0.42, -0.21, -0.60, -0.39], ['c', -0.69, -0.69, -0.69, -1.71, 0.00, -2.40], ['c', 0.21, -0.21, 0.51, -0.39, 0.81, -0.45], ['z']],
w: 3.452,
h: 8.143
},
'-': {
d: [['M', 0.18, -5.34], ['c', 0.09, -0.06, 0.15, -0.06, 2.31, -0.06], ['c', 2.46, 0.00, 2.37, 0.00, 2.46, 0.21], ['c', 0.12, 0.21, 0.03, 0.42, -0.15, 0.54], ['c', -0.09, 0.06, -0.15, 0.06, -2.28, 0.06], ['c', -2.16, 0.00, -2.22, 0.00, -2.31, -0.06], ['c', -0.27, -0.15, -0.27, -0.54, -0.03, -0.69], ['z']],
w: 5.001,
h: 0.81
},
'.': {
d: [['M', 1.32, -3.36], ['c', 1.05, -0.27, 2.10, 0.57, 2.10, 1.65], ['c', 0.00, 1.08, -1.05, 1.92, -2.10, 1.65], ['c', -0.90, -0.21, -1.50, -1.14, -1.26, -2.04], ['c', 0.12, -0.63, 0.63, -1.11, 1.26, -1.26], ['z']],
w: 3.413,
h: 3.402
},
'scripts.wedge': {
d: [['M', -3.66, -7.44], ['c', 0.06, -0.09, 0.00, -0.09, 0.81, 0.03], ['c', 1.86, 0.30, 3.84, 0.30, 5.73, 0.00], ['c', 0.78, -0.12, 0.72, -0.12, 0.78, -0.03], ['c', 0.15, 0.15, 0.12, 0.24, -0.24, 0.60], ['c', -0.93, 0.93, -1.98, 2.76, -2.67, 4.62], ['c', -0.30, 0.78, -0.51, 1.71, -0.51, 2.13], ['c', 0.00, 0.15, 0.00, 0.18, -0.06, 0.27], ['c', -0.12, 0.09, -0.24, 0.09, -0.36, 0.00], ['c', -0.06, -0.09, -0.06, -0.12, -0.06, -0.27], ['c', 0.00, -0.42, -0.21, -1.35, -0.51, -2.13], ['c', -0.69, -1.86, -1.74, -3.69, -2.67, -4.62], ['c', -0.36, -0.36, -0.39, -0.45, -0.24, -0.60], ['z']],
w: 7.49,
h: 7.752
},
'scripts.thumb': {
d: [['M', -0.54, -3.69], ['c', 0.15, -0.03, 0.36, -0.06, 0.51, -0.06], ['c', 1.44, 0.00, 2.58, 1.11, 2.94, 2.85], ['c', 0.09, 0.48, 0.09, 1.32, 0.00, 1.80], ['c', -0.27, 1.41, -1.08, 2.43, -2.16, 2.73], ['l', -0.18, 0.06], ['l', 0.00, 0.12], ['c', 0.03, 0.06, 0.06, 0.45, 0.09, 0.87], ['c', 0.03, 0.57, 0.03, 0.78, 0.00, 0.84], ['c', -0.09, 0.27, -0.39, 0.48, -0.66, 0.48], ['c', -0.27, 0.00, -0.57, -0.21, -0.66, -0.48], ['c', -0.03, -0.06, -0.03, -0.27, 0.00, -0.84], ['c', 0.03, -0.42, 0.06, -0.81, 0.09, -0.87], ['l', 0.00, -0.12], ['l', -0.18, -0.06], ['c', -1.08, -0.30, -1.89, -1.32, -2.16, -2.73], ['c', -0.09, -0.48, -0.09, -1.32, 0.00, -1.80], ['c', 0.15, -0.84, 0.51, -1.53, 1.02, -2.04], ['c', 0.39, -0.39, 0.84, -0.63, 1.35, -0.75], ['z'], ['m', 1.05, 0.90], ['c', -0.15, -0.09, -0.21, -0.09, -0.45, -0.12], ['c', -0.15, 0.00, -0.30, 0.03, -0.39, 0.03], ['c', -0.57, 0.18, -0.90, 0.72, -1.08, 1.74], ['c', -0.06, 0.48, -0.06, 1.80, 0.00, 2.28], ['c', 0.15, 0.90, 0.42, 1.44, 0.90, 1.65], ['c', 0.18, 0.09, 0.21, 0.09, 0.51, 0.09], ['c', 0.30, 0.00, 0.33, 0.00, 0.51, -0.09], ['c', 0.48, -0.21, 0.75, -0.75, 0.90, -1.65], ['c', 0.03, -0.27, 0.03, -0.54, 0.03, -1.14], ['c', 0.00, -0.60, 0.00, -0.87, -0.03, -1.14], ['c', -0.15, -0.90, -0.45, -1.44, -0.90, -1.65], ['z']],
w: 5.955,
h: 9.75
},
'scripts.open': {
d: [['M', -0.54, -3.69], ['c', 0.15, -0.03, 0.36, -0.06, 0.51, -0.06], ['c', 1.44, 0.00, 2.58, 1.11, 2.94, 2.85], ['c', 0.09, 0.48, 0.09, 1.32, 0.00, 1.80], ['c', -0.33, 1.74, -1.47, 2.85, -2.91, 2.85], ['c', -1.44, 0.00, -2.58, -1.11, -2.91, -2.85], ['c', -0.09, -0.48, -0.09, -1.32, 0.00, -1.80], ['c', 0.15, -0.84, 0.51, -1.53, 1.02, -2.04], ['c', 0.39, -0.39, 0.84, -0.63, 1.35, -0.75], ['z'], ['m', 1.11, 0.90], ['c', -0.21, -0.09, -0.27, -0.09, -0.51, -0.12], ['c', -0.30, 0.00, -0.42, 0.03, -0.66, 0.15], ['c', -0.24, 0.12, -0.51, 0.39, -0.66, 0.63], ['c', -0.54, 0.93, -0.63, 2.64, -0.21, 3.81], ['c', 0.21, 0.54, 0.51, 0.90, 0.93, 1.11], ['c', 0.21, 0.09, 0.24, 0.09, 0.54, 0.09], ['c', 0.30, 0.00, 0.33, 0.00, 0.54, -0.09], ['c', 0.42, -0.21, 0.72, -0.57, 0.93, -1.11], ['c', 0.36, -0.99, 0.36, -2.37, 0.00, -3.36], ['c', -0.21, -0.54, -0.51, -0.90, -0.90, -1.11], ['z']],
w: 5.955,
h: 7.5
},
'scripts.longphrase': {
d: [['M', 1.47, -15.09], ['c', 0.36, -0.09, 0.66, -0.18, 0.69, -0.18], ['c', 0.06, 0.00, 0.06, 0.54, 0.06, 11.25], ['l', 0.00, 11.25], ['l', -0.63, 0.15], ['c', -0.66, 0.18, -1.44, 0.39, -1.50, 0.39], ['c', -0.03, 0.00, -0.03, -3.39, -0.03, -11.25], ['l', 0.00, -11.25], ['l', 0.36, -0.09], ['c', 0.21, -0.06, 0.66, -0.18, 1.05, -0.27], ['z']],
w: 2.16,
h: 23.04
},
'scripts.mediumphrase': {
d: [['M', 1.47, -7.59], ['c', 0.36, -0.09, 0.66, -0.18, 0.69, -0.18], ['c', 0.06, 0.00, 0.06, 0.39, 0.06, 7.50], ['l', 0.00, 7.50], ['l', -0.63, 0.15], ['c', -0.66, 0.18, -1.44, 0.39, -1.50, 0.39], ['c', -0.03, 0.00, -0.03, -2.28, -0.03, -7.50], ['l', 0.00, -7.50], ['l', 0.36, -0.09], ['c', 0.21, -0.06, 0.66, -0.18, 1.05, -0.27], ['z']],
w: 2.16,
h: 15.54
},
'scripts.shortphrase': {
d: [['M', 1.47, -7.59], ['c', 0.36, -0.09, 0.66, -0.18, 0.69, -0.18], ['c', 0.06, 0.00, 0.06, 0.21, 0.06, 3.75], ['l', 0.00, 3.75], ['l', -0.42, 0.09], ['c', -0.57, 0.18, -1.65, 0.45, -1.71, 0.45], ['c', -0.03, 0.00, -0.03, -0.72, -0.03, -3.75], ['l', 0.00, -3.75], ['l', 0.36, -0.09], ['c', 0.21, -0.06, 0.66, -0.18, 1.05, -0.27], ['z']],
w: 2.16,
h: 8.04
},
'scripts.snap': {
d: [['M', 4.50, -3.39], ['c', 0.36, -0.03, 0.96, -0.03, 1.35, 0.00], ['c', 1.56, 0.15, 3.15, 0.90, 4.20, 2.01], ['c', 0.24, 0.27, 0.33, 0.42, 0.33, 0.60], ['c', 0.00, 0.27, 0.03, 0.24, -2.46, 2.22], ['c', -1.29, 1.02, -2.40, 1.86, -2.49, 1.92], ['c', -0.18, 0.09, -0.30, 0.09, -0.48, 0.00], ['c', -0.09, -0.06, -1.20, -0.90, -2.49, -1.92], ['c', -2.49, -1.98, -2.46, -1.95, -2.46, -2.22], ['c', 0.00, -0.18, 0.09, -0.33, 0.33, -0.60], ['c', 1.05, -1.08, 2.64, -1.86, 4.17, -2.01], ['z'], ['m', 1.29, 1.17], ['c', -1.47, -0.15, -2.97, 0.30, -4.14, 1.20], ['l', -0.18, 0.15], ['l', 0.06, 0.09], ['c', 0.15, 0.12, 3.63, 2.85, 3.66, 2.85], ['c', 0.03, 0.00, 3.51, -2.73, 3.66, -2.85], ['l', 0.06, -0.09], ['l', -0.18, -0.15], ['c', -0.84, -0.66, -1.89, -1.08, -2.94, -1.20], ['z']],
w: 10.38,
h: 6.84
}
};
// Custom characters that weren't generated from the font:
glyphs['noteheads.slash.whole'] = {
d: [['M', 5, -5], ['l', 1, 1], ['l', -5, 5], ['l', -1, -1], ['z'], ['m', 4, 6], ['l', -5, -5], ['l', 2, -2], ['l', 5, 5], ['z'], ['m', 0, -2], ['l', 1, 1], ['l', -5, 5], ['l', -1, -1], ['z'], ['m', -4, 6], ['l', -5, -5], ['l', 2, -2], ['l', 5, 5], ['z']],
w: 10.81,
h: 15.63
};
glyphs['noteheads.slash.quarter'] = {
d: [['M', 9, -6], ['l', 0, 4], ['l', -9, 9], ['l', 0, -4], ['z']],
w: 9,
h: 9
};
glyphs['noteheads.harmonic.quarter'] = {
d: [['M', 3.63, -4.02], ['c', 0.09, -0.06, 0.18, -0.09, 0.24, -0.03], ['c', 0.03, 0.03, 0.87, 0.93, 1.83, 2.01], ['c', 1.50, 1.65, 1.80, 1.98, 1.80, 2.04], ['c', 0.00, 0.06, -0.30, 0.39, -1.80, 2.04], ['c', -0.96, 1.08, -1.80, 1.98, -1.83, 2.01], ['c', -0.06, 0.06, -0.15, 0.03, -0.24, -0.03], ['c', -0.12, -0.09, -3.54, -3.84, -3.60, -3.93], ['c', -0.03, -0.03, -0.03, -0.09, -0.03, -0.15], ['c', 0.03, -0.06, 3.45, -3.84, 3.63, -3.96], ['z']],
w: 7.5,
h: 8.165
};
glyphs['noteheads.triangle.quarter'] = {
d: [['M', 0, 4], ['l', 9, 0], ['l', -4.5, -9], ['z']],
w: 9,
h: 9
};
var pathClone = function pathClone(pathArray) {
var res = [];
for (var i = 0, ii = pathArray.length; i < ii; i++) {
res[i] = [];
for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
res[i][j] = pathArray[i][j];
}
}
return res;
};
var pathScale = function pathScale(pathArray, kx, ky) {
for (var i = 0, ii = pathArray.length; i < ii; i++) {
var p = pathArray[i];
var j, jj;
for (j = 1, jj = p.length; j < jj; j++) {
p[j] *= j % 2 ? kx : ky;
}
}
};
var Glyphs = {
printSymbol: function printSymbol(x, y, symb, paper, attrs) {
if (!glyphs[symb]) return null;
var pathArray = pathClone(glyphs[symb].d);
pathArray[0][1] += x;
pathArray[0][2] += y;
var path = "";
for (var i = 0; i < pathArray.length; i++) {
path += pathArray[i].join(" ");
}
attrs.path = path;
return paper.path(attrs);
},
getPathForSymbol: function getPathForSymbol(x, y, symb, scalex, scaley) {
scalex = scalex || 1;
scaley = scaley || 1;
if (!glyphs[symb]) return null;
var pathArray = pathClone(glyphs[symb].d);
if (scalex !== 1 || scaley !== 1) pathScale(pathArray, scalex, scaley);
pathArray[0][1] += x;
pathArray[0][2] += y;
return pathArray;
},
getSymbolWidth: function getSymbolWidth(symbol) {
if (glyphs[symbol]) return glyphs[symbol].w;
return 0;
},
symbolHeightInPitches: function symbolHeightInPitches(symbol) {
var height = glyphs[symbol] ? glyphs[symbol].h : 0;
return height / spacing.STEP;
},
getSymbolAlign: function getSymbolAlign(symbol) {
if (symbol.substring(0, 7) === "scripts" && symbol !== "scripts.roll") {
return "center";
}
return "left";
},
getYCorr: function getYCorr(symbol) {
switch (symbol) {
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case "+":
return -2;
case "timesig.common":
case "timesig.cut":
return 0;
case "flags.d32nd":
return -1;
case "flags.d64th":
return -2;
case "flags.u32nd":
return 1;
case "flags.u64th":
return 3;
case "rests.whole":
return 1;
case "rests.half":
return -1;
case "rests.8th":
return -1;
case "rests.quarter":
return -1;
case "rests.16th":
return -1;
case "rests.32nd":
return -1;
case "rests.64th":
return -1;
case "f":
case "m":
case "p":
case "s":
case "z":
return -4;
case "scripts.trill":
case "scripts.upbow":
case "scripts.downbow":
return -2;
case "scripts.ufermata":
case "scripts.wedge":
case "scripts.roll":
case "scripts.shortphrase":
case "scripts.longphrase":
return -1;
case "scripts.dfermata":
return 1;
default:
return 0;
}
},
setSymbol: function setSymbol(name, path) {
glyphs[name] = path;
}
};
module.exports = Glyphs; // we need the glyphs for layout information
/***/ }),
/***/ "./src/write/creation/translate-chord.js":
/*!***********************************************!*\
!*** ./src/write/creation/translate-chord.js ***!
\***********************************************/
/***/ (function(module) {
function germanNote(note) {
switch (note) {
case "B#":
return "H#";
case "B♯":
return "H♯";
case "B":
return "H";
case "Bb":
return "B";
case "B♭":
return "B";
}
return note;
}
function translateChord(chordString, jazzchords, germanAlphabet) {
var lines = chordString.split("\n");
for (var i = 0; i < lines.length; i++) {
var chord = lines[i];
// If the chord isn't in a recognizable format then just skip it.
var reg = chord.match(/^([ABCDEFG][♯♭]?)?([^\/]+)?(\/([ABCDEFG][#b♯♭]?))?/);
if (!reg) {
continue;
}
var baseChord = reg[1] || "";
var modifier = reg[2] || "";
var bassNote = reg[4] || "";
if (germanAlphabet) {
baseChord = germanNote(baseChord);
bassNote = germanNote(bassNote);
}
// This puts markers in the pieces of the chord that are read by the svg creator.
// After the main part of the chord (the letter, a sharp or flat, and "m") a marker is added. Before a slash a marker is added.
var marker = jazzchords ? "\x03" : "";
var bass = bassNote ? "/" + bassNote : "";
lines[i] = [baseChord, modifier, bass].join(marker);
}
return lines.join("\n");
}
module.exports = translateChord;
/***/ }),
/***/ "./src/write/draw/absolute.js":
/*!************************************!*\
!*** ./src/write/draw/absolute.js ***!
\************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var drawTempo = __webpack_require__(/*! ./tempo */ "./src/write/draw/tempo.js");
var drawRelativeElement = __webpack_require__(/*! ./relative */ "./src/write/draw/relative.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var setClass = __webpack_require__(/*! ../helpers/set-class */ "./src/write/helpers/set-class.js");
var elementGroup = __webpack_require__(/*! ./group-elements */ "./src/write/draw/group-elements.js");
function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
if (params.invisible) return;
var isTempo = params.children.length > 0 && params.children[0].type === "TempoElement";
params.elemset = [];
elementGroup.beginGroup(renderer.paper, renderer.controller);
for (var i = 0; i < params.children.length; i++) {
var child = params.children[i];
switch (child.type) {
case "TempoElement":
drawTempo(renderer, child);
break;
default:
var el = drawRelativeElement(renderer, child, bartop);
if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
el.setAttribute('class', 'abcjs-notehead');
}
}
}
var klass = params.type;
if (params.type === 'note' || params.type === 'rest') {
params.counters = renderer.controller.classes.getCurrent();
klass += ' d' + Math.round(params.durationClass * 1000) / 1000;
klass = klass.replace(/\./g, '-');
if (params.abcelem.pitches) {
for (var j = 0; j < params.abcelem.pitches.length; j++) {
klass += ' p' + params.abcelem.pitches[j].pitch;
}
}
}
var g = elementGroup.endGroup(klass, params.type);
if (g) {
// TODO-PER-HACK! This corrects the classes because the tablature is not being created at the right time.
if (params.cloned) {
params.cloned.overrideClasses = g.className.baseVal;
}
if (params.overrideClasses) {
var type = g.classList && g.classList.length > 0 ? g.classList[0] + ' ' : '';
g.setAttribute("class", type + params.overrideClasses);
}
if (isTempo) {
params.startChar = params.abcelem.startChar;
params.endChar = params.abcelem.endChar;
selectables.add(params, g, false, staffPos);
} else {
params.elemset.push(g);
var isSelectable = false;
if (params.type === 'note' || params.type === 'tabNumber') {
isSelectable = true;
}
selectables.add(params, g, isSelectable, staffPos);
}
} else if (params.elemset.length > 0) selectables.add(params, params.elemset[0], params.type === 'note', staffPos);
// If there was no output, then don't add to the selectables. This happens when using the "y" spacer, for instance.
if (params.klass) setClass(params.elemset, "mark", "", "#00ff00");
if (params.hint) setClass(params.elemset, "abcjs-hint", "", null);
params.abcelem.abselem = params;
if (params.heads && params.heads.length > 0) {
params.notePositions = [];
for (var jj = 0; jj < params.heads.length; jj++) {
params.notePositions.push({
x: params.heads[jj].x + params.heads[jj].w / 2,
y: staffPos.zero - params.heads[jj].pitch * spacing.STEP
});
}
}
}
module.exports = drawAbsolute;
/***/ }),
/***/ "./src/write/draw/beam.js":
/*!********************************!*\
!*** ./src/write/draw/beam.js ***!
\********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawBeam(renderer, params) {
if (params.beams.length === 0) return;
var pathString = "";
for (var i = 0; i < params.beams.length; i++) {
var beam = params.beams[i];
if (beam.split) {
var slope = getSlope(renderer, beam.startX, beam.startY, beam.endX, beam.endY);
var xes = [];
for (var j = 0; j < beam.split.length; j += 2) {
xes.push([beam.split[j], beam.split[j + 1]]);
}
for (j = 0; j < xes.length; j++) {
var y1 = getY(beam.startX, beam.startY, slope, xes[j][0]);
var y2 = getY(beam.startX, beam.startY, slope, xes[j][1]);
pathString += draw(renderer, xes[j][0], y1, xes[j][1], y2, beam.dy);
}
} else pathString += draw(renderer, beam.startX, beam.startY, beam.endX, beam.endY, beam.dy);
}
var durationClass = ("abcjs-d" + params.duration).replace(/\./g, "-");
var klasses = renderer.controller.classes.generate('beam-elem ' + durationClass);
var el = printPath(renderer, {
path: pathString,
stroke: "none",
fill: renderer.foregroundColor,
'class': klasses
});
return [el];
}
function draw(renderer, startX, startY, endX, endY, dy) {
// the X coordinates are actual coordinates, but the Y coordinates are in pitches.
startY = roundNumber(renderer.calcY(startY));
endY = roundNumber(renderer.calcY(endY));
startX = roundNumber(startX);
endX = roundNumber(endX);
var startY2 = roundNumber(startY + dy);
var endY2 = roundNumber(endY + dy);
return "M" + startX + " " + startY + " L" + endX + " " + endY + "L" + endX + " " + endY2 + " L" + startX + " " + startY2 + "z";
}
function getSlope(renderer, startX, startY, endX, endY) {
return (endY - startY) / (endX - startX);
}
function getY(startX, startY, slope, currentX) {
var x = currentX - startX;
return startY + x * slope;
}
module.exports = drawBeam;
/***/ }),
/***/ "./src/write/draw/brace.js":
/*!*********************************!*\
!*** ./src/write/draw/brace.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
function drawBrace(renderer, params, selectables) {
// The absoluteY number is the spot where the note on the first ledger line is drawn (i.e. middle C if treble clef)
// The STEP offset here moves it to the top and bottom lines
var startY = params.startVoice.staff.absoluteY - spacing.STEP * 10;
if (params.endVoice && params.endVoice.staff) params.endY = params.endVoice.staff.absoluteY - spacing.STEP * 2;else if (params.lastContinuedVoice && params.lastContinuedVoice.staff) params.endY = params.lastContinuedVoice.staff.absoluteY - spacing.STEP * 2;else params.endY = params.startVoice.staff.absoluteY - spacing.STEP * 2;
return draw(renderer, params.x, startY, params.endY, params.type, params.header, selectables);
}
function straightPath(renderer, xLeft, yTop, yBottom, type) {
xLeft += spacing.STEP;
var xLineWidth = spacing.STEP * 0.75;
var yOverlap = spacing.STEP * 0.75;
var height = yBottom - yTop;
// Straight line
var pathString = sprintf("M %f %f l %f %f l %f %f l %f %f z", xLeft, yTop - yOverlap,
// top left line
0, height + yOverlap * 2,
// bottom left line
xLineWidth, 0,
// bottom right line
0, -(height + yOverlap * 2) // top right line
);
// Top arm
var wCurve = spacing.STEP * 2;
var hCurve = spacing.STEP;
pathString += sprintf("M %f %f q %f %f %f %f q %f %f %f %f z", xLeft + xLineWidth, yTop - yOverlap,
// top left arm
wCurve * 0.6, hCurve * 0.2, wCurve, -hCurve,
// right point
-wCurve * 0.1, hCurve * 0.3, -wCurve, hCurve + spacing.STEP // left bottom
);
// Bottom arm
pathString += sprintf("M %f %f q %f %f %f %f q %f %f %f %f z", xLeft + xLineWidth, yTop + yOverlap + height,
// bottom left arm
wCurve * 0.6, -hCurve * 0.2, wCurve, hCurve,
// right point
-wCurve * 0.1, -hCurve * 0.3, -wCurve, -hCurve - spacing.STEP // left bottom
);
return renderer.paper.path({
path: pathString,
stroke: renderer.foregroundColor,
fill: renderer.foregroundColor,
'class': renderer.controller.classes.generate(type),
"data-name": type
});
}
function curvyPath(renderer, xLeft, yTop, yBottom, type) {
var yHeight = yBottom - yTop;
var pathString = curve(xLeft, yTop, [7.5, -8, 21, 0, 18.5, -10.5, 7.5], [0, yHeight / 5.5, yHeight / 3.14, yHeight / 2, yHeight / 2.93, yHeight / 4.88, 0]);
pathString += curve(xLeft, yTop, [0, 17.5, -7.5, 6.6, -5, 20, 0], [yHeight / 2, yHeight / 1.46, yHeight / 1.22, yHeight, yHeight / 1.19, yHeight / 1.42, yHeight / 2]);
return renderer.paper.path({
path: pathString,
stroke: renderer.foregroundColor,
fill: renderer.foregroundColor,
'class': renderer.controller.classes.generate(type),
"data-name": type
});
}
function curve(xLeft, yTop, xCurve, yCurve) {
return sprintf("M %f %f C %f %f %f %f %f %f C %f %f %f %f %f %f z", xLeft + xCurve[0], yTop + yCurve[0], xLeft + xCurve[1], yTop + yCurve[1], xLeft + xCurve[2], yTop + yCurve[2], xLeft + xCurve[3], yTop + yCurve[3], xLeft + xCurve[4], yTop + yCurve[4], xLeft + xCurve[5], yTop + yCurve[5], xLeft + xCurve[6], yTop + yCurve[6]);
}
var draw = function draw(renderer, xLeft, yTop, yBottom, type, header, selectables) {
//Tony
var ret;
if (header) {
renderer.paper.openGroup({
klass: renderer.controller.classes.generate("staff-extra voice-name"),
"data-name": type
});
var position = yTop + (yBottom - yTop) / 2;
position = position - renderer.controller.getTextSize.baselineToCenter(header, "voicefont", 'staff-extra voice-name', 0, 1);
renderText(renderer, {
x: renderer.padding.left,
y: position,
text: header,
type: 'voicefont',
klass: 'staff-extra voice-name',
anchor: 'start',
centerVertically: true
});
}
if (type === "brace") ret = curvyPath(renderer, xLeft, yTop, yBottom, type);else if (type === "bracket") ret = straightPath(renderer, xLeft, yTop, yBottom, type);
if (header) {
ret = renderer.paper.closeGroup();
}
selectables.wrapSvgEl({
el_type: type,
startChar: -1,
endChar: -1
}, ret);
return ret;
};
module.exports = drawBrace;
/***/ }),
/***/ "./src/write/draw/crescendo.js":
/*!*************************************!*\
!*** ./src/write/draw/crescendo.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawCrescendo(renderer, params, selectables) {
if (params.pitch === undefined) window.console.error("Crescendo Element y-coordinate not set.");
var y = renderer.calcY(params.pitch) + 4; // This is the top pixel to use (it is offset a little so that it looks good with the volume marks.)
var height = 8;
// TODO-PER: This is just a quick hack to make the dynamic marks not crash if they are mismatched. See the slur treatment for the way to get the beginning and end.
var left = params.anchor1 ? params.anchor1.x : 0;
var right = params.anchor2 ? params.anchor2.x : 800;
var el;
if (params.dir === "<") {
el = drawLine(renderer, y + height / 2, y, y + height / 2, y + height, left, right);
} else {
el = drawLine(renderer, y, y + height / 2, y + height, y + height / 2, left, right);
}
selectables.wrapSvgEl({
el_type: "dynamicDecoration",
startChar: -1,
endChar: -1
}, el);
return [el];
}
var drawLine = function drawLine(renderer, y1, y2, y3, y4, left, right) {
y1 = roundNumber(y1);
y2 = roundNumber(y2);
y3 = roundNumber(y3);
y4 = roundNumber(y4);
left = roundNumber(left);
right = roundNumber(right);
var pathString = sprintf("M %f %f L %f %f M %f %f L %f %f", left, y1, right, y2, left, y3, right, y4);
return printPath(renderer, {
path: pathString,
highlight: "stroke",
stroke: renderer.foregroundColor,
'class': renderer.controller.classes.generate('dynamics decoration'),
"data-name": "dynamics"
});
};
module.exports = drawCrescendo;
/***/ }),
/***/ "./src/write/draw/debug-box.js":
/*!*************************************!*\
!*** ./src/write/draw/debug-box.js ***!
\*************************************/
/***/ (function(module) {
function printDebugBox(renderer, attr, comment) {
var box = renderer.paper.rectBeneath(attr);
if (comment) renderer.paper.text(comment, {
x: 0,
y: attr.y + 7,
"text-anchor": "start",
"font-size": "14px",
fill: "rgba(0,0,255,.4)",
stroke: "rgba(0,0,255,.4)"
});
return box;
}
module.exports = printDebugBox;
/***/ }),
/***/ "./src/write/draw/draw.js":
/*!********************************!*\
!*** ./src/write/draw/draw.js ***!
\********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var drawStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/draw/staff-group.js");
var setPaperSize = __webpack_require__(/*! ./set-paper-size */ "./src/write/draw/set-paper-size.js");
var nonMusic = __webpack_require__(/*! ./non-music */ "./src/write/draw/non-music.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/selectables.js");
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset) {
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
var groupClasses = {};
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-top";
renderer.paper.openGroup(groupClasses);
renderer.moveY(renderer.padding.top);
nonMusic(renderer, abcTune.topText, selectables);
renderer.paper.closeGroup();
renderer.moveY(renderer.spacing.music);
var staffgroups = [];
for (var line = 0; line < abcTune.lines.length; line++) {
classes.incrLine();
var abcLine = abcTune.lines[line];
if (abcLine.staff) {
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
renderer.paper.openGroup(groupClasses);
if (abcLine.vskip) {
renderer.moveY(abcLine.vskip);
}
if (staffgroups.length >= 1) addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
staffgroup.line = lineOffset + line; // If there are non-music lines then the staffgroup array won't line up with the line array, so this keeps track.
staffgroups.push(staffgroup);
renderer.paper.closeGroup();
} else if (abcLine.nonMusic) {
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
renderer.paper.openGroup(groupClasses);
nonMusic(renderer, abcLine.nonMusic, selectables);
renderer.paper.closeGroup();
}
}
classes.reset();
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-bottom";
renderer.paper.openGroup(groupClasses);
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
nonMusic(renderer, abcTune.bottomText, selectables);
renderer.paper.closeGroup();
}
setPaperSize(renderer, maxWidth, scale, responsive);
return {
staffgroups: staffgroups,
selectables: selectables.getElements()
};
}
function engraveStaffLine(renderer, staffGroup, selectables, lineNumber) {
drawStaffGroup(renderer, staffGroup, selectables, lineNumber);
var height = staffGroup.height * spacing.STEP;
renderer.y += height;
return staffGroup;
}
function addStaffPadding(renderer, staffSeparation, lastStaffGroup, thisStaffGroup) {
var lastStaff = lastStaffGroup.staffs[lastStaffGroup.staffs.length - 1];
var lastBottomLine = -(lastStaff.bottom - 2); // The 2 is because the scale goes to 2 below the last line.
var nextTopLine = thisStaffGroup.staffs[0].top - 10; // Because 10 represents the top line.
var naturalSeparation = nextTopLine + lastBottomLine; // This is how far apart they'd be without extra spacing
var separationInPixels = naturalSeparation * spacing.STEP;
if (separationInPixels < staffSeparation) renderer.moveY(staffSeparation - separationInPixels);
}
module.exports = draw;
/***/ }),
/***/ "./src/write/draw/dynamics.js":
/*!************************************!*\
!*** ./src/write/draw/dynamics.js ***!
\************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var printSymbol = __webpack_require__(/*! ./print-symbol */ "./src/write/draw/print-symbol.js");
function drawDynamics(renderer, params, selectables) {
if (params.pitch === undefined) window.console.error("Dynamic Element y-coordinate not set.");
var scalex = 1;
var scaley = 1;
var el = printSymbol(renderer, params.anchor.x, params.pitch, params.dec, {
scalex: scalex,
scaley: scaley,
klass: renderer.controller.classes.generate('decoration dynamics'),
fill: renderer.foregroundColor,
stroke: "none",
name: "dynamics"
});
selectables.wrapSvgEl({
el_type: "dynamicDecoration",
startChar: -1,
endChar: -1,
decoration: params.dec
}, el);
return [el];
}
module.exports = drawDynamics;
/***/ }),
/***/ "./src/write/draw/ending.js":
/*!**********************************!*\
!*** ./src/write/draw/ending.js ***!
\**********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawEnding(renderer, params, linestartx, lineendx, selectables) {
if (params.pitch === undefined) window.console.error("Ending Element y-coordinate not set.");
var y = roundNumber(renderer.calcY(params.pitch));
var height = 20;
var pathString = '';
if (params.anchor1) {
linestartx = roundNumber(params.anchor1.x + params.anchor1.w);
pathString += sprintf("M %f %f L %f %f ", linestartx, y, linestartx, roundNumber(y + height));
}
if (params.anchor2) {
lineendx = roundNumber(params.anchor2.x);
pathString += sprintf("M %f %f L %f %f ", lineendx, y, lineendx, roundNumber(y + height));
}
pathString += sprintf("M %f %f L %f %f ", linestartx, y, lineendx, y);
renderer.paper.openGroup({
klass: renderer.controller.classes.generate("ending"),
"data-name": "ending"
});
printPath(renderer, {
path: pathString,
stroke: renderer.foregroundColor,
fill: renderer.foregroundColor,
"data-name": "line"
});
if (params.anchor1) renderText(renderer, {
x: roundNumber(linestartx + 5),
y: roundNumber(renderer.calcY(params.pitch - 0.5)),
text: params.text,
type: 'repeatfont',
klass: 'ending',
anchor: "start",
noClass: true,
name: params.text
});
var g = renderer.paper.closeGroup();
selectables.wrapSvgEl({
el_type: "ending",
startChar: -1,
endChar: -1
}, g);
return [g];
}
module.exports = drawEnding;
/***/ }),
/***/ "./src/write/draw/glissando.js":
/*!*************************************!*\
!*** ./src/write/draw/glissando.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawGlissando(renderer, params, selectables) {
if (!params.anchor1 || !params.anchor2 || !params.anchor1.heads || !params.anchor2.heads || params.anchor1.heads.length === 0 || params.anchor2.heads.length === 0) window.console.error("Glissando Element not set.");
var margin = 4;
var leftY = renderer.calcY(params.anchor1.heads[0].pitch);
var rightY = renderer.calcY(params.anchor2.heads[0].pitch);
var leftX = params.anchor1.x + params.anchor1.w / 2;
var rightX = params.anchor2.x + params.anchor2.w / 2;
var len = lineLength(leftX, leftY, rightX, rightY);
var marginLeft = params.anchor1.w / 2 + margin;
var marginRight = params.anchor2.w / 2 + margin;
var s = slope(leftX, leftY, rightX, rightY);
var leftYAdj = getY(leftY, s, marginLeft);
var rightYAdj = getY(rightY, s, -marginRight);
var num = numSquigglies(len - marginLeft - marginRight);
var el = drawSquiggly(renderer, leftX + marginLeft, leftYAdj, num, s);
selectables.wrapSvgEl({
el_type: "glissando",
startChar: -1,
endChar: -1
}, el);
return [el];
}
function lineLength(leftX, leftY, rightX, rightY) {
// The length from notehead center to notehead center.
var w = rightX - leftX;
var h = rightY - leftY;
return Math.sqrt(w * w + h * h);
}
function slope(leftX, leftY, rightX, rightY) {
return (rightY - leftY) / (rightX - leftX);
}
function getY(y, slope, xOfs) {
return roundNumber(y + xOfs * slope);
}
function numSquigglies(length) {
var endLen = 5; // The width of the end - that is, the non repeating part
return Math.max(2, Math.floor((length - endLen * 2) / 6));
}
var leftStart = [[3.5, -4.8]];
var right = [[1.5, -1], [.3, -.3], [-3.5, 3.8]];
var leftEnd = [[-1.5, 2]];
var top = [[3, 4], [3, -4]];
var bottom = [[-3, 4], [-3, -4]];
function segment(arr, slope) {
var ret = "";
for (var i = 0; i < arr.length; i++) {
ret += 'l' + arr[i][0] + ' ' + getY(arr[i][1], slope, arr[i][0]);
}
return ret;
}
var drawSquiggly = function drawSquiggly(renderer, x, y, num, slope) {
var p = sprintf("M %f %f", x, y);
p += segment(leftStart, slope);
var i;
for (i = 0; i < num; i++) {
p += segment(top, slope);
}
p += segment(right, slope);
for (i = 0; i < num; i++) {
p += segment(bottom, slope);
}
p += segment(leftEnd, slope) + 'z';
return printPath(renderer, {
path: p,
highlight: "stroke",
stroke: renderer.foregroundColor,
'class': renderer.controller.classes.generate('decoration'),
"data-name": "glissando"
});
};
module.exports = drawGlissando;
/***/ }),
/***/ "./src/write/draw/group-elements.js":
/*!******************************************!*\
!*** ./src/write/draw/group-elements.js ***!
\******************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
/**
* Begin a group of glyphs that will always be moved, scaled and highlighted together
*/
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function Group() {
this.ingroup = false;
}
Group.prototype.beginGroup = function (paper, controller) {
this.paper = paper;
this.controller = controller;
this.path = [];
this.lastM = [0, 0];
this.ingroup = true;
this.paper.openGroup();
};
Group.prototype.isInGroup = function () {
return this.ingroup;
};
Group.prototype.addPath = function (path) {
path = path || [];
if (path.length === 0) return;
path[0][0] = "m";
path[0][1] = roundNumber(path[0][1] - this.lastM[0]);
path[0][2] = roundNumber(path[0][2] - this.lastM[1]);
this.lastM[0] += path[0][1];
this.lastM[1] += path[0][2];
this.path.push(path[0]);
for (var i = 1, ii = path.length; i < ii; i++) {
if (path[i][0] === "m") {
this.lastM[0] += path[i][1];
this.lastM[1] += path[i][2];
}
this.path.push(path[i]);
}
};
/**
* End a group of glyphs that will always be moved, scaled and highlighted together
*/
Group.prototype.endGroup = function (klass, name) {
this.ingroup = false;
//if (this.path.length === 0) return null;
var path = "";
for (var i = 0; i < this.path.length; i++) {
path += this.path[i].join(" ");
}
this.path = [];
var ret = this.paper.closeGroup();
if (ret) {
ret.setAttribute("class", this.controller.classes.generate(klass));
ret.setAttribute("fill", this.controller.renderer.foregroundColor);
ret.setAttribute("stroke", "none");
ret.setAttribute("data-name", name);
}
return ret;
};
// There is just a singleton of this object.
var elementGroup = new Group();
module.exports = elementGroup;
/***/ }),
/***/ "./src/write/draw/non-music.js":
/*!*************************************!*\
!*** ./src/write/draw/non-music.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var drawSeparator = __webpack_require__(/*! ./separator */ "./src/write/draw/separator.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
function nonMusic(renderer, obj, selectables) {
for (var i = 0; i < obj.rows.length; i++) {
var row = obj.rows[i];
if (row.absmove) {
renderer.absolutemoveY(row.absmove);
} else if (row.move) {
renderer.moveY(row.move);
} else if (row.text || row.phrases) {
var x = row.left ? row.left : 0;
var el = renderText(renderer, {
x: x,
y: renderer.y,
text: row.text,
phrases: row.phrases,
'dominant-baseline': row['dominant-baseline'],
type: row.font,
klass: row.klass,
name: row.name,
anchor: row.anchor
});
if (row.absElemType) {
selectables.wrapSvgEl({
el_type: row.absElemType,
name: row.name,
startChar: row.startChar,
endChar: row.endChar,
text: row.text
}, el);
}
} else if (row.separator) {
drawSeparator(renderer, row.separator);
} else if (row.startGroup) {
renderer.paper.openGroup({
klass: row.klass,
"data-name": row.name
});
} else if (row.endGroup) {
// TODO-PER: also create a history element with the title "row.endGroup"
var g = renderer.paper.closeGroup();
if (row.absElemType) selectables.wrapSvgEl({
el_type: row.absElemType,
name: row.name,
startChar: row.startChar,
endChar: row.endChar,
text: ""
}, g);
}
}
}
module.exports = nonMusic;
/***/ }),
/***/ "./src/write/draw/print-line.js":
/*!**************************************!*\
!*** ./src/write/draw/print-line.js ***!
\**************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function printLine(renderer, x1, x2, y, klass, name, dy) {
var fill = renderer.foregroundColor;
x1 = roundNumber(x1);
x2 = roundNumber(x2);
var y1 = roundNumber(y - dy);
var y2 = roundNumber(y + dy);
// TODO-PER: This fixes a firefox bug where it isn't displayed
if (renderer.firefox112) {
y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
var attr = {
x1: x1,
x2: x2,
y1: y,
y2: y,
stroke: renderer.foregroundColor,
'stroke-width': Math.abs(dy * 2)
};
if (klass) attr['class'] = klass;
if (name) attr['data-name'] = name;
return renderer.paper.lineToBack(attr);
}
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1, x2, y2, x1, y2);
var options = {
path: pathString,
stroke: "none",
fill: fill
};
if (name) options['data-name'] = name;
if (klass) options['class'] = klass;
var ret = renderer.paper.pathToBack(options);
return ret;
}
module.exports = printLine;
/***/ }),
/***/ "./src/write/draw/print-path.js":
/*!**************************************!*\
!*** ./src/write/draw/print-path.js ***!
\**************************************/
/***/ (function(module) {
function printPath(renderer, attrs, params) {
var ret = renderer.paper.path(attrs);
return ret;
}
module.exports = printPath;
/***/ }),
/***/ "./src/write/draw/print-stem.js":
/*!**************************************!*\
!*** ./src/write/draw/print-stem.js ***!
\**************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var elementGroup = __webpack_require__(/*! ./group-elements */ "./src/write/draw/group-elements.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function printStem(renderer, x, dx, y1, y2, klass, name) {
if (dx < 0 || y1 < y2) {
// correct path "handedness" for intersection with other elements
var tmp = roundNumber(y2);
y2 = roundNumber(y1);
y1 = tmp;
} else {
y1 = roundNumber(y1);
y2 = roundNumber(y2);
}
x = roundNumber(x);
var x2 = roundNumber(x + dx);
// TODO-PER: This fixes a firefox bug where it isn't displayed
if (renderer.firefox112) {
x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
var attr = {
x1: x,
x2: x,
y1: y1,
y2: y2,
stroke: renderer.foregroundColor,
'stroke-width': Math.abs(dx)
};
if (klass) attr['class'] = klass;
if (name) attr['data-name'] = name;
return renderer.paper.lineToBack(attr);
}
var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
var attr = {
path: ""
};
for (var i = 0; i < pathArray.length; i++) {
attr.path += pathArray[i].join(" ");
}
if (klass) attr['class'] = klass;
if (name) attr['data-name'] = name;
if (!elementGroup.isInGroup()) {
attr.stroke = "none";
attr.fill = renderer.foregroundColor;
}
return renderer.paper.pathToBack(attr);
}
module.exports = printStem;
/***/ }),
/***/ "./src/write/draw/print-symbol.js":
/*!****************************************!*\
!*** ./src/write/draw/print-symbol.js ***!
\****************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
var glyphs = __webpack_require__(/*! ../creation/glyphs */ "./src/write/creation/glyphs.js");
var elementGroup = __webpack_require__(/*! ./group-elements */ "./src/write/draw/group-elements.js");
/**
* assumes this.y is set appropriately
* if symbol is a multichar string without a . (as in scripts.staccato) 1 symbol per char is assumed
* not scaled if not in printgroup
*/
function printSymbol(renderer, x, offset, symbol, options) {
// TODO-PER: what happened to scalex, and scaley? That might have been a bug introduced in refactoring
var el;
var ycorr;
if (!symbol) return null;
if (symbol.length > 1 && symbol.indexOf(".") < 0) {
var groupClass = elementGroup.isInGroup() ? '' : options.klass; // If this is already in a group then don't repeat the classes for the sub-group)
renderer.paper.openGroup({
"data-name": options.name,
klass: groupClass
});
var dx = 0;
for (var i = 0; i < symbol.length; i++) {
var s = symbol[i];
ycorr = glyphs.getYCorr(s);
el = glyphs.printSymbol(x + dx, renderer.calcY(offset + ycorr), s, renderer.paper, {
stroke: options.stroke,
fill: options.fill
});
if (el) {
if (i < symbol.length - 1) dx += kernSymbols(s, symbol[i + 1], glyphs.getSymbolWidth(s));
} else {
renderText(renderer, {
x: x,
y: renderer.y,
text: "no symbol:" + symbol,
type: "debugfont",
klass: 'debug-msg',
anchor: 'start'
}, false);
}
}
var g = renderer.paper.closeGroup();
return g;
} else {
ycorr = glyphs.getYCorr(symbol);
if (elementGroup.isInGroup()) {
el = glyphs.printSymbol(x, renderer.calcY(offset + ycorr), symbol, renderer.paper, {
"data-name": options.name
});
} else {
el = glyphs.printSymbol(x, renderer.calcY(offset + ycorr), symbol, renderer.paper, {
klass: options.klass,
stroke: options.stroke,
fill: options.fill,
"data-name": options.name
});
}
if (el) {
return el;
}
renderText(renderer, {
x: x,
y: renderer.y,
text: "no symbol:" + symbol,
type: "debugfont",
klass: 'debug-msg',
anchor: 'start'
}, false);
return null;
}
}
function kernSymbols(lastSymbol, thisSymbol, lastSymbolWidth) {
// This is just some adjustments to make it look better.
var width = lastSymbolWidth;
if (lastSymbol === 'f' && thisSymbol === 'f') width = width * 2 / 3;
if (lastSymbol === 'p' && thisSymbol === 'p') width = width * 5 / 6;
if (lastSymbol === 'f' && thisSymbol === 'z') width = width * 5 / 8;
return width;
}
module.exports = printSymbol;
/***/ }),
/***/ "./src/write/draw/relative.js":
/*!************************************!*\
!*** ./src/write/draw/relative.js ***!
\************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
var printStem = __webpack_require__(/*! ./print-stem */ "./src/write/draw/print-stem.js");
var printStaffLine = __webpack_require__(/*! ./staff-line */ "./src/write/draw/staff-line.js");
var printSymbol = __webpack_require__(/*! ./print-symbol */ "./src/write/draw/print-symbol.js");
function drawRelativeElement(renderer, params, bartop) {
if (params.pitch === undefined) window.console.error(params.type + " Relative Element y-coordinate not set.");
var y = renderer.calcY(params.pitch);
switch (params.type) {
case "symbol":
if (params.c === null) return null;
var klass = "symbol";
if (params.klass) klass += " " + params.klass;
params.graphelem = printSymbol(renderer, params.x, params.pitch, params.c, {
scalex: params.scalex,
scaley: params.scaley,
klass: renderer.controller.classes.generate(klass),
// fill:"none",
// stroke: renderer.foregroundColor,
name: params.name
});
break;
case "debug":
params.graphelem = renderText(renderer, {
x: params.x,
y: renderer.calcY(15),
text: "" + params.c,
type: "debugfont",
klass: renderer.controller.classes.generate('debug-msg'),
anchor: 'start',
centerVertically: false,
dim: params.dim
}, false);
break;
case "tabNumber":
var hAnchor = "middle";
var tabFont = "tabnumberfont";
var tabClass = 'abcjs-tab-number';
if (params.isGrace) {
tabFont = "tabgracefont";
y += 2.5;
tabClass = 'tab-grace';
}
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: "" + params.c,
type: tabFont,
klass: renderer.controller.classes.generate(tabClass),
anchor: hAnchor,
centerVertically: false,
dim: params.dim,
cursor: 'default'
}, false);
break;
case "barNumber":
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: "" + params.c,
type: "measurefont",
klass: renderer.controller.classes.generate('bar-number'),
anchor: "middle",
dim: params.dim,
name: "bar-number"
}, true);
break;
case "lyric":
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: params.c,
type: "vocalfont",
klass: renderer.controller.classes.generate('lyric'),
anchor: "middle",
dim: params.dim,
name: "lyric"
}, false);
break;
case "chord":
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: params.c,
type: 'gchordfont',
klass: renderer.controller.classes.generate("chord"),
anchor: "middle",
dim: params.dim,
lane: params.getLane(),
name: "chord"
}, false);
break;
case "decoration":
// The +6 is to compensate for the placement of text in svg: to be on the same row as symbols, the y-coord needs to compensate for the center line.
params.graphelem = renderText(renderer, {
x: params.x,
y: y + 6,
text: params.c,
type: 'annotationfont',
klass: renderer.controller.classes.generate("annotation"),
anchor: params.anchor,
centerVertically: true,
dim: params.dim
}, false);
break;
case "text":
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: params.c,
type: 'annotationfont',
klass: renderer.controller.classes.generate("annotation"),
anchor: "start",
centerVertically: params.centerVertically,
dim: params.dim,
lane: params.getLane(),
name: "annotation"
}, false);
break;
case "multimeasure-text":
params.graphelem = renderText(renderer, {
x: params.x + params.w / 2,
y: y,
text: params.c,
type: 'tempofont',
klass: renderer.controller.classes.generate("rest"),
anchor: "middle",
centerVertically: false,
dim: params.dim
}, false);
break;
case "part":
params.graphelem = renderText(renderer, {
x: params.x,
y: y,
text: params.c,
type: 'partsfont',
klass: renderer.controller.classes.generate("part"),
anchor: "start",
dim: params.dim,
name: params.c
}, true);
break;
case "bar":
params.graphelem = printStem(renderer, params.x, params.linewidth + renderer.lineThickness, y, bartop ? bartop : renderer.calcY(params.pitch2), null, "bar");
break;
// bartop can't be 0
case "stem":
var stemWidth = params.linewidth > 0 ? params.linewidth + renderer.lineThickness : params.linewidth - renderer.lineThickness;
params.graphelem = printStem(renderer, params.x, stemWidth, y, renderer.calcY(params.pitch2), 'abcjs-stem', 'stem');
break;
case "ledger":
params.graphelem = printStaffLine(renderer, params.x, params.x + params.w, params.pitch, "abcjs-ledger", "ledger", 0.35 + renderer.lineThickness);
break;
}
if (params.scalex !== 1 && params.graphelem) {
scaleExistingElem(renderer.paper, params.graphelem, params.scalex, params.scaley, params.x, y);
}
return params.graphelem;
}
function scaleExistingElem(paper, elem, scaleX, scaleY, x, y) {
paper.setAttributeOnElement(elem, {
style: "transform:scale(" + scaleX + "," + scaleY + ");transform-origin:" + x + "px " + y + "px;"
});
}
module.exports = drawRelativeElement;
/***/ }),
/***/ "./src/write/draw/round-number.js":
/*!****************************************!*\
!*** ./src/write/draw/round-number.js ***!
\****************************************/
/***/ (function(module) {
function roundNumber(x) {
return parseFloat(x.toFixed(2));
}
module.exports = roundNumber;
/***/ }),
/***/ "./src/write/draw/selectables.js":
/*!***************************************!*\
!*** ./src/write/draw/selectables.js ***!
\***************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var highlight = __webpack_require__(/*! ../interactive/highlight */ "./src/write/interactive/highlight.js");
var unhighlight = __webpack_require__(/*! ../interactive/unhighlight */ "./src/write/interactive/unhighlight.js");
function Selectables(paper, selectTypes, tuneNumber) {
this.elements = [];
this.paper = paper;
this.tuneNumber = tuneNumber;
this.selectTypes = selectTypes;
}
Selectables.prototype.getElements = function () {
return this.elements;
};
Selectables.prototype.add = function (absEl, svgEl, isNoteOrTabNumber, staffPos) {
if (!this.canSelect(absEl)) return;
var params;
if (this.selectTypes === undefined) params = {
selectable: false,
"data-index": this.elements.length
}; // This is the old behavior.
else params = {
selectable: true,
tabindex: 0,
"data-index": this.elements.length
};
this.paper.setAttributeOnElement(svgEl, params);
var sel = {
absEl: absEl,
svgEl: svgEl,
isDraggable: isNoteOrTabNumber
};
if (staffPos !== undefined) sel.staffPos = staffPos;
this.elements.push(sel);
};
Selectables.prototype.canSelect = function (absEl) {
if (this.selectTypes === false) return false;
if (!absEl || !absEl.abcelem) return false;
if (this.selectTypes === true) return true;
if (this.selectTypes === undefined) {
// by default, only notes and tab numbers can be clicked.
if (absEl.abcelem.el_type === 'note' || absEl.abcelem.el_type === 'tabNumber') {
return true;
}
return false;
}
return this.selectTypes.indexOf(absEl.abcelem.el_type) >= 0;
};
Selectables.prototype.wrapSvgEl = function (abcelem, el) {
var absEl = {
tuneNumber: this.tuneNumber,
abcelem: abcelem,
elemset: [el],
highlight: highlight,
unhighlight: unhighlight
};
this.add(absEl, el, false);
};
module.exports = Selectables;
/***/ }),
/***/ "./src/write/draw/separator.js":
/*!*************************************!*\
!*** ./src/write/draw/separator.js ***!
\*************************************/
/***/ (function(module) {
function drawSeparator(renderer, width) {
var fill = "rgba(0,0,0,255)";
var stroke = "rgba(0,0,0,0)";
var y = Math.round(renderer.y);
var staffWidth = renderer.controller.width;
var x1 = (staffWidth - width) / 2;
var x2 = x1 + width;
var pathString = 'M ' + x1 + ' ' + y + ' L ' + x2 + ' ' + y + ' L ' + x2 + ' ' + (y + 1) + ' L ' + x1 + ' ' + (y + 1) + ' L ' + x1 + ' ' + y + ' z';
renderer.paper.pathToBack({
path: pathString,
stroke: stroke,
fill: fill,
'class': renderer.controller.classes.generate('defined-text')
});
}
module.exports = drawSeparator;
/***/ }),
/***/ "./src/write/draw/set-paper-size.js":
/*!******************************************!*\
!*** ./src/write/draw/set-paper-size.js ***!
\******************************************/
/***/ (function(module) {
function setPaperSize(renderer, maxwidth, scale, responsive) {
var w = (maxwidth + renderer.padding.left + renderer.padding.right) * scale;
var h = (renderer.y + renderer.padding.bottom) * scale;
if (renderer.isPrint) h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
// TODO-PER: We are letting the page get as long as it needs now, but eventually that should go to a second page.
// for accessibility
if (renderer.ariaLabel !== '') {
var text = "Sheet Music";
if (renderer.abctune && renderer.abctune.metaText && renderer.abctune.metaText.title) text += " for \"" + renderer.abctune.metaText.title + '"';
renderer.paper.setTitle(text);
var label = renderer.ariaLabel ? renderer.ariaLabel : text;
renderer.paper.setAttribute("aria-label", label);
}
// for dragging - don't select during drag
var styles = ["-webkit-touch-callout: none;", "-webkit-user-select: none;", "-khtml-user-select: none;", "-moz-user-select: none;", "-ms-user-select: none;", "user-select: none;"];
renderer.paper.insertStyles(".abcjs-dragging-in-progress text, .abcjs-dragging-in-progress tspan {" + styles.join(" ") + "}");
var parentStyles = {
overflow: "hidden"
};
if (responsive === 'resize') {
renderer.paper.setResponsiveWidth(w, h);
} else {
parentStyles.width = "";
parentStyles.height = h + "px";
if (scale < 1) {
parentStyles.width = w + "px";
renderer.paper.setSize(w / scale, h / scale);
} else renderer.paper.setSize(w, h);
}
renderer.paper.setScale(scale);
renderer.paper.setParentStyles(parentStyles);
}
module.exports = setPaperSize;
/***/ }),
/***/ "./src/write/draw/sprintf.js":
/*!***********************************!*\
!*** ./src/write/draw/sprintf.js ***!
\***********************************/
/***/ (function(module) {
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
/**
* sprintf() for JavaScript v.0.4
*
Copyright (c) 2007-present, Alexandru Mărășteanu
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of this software nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//function str_repeat(i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); }
var sprintf = function sprintf() {
var i = 0,
a,
f = arguments[i++],
o = [],
m,
p,
c,
x;
while (f) {
if (m = /^[^\x25]+/.exec(f)) o.push(m[0]);else if (m = /^\x25{2}/.exec(f)) o.push('%');else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
if ((a = arguments[m[1] || i++]) == null || a == undefined) throw "Too few arguments.";
if (/[^s]/.test(m[7]) && typeof a != 'number') throw "Expecting number but found " + _typeof(a);
switch (m[7]) {
case 'b':
a = a.toString(2);
break;
case 'c':
a = String.fromCharCode(a);
break;
case 'd':
a = parseInt(a);
break;
case 'e':
a = m[6] ? a.toExponential(m[6]) : a.toExponential();
break;
case 'f':
a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a);
break;
case 'o':
a = a.toString(8);
break;
case 's':
a = (a = String(a)) && m[6] ? a.substring(0, m[6]) : a;
break;
case 'u':
a = Math.abs(a);
break;
case 'x':
a = a.toString(16);
break;
case 'X':
a = a.toString(16).toUpperCase();
break;
}
a = /[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a;
c = m[3] ? m[3] == '0' ? '0' : m[3][1] : ' ';
x = m[5] - String(a).length;
p = m[5] ? str_repeat(c, x) : '';
o.push(m[4] ? a + p : p + a);
} else throw "Huh ?!";
f = f.substring(m[0].length);
}
return o.join('');
};
module.exports = sprintf;
/***/ }),
/***/ "./src/write/draw/staff-group.js":
/*!***************************************!*\
!*** ./src/write/draw/staff-group.js ***!
\***************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var drawBrace = __webpack_require__(/*! ./brace */ "./src/write/draw/brace.js");
var drawVoice = __webpack_require__(/*! ./voice */ "./src/write/draw/voice.js");
var printStaff = __webpack_require__(/*! ./staff */ "./src/write/draw/staff.js");
var printDebugBox = __webpack_require__(/*! ./debug-box */ "./src/write/draw/debug-box.js");
var printStem = __webpack_require__(/*! ./print-stem */ "./src/write/draw/print-stem.js");
var nonMusic = __webpack_require__(/*! ./non-music */ "./src/write/draw/non-music.js");
function drawStaffGroup(renderer, params, selectables, lineNumber) {
// We enter this method with renderer.y pointing to the topmost coordinate that we're allowed to draw.
// All of the children that will be drawn have a relative "pitch" set, where zero is the first ledger line below the staff.
// renderer.y will be offset at the beginning of each staff by the amount required to make the relative pitch work.
// If there are multiple staves, then renderer.y will be incremented for each new staff.
var colorIndex;
// An invisible marker is useful to be able to find where each system starts.
//addInvisibleMarker(renderer, "abcjs-top-of-system");
var startY = renderer.y; // So that it can be restored after we're done.
// Set the absolute Y position for each staff here, so the voice drawing below can just use if.
for (var j = 0; j < params.staffs.length; j++) {
var staff1 = params.staffs[j];
//renderer.printHorizontalLine(50, renderer.y, "start");
renderer.moveY(spacing.STEP, staff1.top);
staff1.absoluteY = renderer.y;
if (renderer.showDebug) {
if (renderer.showDebug.indexOf("box") >= 0 && staff1.voices) {
boxAllElements(renderer, params.voices, staff1.voices);
}
if (renderer.showDebug.indexOf("grid") >= 0) {
renderer.paper.dottedLine({
x1: renderer.padding.left,
x2: renderer.padding.left + renderer.controller.width,
y1: startY,
y2: startY,
stroke: "#0000ff"
});
printDebugBox(renderer, {
x: renderer.padding.left,
y: renderer.calcY(staff1.originalTop),
width: renderer.controller.width,
height: renderer.calcY(staff1.originalBottom) - renderer.calcY(staff1.originalTop),
fill: renderer.foregroundColor,
stroke: renderer.foregroundColor,
"fill-opacity": 0.1,
"stroke-opacity": 0.1
});
colorIndex = 0;
debugPrintGridItem(staff1, 'chordHeightAbove');
debugPrintGridItem(staff1, 'chordHeightBelow');
debugPrintGridItem(staff1, 'dynamicHeightAbove');
debugPrintGridItem(staff1, 'dynamicHeightBelow');
debugPrintGridItem(staff1, 'endingHeightAbove');
debugPrintGridItem(staff1, 'lyricHeightAbove');
debugPrintGridItem(staff1, 'lyricHeightBelow');
debugPrintGridItem(staff1, 'partHeightAbove');
debugPrintGridItem(staff1, 'tempoHeightAbove');
debugPrintGridItem(staff1, 'volumeHeightAbove');
debugPrintGridItem(staff1, 'volumeHeightBelow');
}
}
renderer.moveY(spacing.STEP, -staff1.bottom);
if (renderer.showDebug) {
if (renderer.showDebug.indexOf("grid") >= 0) {
renderer.paper.dottedLine({
x1: renderer.padding.left,
x2: renderer.padding.left + renderer.controller.width,
y1: renderer.y,
y2: renderer.y,
stroke: "#0000aa"
});
}
}
}
var topLine; // these are to connect multiple staves. We need to remember where they are.
var bottomLine;
var linePitch = 2;
var bartop = 0;
for (var i = 0; i < params.voices.length; i++) {
var staff = params.voices[i].staff;
var tabName = params.voices[i].tabNameInfos;
renderer.y = staff.absoluteY;
renderer.controller.classes.incrVoice();
//renderer.y = staff.y;
// offset for starting the counting at middle C
if (!params.voices[i].duplicate) {
// renderer.moveY(spacing.STEP, staff.top);
if (!topLine) topLine = renderer.calcY(10);
bottomLine = renderer.calcY(linePitch);
if (staff.lines !== 0) {
if (staff.linePitch) {
linePitch = staff.linePitch;
}
renderer.controller.classes.newMeasure();
var lines = printStaff(renderer, params.startx, params.w, staff.lines, staff.linePitch, 0.35);
bottomLine = lines[1];
staff.bottomLine = bottomLine;
staff.topLine = lines[0];
// rework bartop when tabs are present with current staff
if (staff.hasTab) {
// do not link to staff above (ugly looking)
bartop = staff.topLine;
}
if (staff.hasStaff) {
// this is a tab
bartop = staff.hasStaff.topLine;
params.voices[i].barto = true;
params.voices[i].topLine = topLine;
}
}
printBrace(renderer, staff.absoluteY, params.brace, i, selectables);
printBrace(renderer, staff.absoluteY, params.bracket, i, selectables);
}
drawVoice(renderer, params.voices[i], bartop, selectables, {
top: startY,
zero: renderer.y,
height: params.height * spacing.STEP
});
var tabNameHeight = 0;
if (tabName) {
// print tab infos on staffBottom
var r = {
rows: []
};
r.rows.push({
absmove: bottomLine + 2
});
var leftMargin = 8;
r.rows.push({
left: params.startx + leftMargin,
text: tabName.name,
font: 'tablabelfont',
klass: 'text instrument-name',
anchor: 'start'
});
r.rows.push({
move: tabName.textSize.height
});
nonMusic(renderer, r);
tabNameHeight = tabName.textSize.height;
}
renderer.controller.classes.newMeasure();
if (!params.voices[i].duplicate) {
bartop = renderer.calcY(2 + tabNameHeight); // This connects the bar lines between two different staves.
// if (staff.bottom < 0)
// renderer.moveY(spacing.STEP, -staff.bottom);
}
}
renderer.controller.classes.newMeasure();
// connect all the staves together with a vertical line
var staffSize = params.staffs.length;
if (staffSize > 1) {
topLine = params.staffs[0].topLine;
bottomLine = params.staffs[staffSize - 1].bottomLine;
printStem(renderer, params.startx, 0.6, topLine, bottomLine, null);
}
renderer.y = startY;
function debugPrintGridItem(staff, key) {
var colors = ["rgb(207,27,36)", "rgb(168,214,80)", "rgb(110,161,224)", "rgb(191,119,218)", "rgb(195,30,151)", "rgb(31,170,177)", "rgb(220,166,142)"];
if (staff.positionY && staff.positionY[key]) {
var height = staff.specialY[key] * spacing.STEP;
if (key === "chordHeightAbove" && staff.specialY.chordLines && staff.specialY.chordLines.above) height *= staff.specialY.chordLines.above;
if (key === "chordHeightBelow" && staff.specialY.chordLines && staff.specialY.chordLines.below) height *= staff.specialY.chordLines.below;
printDebugBox(renderer, {
x: renderer.padding.left,
y: renderer.calcY(staff.positionY[key]),
width: renderer.controller.width,
height: height,
fill: colors[colorIndex],
stroke: colors[colorIndex],
"fill-opacity": 0.4,
"stroke-opacity": 0.4
}, key.substr(0, 4));
colorIndex += 1;
if (colorIndex > 6) colorIndex = 0;
}
}
}
function printBrace(renderer, absoluteY, brace, index, selectables) {
if (brace) {
for (var i = 0; i < brace.length; i++) {
if (brace[i].isStartVoice(index)) {
brace[i].startY = absoluteY - spacing.STEP * 10;
brace[i].elemset = drawBrace(renderer, brace[i], selectables);
}
}
}
}
// function addInvisibleMarker(renderer, className) {
// var y = Math.round(renderer.y);
// renderer.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': renderer.controller.classes.generate(className), 'data-vertical': y });
// }
function boxAllElements(renderer, voices, which) {
for (var i = 0; i < which.length; i++) {
var children = voices[which[i]].children;
for (var j = 0; j < children.length; j++) {
var elem = children[j];
var coords = elem.getFixedCoords();
if (elem.invisible || coords.t === undefined || coords.b === undefined) continue;
var height = (coords.t - coords.b) * spacing.STEP;
printDebugBox(renderer, {
x: coords.x,
y: renderer.calcY(coords.t),
width: coords.w,
height: height,
fill: "#88e888",
"fill-opacity": 0.4,
stroke: "#4aa93d",
"stroke-opacity": 0.8
});
for (var k = 0; k < elem.children.length; k++) {
var relElem = elem.children[k];
var chord = relElem.getChordDim();
if (chord) {
var y = renderer.calcY(relElem.pitch);
y += relElem.dim.font.size * relElem.getLane();
printDebugBox(renderer, {
x: chord.left,
y: y,
width: chord.right - chord.left,
height: relElem.dim.font.size,
fill: "none",
stroke: "#4aa93d",
"stroke-opacity": 0.8
});
}
}
}
}
}
module.exports = drawStaffGroup;
/***/ }),
/***/ "./src/write/draw/staff-line.js":
/*!**************************************!*\
!*** ./src/write/draw/staff-line.js ***!
\**************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var printLine = __webpack_require__(/*! ./print-line */ "./src/write/draw/print-line.js");
function printStaffLine(renderer, x1, x2, pitch, klass, name, dy) {
var y = renderer.calcY(pitch);
return printLine(renderer, x1, x2, y, klass, name, dy);
}
module.exports = printStaffLine;
/***/ }),
/***/ "./src/write/draw/staff.js":
/*!*********************************!*\
!*** ./src/write/draw/staff.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var printStaffLine = __webpack_require__(/*! ./staff-line */ "./src/write/draw/staff-line.js");
function printStaff(renderer, startx, endx, numLines, linePitch, dy) {
var klass = "abcjs-top-line";
var pitch = 2;
if (linePitch) {
pitch = linePitch;
}
renderer.paper.openGroup({
prepend: true,
klass: renderer.controller.classes.generate("abcjs-staff")
});
// If there is one line, it is the B line. Otherwise, the bottom line is the E line.
var firstYLine = 0;
var lastYLine = 0;
if (numLines === 1) {
printStaffLine(renderer, startx, endx, 6, klass, null, dy + renderer.lineThickness);
firstYLine = renderer.calcY(10);
lastYLine = renderer.calcY(2);
} else {
for (var i = numLines - 1; i >= 0; i--) {
var curpitch = (i + 1) * pitch;
lastYLine = renderer.calcY(curpitch);
if (firstYLine === 0) {
firstYLine = lastYLine;
}
printStaffLine(renderer, startx, endx, curpitch, klass, null, dy + renderer.lineThickness);
klass = undefined;
}
}
renderer.paper.closeGroup();
return [firstYLine, lastYLine];
}
module.exports = printStaff;
/***/ }),
/***/ "./src/write/draw/tempo.js":
/*!*********************************!*\
!*** ./src/write/draw/tempo.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var drawRelativeElement = __webpack_require__(/*! ./relative */ "./src/write/draw/relative.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
function drawTempo(renderer, params) {
var x = params.x;
if (params.pitch === undefined) window.console.error("Tempo Element y-coordinate not set.");
//var tempoGroup;
params.tempo.el_type = "tempo";
// renderer.wrapInAbsElem(params.tempo, "abcjs-tempo", function () {
//renderer.paper.openGroup({klass: renderer.controller.classes.generate("tempo wha")});
// The text is aligned with extra room for descenders but numbers look like they are a little too high, so bump it a little.
var descenderHeight = 2;
var y = renderer.calcY(params.pitch) + 2;
var text;
var size;
if (params.tempo.preString) {
text = renderText(renderer, {
x: x,
y: y,
text: params.tempo.preString,
type: 'tempofont',
klass: 'abcjs-tempo',
anchor: "start",
noClass: true,
name: "pre"
}, true);
size = renderer.controller.getTextSize.calc(params.tempo.preString, 'tempofont', 'tempo', text);
var preWidth = size.width;
var charWidth = preWidth / params.tempo.preString.length; // Just get some average number to increase the spacing.
x += preWidth + charWidth;
}
if (params.note) {
params.note.setX(x);
for (var i = 0; i < params.note.children.length; i++) {
drawRelativeElement(renderer, params.note.children[i], x);
}
x += params.note.w + 5;
var str = "= " + params.tempo.bpm;
text = renderText(renderer, {
x: x,
y: y,
text: str,
type: 'tempofont',
klass: 'abcjs-tempo',
anchor: "start",
noClass: true,
name: "beats"
});
size = renderer.controller.getTextSize.calc(str, 'tempofont', 'tempo', text);
var postWidth = size.width;
var charWidth2 = postWidth / str.length; // Just get some average number to increase the spacing.
x += postWidth + charWidth2;
}
if (params.tempo.postString) {
renderText(renderer, {
x: x,
y: y,
text: params.tempo.postString,
type: 'tempofont',
klass: 'abcjs-tempo',
anchor: "start",
noClass: true,
name: "post"
}, true);
}
//tempoGroup = renderer.paper.closeGroup();
// });
//return [tempoGroup];
}
module.exports = drawTempo;
/***/ }),
/***/ "./src/write/draw/text.js":
/*!********************************!*\
!*** ./src/write/draw/text.js ***!
\********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function renderText(renderer, params, alreadyInGroup) {
var y = params.y;
// TODO-PER: Probably need to merge the regular text and rich text better. At the least, rich text loses the font box.
if (params.phrases) {
//richTextLine = function (phrases, x, y, klass, anchor, target)
var elem = renderer.paper.richTextLine(params.phrases, params.x, params.y, params.klass, params.anchor);
return elem;
}
if (params.lane) {
var laneMargin = params.dim.font.size * 0.25;
y += (params.dim.font.size + laneMargin) * params.lane;
}
var hash;
if (params.dim) {
hash = params.dim;
hash.attr["class"] = params.klass;
} else hash = renderer.controller.getFontAndAttr.calc(params.type, params.klass);
if (params.anchor) hash.attr["text-anchor"] = params.anchor;
if (params['dominant-baseline']) hash.attr["dominant-baseline"] = params['dominant-baseline'];
hash.attr.x = params.x;
hash.attr.y = y;
if (!params.centerVertically) hash.attr.y += hash.font.size;
if (params.type === 'debugfont') {
console.log("Debug msg: " + params.text);
hash.attr.stroke = "#ff0000";
}
if (params.cursor) {
hash.attr.cursor = params.cursor;
}
var text = params.text.replace(/\n\n/g, "\n \n");
text = text.replace(/^\n/, "\xA0\n");
if (hash.font.box) {
if (!alreadyInGroup) renderer.paper.openGroup({
klass: hash.attr['class'],
fill: renderer.foregroundColor,
"data-name": params.name
});
if (hash.attr["text-anchor"] === "end") {
hash.attr.x -= hash.font.padding;
} else if (hash.attr["text-anchor"] === "start") {
hash.attr.x += hash.font.padding;
}
hash.attr.y += hash.font.padding;
delete hash.attr['class'];
}
if (params.noClass) delete hash.attr['class'];
hash.attr.x = roundNumber(hash.attr.x);
hash.attr.y = roundNumber(hash.attr.y);
if (params.name) hash.attr["data-name"] = params.name;
var elem = renderer.paper.text(text, hash.attr);
if (hash.font.box) {
var size = elem.getBBox();
var delta = 0;
if (hash.attr["text-anchor"] === "middle") {
delta = size.width / 2 + hash.font.padding;
} else if (hash.attr["text-anchor"] === "end") {
delta = size.width + hash.font.padding * 2;
}
var deltaY = 0;
if (params.centerVertically) {
deltaY = size.height - hash.font.padding;
}
renderer.paper.rect({
"data-name": "box",
x: Math.round(params.x - delta),
y: Math.round(y - deltaY),
width: Math.round(size.width + hash.font.padding * 2),
height: Math.round(size.height + hash.font.padding * 2)
});
if (!alreadyInGroup) elem = renderer.paper.closeGroup();
}
return elem;
}
module.exports = renderText;
/***/ }),
/***/ "./src/write/draw/tie.js":
/*!*******************************!*\
!*** ./src/write/draw/tie.js ***!
\*******************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawTie(renderer, params, linestartx, lineendx, selectables) {
layout(params, linestartx, lineendx);
var klass = '';
if (params.anchor1) {
klass += 'abcjs-start-m' + params.anchor1.parent.counters.measure + '-n' + params.anchor1.parent.counters.note;
} else klass += 'abcjs-start-edge';
if (params.anchor2) {
klass += ' abcjs-end-m' + params.anchor2.parent.counters.measure + '-n' + params.anchor2.parent.counters.note;
} else klass += ' abcjs-end-edge';
if (params.hint) klass = "abcjs-hint";
var fudgeY = params.fixedY ? 1.5 : 0; // TODO-PER: This just compensates for drawArc, which contains too much knowledge of ties and slurs.
var el = drawArc(renderer, params.startX, params.endX, params.startY + fudgeY, params.endY + fudgeY, params.above, klass, params.isTie, params.dotted);
var startChar = -1;
// This gets the start and end points of the contents of the slur. We assume that the parenthesis are just to the outside of that.
if (params.anchor1 && !params.isTie) startChar = params.anchor1.parent.abcelem.startChar - 1;
var endChar = -1;
if (params.anchor2 && !params.isTie) endChar = params.anchor2.parent.abcelem.endChar + 1;
selectables.wrapSvgEl({
el_type: "slur",
startChar: startChar,
endChar: endChar
}, el);
return [el];
}
// TODO-PER: I think params part should have been done earlier in the layout pass.
var layout = function layout(params, lineStartX, lineEndX) {
// We now have all of the input variables set, so we can figure out the start and ending x,y coordinates, and finalize the direction of the arc.
// Ties and slurs are handled a little differently, so do calculations for them separately.
if (!params.anchor1 || !params.anchor2) params.isTie = true; // if the slur goes off the end of the line, then draw it like a tie
else if (params.anchor1.pitch === params.anchor2.pitch && params.internalNotes.length === 0) params.isTie = true;else params.isTie = false;
if (params.isTie) {
params.calcTieDirection();
params.calcX(lineStartX, lineEndX);
params.calcTieY();
} else {
params.calcSlurDirection();
params.calcX(lineStartX, lineEndX);
params.calcSlurY();
}
params.avoidCollisionAbove();
};
var drawArc = function drawArc(renderer, x1, x2, pitch1, pitch2, above, klass, isTie, dotted) {
// If it is a tie vs. a slur, draw it shallower.
var spacing = isTie ? 1.2 : 1.5;
x1 = roundNumber(x1 + 6);
x2 = roundNumber(x2 + 4);
pitch1 = pitch1 + (above ? spacing : -spacing);
pitch2 = pitch2 + (above ? spacing : -spacing);
var y1 = roundNumber(renderer.calcY(pitch1));
var y2 = roundNumber(renderer.calcY(pitch2));
//unit direction vector
var dx = x2 - x1;
var dy = y2 - y1;
var norm = Math.sqrt(dx * dx + dy * dy);
var ux = dx / norm;
var uy = dy / norm;
var flatten = norm / 3.5;
var maxFlatten = isTie ? 10 : 25; // If it is a tie vs. a slur, draw it shallower.
var curve = (above ? -1 : 1) * Math.min(maxFlatten, Math.max(4, flatten));
var controlx1 = roundNumber(x1 + flatten * ux - curve * uy);
var controly1 = roundNumber(y1 + flatten * uy + curve * ux);
var controlx2 = roundNumber(x2 - flatten * ux - curve * uy);
var controly2 = roundNumber(y2 - flatten * uy + curve * ux);
var thickness = 2;
if (klass) klass += ' slur';else klass = 'slur';
klass += isTie ? ' tie' : ' legato';
var ret;
if (dotted) {
klass += ' dotted';
var pathString2 = sprintf("M %f %f C %f %f %f %f %f %f", x1, y1, controlx1, controly1, controlx2, controly2, x2, y2);
ret = renderer.paper.path({
path: pathString2,
stroke: renderer.foregroundColor,
fill: "none",
'stroke-dasharray': "5 5",
'class': renderer.controller.classes.generate(klass),
"data-name": isTie ? "tie" : "slur"
});
} else {
var pathString = sprintf("M %f %f C %f %f %f %f %f %f C %f %f %f %f %f %f z", x1, y1, controlx1, controly1, controlx2, controly2, x2, y2, roundNumber(controlx2 - thickness * uy), roundNumber(controly2 + thickness * ux), roundNumber(controlx1 - thickness * uy), roundNumber(controly1 + thickness * ux), x1, y1);
ret = renderer.paper.path({
path: pathString,
stroke: "none",
fill: renderer.foregroundColor,
'class': renderer.controller.classes.generate(klass),
"data-name": isTie ? "tie" : "slur"
});
}
return ret;
};
module.exports = drawTie;
/***/ }),
/***/ "./src/write/draw/triplet.js":
/*!***********************************!*\
!*** ./src/write/draw/triplet.js ***!
\***********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var sprintf = __webpack_require__(/*! ./sprintf */ "./src/write/draw/sprintf.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
var printPath = __webpack_require__(/*! ./print-path */ "./src/write/draw/print-path.js");
var roundNumber = __webpack_require__(/*! ./round-number */ "./src/write/draw/round-number.js");
function drawTriplet(renderer, params, selectables) {
renderer.paper.openGroup({
klass: renderer.controller.classes.generate('triplet ' + params.durationClass),
"data-name": "triplet"
});
if (!params.hasBeam) {
drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote);
}
// HACK: adjust the position of "3". It is too high in all cases so we fudge it by subtracting 1 here.
renderText(renderer, {
x: params.xTextPos,
y: renderer.calcY(params.yTextPos - 1),
text: "" + params.number,
type: 'tripletfont',
anchor: "middle",
centerVertically: true,
noClass: true,
name: "" + params.number
}, true);
var g = renderer.paper.closeGroup();
selectables.wrapSvgEl({
el_type: "triplet",
startChar: -1,
endChar: -1
}, g);
return g;
}
function drawLine(l, t, r, b) {
return sprintf("M %f %f L %f %f", roundNumber(l), roundNumber(t), roundNumber(r), roundNumber(b));
}
function drawBracket(renderer, x1, y1, x2, y2) {
y1 = renderer.calcY(y1);
y2 = renderer.calcY(y2);
var bracketHeight = 5;
// Draw vertical lines at the beginning and end
var pathString = "";
pathString += drawLine(x1, y1, x1, y1 + bracketHeight);
pathString += drawLine(x2, y2, x2, y2 + bracketHeight);
// figure out midpoints to draw the broken line.
var midX = x1 + (x2 - x1) / 2;
//var midY = y1 + (y2-y1)/2;
var gapWidth = 8;
var slope = (y2 - y1) / (x2 - x1);
var leftEndX = midX - gapWidth;
var leftEndY = y1 + (leftEndX - x1) * slope;
pathString += drawLine(x1, y1, leftEndX, leftEndY);
var rightStartX = midX + gapWidth;
var rightStartY = y1 + (rightStartX - x1) * slope;
pathString += drawLine(rightStartX, rightStartY, x2, y2);
printPath(renderer, {
path: pathString,
stroke: renderer.foregroundColor,
"data-name": "triplet-bracket"
});
}
module.exports = drawTriplet;
/***/ }),
/***/ "./src/write/draw/voice.js":
/*!*********************************!*\
!*** ./src/write/draw/voice.js ***!
\*********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var drawGlissando = __webpack_require__(/*! ./glissando */ "./src/write/draw/glissando.js");
var drawCrescendo = __webpack_require__(/*! ./crescendo */ "./src/write/draw/crescendo.js");
var drawDynamics = __webpack_require__(/*! ./dynamics */ "./src/write/draw/dynamics.js");
var drawTriplet = __webpack_require__(/*! ./triplet */ "./src/write/draw/triplet.js");
var drawEnding = __webpack_require__(/*! ./ending */ "./src/write/draw/ending.js");
var drawTie = __webpack_require__(/*! ./tie */ "./src/write/draw/tie.js");
var drawBeam = __webpack_require__(/*! ./beam */ "./src/write/draw/beam.js");
var renderText = __webpack_require__(/*! ./text */ "./src/write/draw/text.js");
var drawAbsolute = __webpack_require__(/*! ./absolute */ "./src/write/draw/absolute.js");
function drawVoice(renderer, params, bartop, selectables, staffPos) {
var width = params.w - 1;
renderer.staffbottom = params.staff.bottom;
var saveColor = renderer.foregroundColor;
if (params.color) renderer.foregroundColor = params.color;
if (params.header) {
// print voice name
var textEl = renderText(renderer, {
x: renderer.padding.left,
y: renderer.calcY(params.headerPosition),
text: params.header,
type: 'voicefont',
klass: 'staff-extra voice-name',
anchor: 'start',
centerVertically: true,
name: "voice-name"
}, true);
selectables.wrapSvgEl({
el_type: "voiceName",
startChar: -1,
endChar: -1,
text: params.header
}, textEl);
}
var i;
var child;
var foundNote = false;
for (i = 0; i < params.children.length; i++) {
child = params.children[i];
if (child.type === 'note' || child.type === 'rest') foundNote = true;
var justInitializedMeasureNumber = false;
if (child.type !== 'staff-extra' && !renderer.controller.classes.isInMeasure()) {
renderer.controller.classes.startMeasure();
justInitializedMeasureNumber = true;
}
if (params.staff.isTabStaff) {
child.invisible = false;
if (child.type == 'bar') {
if (child.abcelem.lastBar) {
bartop = params.topLine;
}
}
}
drawAbsolute(renderer, child, params.barto || i === params.children.length - 1 ? bartop : 0, selectables, staffPos);
if (child.type === 'note' || isNonSpacerRest(child)) renderer.controller.classes.incrNote();
if (child.type === 'bar' && !justInitializedMeasureNumber && foundNote) {
renderer.controller.classes.incrMeasure();
}
}
renderer.controller.classes.startMeasure();
for (i = 0; i < params.beams.length; i++) {
var beam = params.beams[i];
if (beam === 'bar') {
renderer.controller.classes.incrMeasure();
} else drawBeam(renderer, beam, selectables); // beams must be drawn first for proper printing of triplets, slurs and ties.
}
renderer.controller.classes.startMeasure();
for (i = 0; i < params.otherchildren.length; i++) {
child = params.otherchildren[i];
if (child === 'bar') {
renderer.controller.classes.incrMeasure();
} else {
switch (child.type) {
case "GlissandoElem":
child.elemset = drawGlissando(renderer, child, selectables);
break;
case "CrescendoElem":
child.elemset = drawCrescendo(renderer, child, selectables);
break;
case "DynamicDecoration":
child.elemset = drawDynamics(renderer, child, selectables);
break;
case "TripletElem":
drawTriplet(renderer, child, selectables);
break;
case "EndingElem":
child.elemset = drawEnding(renderer, child, params.startx + 10, width, selectables);
break;
case "TieElem":
child.elemset = drawTie(renderer, child, params.startx + 10, width, selectables);
break;
default:
console.log(child);
drawAbsolute(renderer, child, params.startx + 10, width, selectables, staffPos);
}
}
}
renderer.foregroundColor = saveColor;
}
function isNonSpacerRest(elem) {
if (elem.type !== 'rest') return false;
if (elem.abcelem && elem.abcelem.rest && elem.abcelem.rest.type !== 'spacer') return true;
return false;
}
module.exports = drawVoice;
/***/ }),
/***/ "./src/write/engraver-controller.js":
/*!******************************************!*\
!*** ./src/write/engraver-controller.js ***!
\******************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_engraver_controller.js: Controls the engraving process of an ABCJS abstract syntax tree as produced by ABCJS/parse
/*global Math */
var spacing = __webpack_require__(/*! ./helpers/spacing */ "./src/write/helpers/spacing.js");
var AbstractEngraver = __webpack_require__(/*! ./creation/abstract-engraver */ "./src/write/creation/abstract-engraver.js");
var Renderer = __webpack_require__(/*! ./renderer */ "./src/write/renderer.js");
var FreeText = __webpack_require__(/*! ./creation/elements/free-text */ "./src/write/creation/elements/free-text.js");
var Separator = __webpack_require__(/*! ./creation/elements/separator */ "./src/write/creation/elements/separator.js");
var Subtitle = __webpack_require__(/*! ./creation/elements/subtitle */ "./src/write/creation/elements/subtitle.js");
var TopText = __webpack_require__(/*! ./creation/elements/top-text */ "./src/write/creation/elements/top-text.js");
var BottomText = __webpack_require__(/*! ./creation/elements/bottom-text */ "./src/write/creation/elements/bottom-text.js");
var setupSelection = __webpack_require__(/*! ./interactive/selection */ "./src/write/interactive/selection.js");
var layout = __webpack_require__(/*! ./layout/layout */ "./src/write/layout/layout.js");
var Classes = __webpack_require__(/*! ./helpers/classes */ "./src/write/helpers/classes.js");
var GetFontAndAttr = __webpack_require__(/*! ./helpers/get-font-and-attr */ "./src/write/helpers/get-font-and-attr.js");
var GetTextSize = __webpack_require__(/*! ./helpers/get-text-size */ "./src/write/helpers/get-text-size.js");
var draw = __webpack_require__(/*! ./draw/draw */ "./src/write/draw/draw.js");
var tablatures = __webpack_require__(/*! ../tablatures/abc_tablatures */ "./src/tablatures/abc_tablatures.js");
var findSelectableElement = __webpack_require__(/*! ./interactive/find-selectable-element */ "./src/write/interactive/find-selectable-element.js");
/**
* @class
* Controls the engraving process, from ABCJS Abstract Syntax Tree (ABCJS AST) to rendered score sheet
*
* Call engraveABC to run the process. This creates a graphelems ABCJS Abstract Engraving Structure (ABCJS AES) that can be accessed through this.staffgroups
* this data structure is first laid out (giving the graphelems x and y coordinates) and then drawn onto the renderer
* each ABCJS AES represents a single staffgroup - all elements that are not in a staffgroup are rendered directly by the controller
*
* elements in ABCJS AES know their "source data" in the ABCJS AST, and their "target shape"
* in the renderer for highlighting purposes
*
*/
var EngraverController = function EngraverController(paper, params) {
params = params || {};
this.findSelectableElement = findSelectableElement;
this.oneSvgPerLine = params.oneSvgPerLine;
this.selectionColor = params.selectionColor;
this.dragColor = params.dragColor ? params.dragColor : params.selectionColor;
this.dragging = !!params.dragging;
this.selectTypes = params.selectTypes;
this.responsive = params.responsive;
this.space = 3 * spacing.SPACE;
this.initialClef = params.initialClef;
this.timeBasedLayout = params.timeBasedLayout;
this.expandToWidest = !!params.expandToWidest;
this.scale = params.scale ? parseFloat(params.scale) : 0;
this.classes = new Classes({
shouldAddClasses: params.add_classes
});
if (!(this.scale > 0.1)) this.scale = undefined;
if (params.staffwidth) {
// Note: Normally all measurements to the engraver are in POINTS. However, if a person is formatting for the
// screen and directly inputting the width, then it is more logical to have the measurement in pixels.
this.staffwidthScreen = params.staffwidth;
this.staffwidthPrint = params.staffwidth;
} else {
this.staffwidthScreen = 740; // TODO-PER: Not sure where this number comes from, but this is how it's always been.
this.staffwidthPrint = 680; // The number of pixels in 8.5", after 1cm of margin has been removed.
}
this.listeners = [];
if (params.clickListener) this.addSelectListener(params.clickListener);
this.renderer = new Renderer(paper);
this.renderer.setPaddingOverride(params);
if (params.showDebug) this.renderer.showDebug = params.showDebug;
if (params.jazzchords) this.jazzchords = params.jazzchords;
if (params.accentAbove) this.accentAbove = params.accentAbove;
if (params.germanAlphabet) this.germanAlphabet = params.germanAlphabet;
if (params.lineThickness) this.lineThickness = params.lineThickness;
this.renderer.controller = this; // TODO-GD needed for highlighting
this.renderer.foregroundColor = params.foregroundColor ? params.foregroundColor : "currentColor";
if (params.ariaLabel !== undefined) this.renderer.ariaLabel = params.ariaLabel;
this.renderer.minPadding = params.minPadding ? params.minPadding : 0;
this.reset();
};
EngraverController.prototype.reset = function () {
this.selected = [];
this.staffgroups = [];
if (this.engraver) this.engraver.reset();
this.engraver = null;
this.renderer.reset();
this.dragTarget = null;
this.dragIndex = -1;
this.dragMouseStart = {
x: -1,
y: -1
};
this.dragYStep = 0;
if (this.lineThickness) this.renderer.setLineThickness(this.lineThickness);
};
/**
* run the engraving process
*/
EngraverController.prototype.engraveABC = function (abctunes, tuneNumber, lineOffset) {
if (abctunes[0] === undefined) {
abctunes = [abctunes];
}
this.reset();
for (var i = 0; i < abctunes.length; i++) {
if (tuneNumber === undefined) tuneNumber = i;
this.getFontAndAttr = new GetFontAndAttr(abctunes[i].formatting, this.classes);
this.getTextSize = new GetTextSize(this.getFontAndAttr, this.renderer.paper);
this.engraveTune(abctunes[i], tuneNumber, lineOffset);
}
};
/**
* Some of the items on the page are not scaled, so adjust them in the opposite direction of scaling to cancel out the scaling.
*/
EngraverController.prototype.adjustNonScaledItems = function (scale) {
this.width /= scale;
this.renderer.adjustNonScaledItems(scale);
};
EngraverController.prototype.getMeasureWidths = function (abcTune) {
this.reset();
this.getFontAndAttr = new GetFontAndAttr(abcTune.formatting, this.classes);
this.getTextSize = new GetTextSize(this.getFontAndAttr, this.renderer.paper);
var origJazzChords = this.jazzchords;
this.setupTune(abcTune, 0);
this.constructTuneElements(abcTune);
// layout() sets the x-coordinate of the abcTune element here:
// abcTune.lines[0].staffGroup.voices[0].children[0].x
layout(this.renderer, abcTune, 0, this.space, this.timeBasedLayout);
var ret = [];
var section;
var needNewSection = true;
for (var i = 0; i < abcTune.lines.length; i++) {
var abcLine = abcTune.lines[i];
if (abcLine.staff) {
if (needNewSection) {
section = {
left: 0,
measureWidths: [],
//height: this.renderer.padding.top + this.renderer.spacing.music + this.renderer.padding.bottom + 24, // the 24 is the empirical value added to the bottom of all tunes.
total: 0
};
ret.push(section);
needNewSection = false;
}
// At this point, the voices are laid out so that the bar lines are even with each other. So we just need to get the placement of the first voice.
if (abcLine.staffGroup.voices.length > 0) {
var voice = abcLine.staffGroup.voices[0];
var foundNotStaffExtra = false;
var lastXPosition = 0;
for (var k = 0; k < voice.children.length; k++) {
var child = voice.children[k];
if (!foundNotStaffExtra && !child.isClef && !child.isKeySig) {
foundNotStaffExtra = true;
section.left = child.x;
lastXPosition = child.x;
}
if (child.type === 'bar') {
section.measureWidths.push(child.x - lastXPosition);
section.total += child.x - lastXPosition;
lastXPosition = child.x;
}
}
}
//section.height += calcHeight(abcLine.staffGroup) * spacing.STEP;
} else needNewSection = true;
}
this.jazzchords = origJazzChords;
return ret;
};
EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
this.classes.reset();
if (abcTune.formatting.jazzchords !== undefined) this.jazzchords = abcTune.formatting.jazzchords;
if (abcTune.formatting.accentAbove !== undefined) this.accentAbove = abcTune.formatting.accentAbove;
this.renderer.newTune(abcTune);
this.engraver = new AbstractEngraver(this.getTextSize, tuneNumber, {
bagpipes: abcTune.formatting.bagpipes,
flatbeams: abcTune.formatting.flatbeams,
graceSlurs: abcTune.formatting.graceSlurs !== false,
// undefined is the default, which is true
percmap: abcTune.formatting.percmap,
initialClef: this.initialClef,
jazzchords: this.jazzchords,
timeBasedLayout: this.timeBasedLayout,
accentAbove: this.accentAbove,
germanAlphabet: this.germanAlphabet
});
this.engraver.setStemHeight(this.renderer.spacing.stemHeight);
this.engraver.measureLength = abcTune.getMeterFraction().num / abcTune.getMeterFraction().den;
if (abcTune.formatting.staffwidth) {
this.width = abcTune.formatting.staffwidth * 1.33; // The width is expressed in pt; convert to px.
} else {
this.width = this.renderer.isPrint ? this.staffwidthPrint : this.staffwidthScreen;
}
var scale = abcTune.formatting.scale ? abcTune.formatting.scale : this.scale;
if (this.responsive === "resize")
// The resizing will mess with the scaling, so just don't do it explicitly.
scale = undefined;
if (scale === undefined) scale = this.renderer.isPrint ? 0.75 : 1;
this.adjustNonScaledItems(scale);
return scale;
};
EngraverController.prototype.constructTuneElements = function (abcTune) {
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
// Generate the raw staff line data
var i;
var abcLine;
var hasPrintedTempo = false;
var hasSeenNonSubtitle = false;
for (i = 0; i < abcTune.lines.length; i++) {
abcLine = abcTune.lines[i];
if (abcLine.staff) {
hasSeenNonSubtitle = true;
abcLine.staffGroup = this.engraver.createABCLine(abcLine.staff, !hasPrintedTempo ? abcTune.metaText.tempo : null, i);
hasPrintedTempo = true;
} else if (abcLine.subtitle) {
// If the subtitle is at the top, then it was already accounted for. So skip all subtitles until the first non-subtitle line.
if (hasSeenNonSubtitle) {
var center = this.width / 2 + this.renderer.padding.left;
abcLine.nonMusic = new Subtitle(this.renderer.spacing.subtitle, abcTune.formatting, abcLine.subtitle, center, this.renderer.padding.left, this.getTextSize);
}
} else if (abcLine.text !== undefined) {
hasSeenNonSubtitle = true;
abcLine.nonMusic = new FreeText(abcLine.text, abcLine.vskip, this.getFontAndAttr, this.renderer.padding.left, this.width, this.getTextSize);
} else if (abcLine.separator !== undefined && abcLine.separator.lineLength) {
hasSeenNonSubtitle = true;
abcLine.nonMusic = new Separator(abcLine.separator.spaceAbove, abcLine.separator.lineLength, abcLine.separator.spaceBelow);
}
}
abcTune.bottomText = new BottomText(abcTune.metaText, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
};
EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOffset) {
var origJazzChords = this.jazzchords;
var scale = this.setupTune(abcTune, tuneNumber);
// Create all of the element objects that will appear on the page.
this.constructTuneElements(abcTune);
//Set the top text now that we know the width
// Do all the positioning, both horizontally and vertically
var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest, this.timeBasedLayout);
//Set the top text now that we know the width
if (this.expandToWidest && maxWidth > this.width + 1) {
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.classes.shouldAddClasses, this.getTextSize);
if (abcTune.lines && abcTune.lines.length > 0) {
var nlines = abcTune.lines.length;
for (var i = 0; i < nlines; ++i) {
var entry = abcTune.lines[i];
if (entry.nonMusic) {
if (entry.nonMusic.rows && entry.nonMusic.rows.length > 0) {
var nRows = entry.nonMusic.rows.length;
for (var j = 0; j < nRows; ++j) {
var thisRow = entry.nonMusic.rows[j];
// Recenter the element if it's a subtitle or centered text
if (thisRow.left) {
if (entry.subtitle) {
thisRow.left = maxWidth / 2 + this.renderer.padding.left;
} else {
if (entry.text && entry.text.length > 0) {
if (entry.text[0].center) {
thisRow.left = maxWidth / 2 + this.renderer.padding.left;
}
}
}
}
}
}
}
}
}
}
// Deal with tablature for staff
if (abcTune.tablatures) {
tablatures.layoutTablatures(this.renderer, abcTune);
}
// Do all the writing to the SVG
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset);
this.staffgroups = ret.staffgroups;
this.selectables = ret.selectables;
if (this.oneSvgPerLine) {
var div = this.renderer.paper.svg.parentNode;
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive, scale);
} else {
this.svgs = [this.renderer.paper.svg];
}
setupSelection(this, this.svgs);
this.jazzchords = origJazzChords;
};
function splitSvgIntoLines(renderer, output, title, responsive, scale) {
// Each line is a top level in the svg. To split it into separate
// svgs iterate through each of those and put them in a new svg. Since
// they are placed absolutely, the viewBox needs to be manipulated to
// get the correct vertical positioning.
// We copy all the attributes from the original svg except for the aria-label
// since we want that to include a count. And the height is now a fraction of the original svg.
if (!title) title = "Untitled";
var source = output.querySelector("svg");
if (responsive === 'resize') output.style.paddingBottom = '';
var style = source.querySelector("style");
var width = responsive === 'resize' ? source.viewBox.baseVal.width : source.getAttribute("width");
var sections = output.querySelectorAll("svg > g"); // each section is a line, or the top matter or the bottom matter, or text that has been inserted.
var nextTop = 0; // There are often gaps between the elements for spacing, so the actual top and height needs to be inferred.
var wrappers = []; // Create all the elements and place them at once because we use the current svg to get data. It would disappear after placing the first line.
var svgs = [];
for (var i = 0; i < sections.length; i++) {
var section = sections[i];
var box = section.getBBox();
var gapBetweenLines = box.y - nextTop; // take the margin into account
var height = box.height + gapBetweenLines;
var wrapper = document.createElement("div");
var divStyles = "overflow: hidden;";
if (responsive !== 'resize') divStyles += "height:" + height * scale + "px;";
wrapper.setAttribute("style", divStyles);
var svg = duplicateSvg(source);
var fullTitle = "Sheet Music for \"" + title + "\" section " + (i + 1);
svg.setAttribute("aria-label", fullTitle);
if (responsive !== 'resize') svg.setAttribute("height", height);
if (responsive === 'resize') svg.style.position = '';
// TODO-PER: Hack! Not sure why this is needed.
var viewBoxHeight = renderer.firefox112 ? height + 1 : height;
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight);
svg.appendChild(style.cloneNode(true));
var titleEl = document.createElement("title");
titleEl.innerText = fullTitle;
svg.appendChild(titleEl);
svg.appendChild(section);
wrapper.appendChild(svg);
svgs.push(svg);
output.appendChild(wrapper);
//wrappers.push(wrapper)
nextTop = box.y + box.height;
}
// for (i = 0; i < wrappers.length; i++)
// output.appendChild(wrappers[i])
output.removeChild(source);
return svgs;
}
function duplicateSvg(source) {
var svgNS = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(svgNS, "svg");
for (var i = 0; i < source.attributes.length; i++) {
var attr = source.attributes[i];
if (attr.name !== "height" && attr.name != "aria-label") svg.setAttribute(attr.name, attr.value);
}
return svg;
}
EngraverController.prototype.getDim = function (historyEl) {
// Get the dimensions on demand because the getBBox call is expensive.
if (!historyEl.dim) {
var box = historyEl.svgEl.getBBox();
historyEl.dim = {
left: Math.round(box.x),
top: Math.round(box.y),
right: Math.round(box.x + box.width),
bottom: Math.round(box.y + box.height)
};
}
return historyEl.dim;
};
EngraverController.prototype.addSelectListener = function (clickListener) {
this.listeners[this.listeners.length] = clickListener;
};
module.exports = EngraverController;
/***/ }),
/***/ "./src/write/helpers/classes.js":
/*!**************************************!*\
!*** ./src/write/helpers/classes.js ***!
\**************************************/
/***/ (function(module) {
var Classes = function Classes(options) {
this.shouldAddClasses = options.shouldAddClasses;
this.reset();
};
Classes.prototype.reset = function () {
this.lineNumber = null;
this.voiceNumber = null;
this.measureNumber = null;
this.measureTotalPerLine = [];
this.noteNumber = null;
};
Classes.prototype.incrLine = function () {
if (this.lineNumber === null) this.lineNumber = 0;else this.lineNumber++;
this.voiceNumber = null;
this.measureNumber = null;
this.noteNumber = null;
};
Classes.prototype.incrVoice = function () {
if (this.voiceNumber === null) this.voiceNumber = 0;else this.voiceNumber++;
this.measureNumber = null;
this.noteNumber = null;
};
Classes.prototype.isInMeasure = function () {
return this.measureNumber !== null;
};
Classes.prototype.newMeasure = function () {
if (this.measureNumber) this.measureTotalPerLine[this.lineNumber] = this.measureNumber;
this.measureNumber = null;
this.noteNumber = null;
};
Classes.prototype.startMeasure = function () {
this.measureNumber = 0;
this.noteNumber = 0;
};
Classes.prototype.incrMeasure = function () {
this.measureNumber++;
this.noteNumber = 0;
};
Classes.prototype.incrNote = function () {
this.noteNumber++;
};
Classes.prototype.measureTotal = function () {
var total = 0;
for (var i = 0; i < this.lineNumber; i++) {
total += this.measureTotalPerLine[i] ? this.measureTotalPerLine[i] : 0;
} // This can be null when non-music things are present.
if (this.measureNumber) total += this.measureNumber;
return total;
};
Classes.prototype.getCurrent = function (c) {
return {
line: this.lineNumber,
measure: this.measureNumber,
measureTotal: this.measureTotal(),
voice: this.voiceNumber,
note: this.noteNumber
};
};
Classes.prototype.generate = function (c) {
if (!this.shouldAddClasses) return "";
var ret = [];
if (c && c.length > 0) ret.push(c);
if (c === "abcjs-tab-number")
// TODO-PER-HACK! straighten out the tablature
return ret.join(' ');
if (c === "text instrument-name") return "abcjs-text abcjs-instrument-name";
if (this.lineNumber !== null) ret.push("l" + this.lineNumber);
if (this.measureNumber !== null) ret.push("m" + this.measureNumber);
if (this.measureNumber !== null) ret.push("mm" + this.measureTotal()); // measureNumber is null between measures so this is still the test for measureTotal
if (this.voiceNumber !== null) ret.push("v" + this.voiceNumber);
if (c && (c.indexOf('note') >= 0 || c.indexOf('rest') >= 0 || c.indexOf('lyric') >= 0) && this.noteNumber !== null) ret.push("n" + this.noteNumber);
// add a prefix to all classes that abcjs adds.
if (ret.length > 0) {
ret = ret.join(' '); // Some strings are compound classes - that is, specify more than one class in a string.
ret = ret.split(' ');
for (var i = 0; i < ret.length; i++) {
if (ret[i].indexOf('abcjs-') !== 0 && ret[i].length > 0)
// if the prefix doesn't already exist and the class is not blank.
ret[i] = 'abcjs-' + ret[i];
}
}
return ret.join(' ');
};
module.exports = Classes;
/***/ }),
/***/ "./src/write/helpers/get-font-and-attr.js":
/*!************************************************!*\
!*** ./src/write/helpers/get-font-and-attr.js ***!
\************************************************/
/***/ (function(module) {
var GetFontAndAttr = function GetFontAndAttr(formatting, classes) {
this.formatting = formatting;
this.classes = classes;
};
GetFontAndAttr.prototype.updateFonts = function (fontOverrides) {
if (fontOverrides.gchordfont) this.formatting.gchordfont = fontOverrides.gchordfont;
if (fontOverrides.tripletfont) this.formatting.tripletfont = fontOverrides.tripletfont;
if (fontOverrides.annotationfont) this.formatting.annotationfont = fontOverrides.annotationfont;
if (fontOverrides.vocalfont) this.formatting.vocalfont = fontOverrides.vocalfont;
};
GetFontAndAttr.prototype.getFamily = function (type) {
if (type[0] === '"' && type[type.length - 1] === '"') {
return type.substring(1, type.length - 1);
}
return type;
};
GetFontAndAttr.prototype.calc = function (type, klass) {
var font;
if (typeof type === 'string') {
font = this.formatting[type];
// Raphael deliberately changes the font units to pixels for some reason, so we need to change points to pixels here.
if (font) font = {
face: font.face,
size: Math.round(font.size * 4 / 3),
decoration: font.decoration,
style: font.style,
weight: font.weight,
box: font.box
};else font = {
face: "Arial",
size: Math.round(12 * 4 / 3),
decoration: "underline",
style: "normal",
weight: "normal"
};
} else font = {
face: type.face,
size: Math.round(type.size * 4 / 3),
decoration: type.decoration,
style: type.style,
weight: type.weight,
box: type.box
};
var paddingPercent = this.formatting.fontboxpadding ? this.formatting.fontboxpadding : 0.1;
font.padding = font.size * paddingPercent;
var attr = {
"font-size": font.size,
'font-style': font.style,
"font-family": this.getFamily(font.face),
'font-weight': font.weight,
'text-decoration': font.decoration,
'class': this.classes.generate(klass)
};
return {
font: font,
attr: attr
};
};
module.exports = GetFontAndAttr;
/***/ }),
/***/ "./src/write/helpers/get-text-size.js":
/*!********************************************!*\
!*** ./src/write/helpers/get-text-size.js ***!
\********************************************/
/***/ (function(module) {
var GetTextSize = function GetTextSize(getFontAndAttr, svg) {
this.getFontAndAttr = getFontAndAttr;
this.svg = svg;
};
GetTextSize.prototype.updateFonts = function (fontOverrides) {
this.getFontAndAttr.updateFonts(fontOverrides);
};
GetTextSize.prototype.attr = function (type, klass) {
return this.getFontAndAttr.calc(type, klass);
};
GetTextSize.prototype.getFamily = function (type) {
if (type[0] === '"' && type[type.length - 1] === '"') {
return type.substring(1, type.length - 1);
}
return type;
};
GetTextSize.prototype.calc = function (text, type, klass, el) {
var hash;
// This can be passed in either a string or a font. If it is a string it names one of the standard fonts.
if (typeof type === 'string') hash = this.attr(type, klass);else {
hash = {
font: {
face: type.face,
size: type.size,
decoration: type.decoration,
style: type.style,
weight: type.weight
},
attr: {
"font-size": type.size,
"font-style": type.style,
"font-family": this.getFamily(type.face),
"font-weight": type.weight,
"text-decoration": type.decoration,
"class": this.getFontAndAttr.classes.generate(klass)
}
};
}
var size = this.svg.getTextSize(text, hash.attr, el);
if (hash.font.box) {
// Add padding and an equal margin to each side.
return {
height: size.height + hash.font.padding * 4,
width: size.width + hash.font.padding * 4
};
}
return size;
};
GetTextSize.prototype.baselineToCenter = function (text, type, klass, index, total) {
// This is for the case where SVG wants to use the baseline of the first line as the Y coordinate.
// If there are multiple lines of text or there is an array of text then that will not be centered so this adjusts it.
var height = this.calc(text, type, klass).height;
var fontHeight = this.attr(type, klass).font.size;
return height * 0.5 + (total - index - 2) * fontHeight;
};
module.exports = GetTextSize;
/***/ }),
/***/ "./src/write/helpers/set-class.js":
/*!****************************************!*\
!*** ./src/write/helpers/set-class.js ***!
\****************************************/
/***/ (function(module) {
var setClass = function setClass(elemset, addClass, removeClass, color) {
if (!elemset) return;
for (var i = 0; i < elemset.length; i++) {
var el = elemset[i];
var attr = el.getAttribute("highlight");
if (!attr) attr = "fill";
el.setAttribute(attr, color);
var kls = el.getAttribute("class");
if (!kls) kls = "";
kls = kls.replace(removeClass, "");
kls = kls.replace(addClass, "");
if (addClass.length > 0) {
if (kls.length > 0 && kls[kls.length - 1] !== ' ') kls += " ";
kls += addClass;
}
el.setAttribute("class", kls);
}
};
module.exports = setClass;
/***/ }),
/***/ "./src/write/helpers/spacing.js":
/*!**************************************!*\
!*** ./src/write/helpers/spacing.js ***!
\**************************************/
/***/ (function(module) {
var spacing = {};
spacing.FONTEM = 360;
spacing.FONTSIZE = 30;
spacing.STEP = spacing.FONTSIZE * 93 / 720;
spacing.SPACE = 10;
spacing.TOPNOTE = 15;
spacing.STAVEHEIGHT = 100;
spacing.INDENT = 50;
module.exports = spacing;
/***/ }),
/***/ "./src/write/interactive/create-analysis.js":
/*!**************************************************!*\
!*** ./src/write/interactive/create-analysis.js ***!
\**************************************************/
/***/ (function(module) {
function findNumber(klass, match, target, name) {
if (klass.indexOf(match) === 0) {
var value = klass.replace(match, '');
var num = parseInt(value, 10);
if ('' + num === value) target[name] = num;
}
}
function createAnalysis(target, ev) {
var classes = [];
if (target.absEl.elemset) {
var classObj = {};
for (var j = 0; j < target.absEl.elemset.length; j++) {
var es = target.absEl.elemset[j];
if (es) {
var klass = es.getAttribute("class").split(' ');
for (var k = 0; k < klass.length; k++) {
classObj[klass[k]] = true;
}
}
}
for (var kk = 0; kk < Object.keys(classObj).length; kk++) {
classes.push(Object.keys(classObj)[kk]);
}
}
var analysis = {};
for (var ii = 0; ii < classes.length; ii++) {
findNumber(classes[ii], "abcjs-v", analysis, "voice");
findNumber(classes[ii], "abcjs-l", analysis, "line");
findNumber(classes[ii], "abcjs-m", analysis, "measure");
}
if (target.staffPos) analysis.staffPos = target.staffPos;
var closest = ev.target;
while (closest && closest.dataset && !closest.dataset.name && closest.tagName.toLowerCase() !== 'svg') {
closest = closest.parentNode;
}
var parent = ev.target;
while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg') {
parent = parent.parentNode;
}
if (parent && parent.dataset) {
analysis.name = parent.dataset.name;
analysis.clickedName = closest.dataset.name;
analysis.parentClasses = parent.classList;
}
if (closest && closest.classList) analysis.clickedClasses = closest.classList;
analysis.selectableElement = target.svgEl;
return {
classes: classes,
analysis: analysis
};
}
module.exports = createAnalysis;
/***/ }),
/***/ "./src/write/interactive/find-selectable-element.js":
/*!**********************************************************!*\
!*** ./src/write/interactive/find-selectable-element.js ***!
\**********************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var createAnalysis = __webpack_require__(/*! ./create-analysis */ "./src/write/interactive/create-analysis.js");
function findSelectableElement(event) {
var selectable = event;
while (selectable && selectable.attributes && selectable.tagName.toLowerCase() !== 'svg' && !selectable.attributes.selectable) {
selectable = selectable.parentNode;
}
if (selectable && selectable.attributes && selectable.attributes.selectable) {
var index = selectable.attributes['data-index'].nodeValue;
if (index) {
index = parseInt(index, 10);
if (index >= 0 && index < this.selectables.length) {
var element = this.selectables[index];
var ret = createAnalysis(element, event);
ret.index = index;
ret.element = element;
return ret;
}
}
}
return null;
}
module.exports = findSelectableElement;
/***/ }),
/***/ "./src/write/interactive/highlight.js":
/*!********************************************!*\
!*** ./src/write/interactive/highlight.js ***!
\********************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var setClass = __webpack_require__(/*! ../helpers/set-class */ "./src/write/helpers/set-class.js");
var highlight = function highlight(klass, color) {
if (klass === undefined) klass = "abcjs-note_selected";
if (color === undefined) color = "#ff0000";
setClass(this.elemset, klass, "", color);
};
module.exports = highlight;
/***/ }),
/***/ "./src/write/interactive/selection.js":
/*!********************************************!*\
!*** ./src/write/interactive/selection.js ***!
\********************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var createAnalysis = __webpack_require__(/*! ./create-analysis */ "./src/write/interactive/create-analysis.js");
function setupSelection(engraver, svgs) {
engraver.rangeHighlight = rangeHighlight;
if (engraver.dragging) {
for (var h = 0; h < engraver.selectables.length; h++) {
var hist = engraver.selectables[h];
if (hist.svgEl.getAttribute("selectable") === "true") {
hist.svgEl.setAttribute("tabindex", 0);
hist.svgEl.setAttribute("data-index", h);
hist.svgEl.addEventListener("keydown", keyboardDown.bind(engraver));
hist.svgEl.addEventListener("keyup", keyboardSelection.bind(engraver));
hist.svgEl.addEventListener("focus", elementFocused.bind(engraver));
}
}
}
for (var i = 0; i < svgs.length; i++) {
svgs[i].addEventListener('touchstart', mouseDown.bind(engraver), {
passive: true
});
svgs[i].addEventListener('touchmove', mouseMove.bind(engraver), {
passive: true
});
svgs[i].addEventListener('touchend', mouseUp.bind(engraver), {
passive: true
});
svgs[i].addEventListener('mousedown', mouseDown.bind(engraver));
svgs[i].addEventListener('mousemove', mouseMove.bind(engraver));
svgs[i].addEventListener('mouseup', mouseUp.bind(engraver));
}
}
function getCoord(ev) {
var scaleX = 1;
var scaleY = 1;
var svg = ev.target.closest('svg');
var yOffset = 0;
// when renderer.options.responsive === 'resize' the click coords are in relation to the HTML
// element, we need to convert to the SVG viewBox coords
if (svg && svg.viewBox && svg.viewBox.baseVal) {
// Firefox passes null to this when no viewBox is given
// Chrome makes these values null when no viewBox is given.
if (svg.viewBox.baseVal.width !== 0) scaleX = svg.viewBox.baseVal.width / svg.clientWidth;
if (svg.viewBox.baseVal.height !== 0) scaleY = svg.viewBox.baseVal.height / svg.clientHeight;
yOffset = svg.viewBox.baseVal.y;
}
var svgClicked = ev.target && ev.target.tagName === "svg";
var x;
var y;
if (svgClicked) {
x = ev.offsetX;
y = ev.offsetY;
} else {
x = ev.layerX;
y = ev.layerY;
}
x = x * scaleX;
y = y * scaleY;
//console.log(x, y)
return [x, y + yOffset];
}
function elementFocused(ev) {
// If there had been another element focused and is being dragged, then report that before setting the new element up.
if (this.dragMechanism === "keyboard" && this.dragYStep !== 0 && this.dragTarget) notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, ev);
this.dragYStep = 0;
}
function keyboardDown(ev) {
// Swallow the up and down arrow events - they will be used for dragging with the keyboard
switch (ev.keyCode) {
case 38:
case 40:
ev.preventDefault();
}
}
function keyboardSelection(ev) {
// "this" is the EngraverController because of the bind(this) when setting the event listener.
var handled = false;
var index = ev.target.dataset.index;
switch (ev.keyCode) {
case 13:
case 32:
handled = true;
this.dragTarget = this.selectables[index];
this.dragIndex = index;
this.dragMechanism = "keyboard";
mouseUp.bind(this)(ev);
break;
case 38:
// arrow up
handled = true;
this.dragTarget = this.selectables[index];
this.dragIndex = index;
if (this.dragTarget && this.dragTarget.isDraggable) {
if (this.dragging && this.dragTarget.isDraggable) this.dragTarget.absEl.highlight(undefined, this.dragColor);
this.dragYStep--;
this.dragTarget.svgEl.setAttribute("transform", "translate(0," + this.dragYStep * spacing.STEP + ")");
}
break;
case 40:
// arrow down
handled = true;
this.dragTarget = this.selectables[index];
this.dragIndex = index;
this.dragMechanism = "keyboard";
if (this.dragTarget && this.dragTarget.isDraggable) {
if (this.dragging && this.dragTarget.isDraggable) this.dragTarget.absEl.highlight(undefined, this.dragColor);
this.dragYStep++;
this.dragTarget.svgEl.setAttribute("transform", "translate(0," + this.dragYStep * spacing.STEP + ")");
}
break;
case 9:
// tab
// This is losing focus - if there had been dragging, then do the callback
if (this.dragYStep !== 0) {
mouseUp.bind(this)(ev);
}
break;
default:
//console.log(ev);
break;
}
if (handled) ev.preventDefault();
}
function findElementInHistory(selectables, el) {
if (!el) return -1;
for (var i = 0; i < selectables.length; i++) {
if (el.dataset.index === selectables[i].svgEl.dataset.index) return i;
}
return -1;
}
function findElementByCoord(self, x, y) {
var minDistance = 9999999;
var closestIndex = -1;
for (var i = 0; i < self.selectables.length && minDistance > 0; i++) {
var el = self.selectables[i];
self.getDim(el);
if (el.dim.left < x && el.dim.right > x && el.dim.top < y && el.dim.bottom > y) {
// See if it is a direct hit on an element - if so, definitely take it (there are no overlapping elements)
closestIndex = i;
minDistance = 0;
} else if (el.dim.top < y && el.dim.bottom > y) {
// See if it is the same vertical as the element. Then the distance is the x difference
var horiz = Math.min(Math.abs(el.dim.left - x), Math.abs(el.dim.right - x));
if (horiz < minDistance) {
minDistance = horiz;
closestIndex = i;
}
} else if (el.dim.left < x && el.dim.right > x) {
// See if it is the same horizontal as the element. Then the distance is the y difference
var vert = Math.min(Math.abs(el.dim.top - y), Math.abs(el.dim.bottom - y));
if (vert < minDistance) {
minDistance = vert;
closestIndex = i;
}
} else {
// figure out the distance to this element.
var dx = Math.abs(x - el.dim.left) > Math.abs(x - el.dim.right) ? Math.abs(x - el.dim.right) : Math.abs(x - el.dim.left);
var dy = Math.abs(y - el.dim.top) > Math.abs(y - el.dim.bottom) ? Math.abs(y - el.dim.bottom) : Math.abs(y - el.dim.top);
var hypotenuse = Math.sqrt(dx * dx + dy * dy);
if (hypotenuse < minDistance) {
minDistance = hypotenuse;
closestIndex = i;
}
}
}
return closestIndex >= 0 && minDistance <= 12 ? closestIndex : -1;
}
function getBestMatchCoordinates(dim, ev, scale) {
// Different browsers have conflicting meanings for the coordinates that are returned.
// If the item we want is clicked on directly, then we will just see what is the best match.
// This seems like less of a hack than browser sniffing.
if (dim.x <= ev.offsetX && dim.x + dim.width >= ev.offsetX && dim.y <= ev.offsetY && dim.y + dim.height >= ev.offsetY) return [ev.offsetX, ev.offsetY];
// Firefox returns a weird value for offset, but layer is correct.
// Safari and Chrome return the correct value for offset, but layer is multiplied by the scale (that is, if it were rendered with { scale: 2 })
// For instance (if scale is 2):
// Firefox: { offsetY: 5, layerY: 335 }
// Others: {offsetY: 335, layerY: 670} (there could be a little rounding, so the number might not be exactly 2x)
// So, if layerY/scale is approx. offsetY, then use offsetY, otherwise use layerY
var epsilon = Math.abs(ev.layerY / scale - ev.offsetY);
if (epsilon < 3) return [ev.offsetX, ev.offsetY];else return [ev.layerX, ev.layerY];
}
function getTarget(target) {
// This searches up the dom for the first item containing the attribute "selectable", or stopping at the SVG.
if (!target) return null;
if (target.tagName === "svg") return target;
if (!target.getAttribute) return null;
var found = target.getAttribute("selectable");
while (!found) {
if (!target.parentElement) found = true;else {
target = target.parentElement;
if (target.tagName === "svg") found = true;else found = target.getAttribute("selectable");
}
}
return target;
}
function getMousePosition(self, ev) {
// if the user clicked exactly on an element that we're interested in, then we already have the answer.
// This is more reliable than the calculations because firefox returns different coords for offsetX, offsetY
var x;
var y;
var box;
var clickedOn = findElementInHistory(self.selectables, getTarget(ev.target));
if (clickedOn >= 0) {
// There was a direct hit on an element.
box = getBestMatchCoordinates(self.selectables[clickedOn].svgEl.getBBox(), ev, self.scale);
x = box[0];
y = box[1];
//console.log("clicked on", clickedOn, x, y, self.selectables[clickedOn].svgEl.getBBox(), ev.target.getBBox());
} else {
// See if they clicked close to an element.
box = getCoord(ev);
x = box[0];
y = box[1];
clickedOn = findElementByCoord(self, x, y);
//console.log("clicked near", clickedOn, x, y, printEl(ev.target));
}
return {
x: x,
y: y,
clickedOn: clickedOn
};
}
function attachMissingTouchEventAttributes(touchEv) {
if (!touchEv || !touchEv.target || !touchEv.touches || touchEv.touches.length < 1) return;
var rect = touchEv.target.getBoundingClientRect();
var offsetX = touchEv.touches[0].pageX - rect.left;
var offsetY = touchEv.touches[0].pageY - rect.top;
touchEv.touches[0].offsetX = offsetX;
touchEv.touches[0].offsetY = offsetY;
touchEv.touches[0].layerX = touchEv.touches[0].pageX;
touchEv.touches[0].layerY = touchEv.touches[0].pageY;
}
function mouseDown(ev) {
// "this" is the EngraverController because of the bind(this) when setting the event listener.
var _ev = ev;
if (ev.type === 'touchstart') {
attachMissingTouchEventAttributes(ev);
if (ev.touches.length > 0) _ev = ev.touches[0];
}
var positioning = getMousePosition(this, _ev);
// Only start dragging if the user clicked close enough to an element and clicked with the main mouse button.
if (positioning.clickedOn >= 0 && (ev.type === 'touchstart' || ev.button === 0) && this.selectables[positioning.clickedOn]) {
this.dragTarget = this.selectables[positioning.clickedOn];
this.dragIndex = positioning.clickedOn;
this.dragMechanism = "mouse";
this.dragMouseStart = {
x: positioning.x,
y: positioning.y
};
if (this.dragging && this.dragTarget.isDraggable) {
addGlobalClass(this.renderer.paper, "abcjs-dragging-in-progress");
this.dragTarget.absEl.highlight(undefined, this.dragColor);
}
}
}
function mouseMove(ev) {
var _ev = ev;
if (ev.type === 'touchmove') {
attachMissingTouchEventAttributes(ev);
if (ev.touches.length > 0) _ev = ev.touches[0];
}
this.lastTouchMove = ev;
// "this" is the EngraverController because of the bind(this) when setting the event listener.
if (!this.dragTarget || !this.dragging || !this.dragTarget.isDraggable || this.dragMechanism !== 'mouse' || !this.dragMouseStart) return;
var positioning = getMousePosition(this, _ev);
var yDist = Math.round((positioning.y - this.dragMouseStart.y) / spacing.STEP);
if (yDist !== this.dragYStep) {
this.dragYStep = yDist;
this.dragTarget.svgEl.setAttribute("transform", "translate(0," + yDist * spacing.STEP + ")");
}
}
function mouseUp(ev) {
// "this" is the EngraverController because of the bind(this) when setting the event listener.
var _ev = ev;
if (ev.type === 'touchend' && this.lastTouchMove) {
attachMissingTouchEventAttributes(this.lastTouchMove);
if (this.lastTouchMove && this.lastTouchMove.touches && this.lastTouchMove.touches.length > 0) _ev = this.lastTouchMove.touches[0];
}
if (!this.dragTarget) return;
clearSelection.bind(this)();
if (this.dragTarget.absEl && this.dragTarget.absEl.highlight) {
this.selected = [this.dragTarget.absEl];
this.dragTarget.absEl.highlight(undefined, this.selectionColor);
}
notifySelect.bind(this)(this.dragTarget, this.dragYStep, this.selectables.length, this.dragIndex, _ev);
if (this.dragTarget.svgEl && this.dragTarget.svgEl.focus) {
this.dragTarget.svgEl.focus();
this.dragTarget = null;
this.dragIndex = -1;
}
removeGlobalClass(this.renderer.svg, "abcjs-dragging-in-progress");
}
function setSelection(dragIndex) {
if (dragIndex >= 0 && dragIndex < this.selectables.length) {
this.dragTarget = this.selectables[dragIndex];
this.dragIndex = dragIndex;
this.dragMechanism = "keyboard";
mouseUp.bind(this)({
target: this.dragTarget.svgEl
});
}
}
function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
var ret = createAnalysis(target, ev);
var classes = ret.classes;
var analysis = ret.analysis;
for (var i = 0; i < this.listeners.length; i++) {
this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, {
step: dragStep,
max: dragMax,
index: dragIndex,
setSelection: setSelection.bind(this)
}, ev);
}
}
function clearSelection() {
for (var i = 0; i < this.selected.length; i++) {
this.selected[i].unhighlight(undefined, this.renderer.foregroundColor);
}
this.selected = [];
}
function rangeHighlight(start, end) {
clearSelection.bind(this)();
for (var line = 0; line < this.staffgroups.length; line++) {
var voices = this.staffgroups[line].voices;
for (var voice = 0; voice < voices.length; voice++) {
var elems = voices[voice].children;
for (var elem = 0; elem < elems.length; elem++) {
// Since the user can highlight more than an element, or part of an element, a hit is if any of the endpoints
// is inside the other range.
var elStart = elems[elem].abcelem.startChar;
var elEnd = elems[elem].abcelem.endChar;
if (end > elStart && start < elEnd || end === start && end === elEnd) {
// if (elems[elem].abcelem.startChar>=start && elems[elem].abcelem.endChar<=end) {
this.selected[this.selected.length] = elems[elem];
elems[elem].highlight(undefined, this.selectionColor);
}
}
}
}
}
function getClassSet(el) {
var oldClass = el.getAttribute('class');
if (!oldClass) oldClass = "";
var klasses = oldClass.split(" ");
var obj = {};
for (var i = 0; i < klasses.length; i++) {
obj[klasses[i]] = true;
}
return obj;
}
function setClassSet(el, klassSet) {
var klasses = [];
for (var key in klassSet) {
if (klassSet.hasOwnProperty(key)) klasses.push(key);
}
el.setAttribute('class', klasses.join(' '));
}
function addGlobalClass(svg, klass) {
if (svg) {
var obj = getClassSet(svg.svg);
obj[klass] = true;
setClassSet(svg.svg, obj);
}
}
function removeGlobalClass(svg, klass) {
if (svg) {
var obj = getClassSet(svg.svg);
delete obj[klass];
setClassSet(svg.svg, obj);
}
}
module.exports = setupSelection;
/***/ }),
/***/ "./src/write/interactive/unhighlight.js":
/*!**********************************************!*\
!*** ./src/write/interactive/unhighlight.js ***!
\**********************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var setClass = __webpack_require__(/*! ../helpers/set-class */ "./src/write/helpers/set-class.js");
var unhighlight = function unhighlight(klass, color) {
if (klass === undefined) klass = "abcjs-note_selected";
if (color === undefined) color = "#000000";
setClass(this.elemset, "", klass, color);
};
module.exports = unhighlight;
/***/ }),
/***/ "./src/write/layout/beam.js":
/*!**********************************!*\
!*** ./src/write/layout/beam.js ***!
\**********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var RelativeElement = __webpack_require__(/*! ../creation/elements/relative-element */ "./src/write/creation/elements/relative-element.js");
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var getBarYAt = __webpack_require__(/*! ./get-bar-y-at */ "./src/write/layout/get-bar-y-at.js");
var layoutBeam = function layoutBeam(beam) {
if (beam.elems.length === 0 || beam.allrests) return;
var dy = calcDy(beam.stemsUp, beam.isgrace); // This is the width of the beam line.
// create the main beam
var firstElement = beam.elems[0];
var lastElement = beam.elems[beam.elems.length - 1];
var minStemHeight = 0; // The following is to leave space for "!///!" marks.
var referencePitch = beam.stemsUp ? firstElement.abcelem.maxpitch : firstElement.abcelem.minpitch;
minStemHeight = minStem(firstElement, beam.stemsUp, referencePitch, minStemHeight);
minStemHeight = minStem(lastElement, beam.stemsUp, referencePitch, minStemHeight);
minStemHeight = Math.max(beam.stemHeight, minStemHeight + 3); // TODO-PER: The 3 is the width of a 16th beam. The actual height of the beam should be used instead.
var yPos = calcYPos(beam.average, beam.elems.length, minStemHeight, beam.stemsUp, firstElement.abcelem.averagepitch, lastElement.abcelem.averagepitch, beam.isflat, beam.min, beam.max, beam.isgrace);
var xPos = calcXPos(beam.stemsUp, firstElement, lastElement);
beam.addBeam({
startX: xPos[0],
endX: xPos[1],
startY: yPos[0],
endY: yPos[1],
dy: dy
});
// create the rest of the beams (in the case of 1/16th notes, etc.
var beams = createAdditionalBeams(beam.elems, beam.stemsUp, beam.beams[0], beam.isgrace, dy);
for (var i = 0; i < beams.length; i++) {
beam.addBeam(beams[i]);
}
// Now that the main beam is defined, we know how tall the stems should be, so create them and attach them to the original notes.
createStems(beam.elems, beam.stemsUp, beam.beams[0], dy, beam.mainNote);
};
var getDurlog = function getDurlog(duration) {
// TODO-PER: This is a hack to prevent a Chrome lockup. Duration should have been defined already,
// but there's definitely a case where it isn't. [Probably something to do with triplets.]
if (duration === undefined) {
return 0;
}
// console.log("getDurlog: " + duration);
return Math.floor(Math.log(duration) / Math.log(2));
};
//
// private functions
//
function minStem(element, stemsUp, referencePitch, minStemHeight) {
if (!element.children) return minStemHeight;
for (var i = 0; i < element.children.length; i++) {
var elem = element.children[i];
if (stemsUp && elem.top !== undefined && elem.c === "flags.ugrace") minStemHeight = Math.max(minStemHeight, elem.top - referencePitch);else if (!stemsUp && elem.bottom !== undefined && elem.c === "flags.ugrace") minStemHeight = Math.max(minStemHeight, referencePitch - elem.bottom + 7); // The extra 7 is because we are measuring the slash from the top.
}
return minStemHeight;
}
function calcSlant(leftAveragePitch, rightAveragePitch, numStems, isFlat) {
if (isFlat) return 0;
var slant = leftAveragePitch - rightAveragePitch;
var maxSlant = numStems / 2;
if (slant > maxSlant) slant = maxSlant;
if (slant < -maxSlant) slant = -maxSlant;
return slant;
}
function calcDy(asc, isGrace) {
var dy = asc ? spacing.STEP : -spacing.STEP;
if (isGrace) dy = dy * 0.4;
return dy;
}
function calcXPos(asc, firstElement, lastElement) {
var starthead = firstElement.heads[asc ? 0 : firstElement.heads.length - 1];
var endhead = lastElement.heads[asc ? 0 : lastElement.heads.length - 1];
var startX = starthead.x;
if (asc) startX += starthead.w - 0.6;
var endX = endhead.x;
endX += asc ? endhead.w : 0.6;
return [startX, endX];
}
function calcYPos(average, numElements, stemHeight, asc, firstAveragePitch, lastAveragePitch, isFlat, minPitch, maxPitch, isGrace) {
var barpos = stemHeight - 2; // (isGrace)? 5:7;
var barminpos = stemHeight - 2;
var pos = Math.round(asc ? Math.max(average + barpos, maxPitch + barminpos) : Math.min(average - barpos, minPitch - barminpos));
var slant = calcSlant(firstAveragePitch, lastAveragePitch, numElements, isFlat);
var startY = pos + Math.floor(slant / 2);
var endY = pos + Math.floor(-slant / 2);
// If the notes are too high or too low, make the beam go down to the middle
if (!isGrace) {
if (asc && pos < 6) {
startY = 6;
endY = 6;
} else if (!asc && pos > 6) {
startY = 6;
endY = 6;
}
}
return [startY, endY];
}
function createStems(elems, asc, beam, dy, mainNote) {
for (var i = 0; i < elems.length; i++) {
var elem = elems[i];
if (elem.abcelem.rest) continue;
// TODO-PER: This is odd. If it is a regular beam then elems is an array of AbsoluteElements, if it is a grace beam then it is an array of objects , so we directly attach the element to the parent. We tell it if is a grace note because they are passed in as a generic object instead of an AbsoluteElement.
var isGrace = elem.addExtra ? false : true;
var parent = isGrace ? mainNote : elem;
var furthestHead = elem.heads[asc ? 0 : elem.heads.length - 1];
var ovalDelta = 1 / 5; //(isGrace)?1/3:1/5;
var pitch = furthestHead.pitch + (asc ? ovalDelta : -ovalDelta);
var dx = asc ? furthestHead.w : 0; // down-pointing stems start on the left side of the note, up-pointing stems start on the right side, so we offset by the note width.
if (!isGrace) dx += furthestHead.dx;
var x = furthestHead.x + dx; // this is now the actual x location in pixels.
var bary = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, x);
var lineWidth = asc ? -0.6 : 0.6;
if (!asc) bary -= dy / 2 / spacing.STEP; // TODO-PER: This is just a fudge factor so the down-pointing stems don't overlap.
if (isGrace) dx += elem.heads[0].dx;
// TODO-PER-HACK: One type of note head has a different placement of the stem. This should be more generically calculated:
if (furthestHead.c === 'noteheads.slash.quarter') {
if (asc) pitch += 1;else pitch -= 1;
}
var stem = new RelativeElement(null, dx, 0, pitch, {
"type": "stem",
"pitch2": bary,
linewidth: lineWidth
});
stem.setX(parent.x); // This is after the x coordinates were set, so we have to set it directly.
parent.addRight(stem);
}
}
function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
var beams = [];
var auxBeams = []; // auxbeam will be {x, y, durlog, single} auxbeam[0] should match with durlog=-4 (16th) (j=-4-durlog)
for (var i = 0; i < elems.length; i++) {
var elem = elems[i];
if (elem.abcelem.rest) continue;
var furthestHead = elem.heads[asc ? 0 : elem.heads.length - 1];
var x = furthestHead.x + (asc ? furthestHead.w : 0);
var bary = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, x);
var sy = asc ? -1.5 : 1.5;
if (isGrace) sy = sy * 2 / 3; // This makes the second beam on grace notes closer to the first one.
var duration = elem.abcelem.duration; // get the duration via abcelem because of triplets
if (duration === 0) duration = 0.25; // if this is stemless, then we use quarter note as the duration.
for (var durlog = getDurlog(duration); durlog < -3; durlog++) {
var index = -4 - durlog;
if (auxBeams[index]) {
auxBeams[index].single = false;
} else {
auxBeams[index] = {
x: x + (asc ? -0.6 : 0),
y: bary + sy * (index + 1),
durlog: durlog,
single: true
};
}
if (i > 0 && elem.abcelem.beambr && elem.abcelem.beambr <= index + 1) {
if (!auxBeams[index].split) auxBeams[index].split = [auxBeams[index].x];
var xPos = calcXPos(asc, elems[i - 1], elem);
if (auxBeams[index].split[auxBeams[index].split.length - 1] >= xPos[0]) {
// the reduction in beams leaves a note unattached so create a small flag for it.
xPos[0] += elem.w;
}
auxBeams[index].split.push(xPos[0]);
auxBeams[index].split.push(xPos[1]);
}
}
for (var j = auxBeams.length - 1; j >= 0; j--) {
if (i === elems.length - 1 || getDurlog(elems[i + 1].abcelem.duration) > -j - 4) {
var auxBeamEndX = x;
var auxBeamEndY = bary + sy * (j + 1);
if (auxBeams[j].single) {
auxBeamEndX = i === 0 ? x + 5 : x - 5;
auxBeamEndY = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, auxBeamEndX) + sy * (j + 1);
}
var b = {
startX: auxBeams[j].x,
endX: auxBeamEndX,
startY: auxBeams[j].y,
endY: auxBeamEndY,
dy: dy
};
if (auxBeams[j].split !== undefined) {
var split = auxBeams[j].split;
if (b.endX <= split[split.length - 1]) {
// the reduction in beams leaves the last note by itself, so create a little flag for it
split[split.length - 1] -= elem.w;
}
split.push(b.endX);
b.split = auxBeams[j].split;
}
beams.push(b);
auxBeams = auxBeams.slice(0, j);
}
}
}
return beams;
}
module.exports = layoutBeam;
/***/ }),
/***/ "./src/write/layout/get-bar-y-at.js":
/*!******************************************!*\
!*** ./src/write/layout/get-bar-y-at.js ***!
\******************************************/
/***/ (function(module) {
function getBarYAt(startx, starty, endx, endy, x) {
return starty + (endy - starty) / (endx - startx) * (x - startx);
}
module.exports = getBarYAt;
/***/ }),
/***/ "./src/write/layout/get-left-edge-of-staff.js":
/*!****************************************************!*\
!*** ./src/write/layout/get-left-edge-of-staff.js ***!
\****************************************************/
/***/ (function(module) {
function getLeftEdgeOfStaff(renderer, getTextSize, voices, brace, bracket) {
var x = renderer.padding.left;
// find out how much space will be taken up by voice headers
var voiceheaderw = 0;
var i;
var size;
for (i = 0; i < voices.length; i++) {
if (voices[i].header) {
size = getTextSize.calc(voices[i].header, 'voicefont', '');
voiceheaderw = Math.max(voiceheaderw, size.width);
}
}
voiceheaderw = addBraceSize(voiceheaderw, brace, getTextSize);
voiceheaderw = addBraceSize(voiceheaderw, bracket, getTextSize);
if (voiceheaderw) {
// Give enough spacing to the right - we use the width of an A for the amount of spacing.
var sizeW = getTextSize.calc("A", 'voicefont', '');
voiceheaderw += sizeW.width;
}
x += voiceheaderw;
var ofs = 0;
ofs = setBraceLocation(brace, x, ofs);
ofs = setBraceLocation(bracket, x, ofs);
return x + ofs;
}
function addBraceSize(voiceheaderw, brace, getTextSize) {
if (brace) {
for (var i = 0; i < brace.length; i++) {
if (brace[i].header) {
var size = getTextSize.calc(brace[i].header, 'voicefont', '');
voiceheaderw = Math.max(voiceheaderw, size.width);
}
}
}
return voiceheaderw;
}
function setBraceLocation(brace, x, ofs) {
if (brace) {
for (var i = 0; i < brace.length; i++) {
setLocation(x, brace[i]);
ofs = Math.max(ofs, brace[i].getWidth());
}
}
return ofs;
}
function setLocation(x, element) {
element.x = x;
}
module.exports = getLeftEdgeOfStaff;
/***/ }),
/***/ "./src/write/layout/layout-in-grid.js":
/*!********************************************!*\
!*** ./src/write/layout/layout-in-grid.js ***!
\********************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
function layoutInGrid(renderer, staffGroup, timeBasedLayout) {
var leftEdge = getLeftEdgeOfStaff(renderer, staffGroup.getTextSize, staffGroup.voices, staffGroup.brace, staffGroup.bracket);
var ret = getTotalDuration(staffGroup, timeBasedLayout.minPadding);
var totalDuration = ret.totalDuration;
var minSpacing = ret.minSpacing;
var totalWidth = minSpacing * totalDuration;
if (timeBasedLayout.minWidth) totalWidth = Math.max(totalWidth, timeBasedLayout.minWidth);
var leftAlignPadding = timeBasedLayout.minPadding ? timeBasedLayout.minPadding / 2 : 2; // If the padding isn't specified still give it some
staffGroup.startx = leftEdge;
staffGroup.w = totalWidth + leftEdge;
for (var i = 0; i < staffGroup.voices.length; i++) {
var voice = staffGroup.voices[i];
voice.startx = leftEdge;
voice.w = totalWidth + leftEdge;
var x = leftEdge;
var afterFixedLeft = false;
var durationUnit = 0;
for (var j = 0; j < voice.children.length; j++) {
var child = voice.children[j];
if (!afterFixedLeft) {
if (child.duration !== 0) {
// We got to the first music element on the line
afterFixedLeft = true;
durationUnit = (totalWidth + leftEdge - x) / totalDuration;
staffGroup.gridStart = x;
} else {
// We are still doing the preliminary stuff - clef, time sig, etc.
child.x = x;
x += child.w + child.minspacing;
}
}
if (afterFixedLeft) {
if (timeBasedLayout.align === 'center') child.x = x + child.duration * durationUnit / 2 - child.w / 2;else {
// left align with padding - but no padding for barlines, they should be right aligned.
// TODO-PER: it looks better to move bar lines one pixel to right. Not sure why.
if (child.duration === 0) {
child.x = x + 1 - child.w;
} else {
// child.extraw has the width of the accidentals - push the note to the right to take that into consideration. It will be 0 if there is nothing to the left.
child.x = x + leftAlignPadding - child.extraw;
}
}
x += child.duration * durationUnit;
}
for (var k = 0; k < child.children.length; k++) {
var grandchild = child.children[k];
// some elements don't have a dx - Tempo, for instance
var dx = grandchild.dx ? grandchild.dx : 0;
grandchild.x = child.x + dx;
}
}
staffGroup.gridEnd = x;
}
return totalWidth;
}
function getTotalDuration(staffGroup, timeBasedLayout) {
var maxSpacing = 0;
var maxCount = 0;
for (var i = 0; i < staffGroup.voices.length; i++) {
var count = 0;
var voice = staffGroup.voices[i];
for (var j = 0; j < voice.children.length; j++) {
var element = voice.children[j];
count += element.duration;
if (element.duration) {
var width = (element.w + timeBasedLayout) / element.duration;
maxSpacing = Math.max(maxSpacing, width);
}
}
maxCount = Math.max(maxCount, count);
}
return {
totalDuration: maxCount,
minSpacing: maxSpacing
};
}
module.exports = layoutInGrid;
/***/ }),
/***/ "./src/write/layout/layout.js":
/*!************************************!*\
!*** ./src/write/layout/layout.js ***!
\************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var layoutVoice = __webpack_require__(/*! ./voice */ "./src/write/layout/voice.js");
var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-elements */ "./src/write/layout/set-upper-and-lower-elements.js");
var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
var layoutInGrid = __webpack_require__(/*! ./layout-in-grid */ "./src/write/layout/layout-in-grid.js");
// This sets the "x" attribute on all the children in abctune.lines
// It also sets the "w" and "startx" attributes on "voices"
// It also sets the "w" and "startx" attributes on "voices.children"
var layout = function layout(renderer, abctune, width, space, expandToWidest, timeBasedLayout) {
var i;
var abcLine;
// Adjust the x-coordinates to their absolute positions
var maxWidth = width;
for (i = 0; i < abctune.lines.length; i++) {
abcLine = abctune.lines[i];
if (abcLine.staff) {
// console.log("=== line", i)
var thisWidth;
if (timeBasedLayout !== undefined) thisWidth = layoutInGrid(renderer, abcLine.staffGroup, timeBasedLayout);else thisWidth = setXSpacing(renderer, maxWidth, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
// console.log(thisWidth, maxWidth)
if (Math.round(thisWidth) > Math.round(maxWidth)) {
// to take care of floating point weirdness
maxWidth = thisWidth;
if (expandToWidest) i = -1; // do the calculations over with the new width
}
}
}
// Layout the beams and add the stems to the beamed notes.
for (i = 0; i < abctune.lines.length; i++) {
abcLine = abctune.lines[i];
if (abcLine.staffGroup && abcLine.staffGroup.voices) {
for (var j = 0; j < abcLine.staffGroup.voices.length; j++) {
layoutVoice(abcLine.staffGroup.voices[j]);
}
setUpperAndLowerElements(renderer, abcLine.staffGroup);
}
}
// Set the staff spacing
// TODO-PER: we should have been able to do this by the time we called setUpperAndLowerElements, but for some reason the "bottom" element seems to be set as a side effect of setting the X spacing.
for (i = 0; i < abctune.lines.length; i++) {
abcLine = abctune.lines[i];
if (abcLine.staffGroup) {
abcLine.staffGroup.setHeight();
}
}
return maxWidth;
};
// Do the x-axis positioning for a single line (a group of related staffs)
var setXSpacing = function setXSpacing(renderer, width, space, staffGroup, formatting, isLastLine, debug) {
var leftEdge = getLeftEdgeOfStaff(renderer, staffGroup.getTextSize, staffGroup.voices, staffGroup.brace, staffGroup.bracket);
var newspace = space;
//dumpGroup("before", staffGroup)
for (var it = 0; it < 8; it++) {
// TODO-PER: shouldn't need multiple passes, but each pass gets it closer to the right spacing. (Only affects long lines: normal lines break out of this loop quickly.)
// console.log("iteration", it)
var ret = layoutStaffGroup(newspace, renderer.minPadding, debug, staffGroup, leftEdge);
newspace = calcHorizontalSpacing(isLastLine, formatting.stretchlast, width + renderer.padding.left, staffGroup.w, newspace, ret.spacingUnits, ret.minSpace, renderer.padding.left + renderer.padding.right);
if (debug) console.log("setXSpace", it, staffGroup.w, newspace, staffGroup.minspace);
if (newspace === null) break;
}
//dumpGroup("after",staffGroup)
centerWholeRests(staffGroup.voices);
return staffGroup.w - leftEdge;
};
function replacer(key, value) {
// Filtering out properties
if (key === 'parent') {
return 'parent';
}
if (key === 'beam') {
return 'beam';
}
return value;
}
function dumpGroup(label, staffGroup) {
console.log("=================== " + label + " =========================");
console.log(staffGroup);
console.log(JSON.stringify(staffGroup, replacer, "\t"));
}
function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth, spacing, spacingUnits, minSpace, padding) {
if (isLastLine) {
if (stretchLast === undefined) {
if (lineWidth / targetWidth < 0.66) return null; // keep this for backward compatibility. The break isn't quite the same for some reason.
} else {
// "Stretch the last music line of a tune when it lacks less than the float fraction of the page width."
var lack = 1 - (lineWidth + padding) / targetWidth;
var stretch = lack < stretchLast;
if (!stretch) return null; // don't stretch last line too much
}
}
if (Math.abs(targetWidth - lineWidth) < 2) return null; // if we are already near the target width, we're done.
var relSpace = spacingUnits * spacing;
var constSpace = lineWidth - relSpace;
if (spacingUnits > 0) {
spacing = (targetWidth - constSpace) / spacingUnits;
if (spacing * minSpace > 50) {
spacing = 50 / minSpace;
}
return spacing;
}
return null;
}
function centerWholeRests(voices) {
// whole rests are a special case: if they are by themselves in a measure, then they should be centered.
// (If they are not by themselves, that is probably a user error, but we'll just center it between the two items to either side of it.)
for (var i = 0; i < voices.length; i++) {
var voice = voices[i];
// Look through all of the elements except for the first and last. If the whole note appears there then there isn't anything to center it between anyway.
for (var j = 1; j < voice.children.length - 1; j++) {
var absElem = voice.children[j];
if (absElem.abcelem.rest && (absElem.abcelem.rest.type === 'whole' || absElem.abcelem.rest.type === 'multimeasure')) {
var before = voice.children[j - 1];
var after = voice.children[j + 1];
absElem.center(before, after);
}
}
}
}
module.exports = layout;
/***/ }),
/***/ "./src/write/layout/set-upper-and-lower-elements.js":
/*!**********************************************************!*\
!*** ./src/write/layout/set-upper-and-lower-elements.js ***!
\**********************************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staffGroup) {
// Each staff already has the top and bottom set, now we see if there are elements that are always on top and bottom, and resolve their pitch.
// Also, get the overall height of all the staves in this group.
var lastStaffBottom;
for (var i = 0; i < staffGroup.staffs.length; i++) {
var staff = staffGroup.staffs[i];
// the vertical order of elements that are above is: tempo, part, volume/dynamic, ending/chord, lyric
// the vertical order of elements that are below is: lyric, chord, volume/dynamic
var positionY = {
tempoHeightAbove: 0,
partHeightAbove: 0,
volumeHeightAbove: 0,
dynamicHeightAbove: 0,
endingHeightAbove: 0,
chordHeightAbove: 0,
lyricHeightAbove: 0,
lyricHeightBelow: 0,
chordHeightBelow: 0,
volumeHeightBelow: 0,
dynamicHeightBelow: 0
};
if (renderer.showDebug && renderer.showDebug.indexOf("box") >= 0) {
staff.originalTop = staff.top; // This is just being stored for debugging purposes.
staff.originalBottom = staff.bottom; // This is just being stored for debugging purposes.
}
incTop(staff, positionY, 'lyricHeightAbove');
incTop(staff, positionY, 'chordHeightAbove', staff.specialY.chordLines.above);
if (staff.specialY.endingHeightAbove) {
if (staff.specialY.chordHeightAbove) staff.top += 2;else staff.top += staff.specialY.endingHeightAbove + margin;
positionY.endingHeightAbove = staff.top;
}
if (staff.specialY.dynamicHeightAbove && staff.specialY.volumeHeightAbove) {
staff.top += Math.max(staff.specialY.dynamicHeightAbove, staff.specialY.volumeHeightAbove) + margin;
positionY.dynamicHeightAbove = staff.top;
positionY.volumeHeightAbove = staff.top;
} else {
incTop(staff, positionY, 'dynamicHeightAbove');
incTop(staff, positionY, 'volumeHeightAbove');
}
incTop(staff, positionY, 'partHeightAbove');
incTop(staff, positionY, 'tempoHeightAbove');
if (staff.specialY.lyricHeightBelow) {
staff.specialY.lyricHeightBelow += renderer.spacing.vocal / spacing.STEP;
positionY.lyricHeightBelow = staff.bottom;
staff.bottom -= staff.specialY.lyricHeightBelow + margin;
}
if (staff.specialY.chordHeightBelow) {
positionY.chordHeightBelow = staff.bottom;
var hgt = staff.specialY.chordHeightBelow;
if (staff.specialY.chordLines.below) hgt *= staff.specialY.chordLines.below;
staff.bottom -= hgt + margin;
}
if (staff.specialY.volumeHeightBelow && staff.specialY.dynamicHeightBelow) {
positionY.volumeHeightBelow = staff.bottom;
positionY.dynamicHeightBelow = staff.bottom;
staff.bottom -= Math.max(staff.specialY.volumeHeightBelow, staff.specialY.dynamicHeightBelow) + margin;
} else if (staff.specialY.volumeHeightBelow) {
positionY.volumeHeightBelow = staff.bottom;
staff.bottom -= staff.specialY.volumeHeightBelow + margin;
} else if (staff.specialY.dynamicHeightBelow) {
positionY.dynamicHeightBelow = staff.bottom;
staff.bottom -= staff.specialY.dynamicHeightBelow + margin;
}
if (renderer.showDebug && renderer.showDebug.indexOf("box") >= 0) staff.positionY = positionY; // This is just being stored for debugging purposes.
for (var j = 0; j < staff.voices.length; j++) {
var voice = staffGroup.voices[staff.voices[j]];
setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
}
// We might need a little space in between staves if the staves haven't been pushed far enough apart by notes or extra vertical stuff.
// Only try to put in extra space if this isn't the top staff.
if (lastStaffBottom !== undefined) {
var thisStaffTop = staff.top - 10;
var forcedSpacingBetween = lastStaffBottom + thisStaffTop;
var minSpacingInPitches = renderer.spacing.systemStaffSeparation / spacing.STEP;
var addedSpace = minSpacingInPitches - forcedSpacingBetween;
if (addedSpace > 0) staff.top += addedSpace;
}
staff.top += renderer.spacing.staffTopMargin / spacing.STEP;
lastStaffBottom = 2 - staff.bottom; // the staff starts at position 2 and the bottom variable is negative. Therefore to find out how large the bottom is, we reverse the sign of the bottom, and add the 2 in.
// Now we need a little margin on the top, so we'll just throw that in.
//staff.top += 4;
//console.log("Staff Y: ",i,heightInPitches,staff.top,staff.bottom);
}
//console.log("Staff Height: ",heightInPitches,this.height);
};
var margin = 1;
function incTop(staff, positionY, item, count) {
if (staff.specialY[item]) {
var height = staff.specialY[item];
if (count) height *= count;
staff.top += height + margin;
positionY[item] = staff.top;
}
}
function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
var i;
var abselem;
for (i = 0; i < voice.children.length; i++) {
abselem = voice.children[i];
setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
}
for (i = 0; i < voice.otherchildren.length; i++) {
abselem = voice.otherchildren[i];
switch (abselem.type) {
case 'CrescendoElem':
setUpperAndLowerCrescendoElements(positionY, abselem);
break;
case 'DynamicDecoration':
setUpperAndLowerDynamicElements(positionY, abselem);
break;
case 'EndingElem':
setUpperAndLowerEndingElements(positionY, abselem);
break;
case 'TieElem':
// If a tie element is the highest or lowest thing then space might need to make room for it.
var yBounds = abselem.getYBounds();
voice.staff.top = Math.max(voice.staff.top, yBounds[0]);
voice.staff.top = Math.max(voice.staff.top, yBounds[1]);
voice.staff.bottom = Math.min(voice.staff.bottom, yBounds[0]);
voice.staff.bottom = Math.min(voice.staff.bottom, yBounds[1]);
break;
}
}
}
// For each of the relative elements that can't be placed in advance (because their vertical placement depends on everything
// else on the line), this iterates through them and sets their pitch. By the time this is called, specialYResolved contains a
// hash with the vertical placement (in pitch units) for each type.
// TODO-PER: I think this needs to be separated by "above" and "below". How do we know that for dynamics at the point where they are being defined, though? We need a pass through all the relative elements to set "above" and "below".
function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
// specialYResolved contains the actual pitch for each of the classes of elements.
for (var i = 0; i < element.children.length; i++) {
var child = element.children[i];
for (var key in element.specialY) {
// for each class of element that needs to be placed vertically
if (element.specialY.hasOwnProperty(key)) {
if (child[key]) {
// If this relative element has defined a height for this class of element
child.pitch = specialYResolved[key];
if (child.top === undefined) {
// TODO-PER: HACK! Not sure this is the right place to do this.
if (child.type === 'TempoElement') {
setUpperAndLowerTempoElement(specialYResolved, child);
} else {
setUpperAndLowerRelativeElements(specialYResolved, child, spacing);
}
element.pushTop(child.top);
element.pushBottom(child.bottom);
}
}
}
}
}
}
function setUpperAndLowerCrescendoElements(positionY, element) {
if (element.dynamicHeightAbove) element.pitch = positionY.dynamicHeightAbove;else element.pitch = positionY.dynamicHeightBelow;
}
function setUpperAndLowerDynamicElements(positionY, element) {
if (element.volumeHeightAbove) element.pitch = positionY.volumeHeightAbove;else element.pitch = positionY.volumeHeightBelow;
}
function setUpperAndLowerEndingElements(positionY, element) {
element.pitch = positionY.endingHeightAbove - 2;
}
function setUpperAndLowerTempoElement(positionY, element) {
element.pitch = positionY.tempoHeightAbove;
element.top = positionY.tempoHeightAbove;
element.bottom = positionY.tempoHeightAbove;
if (element.note) {
var tempoPitch = element.pitch - element.totalHeightInPitches + 1; // The pitch we receive is the top of the allotted area: change that to practically the bottom.
element.note.top = tempoPitch;
element.note.bottom = tempoPitch;
for (var i = 0; i < element.note.children.length; i++) {
var child = element.note.children[i];
child.top += tempoPitch;
child.bottom += tempoPitch;
child.pitch += tempoPitch;
if (child.pitch2 !== undefined) child.pitch2 += tempoPitch;
}
}
}
function setUpperAndLowerRelativeElements(positionY, element, renderSpacing) {
switch (element.type) {
case "part":
element.top = positionY.partHeightAbove + element.height;
element.bottom = positionY.partHeightAbove;
break;
case "text":
case "chord":
if (element.chordHeightAbove) {
element.top = positionY.chordHeightAbove;
element.bottom = positionY.chordHeightAbove;
} else {
element.top = positionY.chordHeightBelow;
element.bottom = positionY.chordHeightBelow;
}
break;
case "lyric":
if (element.lyricHeightAbove) {
element.top = positionY.lyricHeightAbove;
element.bottom = positionY.lyricHeightAbove;
} else {
element.top = positionY.lyricHeightBelow + renderSpacing.vocal / spacing.STEP;
element.bottom = positionY.lyricHeightBelow + renderSpacing.vocal / spacing.STEP;
element.pitch -= renderSpacing.vocal / spacing.STEP;
}
break;
case "debug":
element.top = positionY.chordHeightAbove;
element.bottom = positionY.chordHeightAbove;
break;
}
if (element.pitch === undefined || element.top === undefined) console.error("RelativeElement position not set.", element.type, element.pitch, element.top, positionY);
}
module.exports = setUpperAndLowerElements;
/***/ }),
/***/ "./src/write/layout/staff-group.js":
/*!*****************************************!*\
!*** ./src/write/layout/staff-group.js ***!
\*****************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var layoutVoiceElements = __webpack_require__(/*! ./voice-elements */ "./src/write/layout/voice-elements.js");
function checkLastBarX(voices) {
var maxX = 0;
for (var i = 0; i < voices.length; i++) {
var curVoice = voices[i];
if (curVoice.children.length > 0) {
var lastChild = curVoice.children.length - 1;
var maxChild = curVoice.children[lastChild];
if (maxChild.abcelem.el_type === 'bar') {
var barX = maxChild.children[0].x;
if (barX > maxX) {
maxX = barX;
} else {
maxChild.children[0].x = maxX;
}
}
}
}
}
var layoutStaffGroup = function layoutStaffGroup(spacing, minPadding, debug, staffGroup, leftEdge) {
var epsilon = 0.0000001; // Fudging for inexactness of floating point math.
var spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
var minspace = 1000; // a big number to start off with - used to find out what the smallest space between two notes is -- GD 2014.1.7
var x = leftEdge;
staffGroup.startx = x;
var i;
var currentduration = 0;
if (debug) console.log("init layout", spacing);
for (i = 0; i < staffGroup.voices.length; i++) {
layoutVoiceElements.beginLayout(x, staffGroup.voices[i]);
}
var spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
while (!finished(staffGroup.voices)) {
// find first duration level to be laid out among candidates across voices
currentduration = null; // candidate smallest duration level
for (i = 0; i < staffGroup.voices.length; i++) {
if (!layoutVoiceElements.layoutEnded(staffGroup.voices[i]) && (!currentduration || getDurationIndex(staffGroup.voices[i]) < currentduration)) currentduration = getDurationIndex(staffGroup.voices[i]);
}
// isolate voices at current duration level
var currentvoices = [];
var othervoices = [];
for (i = 0; i < staffGroup.voices.length; i++) {
var durationIndex = getDurationIndex(staffGroup.voices[i]);
// PER: Because of the inexactness of JS floating point math, we just get close.
if (durationIndex - currentduration > epsilon) {
othervoices.push(staffGroup.voices[i]);
//console.log("out: voice ",i);
} else {
currentvoices.push(staffGroup.voices[i]);
//if (debug) console.log("in: voice ",i);
}
}
// among the current duration level find the one which needs starting furthest right
spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
var spacingduration = 0;
for (i = 0; i < currentvoices.length; i++) {
//console.log("greatest spacing unit", x, layoutVoiceElements.getNextX(currentvoices[i]), layoutVoiceElements.getSpacingUnits(currentvoices[i]), currentvoices[i].spacingduration);
if (layoutVoiceElements.getNextX(currentvoices[i]) > x) {
x = layoutVoiceElements.getNextX(currentvoices[i]);
spacingunit = layoutVoiceElements.getSpacingUnits(currentvoices[i]);
spacingduration = currentvoices[i].spacingduration;
}
}
spacingunits += spacingunit;
minspace = Math.min(minspace, spacingunit);
if (debug) console.log("currentduration: ", currentduration, spacingunits, minspace);
var lastTopVoice = undefined;
for (i = 0; i < currentvoices.length; i++) {
var v = currentvoices[i];
if (v.voicenumber === 0) lastTopVoice = i;
var topVoice = lastTopVoice !== undefined && currentvoices[lastTopVoice].voicenumber !== v.voicenumber ? currentvoices[lastTopVoice] : undefined;
if (!isSameStaff(v, topVoice)) topVoice = undefined;
var voicechildx = layoutVoiceElements.layoutOneItem(x, spacing, v, minPadding, topVoice);
var dx = voicechildx - x;
if (dx > 0) {
x = voicechildx; //update x
for (var j = 0; j < i; j++) {
// shift over all previously laid out elements
layoutVoiceElements.shiftRight(dx, currentvoices[j]);
}
}
}
// remove the value of already counted spacing units in other voices (e.g. if a voice had planned to use up 5 spacing units but is not in line to be laid out at this duration level - where we've used 2 spacing units - then we must use up 3 spacing units, not 5)
for (i = 0; i < othervoices.length; i++) {
othervoices[i].spacingduration -= spacingduration;
layoutVoiceElements.updateNextX(x, spacing, othervoices[i]); // adjust other voices expectations
}
// update indexes of currently laid out elems
for (i = 0; i < currentvoices.length; i++) {
var voice = currentvoices[i];
layoutVoiceElements.updateIndices(voice);
}
} // finished laying out
// find the greatest remaining x as a base for the width
for (i = 0; i < staffGroup.voices.length; i++) {
if (layoutVoiceElements.getNextX(staffGroup.voices[i]) > x) {
x = layoutVoiceElements.getNextX(staffGroup.voices[i]);
spacingunit = layoutVoiceElements.getSpacingUnits(staffGroup.voices[i]);
}
}
// adjust lastBar when needed (multi staves)
checkLastBarX(staffGroup.voices);
//console.log("greatest remaining",spacingunit,x);
spacingunits += spacingunit;
staffGroup.setWidth(x);
return {
spacingUnits: spacingunits,
minSpace: minspace
};
};
function finished(voices) {
for (var i = 0; i < voices.length; i++) {
if (!layoutVoiceElements.layoutEnded(voices[i])) return false;
}
return true;
}
function getDurationIndex(element) {
return element.durationindex - (element.children[element.i] && element.children[element.i].duration > 0 ? 0 : 0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
}
function isSameStaff(voice1, voice2) {
if (!voice1 || !voice1.staff || !voice1.staff.voices || voice1.staff.voices.length === 0) return false;
if (!voice2 || !voice2.staff || !voice2.staff.voices || voice2.staff.voices.length === 0) return false;
return voice1.staff.voices[0] === voice2.staff.voices[0];
}
module.exports = layoutStaffGroup;
/***/ }),
/***/ "./src/write/layout/triplet.js":
/*!*************************************!*\
!*** ./src/write/layout/triplet.js ***!
\*************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var getBarYAt = __webpack_require__(/*! ./get-bar-y-at */ "./src/write/layout/get-bar-y-at.js");
function layoutTriplet(element) {
// TODO end and beginning of line (PER: P.S. I'm not sure this can happen: I think the parser will always specify both the start and end points.)
if (element.anchor1 && element.anchor2) {
element.hasBeam = !!element.anchor1.parent.beam && element.anchor1.parent.beam === element.anchor2.parent.beam;
var beam = element.anchor1.parent.beam;
// if hasBeam is true, then the first and last element in the triplet have the same beam.
// We also need to check if the beam doesn't contain other notes so that `(3 dcdcc` will do a bracket.
if (element.hasBeam && (beam.elems[0] !== element.anchor1.parent || beam.elems[beam.elems.length - 1] !== element.anchor2.parent)) element.hasBeam = false;
if (element.hasBeam) {
// If there is a beam then we don't need to draw anything except the text. The beam could either be above or below.
var left = isAbove(beam) ? element.anchor1.x + element.anchor1.w : element.anchor1.x;
element.yTextPos = heightAtMidpoint(left, element.anchor2.x, beam);
element.yTextPos += isAbove(beam) ? 3 : -2; // This creates some space between the beam and the number.
element.xTextPos = xAtMidpoint(left, element.anchor2.x);
element.top = element.yTextPos + 1;
element.bottom = element.yTextPos - 2;
if (isAbove(beam)) element.endingHeightAbove = 4;
} else {
// If there isn't a beam, then we need to draw the bracket and the text. The bracket is always above.
// The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is
// a tall note in the middle, the bracket is horizontal and above the highest note.
element.startNote = Math.max(element.anchor1.parent.top, 9) + 4;
element.endNote = Math.max(element.anchor2.parent.top, 9) + 4;
// If it starts or ends on a rest, make the beam horizontal
if (element.anchor1.parent.type === "rest" && element.anchor2.parent.type !== "rest") element.startNote = element.endNote;else if (element.anchor2.parent.type === "rest" && element.anchor1.parent.type !== "rest") element.endNote = element.startNote;
// See if the middle note is really high.
var max = 0;
for (var i = 0; i < element.middleElems.length; i++) {
max = Math.max(max, element.middleElems[i].top);
}
max += 4;
if (max > element.startNote || max > element.endNote) {
element.startNote = max;
element.endNote = max;
}
if (element.flatBeams) {
element.startNote = Math.max(element.startNote, element.endNote);
element.endNote = Math.max(element.startNote, element.endNote);
}
element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2;
element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2;
element.top = element.yTextPos + 1;
}
}
delete element.middleElems;
delete element.flatBeams;
}
function isAbove(beam) {
return beam.stemsUp;
}
// We can't just use the entire beam for the calculation. The range has to be passed in, because the beam might extend into some unrelated notes. for instance, (3_a'f'e'f'2 when L:16
function heightAtMidpoint(startX, endX, beam) {
if (beam.beams.length === 0) return 0;
beam = beam.beams[0];
var midPoint = startX + (endX - startX) / 2;
return getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, midPoint);
}
function xAtMidpoint(startX, endX) {
return startX + (endX - startX) / 2;
}
module.exports = layoutTriplet;
/***/ }),
/***/ "./src/write/layout/voice-elements.js":
/*!********************************************!*\
!*** ./src/write/layout/voice-elements.js ***!
\********************************************/
/***/ (function(module) {
var VoiceElement = function VoiceElements() {};
VoiceElement.beginLayout = function (startx, voice) {
voice.i = 0;
voice.durationindex = 0;
//this.ii=this.children.length;
voice.startx = startx;
voice.minx = startx; // furthest left to where negatively positioned elements are allowed to go
voice.nextx = startx; // x position where the next element of this voice should be placed assuming no other voices and no fixed width constraints
voice.spacingduration = 0; // duration left to be laid out in current iteration (omitting additional spacing due to other aspects, such as bars, dots, sharps and flats)
};
VoiceElement.layoutEnded = function (voice) {
return voice.i >= voice.children.length;
};
VoiceElement.getNextX = function (voice) {
return Math.max(voice.minx, voice.nextx);
};
// number of spacing units expected for next positioning
VoiceElement.getSpacingUnits = function (voice) {
return Math.sqrt(voice.spacingduration * 8);
};
// Try to layout the element at index this.i
// x - position to try to layout the element at
// spacing - base spacing
// can't call this function more than once per iteration
VoiceElement.layoutOneItem = function (x, spacing, voice, minPadding, firstVoice) {
var child = voice.children[voice.i];
if (!child) return 0;
var er = x - voice.minx; // available extrawidth to the left
var pad = voice.durationindex + child.duration > 0 ? minPadding : 0; // only add padding to the items that aren't fixed to the left edge.
// See if this item overlaps the item in the first voice. If firstVoice is undefined then there's nothing to compare.
if (child.abcelem.el_type === "note" && !child.abcelem.rest && voice.voicenumber !== 0 && firstVoice) {
var firstChild = firstVoice.children[firstVoice.i];
// It overlaps if the either the child's top or bottom is inside the firstChild's or at least within 1
// A special case is if the element is on the same line then it can share a note head, if the notehead is the same
var overlaps = firstChild && (child.abcelem.maxpitch <= firstChild.abcelem.maxpitch + 1 && child.abcelem.maxpitch >= firstChild.abcelem.minpitch - 1 || child.abcelem.minpitch <= firstChild.abcelem.maxpitch + 1 && child.abcelem.minpitch >= firstChild.abcelem.minpitch - 1);
// See if they can share a note head
if (overlaps && child.abcelem.minpitch === firstChild.abcelem.minpitch && child.abcelem.maxpitch === firstChild.abcelem.maxpitch && firstChild.heads && firstChild.heads.length > 0 && child.heads && child.heads.length > 0 && firstChild.heads[0].c === child.heads[0].c) overlaps = false;
// If this note overlaps the note in the first voice and we haven't moved the note yet (this can be called multiple times)
if (overlaps) {
// I think that firstChild should always have at least one note head, but defensively make sure.
// There was a problem with this being called more than once so if a value is adjusted then it is saved so it is only adjusted once.
var firstChildNoteWidth = firstChild.heads && firstChild.heads.length > 0 ? firstChild.heads[0].realWidth : firstChild.fixed.w;
if (!child.adjustedWidth) child.adjustedWidth = firstChildNoteWidth + child.w;
child.w = child.adjustedWidth;
for (var j = 0; j < child.children.length; j++) {
var relativeChild = child.children[j];
if (relativeChild.name.indexOf("accidental") < 0) {
if (!relativeChild.adjustedWidth) relativeChild.adjustedWidth = relativeChild.dx + firstChildNoteWidth;
relativeChild.dx = relativeChild.adjustedWidth;
}
}
}
}
var extraWidth = getExtraWidth(child, pad);
if (er < extraWidth) {
// shift right by needed amount
// There's an exception if a bar element is after a Part element, there is no shift.
if (voice.i === 0 || child.type !== 'bar' || voice.children[voice.i - 1].type !== 'part' && voice.children[voice.i - 1].type !== 'tempo') x += extraWidth - er;
}
child.setX(x);
voice.spacingduration = child.duration;
//update minx
voice.minx = x + getMinWidth(child); // add necessary layout space
if (voice.i !== voice.children.length - 1) voice.minx += child.minspacing; // add minimumspacing except on last elem
this.updateNextX(x, spacing, voice);
// contribute to staff y position
//this.staff.top = Math.max(child.top,this.staff.top);
//this.staff.bottom = Math.min(child.bottom,this.staff.bottom);
return x; // where we end up having placed the child
};
VoiceElement.shiftRight = function (dx, voice) {
var child = voice.children[voice.i];
if (!child) return;
child.setX(child.x + dx);
voice.minx += dx;
voice.nextx += dx;
};
// call when spacingduration has been updated
VoiceElement.updateNextX = function (x, spacing, voice) {
voice.nextx = x + spacing * this.getSpacingUnits(voice);
};
VoiceElement.updateIndices = function (voice) {
if (!this.layoutEnded(voice)) {
voice.durationindex += voice.children[voice.i].duration;
if (voice.children[voice.i].type === 'bar') voice.durationindex = Math.round(voice.durationindex * 64) / 64; // everytime we meet a barline, do rounding to nearest 64th
voice.i++;
}
};
function getExtraWidth(child, minPadding) {
// space needed to the left of the note
var padding = 0;
if (child.type === 'note' || child.type === 'bar') padding = minPadding;
return -child.extraw + padding;
}
function getMinWidth(child) {
// absolute space taken to the right of the note
return child.w;
}
module.exports = VoiceElement;
/***/ }),
/***/ "./src/write/layout/voice.js":
/*!***********************************!*\
!*** ./src/write/layout/voice.js ***!
\***********************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
var layoutBeam = __webpack_require__(/*! ./beam */ "./src/write/layout/beam.js");
var getBarYAt = __webpack_require__(/*! ./get-bar-y-at */ "./src/write/layout/get-bar-y-at.js");
var layoutTriplet = __webpack_require__(/*! ./triplet */ "./src/write/layout/triplet.js");
var layoutVoice = function layoutVoice(voice) {
for (var i = 0; i < voice.beams.length; i++) {
if (voice.beams[i].type === 'BeamElem') {
layoutBeam(voice.beams[i]);
moveDecorations(voice.beams[i]);
// The above will change the top and bottom of the abselem children, so see if we need to expand our range.
for (var j = 0; j < voice.beams[i].elems.length; j++) {
voice.adjustRange(voice.beams[i].elems[j]);
}
}
}
voice.staff.specialY.chordLines = setLaneForChord(voice.children);
// Now we can layout the triplets
for (i = 0; i < voice.otherchildren.length; i++) {
var child = voice.otherchildren[i];
if (child.type === 'TripletElem') {
layoutTriplet(child);
voice.adjustRange(child);
}
}
voice.staff.top = Math.max(voice.staff.top, voice.top);
voice.staff.bottom = Math.min(voice.staff.bottom, voice.bottom);
};
function moveDecorations(beam) {
var padding = 1.5; // This is the vertical padding between elements, in pitches.
for (var ch = 0; ch < beam.elems.length; ch++) {
var child = beam.elems[ch];
if (child.top) {
// We now know where the ornaments should have been placed, so move them if they would overlap.
var top = yAtNote(child, beam);
for (var i = 0; i < child.children.length; i++) {
var el = child.children[i];
if (el.klass === 'ornament' && el.position !== 'below') {
if (el.bottom - padding < top) {
var distance = top - el.bottom + padding; // Find the distance that it needs to move and add a little margin so the element doesn't touch the beam.
el.bottom += distance;
el.top += distance;
el.pitch += distance;
top = child.top = el.top;
}
}
}
}
}
}
function placeInLane(rightMost, relElem) {
// These items are centered so figure the coordinates accordingly.
// The font reports some extra space so the margin is built in.
var xCoords = relElem.getChordDim();
if (xCoords) {
for (var i = 0; i < rightMost.length; i++) {
var fits = rightMost[i] < xCoords.left;
if (fits) {
if (i > 0) relElem.putChordInLane(i);
rightMost[i] = xCoords.right;
return;
}
}
// If we didn't return early, then we need a new row
rightMost.push(xCoords.right);
relElem.putChordInLane(rightMost.length - 1);
}
}
function setLaneForChord(absElems) {
// Criteria:
// 1) lane numbers start from the bottom so that as many items as possible are in lane 0, closest to the music.
// 2) a chord can have more than one line (for instance "C\nD") each line is a lane.
// 3) if two adjoining items would touch then push the second one to the next lane.
// 4) use as many lanes as is necessary to get everything to not touch.
// 5) leave a margin between items, so use another lane if the chords would have less than a character's width.
// 6) if the chord only has one character, allow it to be closer than if the chord has more than one character.
var rightMostAbove = [0];
var rightMostBelow = [0];
var i;
var j;
var relElem;
for (i = 0; i < absElems.length; i++) {
for (j = 0; j < absElems[i].children.length; j++) {
relElem = absElems[i].children[j];
if (relElem.chordHeightAbove) {
placeInLane(rightMostAbove, relElem);
}
}
for (j = absElems[i].children.length - 1; j >= 0; j--) {
relElem = absElems[i].children[j];
if (relElem.chordHeightBelow) {
placeInLane(rightMostBelow, relElem);
}
}
}
// If we used a second line, then we need to go back and set the first lines.
// Also we need to flip the indexes of the names so that we can count from the top line.
if (rightMostAbove.length > 1 || rightMostBelow.length > 1) setLane(absElems, rightMostAbove.length, rightMostBelow.length);
return {
above: rightMostAbove.length,
below: rightMostBelow.length
};
}
function numAnnotationsBelow(absElem) {
var count = 0;
for (var j = 0; j < absElem.children.length; j++) {
var relElem = absElem.children[j];
if (relElem.chordHeightBelow) count++;
}
return count;
}
function setLane(absElems, numLanesAbove, numLanesBelow) {
for (var i = 0; i < absElems.length; i++) {
var below = numAnnotationsBelow(absElems[i]);
for (var j = 0; j < absElems[i].children.length; j++) {
var relElem = absElems[i].children[j];
if (relElem.chordHeightAbove) {
relElem.invertLane(numLanesAbove);
// } else if (relElem.chordHeightBelow) {
// relElem.invertLane(below);
}
}
}
}
function yAtNote(element, beam) {
beam = beam.beams[0];
return getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, element.x);
}
module.exports = layoutVoice;
/***/ }),
/***/ "./src/write/renderer.js":
/*!*******************************!*\
!*** ./src/write/renderer.js ***!
\*******************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
// abc_renderer.js: API to render to SVG/Raphael/whatever rendering engine
/*global Math */
var spacing = __webpack_require__(/*! ./helpers/spacing */ "./src/write/helpers/spacing.js");
var Svg = __webpack_require__(/*! ./svg */ "./src/write/svg.js");
/**
* Implements the API for rendering ABCJS Abstract Rendering Structure to a canvas/paper (e.g. SVG, Raphael, etc)
* @param {Object} paper
*/
var Renderer = function Renderer(paper) {
this.paper = new Svg(paper);
this.controller = null;
this.space = 3 * spacing.SPACE;
this.padding = {}; // renderer's padding is managed by the controller
this.reset();
this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0;
};
Renderer.prototype.reset = function () {
this.paper.clear();
this.y = 0;
this.abctune = null;
this.path = null;
this.isPrint = false;
this.lineThickness = 0;
this.initVerticalSpace();
};
Renderer.prototype.newTune = function (abcTune) {
this.abctune = abcTune; // TODO-PER: this is just to get the font info.
this.setVerticalSpace(abcTune.formatting);
//this.measureNumber = null;
//this.noteNumber = null;
this.isPrint = abcTune.media === 'print';
this.setPadding(abcTune);
};
Renderer.prototype.setLineThickness = function (lineThickness) {
this.lineThickness = lineThickness;
};
Renderer.prototype.setPaddingOverride = function (params) {
this.paddingOverride = {
top: params.paddingtop,
bottom: params.paddingbottom,
right: params.paddingright,
left: params.paddingleft
};
};
Renderer.prototype.setPadding = function (abctune) {
// If the padding is set in the tune, then use that.
// Otherwise, if the padding is set in the override, use that.
// Otherwise, use the defaults (there are a different set of defaults for screen and print.)
function setPaddingVariable(self, paddingKey, formattingKey, printDefault, screenDefault) {
if (abctune.formatting[formattingKey] !== undefined) self.padding[paddingKey] = abctune.formatting[formattingKey];else if (self.paddingOverride[paddingKey] !== undefined) self.padding[paddingKey] = self.paddingOverride[paddingKey];else if (self.isPrint) self.padding[paddingKey] = printDefault;else self.padding[paddingKey] = screenDefault;
}
// 1cm x 0.393701in/cm x 72pt/in x 1.33px/pt = 38px
// 1.8cm x 0.393701in/cm x 72pt/in x 1.33px/pt = 68px
setPaddingVariable(this, 'top', 'topmargin', 38, 15);
setPaddingVariable(this, 'bottom', 'botmargin', 38, 15);
setPaddingVariable(this, 'left', 'leftmargin', 68, 15);
setPaddingVariable(this, 'right', 'rightmargin', 68, 15);
};
/**
* Some of the items on the page are not scaled, so adjust them in the opposite direction of scaling to cancel out the scaling.
* @param {float} scale
*/
Renderer.prototype.adjustNonScaledItems = function (scale) {
this.padding.top /= scale;
this.padding.bottom /= scale;
this.padding.left /= scale;
this.padding.right /= scale;
this.abctune.formatting.headerfont.size /= scale;
this.abctune.formatting.footerfont.size /= scale;
};
/**
* Set the the values for all the configurable vertical space options.
*/
Renderer.prototype.initVerticalSpace = function () {
// conversion: 37.7953 = conversion factor for cm to px.
// All of the following values are in px.
this.spacing = {
composer: 7.56,
// Set the vertical space above the composer.
graceBefore: 8.67,
// Define the space before, inside and after the grace notes.
graceInside: 10.67,
graceAfter: 16,
info: 0,
// Set the vertical space above the infoline.
lineSkipFactor: 1.1,
// Set the factor for spacing between lines of text. (multiply this by the font size)
music: 7.56,
// Set the vertical space above the first staff.
paragraphSkipFactor: 0.4,
// Set the factor for spacing between text paragraphs. (multiply this by the font size)
parts: 11.33,
// Set the vertical space above a new part.
slurHeight: 1.0,
// Set the slur height factor.
staffSeparation: 61.33,
// Do not put a staff system closer than from the previous system.
staffTopMargin: 0,
stemHeight: 26.67 + 10,
// Set the stem height.
subtitle: 3.78,
// Set the vertical space above the subtitle.
systemStaffSeparation: 48,
// Do not place the staves closer than inside a system. * This values applies to all staves when in the tune header. Otherwise, it applies to the next staff
text: 18.9,
// Set the vertical space above the history.
title: 7.56,
// Set the vertical space above the title.
top: 30.24,
//Set the vertical space above the tunes and on the top of the continuation pages.
vocal: 0,
// Set the vertical space above the lyrics under the staves.
words: 0 // Set the vertical space above the lyrics at the end of the tune.
};
/*
TODO-PER: Handle the x-coordinate spacing items, too.
maxshrink Default: 0.65
Set how much to compress horizontally when music line breaks
are automatic.
must be between 0 (natural spacing)
and 1 (max shrinking).
// This next value is used to compute the natural spacing of
// the notes. The base spacing of the crotchet is always
// 40 pts. When the duration of a note type is twice the
// duration of an other note type, its spacing is multiplied
// by this factor.
// The default value causes the note spacing to be multiplied
// by 2 when its duration is multiplied by 4, i.e. the
// space of the semibreve is 80 pts and the space of the
// semiquaver is 20 pts.
// Setting this value to 1 sets all note spacing to 40 pts.
noteSpacingFactor: 1.414, // Set the note spacing factor to (range 1..2).
scale Default: 0.75 Set the page scale factor. Note that the header and footer are not scaled.
stretchlast Default: 0.8
Stretch the last music line of a tune when it exceeds
the fraction of the page width.
range is 0.0 to 1.0.
*/
};
Renderer.prototype.setVerticalSpace = function (formatting) {
// conversion from pts to px 4/3
if (formatting.staffsep !== undefined) this.spacing.staffSeparation = formatting.staffsep * 4 / 3;
if (formatting.composerspace !== undefined) this.spacing.composer = formatting.composerspace * 4 / 3;
if (formatting.partsspace !== undefined) this.spacing.parts = formatting.partsspace * 4 / 3;
if (formatting.textspace !== undefined) this.spacing.text = formatting.textspace * 4 / 3;
if (formatting.musicspace !== undefined) this.spacing.music = formatting.musicspace * 4 / 3;
if (formatting.titlespace !== undefined) this.spacing.title = formatting.titlespace * 4 / 3;
if (formatting.sysstaffsep !== undefined) this.spacing.systemStaffSeparation = formatting.sysstaffsep * 4 / 3;
if (formatting.stafftopmargin !== undefined) this.spacing.staffTopMargin = formatting.stafftopmargin * 4 / 3;
if (formatting.subtitlespace !== undefined) this.spacing.subtitle = formatting.subtitlespace * 4 / 3;
if (formatting.topspace !== undefined) this.spacing.top = formatting.topspace * 4 / 3;
if (formatting.vocalspace !== undefined) this.spacing.vocal = formatting.vocalspace * 4 / 3;
if (formatting.wordsspace !== undefined) this.spacing.words = formatting.wordsspace * 4 / 3;
};
/**
* Calculates the y for a given pitch value (relative to the stave the renderer is currently printing)
* @param {number} ofs pitch value (bottom C on a G clef = 0, D=1, etc.)
*/
Renderer.prototype.calcY = function (ofs) {
return this.y - ofs * spacing.STEP;
};
Renderer.prototype.moveY = function (em, numLines) {
if (numLines === undefined) numLines = 1;
this.y += em * numLines;
};
Renderer.prototype.absolutemoveY = function (y) {
this.y = y;
};
module.exports = Renderer;
/***/ }),
/***/ "./src/write/svg.js":
/*!**************************!*\
!*** ./src/write/svg.js ***!
\**************************/
/***/ (function(module) {
// abc_voice_element.js: Definition of the VoiceElement class.
/*global module */
var svgNS = "http://www.w3.org/2000/svg";
function Svg(wrapper) {
this.svg = createSvg();
this.currentGroup = [];
wrapper.appendChild(this.svg);
}
Svg.prototype.clear = function () {
if (this.svg) {
var wrapper = this.svg.parentNode;
this.svg = createSvg();
this.currentGroup = [];
if (wrapper) {
// TODO-PER: If the wrapper is not present, then the underlying div was pulled out from under this instance. It's possible that is still useful (for creating the music off page?)
wrapper.innerHTML = "";
wrapper.appendChild(this.svg);
}
}
};
Svg.prototype.setTitle = function (title) {
var titleEl = document.createElement("title");
var titleNode = document.createTextNode(title);
titleEl.appendChild(titleNode);
this.svg.insertBefore(titleEl, this.svg.firstChild);
};
Svg.prototype.setResponsiveWidth = function (w, h) {
// this technique is from: http://thenewcode.com/744/Make-SVG-Responsive, thx to https://github.com/iantresman
this.svg.setAttribute("viewBox", "0 0 " + w + " " + h);
this.svg.setAttribute("preserveAspectRatio", "xMinYMin meet");
this.svg.removeAttribute("height");
this.svg.removeAttribute("width");
this.svg.style['display'] = "inline-block";
this.svg.style['position'] = "absolute";
this.svg.style['top'] = "0";
this.svg.style['left'] = "0";
if (this.svg.parentNode) {
var cls = this.svg.parentNode.getAttribute("class");
if (!cls) this.svg.parentNode.setAttribute("class", "abcjs-container");else if (cls.indexOf("abcjs-container") < 0) this.svg.parentNode.setAttribute("class", cls + " abcjs-container");
this.svg.parentNode.style['display'] = "inline-block";
this.svg.parentNode.style['position'] = "relative";
this.svg.parentNode.style['width'] = "100%";
// PER: I changed the padding from 100% to this through trial and error.
// The example was using a square image, but this music might be either wider or taller.
var padding = h / w * 100;
this.svg.parentNode.style['padding-bottom'] = padding + "%";
this.svg.parentNode.style['vertical-align'] = "middle";
this.svg.parentNode.style['overflow'] = "hidden";
}
};
Svg.prototype.setSize = function (w, h) {
this.svg.setAttribute('width', w);
this.svg.setAttribute('height', h);
};
Svg.prototype.setAttribute = function (attr, value) {
this.svg.setAttribute(attr, value);
};
Svg.prototype.setScale = function (scale) {
if (scale !== 1) {
this.svg.style.transform = "scale(" + scale + "," + scale + ")";
this.svg.style['-ms-transform'] = "scale(" + scale + "," + scale + ")";
this.svg.style['-webkit-transform'] = "scale(" + scale + "," + scale + ")";
this.svg.style['transform-origin'] = "0 0";
this.svg.style['-ms-transform-origin-x'] = "0";
this.svg.style['-ms-transform-origin-y'] = "0";
this.svg.style['-webkit-transform-origin-x'] = "0";
this.svg.style['-webkit-transform-origin-y'] = "0";
} else {
this.svg.style.transform = "";
this.svg.style['-ms-transform'] = "";
this.svg.style['-webkit-transform'] = "";
}
};
Svg.prototype.insertStyles = function (styles) {
var el = document.createElementNS(svgNS, "style");
el.textContent = styles;
this.svg.insertBefore(el, this.svg.firstChild); // prepend is not available on older browsers.
// this.svg.prepend(el);
};
Svg.prototype.setParentStyles = function (attr) {
// This is needed to get the size right when there is scaling involved.
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
if (this.svg.parentNode) this.svg.parentNode.style[key] = attr[key];
}
}
// This is the last thing that gets called, so delete the temporary SVG if one was created
if (this.dummySvg) {
var body = document.querySelector('body');
body.removeChild(this.dummySvg);
this.dummySvg = null;
}
};
function constructHLine(x1, y1, x2) {
var len = x2 - x1;
return "M " + x1 + " " + y1 + " l " + len + ' ' + 0 + " l " + 0 + " " + 1 + " " + " l " + -len + " " + 0 + " " + " z ";
}
function constructVLine(x1, y1, y2) {
var len = y2 - y1;
return "M " + x1 + " " + y1 + " l " + 0 + ' ' + len + " l " + 1 + " " + 0 + " " + " l " + 0 + " " + -len + " " + " z ";
}
Svg.prototype.rect = function (attr) {
// This uses path instead of rect so that it can be hollow and the color changes with "fill" instead of "stroke".
var lines = [];
var x1 = attr.x;
var y1 = attr.y;
var x2 = attr.x + attr.width;
var y2 = attr.y + attr.height;
lines.push(constructHLine(x1, y1, x2));
lines.push(constructHLine(x1, y2, x2));
lines.push(constructVLine(x2, y1, y2));
lines.push(constructVLine(x1, y2, y1));
return this.path({
path: lines.join(" "),
stroke: "none",
"data-name": attr["data-name"]
});
};
Svg.prototype.dottedLine = function (attr) {
var el = document.createElementNS(svgNS, 'line');
el.setAttribute("x1", attr.x1);
el.setAttribute("x2", attr.x2);
el.setAttribute("y1", attr.y1);
el.setAttribute("y2", attr.y2);
el.setAttribute("stroke", attr.stroke);
el.setAttribute("stroke-dasharray", "5,5");
this.svg.insertBefore(el, this.svg.firstChild);
};
Svg.prototype.rectBeneath = function (attr) {
var el = document.createElementNS(svgNS, 'rect');
el.setAttribute("x", attr.x);
el.setAttribute("width", attr.width);
el.setAttribute("y", attr.y);
el.setAttribute("height", attr.height);
if (attr.stroke) el.setAttribute("stroke", attr.stroke);
if (attr['stroke-opacity']) el.setAttribute("stroke-opacity", attr['stroke-opacity']);
if (attr.fill) el.setAttribute("fill", attr.fill);
if (attr['fill-opacity']) el.setAttribute("fill-opacity", attr['fill-opacity']);
this.svg.insertBefore(el, this.svg.firstChild);
};
Svg.prototype.text = function (text, attr, target) {
var el = document.createElementNS(svgNS, 'text');
el.setAttribute("stroke", "none");
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
el.setAttribute(key, attr[key]);
}
}
var lines = ("" + text).split("\n");
for (var i = 0; i < lines.length; i++) {
var line = document.createElementNS(svgNS, 'tspan');
line.setAttribute("x", attr.x ? attr.x : 0);
if (i !== 0) line.setAttribute("dy", "1.2em");
if (lines[i].indexOf("\x03") !== -1) {
var parts = lines[i].split('\x03');
line.textContent = parts[0];
if (parts[1]) {
var ts2 = document.createElementNS(svgNS, 'tspan');
ts2.setAttribute("dy", "-0.3em");
ts2.setAttribute("style", "font-size:0.7em");
ts2.textContent = parts[1];
line.appendChild(ts2);
}
if (parts[2]) {
var dist = parts[1] ? "0.4em" : "0.1em";
var ts3 = document.createElementNS(svgNS, 'tspan');
ts3.setAttribute("dy", dist);
ts3.setAttribute("style", "font-size:0.7em");
ts3.textContent = parts[2];
line.appendChild(ts3);
}
} else line.textContent = lines[i];
el.appendChild(line);
}
if (target) target.appendChild(el);else this.append(el);
return el;
};
Svg.prototype.richTextLine = function (phrases, x, y, klass, anchor, target) {
var el = document.createElementNS(svgNS, 'text');
el.setAttribute("stroke", "none");
el.setAttribute("class", klass);
el.setAttribute("x", x);
el.setAttribute("y", y);
el.setAttribute("text-anchor", anchor);
el.setAttribute("dominant-baseline", "middle");
for (var i = 0; i < phrases.length; i++) {
var phrase = phrases[i];
var tspan = document.createElementNS(svgNS, 'tspan');
var attrs = Object.keys(phrase.attrs);
for (var j = 0; j < attrs.length; j++) {
var value = phrase.attrs[attrs[j]];
if (value !== '') tspan.setAttribute(attrs[j], value);
}
tspan.textContent = phrase.content;
el.appendChild(tspan);
}
if (target) target.appendChild(el);else this.append(el);
return el;
};
Svg.prototype.guessWidth = function (text, attr) {
var svg = this.createDummySvg();
var el = this.text(text, attr, svg);
var size;
try {
size = el.getBBox();
if (isNaN(size.height) || !size.height)
// TODO-PER: I don't think this can happen unless there isn't a browser at all.
size = {
width: attr['font-size'] / 2,
height: attr['font-size'] + 2
}; // Just a wild guess.
else size = {
width: size.width,
height: size.height
};
} catch (ex) {
size = {
width: attr['font-size'] / 2,
height: attr['font-size'] + 2
}; // Just a wild guess.
}
svg.removeChild(el);
return size;
};
Svg.prototype.createDummySvg = function () {
if (!this.dummySvg) {
this.dummySvg = createSvg();
var styles = ["display: block !important;", "height: 1px;", "width: 1px;", "position: absolute;"];
this.dummySvg.setAttribute('style', styles.join(""));
var body = document.querySelector('body');
body.appendChild(this.dummySvg);
}
return this.dummySvg;
};
var sizeCache = {};
Svg.prototype.getTextSize = function (text, attr, el) {
if (typeof text === 'number') text = '' + text;
if (!text || text.match(/^\s+$/)) return {
width: 0,
height: 0
};
var key;
if (text.length < 20) {
// The short text tends to be repetitive and getBBox is really slow, so lets cache.
key = text + JSON.stringify(attr);
if (sizeCache[key]) return sizeCache[key];
}
var removeLater = !el;
if (!el) el = this.text(text, attr);
var size;
try {
size = el.getBBox();
if (isNaN(size.height) || !size.height) size = this.guessWidth(text, attr);else size = {
width: size.width,
height: size.height
};
} catch (ex) {
size = this.guessWidth(text, attr);
}
if (removeLater) {
if (this.currentGroup.length > 0) this.currentGroup[0].removeChild(el);else this.svg.removeChild(el);
}
if (key) sizeCache[key] = size;
return size;
};
Svg.prototype.openGroup = function (options) {
options = options ? options : {};
var el = document.createElementNS(svgNS, "g");
if (options.klass) el.setAttribute("class", options.klass);
if (options.fill) el.setAttribute("fill", options.fill);
if (options.stroke) el.setAttribute("stroke", options.stroke);
if (options['data-name']) el.setAttribute("data-name", options['data-name']);
if (options.prepend) this.prepend(el);else this.append(el);
this.currentGroup.unshift(el);
return el;
};
Svg.prototype.closeGroup = function () {
var g = this.currentGroup.shift();
if (g && g.children.length === 0) {
// If nothing was added to the group it is because all the elements were invisible. We don't need the group, then.
g.parentElement.removeChild(g);
return null;
}
return g;
};
Svg.prototype.path = function (attr) {
var el = document.createElementNS(svgNS, "path");
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
if (key === 'path') el.setAttributeNS(null, 'd', attr.path);else if (key === 'klass') el.setAttributeNS(null, "class", attr[key]);else if (attr[key] !== undefined) el.setAttributeNS(null, key, attr[key]);
}
}
this.append(el);
return el;
};
Svg.prototype.pathToBack = function (attr) {
var el = document.createElementNS(svgNS, "path");
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
if (key === 'path') el.setAttributeNS(null, 'd', attr.path);else if (key === 'klass') el.setAttributeNS(null, "class", attr[key]);else el.setAttributeNS(null, key, attr[key]);
}
}
this.prepend(el);
return el;
};
Svg.prototype.lineToBack = function (attr) {
var el = document.createElementNS(svgNS, 'line');
var keys = Object.keys(attr);
for (var i = 0; i < keys.length; i++) {
el.setAttribute(keys[i], attr[keys[i]]);
}
this.prepend(el);
return el;
};
Svg.prototype.append = function (el) {
if (this.currentGroup.length > 0) this.currentGroup[0].appendChild(el);else this.svg.appendChild(el);
};
Svg.prototype.prepend = function (el) {
// The entire group is prepended, so don't prepend the individual elements.
if (this.currentGroup.length > 0) this.currentGroup[0].appendChild(el);else this.svg.insertBefore(el, this.svg.firstChild);
};
Svg.prototype.setAttributeOnElement = function (el, attr) {
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
el.setAttributeNS(null, key, attr[key]);
}
}
};
Svg.prototype.moveElementToChild = function (parent, child) {
parent.appendChild(child);
};
function createSvg() {
var svg = document.createElementNS(svgNS, "svg");
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
svg.setAttribute('role', 'img'); // for accessibility
svg.setAttribute('fill', 'currentColor'); // for automatically picking up dark mode and high contrast
svg.setAttribute('stroke', 'currentColor'); // for automatically picking up dark mode and high contrast
return svg;
}
module.exports = Svg;
/***/ }),
/***/ "./version.js":
/*!********************!*\
!*** ./version.js ***!
\********************/
/***/ (function(module) {
var version = '6.4.3';
module.exports = version;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__("./index.js");
/******/
/******/ return __webpack_exports__;
/******/ })()
;
});
//# sourceMappingURL=abcjs-basic.js.map