vibe-editor 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/package.json +56 -0
- package/src/fonts/bravura/FONTLOG.txt +243 -0
- package/src/fonts/bravura/OFL-FAQ.txt +369 -0
- package/src/fonts/bravura/OFL.txt +94 -0
- package/src/fonts/bravura/bravura-text.md +153 -0
- package/src/fonts/bravura/bravura_metadata.json +34718 -0
- package/src/fonts/bravura/eot/Bravura.eot +0 -0
- package/src/fonts/bravura/eot/BravuraText.eot +0 -0
- package/src/fonts/bravura/otf/Bravura.otf +0 -0
- package/src/fonts/bravura/otf/BravuraText.otf +0 -0
- package/src/fonts/bravura/svg/Bravura.svg +3517 -0
- package/src/fonts/bravura/svg/BravuraText.svg +18879 -0
- package/src/fonts/bravura/woff/Bravura.woff +0 -0
- package/src/fonts/bravura/woff/BravuraText.woff +0 -0
- package/src/fonts/colaborate/ColabBol-webfont.eot +0 -0
- package/src/fonts/colaborate/ColabBol-webfont.svg +976 -0
- package/src/fonts/colaborate/ColabBol-webfont.ttf +0 -0
- package/src/fonts/colaborate/ColabBol-webfont.woff +0 -0
- package/src/fonts/colaborate/ColabLig-webfont.eot +0 -0
- package/src/fonts/colaborate/ColabLig-webfont.svg +976 -0
- package/src/fonts/colaborate/ColabLig-webfont.ttf +0 -0
- package/src/fonts/colaborate/ColabLig-webfont.woff +0 -0
- package/src/fonts/colaborate/ColabMed-webfont.eot +0 -0
- package/src/fonts/colaborate/ColabMed-webfont.svg +976 -0
- package/src/fonts/colaborate/ColabMed-webfont.ttf +0 -0
- package/src/fonts/colaborate/ColabMed-webfont.woff +0 -0
- package/src/fonts/colaborate/ColabReg-webfont.eot +0 -0
- package/src/fonts/colaborate/ColabReg-webfont.svg +976 -0
- package/src/fonts/colaborate/ColabReg-webfont.ttf +0 -0
- package/src/fonts/colaborate/ColabReg-webfont.woff +0 -0
- package/src/fonts/colaborate/ColabThi-webfont.eot +0 -0
- package/src/fonts/colaborate/ColabThi-webfont.svg +970 -0
- package/src/fonts/colaborate/ColabThi-webfont.ttf +0 -0
- package/src/fonts/colaborate/ColabThi-webfont.woff +0 -0
- package/src/images/GUI/.DS_Store +0 -0
- package/src/images/GUI/backward-fast-solid.svg +1 -0
- package/src/images/GUI/bars-solid.svg +1 -0
- package/src/images/GUI/caret-left-solid.svg +1 -0
- package/src/images/GUI/caret-right-solid.svg +1 -0
- package/src/images/GUI/edit-solid.svg +1 -0
- package/src/images/GUI/pause-solid.svg +1 -0
- package/src/images/GUI/play-solid.svg +1 -0
- package/src/images/GUI/triplet.svg +21 -0
- package/src/images/GUI/zoomin.svg +1 -0
- package/src/images/GUI/zoomout.svg +1 -0
- package/src/images/bravura_notes/.DS_Store +0 -0
- package/src/images/bravura_notes/16th.svg +1 -0
- package/src/images/bravura_notes/32th.svg +1 -0
- package/src/images/bravura_notes/alterDDown.svg +1 -0
- package/src/images/bravura_notes/alterDUp.svg +1 -0
- package/src/images/bravura_notes/alterDown.svg +1 -0
- package/src/images/bravura_notes/alterNeutral.svg +1 -0
- package/src/images/bravura_notes/alterUp.svg +1 -0
- package/src/images/bravura_notes/beams.svg +1 -0
- package/src/images/bravura_notes/eigth.svg +1 -0
- package/src/images/bravura_notes/full.svg +1 -0
- package/src/images/bravura_notes/half.svg +1 -0
- package/src/images/bravura_notes/oneDot.svg +1 -0
- package/src/images/bravura_notes/pauseNote.svg +1 -0
- package/src/images/bravura_notes/quarter.svg +1 -0
- package/src/images/bravura_notes/tie.svg +1 -0
- package/src/images/bravura_notes/twoDot.svg +1 -0
- package/src/scripts/js/.DS_Store +0 -0
- package/src/scripts/js/Core.js +887 -0
- package/src/scripts/js/MusicPlayer.js +572 -0
- package/src/scripts/js/MusicProcessor.js +652 -0
- package/src/scripts/js/VerovioScoreEditor.js +183 -0
- package/src/scripts/js/assets/mei_template.js +161 -0
- package/src/scripts/js/constants.js +20 -0
- package/src/scripts/js/datastructures/MeasureMatrix.js +235 -0
- package/src/scripts/js/datastructures/ScoreGraph.js +432 -0
- package/src/scripts/js/datastructures/ScoreNode.js +78 -0
- package/src/scripts/js/entry.js +4 -0
- package/src/scripts/js/gui/Annotations.js +456 -0
- package/src/scripts/js/gui/Cursor.js +203 -0
- package/src/scripts/js/gui/CustomAnnotationDrawer.js +114 -0
- package/src/scripts/js/gui/CustomAnnotationShapeDrawer.js +114 -0
- package/src/scripts/js/gui/HarmonyLabel.js +104 -0
- package/src/scripts/js/gui/Label.js +2 -0
- package/src/scripts/js/gui/PhantomElement.js +132 -0
- package/src/scripts/js/gui/ScoreManipulator.js +156 -0
- package/src/scripts/js/gui/Tabbar.js +675 -0
- package/src/scripts/js/gui/TempoLabel.js +60 -0
- package/src/scripts/js/gui/Toolbar copy.js +614 -0
- package/src/scripts/js/gui/Toolbar.js +618 -0
- package/src/scripts/js/handlers/AnnotationChangeHandler.js +567 -0
- package/src/scripts/js/handlers/AnnotationDragHandler.js +113 -0
- package/src/scripts/js/handlers/AnnotationLineHandler.js +113 -0
- package/src/scripts/js/handlers/ArticulationHandler.js +20 -0
- package/src/scripts/js/handlers/ClickModeHandler.js +265 -0
- package/src/scripts/js/handlers/CustomAnnotationShapeDrawer.js +131 -0
- package/src/scripts/js/handlers/CustomToolbarHandler.js +297 -0
- package/src/scripts/js/handlers/DeleteHandler.js +102 -0
- package/src/scripts/js/handlers/GlobalKeyboardHandler.js +367 -0
- package/src/scripts/js/handlers/Handler.js +2 -0
- package/src/scripts/js/handlers/HarmonyHandler.js +282 -0
- package/src/scripts/js/handlers/InsertModeHandler copy.js +423 -0
- package/src/scripts/js/handlers/InsertModeHandler.js +380 -0
- package/src/scripts/js/handlers/InsertModeHandler_deprecated.js +424 -0
- package/src/scripts/js/handlers/KeyModeHandler copy.js +407 -0
- package/src/scripts/js/handlers/KeyModeHandler.js +456 -0
- package/src/scripts/js/handlers/KeyModeHandler_deprecated.js +411 -0
- package/src/scripts/js/handlers/LabelHandler.js +461 -0
- package/src/scripts/js/handlers/ModHandler.js +311 -0
- package/src/scripts/js/handlers/NoteDragHandler copy.js +148 -0
- package/src/scripts/js/handlers/NoteDragHandler.js +97 -0
- package/src/scripts/js/handlers/NoteDragHandler_deprecated.js +150 -0
- package/src/scripts/js/handlers/PhantomElementHandler.js +168 -0
- package/src/scripts/js/handlers/ScoreManipulatorHandler.js +135 -0
- package/src/scripts/js/handlers/SelectionHandler.js +218 -0
- package/src/scripts/js/handlers/SideBarHandler.js +499 -0
- package/src/scripts/js/handlers/TooltipHandler.js +132 -0
- package/src/scripts/js/handlers/WindowHandler.js +257 -0
- package/src/scripts/js/utils/DOMCreator.js +174 -0
- package/src/scripts/js/utils/MEIConverter.js +64 -0
- package/src/scripts/js/utils/MEIOperations.js +2112 -0
- package/src/scripts/js/utils/Mouse2MEI.js +735 -0
- package/src/scripts/js/utils/Mouse2SVG.js +737 -0
- package/src/scripts/js/utils/SVGEditor.js +352 -0
- package/src/scripts/js/utils/SVGFiller.js +245 -0
- package/src/scripts/js/utils/Types.js +2 -0
- package/src/scripts/js/utils/VerovioWrapper copy.js +156 -0
- package/src/scripts/js/utils/VerovioWrapper.js +165 -0
- package/src/scripts/js/utils/VerovioWrapperLocal.js +156 -0
- package/src/scripts/js/utils/convenienceQueries.js +37 -0
- package/src/scripts/js/utils/coordinates.js +54 -0
- package/src/scripts/js/utils/firefoxBBoxes.js +143 -0
- package/src/scripts/js/utils/mappings.js +332 -0
- package/src/scripts/js/utils/random.js +45 -0
- package/src/styles/VerovioScoreEditor.css +694 -0
@@ -0,0 +1,2112 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.setArticulation = exports.getTimestamp = exports.getElementTimestampById = exports.insertTempo = exports.insertMeter = exports.replaceMeterInScoreDef = exports.insertKey = exports.replaceKeyInScoreDef = exports.insertClef = exports.replaceClefinScoreDef = exports.paste = exports.createTuplet = exports.removeStaff = exports.addStaff = exports.removeMeasure = exports.addMeasure = exports.cleanUp = exports.elementIsOverfilling = exports.changeDur = exports.changeDurationsInLayer = exports.fillLayerWithRests = exports.fillWithRests = exports.disableFeatures = exports.changeMeter = exports.transposeByStep = exports.mergeArticToParent = exports.mergeSectionScoreDefToLayer = exports.adjustAccids = exports.extrapolateMeter = exports.getAbsoluteRatio = exports.connectNotes = exports.addToMEI = exports.getMeterRatioLocal = exports.removeFromMEI = void 0;
|
4
|
+
const meiConverter = require("./MEIConverter");
|
5
|
+
const random_1 = require("./random");
|
6
|
+
const constants_1 = require("../constants");
|
7
|
+
const mappings_1 = require("./mappings");
|
8
|
+
const mei_template_1 = require("../assets/mei_template");
|
9
|
+
const MeasureMatrix_1 = require("../datastructures/MeasureMatrix");
|
10
|
+
const countableNoteUnitSelector = ":scope > *[dur]:not([grace])";
|
11
|
+
const overfillMeasure = false;
|
12
|
+
const siblingNames = ["note", "chord", "clef", "beam"];
|
13
|
+
// ":scope > note:not([grace])," +
|
14
|
+
// ":scope > chord," +
|
15
|
+
// ":scope > beam > chord," +
|
16
|
+
// ":scope > beam > note:not([grace])," +
|
17
|
+
// ":scope > rest"
|
18
|
+
////// DELETE //////
|
19
|
+
/**
|
20
|
+
* Remove Elements from MEI.
|
21
|
+
* Some Elements (such as accid...) could are not represeented as elements in the current MEI.
|
22
|
+
* These have to be found in the parent element which have these as an attribute.
|
23
|
+
* @param scoreElements Array of Elements which are marked in the SVG Representation (notes, chords, slur, tie, accid etc..)
|
24
|
+
* @param currentMEI
|
25
|
+
* @returns
|
26
|
+
*/
|
27
|
+
function removeFromMEI(scoreElements, currentMEI) {
|
28
|
+
return new Promise((resolve) => {
|
29
|
+
scoreElements.forEach(se => {
|
30
|
+
var _a;
|
31
|
+
if (currentMEI.getElementById(se === null || se === void 0 ? void 0 : se.id) !== null) { // this only applies for <note> and <rest>
|
32
|
+
//do not remove completely, replace with rest
|
33
|
+
//currentMEI.getElementById(note.id).remove()
|
34
|
+
if (["note", "chord"].some(s => se.classList.contains(s))) {
|
35
|
+
replaceWithRest(se, currentMEI);
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
currentMEI.getElementById(se.id).remove(); // possibility to remove rests entirely
|
39
|
+
}
|
40
|
+
// remove all tie/ harms etc when startid is no more a note or is deleted
|
41
|
+
//currentMEI.querySelectorAll("tie").forEach(t => {
|
42
|
+
currentMEI.querySelectorAll("[startid]").forEach(t => {
|
43
|
+
var _a;
|
44
|
+
if (t.getAttribute("startid").includes(se.id)) {
|
45
|
+
if (currentMEI.getElementById(se.id) === null) {
|
46
|
+
(_a = currentMEI.querySelector(t.getAttribute("endid"))) === null || _a === void 0 ? void 0 : _a.remove();
|
47
|
+
}
|
48
|
+
t.remove();
|
49
|
+
}
|
50
|
+
});
|
51
|
+
}
|
52
|
+
else {
|
53
|
+
//may be some of the following: accid
|
54
|
+
var closestNote = currentMEI.getElementById((_a = se === null || se === void 0 ? void 0 : se.closest(".note")) === null || _a === void 0 ? void 0 : _a.id);
|
55
|
+
if (closestNote !== null) {
|
56
|
+
//console.log("removing ", se)
|
57
|
+
var attrName = se.classList.item(0).toLowerCase();
|
58
|
+
closestNote.removeAttribute(attrName);
|
59
|
+
if (attrName === "accid") {
|
60
|
+
closestNote.removeAttribute("accid.ges");
|
61
|
+
}
|
62
|
+
}
|
63
|
+
else if (se.closest(".tuplet") !== null) {
|
64
|
+
var tuplet = currentMEI.querySelector("#" + se.closest(".tuplet").id);
|
65
|
+
if (tuplet !== null)
|
66
|
+
tuplet.outerHTML = tuplet.innerHTML; //the element could be gone in current mei, but se is still present
|
67
|
+
}
|
68
|
+
}
|
69
|
+
});
|
70
|
+
//removeEmptyElements(currentMEI)
|
71
|
+
// For now: No Shifts (22.07.2021)
|
72
|
+
// if($(".measure").length > 1){
|
73
|
+
// checkDeleteShifts(currentMEI);
|
74
|
+
// }
|
75
|
+
cleanUp(currentMEI);
|
76
|
+
//fillWithRests(currentMEI)
|
77
|
+
// Warum ist das ein Problem?
|
78
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
79
|
+
resolve(currentMEI);
|
80
|
+
});
|
81
|
+
}
|
82
|
+
exports.removeFromMEI = removeFromMEI;
|
83
|
+
function checkDeleteShifts(currentMEI) {
|
84
|
+
var meterRatio = getMeterRatioGlobal(currentMEI);
|
85
|
+
var shifters = new Array;
|
86
|
+
var elements = currentMEI.getElementsByTagName("layer");
|
87
|
+
Array.from(elements).forEach(layer => {
|
88
|
+
var actualMeterFill = getAbsoluteRatio(layer);
|
89
|
+
var layerLevel = layer.getAttribute("n");
|
90
|
+
var staffLevel = layer.closest("staff").getAttribute("n");
|
91
|
+
var nextSibling = layer.closest("measure").nextElementSibling;
|
92
|
+
if (actualMeterFill < meterRatio && nextSibling !== null) {
|
93
|
+
let hasStaff = nextSibling.querySelector("staff[n$='" + staffLevel + "'") !== null ? true : false;
|
94
|
+
let hasLayer = nextSibling.querySelector("layer[n$='" + layerLevel + "'") !== null ? true : false;
|
95
|
+
if (hasStaff && hasLayer) {
|
96
|
+
nextSibling = nextSibling.querySelector("staff[n$='" + staffLevel + "'").querySelector("layer[n$='" + layerLevel + "'");
|
97
|
+
Array.from(nextSibling.querySelectorAll(countableNoteUnitSelector)).forEach(node => {
|
98
|
+
if (actualMeterFill < meterRatio) {
|
99
|
+
shifters.push(node);
|
100
|
+
}
|
101
|
+
actualMeterFill += 1 / parseInt(node.getAttribute("dur"));
|
102
|
+
});
|
103
|
+
}
|
104
|
+
}
|
105
|
+
if (shifters.length > 0) {
|
106
|
+
doShiftLeft(shifters, meterRatio);
|
107
|
+
shifters.length = 0;
|
108
|
+
checkDeleteShifts(currentMEI);
|
109
|
+
}
|
110
|
+
});
|
111
|
+
}
|
112
|
+
function getMeterRatioGlobal(currentMEI) {
|
113
|
+
var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
|
114
|
+
var meterRatio = null;
|
115
|
+
//Do I know the meter?
|
116
|
+
if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
|
117
|
+
meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
meterRatio = extrapolateMeter(currentMEI);
|
121
|
+
}
|
122
|
+
return meterRatio;
|
123
|
+
}
|
124
|
+
/**
|
125
|
+
*
|
126
|
+
* @param currentMEI
|
127
|
+
* @param refElement Must be a staff-Element at most
|
128
|
+
*/
|
129
|
+
function getMeterRatioLocal(currentMEI, refElement) {
|
130
|
+
var _a, _b;
|
131
|
+
var staffElement;
|
132
|
+
if (refElement.tagName !== "staff") {
|
133
|
+
if (refElement.closest("staff") === null) {
|
134
|
+
staffElement = ((_a = currentMEI.getElementById(refElement.id)) === null || _a === void 0 ? void 0 : _a.closest("staff")) || ((_b = currentMEI.getElementById(refElement.parentElement.id)) === null || _b === void 0 ? void 0 : _b.closest("staff"));
|
135
|
+
}
|
136
|
+
else {
|
137
|
+
staffElement = refElement.closest("staff");
|
138
|
+
}
|
139
|
+
}
|
140
|
+
else {
|
141
|
+
staffElement = refElement;
|
142
|
+
}
|
143
|
+
if (staffElement === null || staffElement == undefined) {
|
144
|
+
throw new Error("RefElement must be a staff-Element at most");
|
145
|
+
}
|
146
|
+
var mm = new MeasureMatrix_1.default();
|
147
|
+
mm.populateFromMEI(currentMEI);
|
148
|
+
var measureIdx = staffElement.closest("measure").getAttribute("n");
|
149
|
+
var staffIdx = staffElement.getAttribute("n");
|
150
|
+
var mmStaff = mm.get(measureIdx, staffIdx);
|
151
|
+
return parseInt(mmStaff.meterSig.count) / parseInt(mmStaff.meterSig.unit);
|
152
|
+
}
|
153
|
+
exports.getMeterRatioLocal = getMeterRatioLocal;
|
154
|
+
//////// INSERT //////////
|
155
|
+
/**
|
156
|
+
* Insert given sound event into MEI
|
157
|
+
* @param newSound NewNote or NewChord to be inserted
|
158
|
+
* @param currentMEI MEI as Document
|
159
|
+
* @param replace Switching to replaceMode (default: False)
|
160
|
+
* @param scoreGraph
|
161
|
+
* @returns mei
|
162
|
+
*/
|
163
|
+
function addToMEI(newSound, currentMEI, replace = false, scoreGraph = null) {
|
164
|
+
var _a, _b;
|
165
|
+
//return new Promise<Document>((resolve): void => {
|
166
|
+
var currMeiClone = currentMEI.cloneNode(true);
|
167
|
+
var newElem;
|
168
|
+
var nearestNoteIsSameDurRest = false;
|
169
|
+
if (newSound.hasOwnProperty("pname")) {
|
170
|
+
var newNote = newSound;
|
171
|
+
if (newNote.rest) {
|
172
|
+
newElem = currentMEI.createElement("rest");
|
173
|
+
}
|
174
|
+
else {
|
175
|
+
newElem = currentMEI.createElement("note");
|
176
|
+
newElem.setAttribute("pname", newNote.pname);
|
177
|
+
newElem.setAttribute("oct", newNote.oct);
|
178
|
+
if (newNote.accid != undefined) {
|
179
|
+
newElem.setAttribute("accid.ges", newNote.accid);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
newElem.setAttribute("dur", newNote.dur);
|
183
|
+
if (newNote.dots != undefined) {
|
184
|
+
newElem.setAttribute("dots", newNote.dots);
|
185
|
+
}
|
186
|
+
if (newNote.id != undefined && newNote.id !== null) {
|
187
|
+
newElem.setAttribute("id", newNote.id);
|
188
|
+
}
|
189
|
+
//break up an cache beams for easier processing
|
190
|
+
//later all the beams will be reastablished
|
191
|
+
var beams = new Array();
|
192
|
+
currentMEI.querySelector("#" + newNote.nearestNoteId).closest("layer").querySelectorAll("beam").forEach(b => {
|
193
|
+
beams.push(b.cloneNode(true));
|
194
|
+
Array.from(b.children).forEach(e => {
|
195
|
+
b.parentElement.insertBefore(e, b);
|
196
|
+
});
|
197
|
+
b.remove();
|
198
|
+
});
|
199
|
+
//Do sthm with chords
|
200
|
+
if (newNote.chordElement != undefined && !newNote.rest) {
|
201
|
+
var chord;
|
202
|
+
var meiChordEl = currentMEI.getElementById(newNote.chordElement.id);
|
203
|
+
if (newNote.chordElement.classList.contains("chord") || newNote.chordElement.tagName === "chord") {
|
204
|
+
chord = meiChordEl;
|
205
|
+
chord.appendChild(newElem);
|
206
|
+
}
|
207
|
+
else {
|
208
|
+
chord = document.createElement("chord");
|
209
|
+
chord.setAttribute("id", random_1.uuidv4());
|
210
|
+
chord.setAttribute("dur", meiChordEl.getAttribute("dur"));
|
211
|
+
if (meiChordEl.getAttribute("dots") !== null) {
|
212
|
+
chord.setAttribute("dots", meiChordEl.getAttribute("dots"));
|
213
|
+
}
|
214
|
+
chord.appendChild(newElem);
|
215
|
+
meiChordEl.parentElement.insertBefore(chord, meiChordEl);
|
216
|
+
chord.appendChild(meiChordEl);
|
217
|
+
}
|
218
|
+
chord.childNodes.forEach((n) => {
|
219
|
+
n.removeAttribute("dur");
|
220
|
+
n.removeAttribute("dots");
|
221
|
+
});
|
222
|
+
// check for existing ties within the chord and make one for the new element as well
|
223
|
+
currentMEI.querySelectorAll("tie").forEach(t => {
|
224
|
+
if (Array.from(chord.querySelectorAll("note")).some(n => t.getAttribute("startid") === "#" + n.id)) {
|
225
|
+
if (!currentMEI.querySelector("tie[startid='#" + newNote.id + "']")) { // just make the tie once (since this can be called twice in recursion)
|
226
|
+
var addToChord = Object.assign({}, newNote);
|
227
|
+
addToChord.id = random_1.uuidv4();
|
228
|
+
var tieEnd = currentMEI.querySelector(t.getAttribute("endid"));
|
229
|
+
tieEnd = tieEnd.closest("chord") || tieEnd;
|
230
|
+
addToChord.chordElement = tieEnd;
|
231
|
+
addToMEI(addToChord, currentMEI, replace);
|
232
|
+
connectNotes(currentMEI.querySelector("#" + newNote.id), currentMEI.querySelector("#" + addToChord.id), "tie");
|
233
|
+
}
|
234
|
+
}
|
235
|
+
});
|
236
|
+
}
|
237
|
+
else if (newNote.nearestNoteId !== null) {
|
238
|
+
var sibling = currentMEI.getElementById(newNote.nearestNoteId);
|
239
|
+
if (sibling === null)
|
240
|
+
return;
|
241
|
+
nearestNoteIsSameDurRest = sibling.tagName === "rest" && sibling.getAttribute("dur") === newElem.getAttribute("dur") && sibling.getAttribute("dots") === newElem.getAttribute("dots");
|
242
|
+
//special rule for first element in layer
|
243
|
+
if (sibling.tagName === "layer") {
|
244
|
+
if (scoreGraph !== null) {
|
245
|
+
sibling = (_a = currentMEI.getElementById(scoreGraph.getCurrentNode().getRight().getId())) === null || _a === void 0 ? void 0 : _a.parentElement;
|
246
|
+
}
|
247
|
+
var firstChild = sibling.firstChild;
|
248
|
+
sibling.insertBefore(newElem, firstChild);
|
249
|
+
if (replace) {
|
250
|
+
changeDurationsInLayer(currentMEI, [firstChild], newElem);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
else {
|
254
|
+
var parentLayer = sibling.closest("layer");
|
255
|
+
var trueParent = sibling.parentElement;
|
256
|
+
var isTrueSibling = parentLayer == trueParent;
|
257
|
+
var trueSibling = sibling;
|
258
|
+
if (!isTrueSibling) {
|
259
|
+
var currParent = trueParent;
|
260
|
+
while (!isTrueSibling) {
|
261
|
+
trueSibling = currParent;
|
262
|
+
currParent = currParent === null || currParent === void 0 ? void 0 : currParent.parentElement;
|
263
|
+
isTrueSibling = currParent.tagName === "layer";
|
264
|
+
}
|
265
|
+
}
|
266
|
+
if (replace) {
|
267
|
+
let ms = Array.from(trueSibling.parentElement.querySelectorAll("note:not(chord note), chord, rest, mRest")); //querySelectorAll(":scope > *")
|
268
|
+
if (newNote.relPosX === "left") {
|
269
|
+
var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling));
|
270
|
+
trueSibling.parentElement.insertBefore(newElem, trueSibling);
|
271
|
+
changeDurationsInLayer(currentMEI, measureSiblings, newElem);
|
272
|
+
}
|
273
|
+
else {
|
274
|
+
if (["clef"].every(el => { var _a; return ((_a = trueSibling.nextElementSibling) === null || _a === void 0 ? void 0 : _a.tagName) !== el; }) && trueSibling.nextElementSibling !== null) {
|
275
|
+
var measureSiblings = ms.filter((_, i) => i >= ms.indexOf(trueSibling.nextElementSibling));
|
276
|
+
trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
|
277
|
+
changeDurationsInLayer(currentMEI, measureSiblings, newElem);
|
278
|
+
}
|
279
|
+
else {
|
280
|
+
trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
else {
|
285
|
+
if (newNote.relPosX === "left") {
|
286
|
+
trueSibling.parentElement.insertBefore(newElem, trueSibling);
|
287
|
+
}
|
288
|
+
else {
|
289
|
+
trueSibling.parentElement.insertBefore(newElem, trueSibling.nextElementSibling);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
else {
|
295
|
+
currentMEI.getElementById(newNote.staffId).querySelector("layer").appendChild(newElem);
|
296
|
+
}
|
297
|
+
}
|
298
|
+
else { // is newChord
|
299
|
+
//TODO
|
300
|
+
var newChord = newSound;
|
301
|
+
newElem = convertToElement(newChord, currentMEI);
|
302
|
+
var nearestElem = currentMEI.getElementById(newChord.nearestNoteId);
|
303
|
+
if ((nearestElem === null || nearestElem === void 0 ? void 0 : nearestElem.tagName) === "layer") {
|
304
|
+
nearestElem.insertBefore(newElem, nearestElem.firstChild);
|
305
|
+
}
|
306
|
+
else if (newChord.relPosX === "left") {
|
307
|
+
nearestElem.parentElement.insertBefore(newElem, currentMEI.getElementById(newChord.nearestNoteId));
|
308
|
+
}
|
309
|
+
else {
|
310
|
+
nearestElem.parentElement.insertBefore(newElem, currentMEI.getElementById(newChord.nearestNoteId).nextSibling);
|
311
|
+
}
|
312
|
+
}
|
313
|
+
// if measure overfills tie note to next measure
|
314
|
+
var currentLayer = newElem.closest("layer");
|
315
|
+
if (!overfillMeasure) {
|
316
|
+
var newMeasureRatio = getAbsoluteRatio(newElem.closest("layer"));
|
317
|
+
var measureRatio = getMeterRatioLocal(currMeiClone, newElem);
|
318
|
+
if (newMeasureRatio > measureRatio) {
|
319
|
+
//currentMEI = currMeiClone as Document
|
320
|
+
// Decide if next measure should be created and what should be added
|
321
|
+
var lastElement = Array.from(currentLayer.querySelectorAll(":scope > :is(note, chord)")).reverse()[0];
|
322
|
+
var lastElementRatio = getAbsoluteRatio(lastElement);
|
323
|
+
var measureOverhead = newMeasureRatio - measureRatio;
|
324
|
+
var newRatio = lastElementRatio - measureOverhead;
|
325
|
+
if (newRatio > 0) {
|
326
|
+
changeDur(lastElement, newRatio);
|
327
|
+
// create new Element and if needed, new measure
|
328
|
+
var splittedElement = lastElement.tagName === "note" ? convertToNewNote(lastElement) : convertToNewChord(lastElement);
|
329
|
+
splittedElement.id = random_1.uuidv4();
|
330
|
+
var newDur = ratioToDur(measureOverhead);
|
331
|
+
splittedElement.dur = newDur[0].toString();
|
332
|
+
splittedElement.dots = newDur[1].toString();
|
333
|
+
if (currentLayer.closest("measure") === currentLayer.closest("section").lastElementChild) {
|
334
|
+
addMeasure(currentMEI);
|
335
|
+
}
|
336
|
+
var newLayer = currentMEI.querySelector("measure[n='" + (parseInt(currentLayer.closest("measure").getAttribute("n")) + 1).toString() + "'] layer[n='" + currentLayer.getAttribute("n") + "']");
|
337
|
+
splittedElement.nearestNoteId = newLayer.id;
|
338
|
+
splittedElement.staffId = newLayer.closest("staff").id;
|
339
|
+
splittedElement.relPosX = "left";
|
340
|
+
addToMEI(splittedElement, currentMEI, replace);
|
341
|
+
connectNotes(lastElement, currentMEI.querySelector("#" + splittedElement.id), "tie");
|
342
|
+
}
|
343
|
+
else if (newRatio == 0 && measureOverhead > 0) { // if the measure is perfectly fine and there should be no tie, modify the newsound (push it one position to the right) and compute again
|
344
|
+
currentMEI = currMeiClone;
|
345
|
+
if (currentLayer.closest("measure").nextElementSibling === null) { // add measure if there is none to extend into
|
346
|
+
addMeasure(currentMEI);
|
347
|
+
}
|
348
|
+
var cl = currentMEI.getElementById(currentLayer.id);
|
349
|
+
newSound.nearestNoteId = (_b = cl.closest("measure").nextElementSibling.querySelector("staff[n='" + cl.closest("staff").getAttribute("n") + "'] layer[n='" + cl.getAttribute("n") + "'] :is(chord, note, rest, mRest)")) === null || _b === void 0 ? void 0 : _b.id;
|
350
|
+
newSound.relPosX = "left";
|
351
|
+
if (newSound.nearestNoteId !== null) {
|
352
|
+
currentMEI = addToMEI(newSound, currentMEI, replace);
|
353
|
+
}
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
newElem = currentMEI.querySelector("#" + newElem.id);
|
358
|
+
fillLayerWithRests(currentLayer, currentMEI);
|
359
|
+
//reestablish beams
|
360
|
+
beams.forEach(b => {
|
361
|
+
var first;
|
362
|
+
var last;
|
363
|
+
Array.from(b.children).forEach(bc => {
|
364
|
+
var existingChild = currentMEI.querySelector("#" + bc.id);
|
365
|
+
if (existingChild !== null) {
|
366
|
+
if (first == undefined) {
|
367
|
+
first = bc;
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
last = bc;
|
371
|
+
}
|
372
|
+
}
|
373
|
+
else if (first == undefined) {
|
374
|
+
first = newElem;
|
375
|
+
}
|
376
|
+
});
|
377
|
+
if (last == undefined) {
|
378
|
+
last = newElem;
|
379
|
+
}
|
380
|
+
if (first != undefined && last != undefined) {
|
381
|
+
var newBeam = currentMEI.createElement("beam");
|
382
|
+
newBeam.id = b.id;
|
383
|
+
var beamElements = currentMEI.querySelectorAll("#" + first.id + ", #" + first.id + "~ *:not(#" + last.id + "~ *)"); // all elements in between first and last
|
384
|
+
beamElements[0].insertAdjacentElement("beforebegin", newBeam);
|
385
|
+
beamElements.forEach(be => {
|
386
|
+
newBeam.append(be);
|
387
|
+
});
|
388
|
+
}
|
389
|
+
});
|
390
|
+
cleanUp(currentMEI);
|
391
|
+
adjustAccids(currentMEI);
|
392
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
393
|
+
return currentMEI;
|
394
|
+
}
|
395
|
+
exports.addToMEI = addToMEI;
|
396
|
+
/**
|
397
|
+
* Check if notes have to be shifted after insertion
|
398
|
+
* @param currentMEI
|
399
|
+
*/
|
400
|
+
function checkInsertShifts(currentMEI) {
|
401
|
+
var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
|
402
|
+
var meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
|
403
|
+
if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
|
404
|
+
meterRatio = parseInt(staffDef.getAttribute(constants_1.constants._METERCOUNT_)) / parseInt(staffDef.getAttribute(constants_1.constants._METERUNIT_));
|
405
|
+
}
|
406
|
+
else {
|
407
|
+
meterRatio = extrapolateMeter(currentMEI);
|
408
|
+
}
|
409
|
+
var shifters = new Array;
|
410
|
+
var elements = currentMEI.getElementsByTagName("layer");
|
411
|
+
Array.from(elements).forEach(layer => {
|
412
|
+
var i = 0;
|
413
|
+
var layerChildern = layer.querySelectorAll(countableNoteUnitSelector);
|
414
|
+
Array.from(layerChildern).forEach(node => {
|
415
|
+
i += getAbsoluteRatio(node); //1/parseInt(node.getAttribute("dur"))
|
416
|
+
if (i > meterRatio) {
|
417
|
+
shifters.push(node);
|
418
|
+
}
|
419
|
+
});
|
420
|
+
if (shifters.length > 0) {
|
421
|
+
doShiftRight(shifters, meterRatio, layer);
|
422
|
+
shifters.length = 0;
|
423
|
+
checkInsertShifts(currentMEI);
|
424
|
+
}
|
425
|
+
});
|
426
|
+
}
|
427
|
+
/**
|
428
|
+
* Shift all Elements to the right (according to measure borders)
|
429
|
+
* @param arr Array of Elements to be shifted
|
430
|
+
* @param meterRatio
|
431
|
+
* @param currentLayer
|
432
|
+
*/
|
433
|
+
function doShiftRight(arr, meterRatio, currentLayer) {
|
434
|
+
arr.forEach((element, elementIdx) => {
|
435
|
+
var parentMeasure = element.closest("measure");
|
436
|
+
var parentMeasureSibling = null;
|
437
|
+
parentMeasureSibling = parentMeasure.nextElementSibling;
|
438
|
+
if (parentMeasureSibling === null) {
|
439
|
+
parentMeasureSibling = parentMeasure.parentElement.appendChild(createEmptyCopy(parentMeasure));
|
440
|
+
}
|
441
|
+
var layerLevel = element.closest("layer").getAttribute("n");
|
442
|
+
var staffLevel = element.closest("staff").getAttribute("n");
|
443
|
+
var targetStaff = parentMeasureSibling.querySelector("staff[n$='" + staffLevel + "'");
|
444
|
+
var targetLayer;
|
445
|
+
if (targetStaff.querySelector("layer[n$='" + layerLevel + "'") !== null) {
|
446
|
+
targetLayer = targetStaff.querySelector("layer[n$='" + layerLevel + "'");
|
447
|
+
}
|
448
|
+
else {
|
449
|
+
targetLayer = document.createElement("layer");
|
450
|
+
targetLayer.setAttribute("id", "layer-" + random_1.uuidv4());
|
451
|
+
targetLayer.setAttribute("n", layerLevel);
|
452
|
+
targetStaff.appendChild(targetLayer);
|
453
|
+
}
|
454
|
+
var absLayerRatio = getAbsoluteRatio(currentLayer);
|
455
|
+
var elementRatio = getAbsoluteRatio(element);
|
456
|
+
var chunkDurRight = absLayerRatio - meterRatio;
|
457
|
+
var chunkDurLeft = elementRatio - chunkDurRight;
|
458
|
+
if (chunkDurRight > elementRatio) {
|
459
|
+
chunkDurRight = elementRatio;
|
460
|
+
chunkDurLeft = 0;
|
461
|
+
}
|
462
|
+
//check if note must be split
|
463
|
+
if ((absLayerRatio + elementRatio) > meterRatio && chunkDurRight * chunkDurLeft !== 0) {
|
464
|
+
//check for dots
|
465
|
+
if (Number.isInteger(1 / chunkDurLeft) && Number.isInteger(1 / chunkDurRight)) {
|
466
|
+
element.removeAttribute("dots");
|
467
|
+
var splitRightElement = element.cloneNode(true);
|
468
|
+
splitRightElement.setAttribute("id", random_1.uuidv4());
|
469
|
+
splitRightElement.setAttribute("dur", (Math.abs(1 / chunkDurRight)).toString());
|
470
|
+
var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
|
471
|
+
targetLayer.insertBefore(splitRightElement, beforeElement);
|
472
|
+
//change already existing element
|
473
|
+
element.setAttribute("dur", (Math.abs(1 / chunkDurLeft)).toString());
|
474
|
+
}
|
475
|
+
else {
|
476
|
+
var dottedElements = splitDottedNote(element, chunkDurLeft, chunkDurRight);
|
477
|
+
dottedElements.left.forEach(lel => currentLayer.appendChild(lel));
|
478
|
+
var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
|
479
|
+
dottedElements.right.forEach(rel => {
|
480
|
+
rel.setAttribute("id", random_1.uuidv4());
|
481
|
+
if (rel.tagName === "chord") {
|
482
|
+
rel.querySelectorAll("note").forEach(rl => {
|
483
|
+
rl.setAttribute("id", random_1.uuidv4());
|
484
|
+
});
|
485
|
+
}
|
486
|
+
targetLayer.insertBefore(rel, beforeElement);
|
487
|
+
});
|
488
|
+
element.remove();
|
489
|
+
}
|
490
|
+
}
|
491
|
+
else {
|
492
|
+
var beforeElement = elementIdx === 0 ? targetLayer.firstChild : targetLayer.children.item(elementIdx);
|
493
|
+
targetLayer.insertBefore(element, beforeElement);
|
494
|
+
}
|
495
|
+
});
|
496
|
+
}
|
497
|
+
function createEmptyCopy(element) {
|
498
|
+
let copy = element.cloneNode(true);
|
499
|
+
let childrenToDelete = Array.from(copy.querySelectorAll("layer > *, measure > slur"));
|
500
|
+
childrenToDelete.forEach(child => {
|
501
|
+
child.parentNode.removeChild(child);
|
502
|
+
});
|
503
|
+
//set new ids for everything
|
504
|
+
copy.setAttribute("id", random_1.uuidv4());
|
505
|
+
copy.setAttribute("n", (parseInt(element.getAttribute("n")) + 1).toString());
|
506
|
+
let allElements = copy.querySelectorAll("*");
|
507
|
+
allElements.forEach(e => e.setAttribute("id", random_1.uuidv4()));
|
508
|
+
return copy;
|
509
|
+
}
|
510
|
+
function connectNotes(left, right, connectionShape) {
|
511
|
+
var leftpname = left.getAttribute("pname");
|
512
|
+
var leftoct = left.getAttribute("oct");
|
513
|
+
var leftAccid = left.getAttribute("accid") || left.getAttribute("accid.ges");
|
514
|
+
var rightpname = right.getAttribute("pname");
|
515
|
+
var rightoct = right.getAttribute("oct");
|
516
|
+
var rightAccid = right.getAttribute("accid") || right.getAttribute("accid.ges");
|
517
|
+
if (!(leftpname === rightpname && leftoct === rightoct && leftAccid === rightAccid)) {
|
518
|
+
connectionShape = "slur";
|
519
|
+
}
|
520
|
+
var currentMEI = left.getRootNode();
|
521
|
+
var connections = currentMEI.querySelectorAll("tie, slur");
|
522
|
+
var deleted = false;
|
523
|
+
connections.forEach(c => {
|
524
|
+
var sid = c.getAttribute("startid").replace("#", "");
|
525
|
+
var eid = c.getAttribute("endid").replace("#", "");
|
526
|
+
if (sid === left.id && eid === right.id) {
|
527
|
+
c.remove();
|
528
|
+
deleted = true;
|
529
|
+
}
|
530
|
+
});
|
531
|
+
if (!deleted) {
|
532
|
+
var tieElement = currentMEI.createElementNS(constants_1.constants._MEINS_, connectionShape);
|
533
|
+
tieElement.setAttribute("startid", "#" + left.id);
|
534
|
+
tieElement.setAttribute("endid", "#" + right.id);
|
535
|
+
tieElement.setAttribute("id", random_1.uuidv4());
|
536
|
+
currentMEI.getElementById(left.id).closest("measure").append(tieElement);
|
537
|
+
}
|
538
|
+
}
|
539
|
+
exports.connectNotes = connectNotes;
|
540
|
+
///// GENERAL OPERATIONS /////
|
541
|
+
function getAbsoluteRatio(el) {
|
542
|
+
var i = 0;
|
543
|
+
var arr;
|
544
|
+
if (el === null) {
|
545
|
+
return 0;
|
546
|
+
}
|
547
|
+
if (el.tagName !== "layer") { //if single Element is given, eg. chord, note
|
548
|
+
arr = [el];
|
549
|
+
}
|
550
|
+
else {
|
551
|
+
arr = Array.from(el.querySelectorAll(countableNoteUnitSelector));
|
552
|
+
}
|
553
|
+
arr.forEach(node => {
|
554
|
+
i += 1 / parseInt(node.getAttribute("dur"));
|
555
|
+
let baseDur = parseInt(node.getAttribute("dur"));
|
556
|
+
if (node.getAttribute("dots") !== null) {
|
557
|
+
let dots = parseInt(node.getAttribute("dots"));
|
558
|
+
i += dots == 0 ? 0 : (dots * 2 - 1) / (baseDur * 2 * dots);
|
559
|
+
}
|
560
|
+
});
|
561
|
+
return i;
|
562
|
+
}
|
563
|
+
exports.getAbsoluteRatio = getAbsoluteRatio;
|
564
|
+
function ratioToDur(ratio) {
|
565
|
+
var dur;
|
566
|
+
var dots = 0;
|
567
|
+
//1. next smallest ratio of basedur
|
568
|
+
var basedur = 1;
|
569
|
+
while (basedur > ratio) {
|
570
|
+
basedur = basedur / 2;
|
571
|
+
}
|
572
|
+
dur = 1 / basedur;
|
573
|
+
ratio -= basedur;
|
574
|
+
if (ratio > 0) {
|
575
|
+
if (ratio > dur / 2) {
|
576
|
+
dots = 2;
|
577
|
+
}
|
578
|
+
else {
|
579
|
+
dots = 1;
|
580
|
+
}
|
581
|
+
}
|
582
|
+
return [dur, dots];
|
583
|
+
}
|
584
|
+
/**
|
585
|
+
* Shift Elements to left (according to measure borders)
|
586
|
+
* @param arr Array of Elements to shift
|
587
|
+
* @param meterRatio meterRatio of the piece
|
588
|
+
*/
|
589
|
+
function doShiftLeft(arr, meterRatio) {
|
590
|
+
arr.forEach(element => {
|
591
|
+
var parentMeasure = element.closest("measure");
|
592
|
+
var parentMeasureSibling = parentMeasure.previousElementSibling;
|
593
|
+
var layerLevel = element.closest("layer").getAttribute("n");
|
594
|
+
var targetLayer = parentMeasureSibling.querySelector("layer[n$='" + layerLevel + "'"); // should be <layer>
|
595
|
+
var absLayerRatio = getAbsoluteRatio(targetLayer);
|
596
|
+
var elementRatio = getAbsoluteRatio(element);
|
597
|
+
//check if note must be split
|
598
|
+
if ((absLayerRatio + elementRatio) > meterRatio) {
|
599
|
+
var chunkDurLeft = meterRatio - absLayerRatio;
|
600
|
+
var chunkDurRight = elementRatio - chunkDurLeft;
|
601
|
+
//check for dots
|
602
|
+
if (Number.isInteger(1 / chunkDurLeft) && Number.isInteger(1 / chunkDurRight)) {
|
603
|
+
element.removeAttribute("dots");
|
604
|
+
var splitLeftElement = element.cloneNode(true);
|
605
|
+
splitLeftElement.setAttribute("id", random_1.uuidv4());
|
606
|
+
splitLeftElement.setAttribute("dur", (Math.abs(1 / chunkDurLeft)).toString());
|
607
|
+
targetLayer.appendChild(splitLeftElement);
|
608
|
+
//change already existing element
|
609
|
+
element.setAttribute("dur", (Math.abs(1 / chunkDurRight)).toString());
|
610
|
+
}
|
611
|
+
else {
|
612
|
+
var elements = splitDottedNote(element, chunkDurLeft, chunkDurRight);
|
613
|
+
elements.left.forEach(lel => {
|
614
|
+
lel.setAttribute("id", random_1.uuidv4());
|
615
|
+
if (lel.tagName === "chord") {
|
616
|
+
lel.querySelectorAll("note").forEach(ll => {
|
617
|
+
ll.setAttribute("id", random_1.uuidv4());
|
618
|
+
});
|
619
|
+
}
|
620
|
+
targetLayer.appendChild(lel);
|
621
|
+
});
|
622
|
+
elements.right.forEach(rel => element.parentElement.insertBefore(rel, element));
|
623
|
+
element.remove();
|
624
|
+
}
|
625
|
+
}
|
626
|
+
else {
|
627
|
+
targetLayer.appendChild(element);
|
628
|
+
//is current Layer empty and should be deleted? if split occured this should not be the case
|
629
|
+
var parentLayer = parentMeasure.querySelector("layer[n$='" + layerLevel + "'"); // should always be <layer>
|
630
|
+
// if(parentLayer.childNodes.length === 0){
|
631
|
+
// parentMeasure.remove();
|
632
|
+
// }
|
633
|
+
}
|
634
|
+
});
|
635
|
+
}
|
636
|
+
/**
|
637
|
+
* Operations to split dotted notes
|
638
|
+
* @param note reference note elements
|
639
|
+
* @param chunkLeftDur calculated ratio left
|
640
|
+
* @param chunkRightDur calculated ratio right
|
641
|
+
* @returns collection of right ans left elements
|
642
|
+
*/
|
643
|
+
function splitDottedNote(note, chunkLeftDur, chunkRightDur) {
|
644
|
+
let gcdLeft = gcd(chunkLeftDur);
|
645
|
+
let gcdRight = gcd(chunkRightDur);
|
646
|
+
let countLeftSubNotes = findDotsRecursive(chunkLeftDur, gcdLeft); //return z.B.: [8, 16]
|
647
|
+
let countRightSubNotes = findDotsRecursive(chunkRightDur, gcdRight); //return z.B. [2, 8, 16]
|
648
|
+
let newLeftElement = createElementsFromSubNotes(note, countLeftSubNotes);
|
649
|
+
let newRightElement = createElementsFromSubNotes(note, countRightSubNotes);
|
650
|
+
return { left: newLeftElement, right: newRightElement };
|
651
|
+
}
|
652
|
+
/**
|
653
|
+
* Create actual XML Elements from sequence of dotted notes
|
654
|
+
* @param note
|
655
|
+
* @param subNoteDurs
|
656
|
+
* @returns
|
657
|
+
*/
|
658
|
+
function createElementsFromSubNotes(note, subNoteDurs) {
|
659
|
+
let newElements = new Array();
|
660
|
+
//find sliceBoundaries in array
|
661
|
+
let arraySliceIdx = new Array();
|
662
|
+
for (var i = 0; i < subNoteDurs.length; i++) {
|
663
|
+
if (i > 0) {
|
664
|
+
if (subNoteDurs[i] !== subNoteDurs[i - 1] * 2) {
|
665
|
+
arraySliceIdx.push(i);
|
666
|
+
}
|
667
|
+
}
|
668
|
+
}
|
669
|
+
//find actual slices
|
670
|
+
let durSlices = new Array();
|
671
|
+
for (var i = 0; i < arraySliceIdx.length + 1; i++) {
|
672
|
+
if (i === 0) {
|
673
|
+
durSlices.push(subNoteDurs.slice(0, arraySliceIdx[i]));
|
674
|
+
}
|
675
|
+
else if (i === arraySliceIdx.length) {
|
676
|
+
durSlices.push(subNoteDurs.slice(arraySliceIdx[i - 1]));
|
677
|
+
}
|
678
|
+
else {
|
679
|
+
durSlices.push(subNoteDurs.slice(arraySliceIdx[i - 1], arraySliceIdx[i]));
|
680
|
+
}
|
681
|
+
}
|
682
|
+
//create notes
|
683
|
+
let createArr = durSlices.length > 0 ? durSlices : [subNoteDurs];
|
684
|
+
createArr.forEach(durs => {
|
685
|
+
let newElement = note.cloneNode(true);
|
686
|
+
newElement.removeAttribute("dots"); //eventual dots could be in original note value
|
687
|
+
newElement.setAttribute("dur", Math.abs(durs[0]).toString());
|
688
|
+
let dots = 0;
|
689
|
+
durs.forEach((dur, i) => {
|
690
|
+
if (i > 0) {
|
691
|
+
dots += 1;
|
692
|
+
}
|
693
|
+
});
|
694
|
+
if (dots > 0) {
|
695
|
+
newElement.setAttribute("dots", dots.toString());
|
696
|
+
}
|
697
|
+
newElements.push(newElement);
|
698
|
+
});
|
699
|
+
return newElements;
|
700
|
+
}
|
701
|
+
/**
|
702
|
+
* Compute greatest integer divisor
|
703
|
+
* @param chunkDur Duration of given Chunk
|
704
|
+
* @returns
|
705
|
+
*/
|
706
|
+
function gcd(chunkDur) {
|
707
|
+
var largestModulo = null;
|
708
|
+
var baseValue = 1;
|
709
|
+
var mod = 0;
|
710
|
+
while (largestModulo === null) {
|
711
|
+
mod = chunkDur % baseValue;
|
712
|
+
if (mod === 0) {
|
713
|
+
largestModulo = baseValue;
|
714
|
+
}
|
715
|
+
baseValue = baseValue / 2;
|
716
|
+
}
|
717
|
+
return largestModulo;
|
718
|
+
}
|
719
|
+
/**
|
720
|
+
* Splits duration of given chunk into possible dotted sequences
|
721
|
+
* @param chunk
|
722
|
+
* @param smallestUnit = greatest integer divisor
|
723
|
+
* @returns
|
724
|
+
*/
|
725
|
+
function findDotsRecursive(chunk, smallestUnit) {
|
726
|
+
var arr = new Array();
|
727
|
+
var sliceChunk = chunk / smallestUnit;
|
728
|
+
if (Math.floor(sliceChunk) > 1) {
|
729
|
+
arr = arr.concat(findDotsRecursive(chunk, smallestUnit * 2));
|
730
|
+
}
|
731
|
+
else if (Math.floor(sliceChunk) < 1) {
|
732
|
+
arr = arr.concat(findDotsRecursive(chunk, smallestUnit / 2));
|
733
|
+
}
|
734
|
+
else if (!Number.isInteger(sliceChunk)) {
|
735
|
+
arr.push(1 / 1 / smallestUnit);
|
736
|
+
arr = arr.concat(findDotsRecursive(chunk - smallestUnit, smallestUnit));
|
737
|
+
}
|
738
|
+
else {
|
739
|
+
arr.push(1 / 1 / smallestUnit);
|
740
|
+
}
|
741
|
+
return arr; //.sort((a,b) => a-b)
|
742
|
+
}
|
743
|
+
/**
|
744
|
+
* Extrapolates meter, if is not given in scoreDef. Iterates through each staff to get the mostly found ratio
|
745
|
+
* @param currentMEI
|
746
|
+
* @returns meter ratio
|
747
|
+
*/
|
748
|
+
function extrapolateMeter(currentMEI) {
|
749
|
+
var ratioMap = new Map();
|
750
|
+
var xmlCopy = currentMEI.cloneNode(true);
|
751
|
+
var layers = Array.from(xmlCopy.querySelectorAll("layer"));
|
752
|
+
var mostlyUsedRatio = 0;
|
753
|
+
layers.forEach(layer => {
|
754
|
+
if (layer.childElementCount === 0) {
|
755
|
+
return;
|
756
|
+
}
|
757
|
+
//strip all unnecessary elements: garce notes, beams
|
758
|
+
//which do not contribute to count of measure duration
|
759
|
+
var beams = Array.from(layer.querySelectorAll("beam"));
|
760
|
+
beams.forEach(beam => {
|
761
|
+
Array.from(beam.children).forEach(c => {
|
762
|
+
beam.parentElement.append(c);
|
763
|
+
});
|
764
|
+
xmlCopy.getElementById(beam.id).remove();
|
765
|
+
});
|
766
|
+
var graceNotes = Array.from(layer.querySelectorAll("[grace]"));
|
767
|
+
graceNotes.forEach(g => {
|
768
|
+
xmlCopy.getElementById(g.id).remove();
|
769
|
+
});
|
770
|
+
var childElements = Array.from(layer.children);
|
771
|
+
var ratio = 0;
|
772
|
+
childElements.forEach(element => {
|
773
|
+
ratio += getAbsoluteRatio(element);
|
774
|
+
});
|
775
|
+
if (!ratioMap.has(ratio)) {
|
776
|
+
ratioMap.set(ratio, 1);
|
777
|
+
}
|
778
|
+
else {
|
779
|
+
ratioMap.set(ratio, ratioMap.get(ratio) + 1);
|
780
|
+
}
|
781
|
+
var prevItCount = 0;
|
782
|
+
for (const [key, value] of ratioMap.entries()) {
|
783
|
+
if (value > prevItCount) {
|
784
|
+
prevItCount = value;
|
785
|
+
mostlyUsedRatio = key;
|
786
|
+
}
|
787
|
+
}
|
788
|
+
});
|
789
|
+
return mostlyUsedRatio;
|
790
|
+
}
|
791
|
+
exports.extrapolateMeter = extrapolateMeter;
|
792
|
+
/**
|
793
|
+
* Adjust all accids according to key signature
|
794
|
+
* e.g. after changing global Key
|
795
|
+
* @param currentMEI
|
796
|
+
* @returns
|
797
|
+
*/
|
798
|
+
function adjustAccids(currentMEI) {
|
799
|
+
var measureMatrix = new MeasureMatrix_1.default();
|
800
|
+
measureMatrix.populateFromMEI(currentMEI);
|
801
|
+
var prevAccidMap = new Map(); // key: pname+oct
|
802
|
+
var currentLayer = "1";
|
803
|
+
var currentStaff = "1";
|
804
|
+
var currentMeasure = "1";
|
805
|
+
currentMEI.querySelectorAll("note").forEach(note => {
|
806
|
+
var layerN = note.closest("layer").getAttribute("n");
|
807
|
+
var staffN = note.closest("staff").getAttribute("n");
|
808
|
+
var measureN = note.closest("measure").getAttribute("n");
|
809
|
+
if (layerN !== currentLayer || staffN !== currentStaff || measureN !== currentMeasure) {
|
810
|
+
prevAccidMap = new Map(); // key: pname+oct
|
811
|
+
currentLayer = layerN;
|
812
|
+
currentStaff = staffN;
|
813
|
+
currentMeasure = measureN;
|
814
|
+
}
|
815
|
+
var sig = measureMatrix.get(measureN, staffN).keysig;
|
816
|
+
var sigSymbol = sig.charAt(1);
|
817
|
+
var signedNotes = mappings_1.keysigToNotes.get(sig);
|
818
|
+
var accid = note.getAttribute("accid") || note.getAttribute("accid.ges");
|
819
|
+
accid = accid === "n" || accid === "" ? null : accid;
|
820
|
+
// remove all accids so I don't have to look it up several times
|
821
|
+
note.removeAttribute("accid");
|
822
|
+
note.removeAttribute("accid.ges");
|
823
|
+
var pname = note.getAttribute("pname");
|
824
|
+
var oct = note.getAttribute("oct");
|
825
|
+
var mapKey = pname + oct;
|
826
|
+
var noteInKey = signedNotes.some(sn => sn === pname);
|
827
|
+
if (accid === null) { // "I have no accid"
|
828
|
+
accid = "n";
|
829
|
+
if (prevAccidMap.has(mapKey)) { // "does someone before me has any accid?"
|
830
|
+
if (prevAccidMap.get(mapKey) === "n") {
|
831
|
+
prevAccidMap.delete(mapKey);
|
832
|
+
}
|
833
|
+
else {
|
834
|
+
note.setAttribute("accid", accid);
|
835
|
+
prevAccidMap.set(mapKey, accid);
|
836
|
+
}
|
837
|
+
}
|
838
|
+
else if (noteInKey) {
|
839
|
+
note.setAttribute("accid", accid);
|
840
|
+
prevAccidMap.set(mapKey, accid);
|
841
|
+
}
|
842
|
+
}
|
843
|
+
else { // "I have accid"
|
844
|
+
if (prevAccidMap.has(mapKey)) {
|
845
|
+
if (prevAccidMap.get(mapKey) === accid) {
|
846
|
+
note.setAttribute("accid.ges", accid);
|
847
|
+
prevAccidMap.set(mapKey, accid);
|
848
|
+
}
|
849
|
+
else {
|
850
|
+
note.setAttribute("accid", accid);
|
851
|
+
prevAccidMap.set(mapKey, accid);
|
852
|
+
}
|
853
|
+
}
|
854
|
+
else {
|
855
|
+
if (noteInKey) {
|
856
|
+
if (sigSymbol === accid) {
|
857
|
+
if (prevAccidMap.has(mapKey)) {
|
858
|
+
if (prevAccidMap.get(mapKey) === accid) {
|
859
|
+
note.setAttribute("accid.ges", accid);
|
860
|
+
prevAccidMap.set(mapKey, accid);
|
861
|
+
}
|
862
|
+
else {
|
863
|
+
note.setAttribute("accid", accid);
|
864
|
+
prevAccidMap.set(mapKey, accid);
|
865
|
+
}
|
866
|
+
}
|
867
|
+
else {
|
868
|
+
if (sigSymbol === accid) {
|
869
|
+
note.setAttribute("accid.ges", accid);
|
870
|
+
prevAccidMap.set(mapKey, accid);
|
871
|
+
}
|
872
|
+
else {
|
873
|
+
note.setAttribute("accid", accid);
|
874
|
+
prevAccidMap.set(mapKey, accid);
|
875
|
+
}
|
876
|
+
}
|
877
|
+
}
|
878
|
+
else {
|
879
|
+
note.setAttribute("accid", accid);
|
880
|
+
prevAccidMap.set(mapKey, accid);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
else {
|
884
|
+
note.setAttribute("accid", accid);
|
885
|
+
prevAccidMap.set(mapKey, accid);
|
886
|
+
}
|
887
|
+
}
|
888
|
+
}
|
889
|
+
});
|
890
|
+
return currentMEI;
|
891
|
+
}
|
892
|
+
exports.adjustAccids = adjustAccids;
|
893
|
+
/**
|
894
|
+
* Merge all scoreDefs in sections in the respective layers, e.g. when importing a file.
|
895
|
+
* This is important since MeasureMartrix only handles signature elements (keySig, meterSig, clef) in layers
|
896
|
+
* @param currentMEI
|
897
|
+
*/
|
898
|
+
function mergeSectionScoreDefToLayer(currentMEI) {
|
899
|
+
var mei;
|
900
|
+
if (typeof currentMEI === "string") {
|
901
|
+
mei = new DOMParser().parseFromString(currentMEI, "text/xml");
|
902
|
+
}
|
903
|
+
else {
|
904
|
+
mei = currentMEI;
|
905
|
+
}
|
906
|
+
mei.querySelectorAll("section").forEach(sec => {
|
907
|
+
var secChildren = sec.querySelectorAll(":scope > *");
|
908
|
+
secChildren.forEach((e, i) => {
|
909
|
+
if (e.tagName === "scoreDef") {
|
910
|
+
secChildren[i + 1].querySelectorAll("layer").forEach(layer => {
|
911
|
+
e.querySelectorAll(":scope > *").forEach(sig => {
|
912
|
+
var newElem = sig.cloneNode(true);
|
913
|
+
layer.prepend(newElem);
|
914
|
+
});
|
915
|
+
});
|
916
|
+
}
|
917
|
+
});
|
918
|
+
sec.querySelectorAll(":scope > scoreDef").forEach(sd => sd.remove());
|
919
|
+
});
|
920
|
+
return mei;
|
921
|
+
}
|
922
|
+
exports.mergeSectionScoreDefToLayer = mergeSectionScoreDefToLayer;
|
923
|
+
function mergeArticToParent(currentMEI) {
|
924
|
+
currentMEI.querySelectorAll("artic").forEach(a => {
|
925
|
+
a.parentElement.setAttribute("artic", a.getAttribute("artic"));
|
926
|
+
a.remove();
|
927
|
+
});
|
928
|
+
return currentMEI;
|
929
|
+
}
|
930
|
+
exports.mergeArticToParent = mergeArticToParent;
|
931
|
+
/**
|
932
|
+
* Transpose marked notes according to direcion (up or down)
|
933
|
+
* @param currentMEI
|
934
|
+
* @param direction
|
935
|
+
* @returns
|
936
|
+
*/
|
937
|
+
function transposeByStep(currentMEI, direction) {
|
938
|
+
//document.querySelectorAll(".activeContainer #vrvSVG :is(.note.marked, .note.lastAdded)").forEach(nm => {
|
939
|
+
document.querySelectorAll(".activeContainer :is(.note.marked, .note.lastAdded)").forEach(nm => {
|
940
|
+
if (nm.id === null || nm.id == undefined || nm.id === "")
|
941
|
+
return; // make shure that only the id is taken from the verovio svg so that the element will only be effected once
|
942
|
+
var id = nm.id;
|
943
|
+
var noteMEI = currentMEI.getElementById(id);
|
944
|
+
var pname = noteMEI.getAttribute("pname");
|
945
|
+
var oct = parseInt(noteMEI.getAttribute("oct"));
|
946
|
+
var accid = noteMEI.getAttribute("accid") || noteMEI.getAttribute("accid.ges");
|
947
|
+
if (accid === null || typeof accid == "undefined" || accid === "n") {
|
948
|
+
accid = "";
|
949
|
+
}
|
950
|
+
var nextNote;
|
951
|
+
if (direction === "up") {
|
952
|
+
nextNote = mappings_1.nextStepUp.get(pname + accid);
|
953
|
+
}
|
954
|
+
else if (direction === "down") {
|
955
|
+
nextNote = mappings_1.nextStepDown.get(pname + accid);
|
956
|
+
}
|
957
|
+
noteMEI.setAttribute("pname", nextNote.charAt(0));
|
958
|
+
if (nextNote.charAt(1) !== "") {
|
959
|
+
noteMEI.setAttribute("accid", nextNote.charAt(1));
|
960
|
+
}
|
961
|
+
else {
|
962
|
+
noteMEI.removeAttribute("accid");
|
963
|
+
noteMEI.removeAttribute("accid.ges");
|
964
|
+
}
|
965
|
+
//Change Octave
|
966
|
+
if (["c", "cf"].includes(pname + accid) && direction === "down") {
|
967
|
+
noteMEI.setAttribute("oct", (oct - 1).toString());
|
968
|
+
}
|
969
|
+
if (["b", "bs"].includes(pname + accid) && direction === "up") {
|
970
|
+
noteMEI.setAttribute("oct", (oct + 1).toString());
|
971
|
+
}
|
972
|
+
});
|
973
|
+
return adjustAccids(currentMEI);
|
974
|
+
}
|
975
|
+
exports.transposeByStep = transposeByStep;
|
976
|
+
/**
|
977
|
+
* Change Meter according to #timeUnit and #timeCount in side bar option.
|
978
|
+
* @param currentMEI
|
979
|
+
* @returns changed mei; null, if input has no valid values
|
980
|
+
*/
|
981
|
+
function changeMeter(currentMEI) {
|
982
|
+
var timeCount = document.querySelector(".activeElement #timeCount");
|
983
|
+
var timeUnit = document.querySelector(".activeElement #timeUnit");
|
984
|
+
//@ts-ignore
|
985
|
+
var timeCountValue = timeCount.value; //getAttribute("value")
|
986
|
+
//@ts-ignore
|
987
|
+
var timeUnitValue = timeUnit.value; //getAttribute("value")
|
988
|
+
if (timeCountValue !== null && timeUnitValue !== null) {
|
989
|
+
timeCountValue = timeCountValue.trim();
|
990
|
+
timeUnitValue = timeUnitValue.trim();
|
991
|
+
if (!isNaN(parseInt(timeCountValue)) && !isNaN(parseInt(timeUnitValue))) {
|
992
|
+
var oldMeterRatio = getMeterRatioGlobal(currentMEI);
|
993
|
+
currentMEI.querySelectorAll("staffDef").forEach(sd => {
|
994
|
+
sd.setAttribute("meter.count", timeCountValue);
|
995
|
+
sd.setAttribute("meter.unit", timeUnitValue);
|
996
|
+
});
|
997
|
+
// adjust noteposition
|
998
|
+
var newMeterRatio = getMeterRatioGlobal(currentMEI);
|
999
|
+
if (oldMeterRatio > newMeterRatio) {
|
1000
|
+
checkInsertShifts(currentMEI);
|
1001
|
+
}
|
1002
|
+
else if (oldMeterRatio < newMeterRatio) {
|
1003
|
+
checkDeleteShifts(currentMEI);
|
1004
|
+
}
|
1005
|
+
if (oldMeterRatio !== newMeterRatio) {
|
1006
|
+
return currentMEI;
|
1007
|
+
}
|
1008
|
+
}
|
1009
|
+
}
|
1010
|
+
return currentMEI; //null
|
1011
|
+
}
|
1012
|
+
exports.changeMeter = changeMeter;
|
1013
|
+
/**
|
1014
|
+
* disable features if necesseray (only supposed to be used for debugging)
|
1015
|
+
* @param features Array of TagNames and AttributeNames which have to be disabled (deleted)
|
1016
|
+
* @param currentMEI mei
|
1017
|
+
* @returns
|
1018
|
+
*/
|
1019
|
+
function disableFeatures(features, currentMEI) {
|
1020
|
+
//console.log("Features disabled:", features)
|
1021
|
+
features.forEach(f => {
|
1022
|
+
var elements = Array.from(currentMEI.getElementsByTagName(f));
|
1023
|
+
elements.forEach(e => {
|
1024
|
+
let parent = e.parentElement;
|
1025
|
+
e.remove();
|
1026
|
+
if (parent.childElementCount === 0) {
|
1027
|
+
parent.remove();
|
1028
|
+
}
|
1029
|
+
});
|
1030
|
+
elements = Array.from(currentMEI.querySelectorAll("*[" + f + "]"));
|
1031
|
+
elements.forEach(e => {
|
1032
|
+
let parent = e.parentElement;
|
1033
|
+
e.remove();
|
1034
|
+
if (parent.childElementCount === 0) {
|
1035
|
+
parent.remove();
|
1036
|
+
}
|
1037
|
+
});
|
1038
|
+
});
|
1039
|
+
return currentMEI;
|
1040
|
+
}
|
1041
|
+
exports.disableFeatures = disableFeatures;
|
1042
|
+
/**
|
1043
|
+
* When a note is shortened, fill old remaining duration with rests
|
1044
|
+
* @param newElement
|
1045
|
+
* @param oldElement
|
1046
|
+
* @param currentMEI
|
1047
|
+
*/
|
1048
|
+
function fillWithRests(newElement, oldElement, currentMEI) {
|
1049
|
+
var newRatio = getAbsoluteRatio(newElement);
|
1050
|
+
var oldRatio = getAbsoluteRatio(oldElement);
|
1051
|
+
if (newRatio < oldRatio) {
|
1052
|
+
var remainRatio = oldRatio - newRatio;
|
1053
|
+
var smallestUnit = gcd(remainRatio);
|
1054
|
+
var restDur = ratioToDur(smallestUnit)[0];
|
1055
|
+
var restCount = remainRatio / smallestUnit;
|
1056
|
+
newElement.classList.add("changed");
|
1057
|
+
for (var i = 0; i < restCount; i++) {
|
1058
|
+
var rest = createNewRestElement(restDur);
|
1059
|
+
currentMEI.getElementById(newElement.id).parentElement.insertBefore(rest, newElement.nextElementSibling);
|
1060
|
+
}
|
1061
|
+
}
|
1062
|
+
return currentMEI;
|
1063
|
+
}
|
1064
|
+
exports.fillWithRests = fillWithRests;
|
1065
|
+
function fillLayerWithRests(layer, currentMEI) {
|
1066
|
+
var targetDur = getMeterRatioLocal(currentMEI, layer);
|
1067
|
+
var currentDur = getAbsoluteRatio(layer);
|
1068
|
+
if (currentDur < targetDur) {
|
1069
|
+
var remainRatio = targetDur - currentDur;
|
1070
|
+
var smallestUnit = gcd(remainRatio);
|
1071
|
+
var restDur = ratioToDur(smallestUnit)[0];
|
1072
|
+
var restCount = remainRatio / smallestUnit;
|
1073
|
+
for (var i = 0; i < restCount; i++) {
|
1074
|
+
var rest = createNewRestElement(restDur);
|
1075
|
+
Array.from(layer.querySelectorAll("note, chord, rest")).reverse()[0].insertAdjacentElement("afterend", rest);
|
1076
|
+
}
|
1077
|
+
}
|
1078
|
+
}
|
1079
|
+
exports.fillLayerWithRests = fillLayerWithRests;
|
1080
|
+
/**
|
1081
|
+
* Fill Empty Space with rest
|
1082
|
+
* @deprecated
|
1083
|
+
* @param currentMEI
|
1084
|
+
*/
|
1085
|
+
function _fillWithRests(currentMEI) {
|
1086
|
+
var staffDef = currentMEI.getElementsByTagName("staffDef").item(0);
|
1087
|
+
var meterCount;
|
1088
|
+
var meterUnit;
|
1089
|
+
var meterRatio;
|
1090
|
+
if (staffDef.getAttribute(constants_1.constants._METERCOUNT_) !== null && staffDef.getAttribute(constants_1.constants._METERUNIT_) !== null) {
|
1091
|
+
meterCount = staffDef.getAttribute(constants_1.constants._METERCOUNT_);
|
1092
|
+
meterUnit = staffDef.getAttribute(constants_1.constants._METERUNIT_);
|
1093
|
+
meterRatio = parseInt(meterCount) / parseInt(meterUnit);
|
1094
|
+
}
|
1095
|
+
else {
|
1096
|
+
var meterRatio = getMeterRatioGlobal(currentMEI);
|
1097
|
+
meterCount = (meterRatio * 4).toString();
|
1098
|
+
meterUnit = "4";
|
1099
|
+
}
|
1100
|
+
currentMEI.querySelectorAll("measure").forEach(m => {
|
1101
|
+
m.querySelectorAll("staff").forEach(s => {
|
1102
|
+
s.querySelectorAll("layer").forEach((l, idx) => {
|
1103
|
+
//mRest for empty Layer
|
1104
|
+
if (l.childElementCount === 0) {
|
1105
|
+
if (idx === 0) {
|
1106
|
+
var restEl = document.createElementNS(constants_1.constants._MEINS_, "mRest");
|
1107
|
+
l.appendChild(restEl);
|
1108
|
+
}
|
1109
|
+
else { // remove 1+ empty layer
|
1110
|
+
l.remove();
|
1111
|
+
}
|
1112
|
+
}
|
1113
|
+
else {
|
1114
|
+
var actualMeterFill = getAbsoluteRatio(l);
|
1115
|
+
var ratioDiff = Math.abs(actualMeterFill - meterRatio);
|
1116
|
+
var smallestValue = gcd(ratioDiff);
|
1117
|
+
//var restDurs = findDotsRecursive(ratioDiff, gcd(ratioDiff))
|
1118
|
+
if (Number.isInteger(ratioDiff / smallestValue) && ratioDiff > 0) {
|
1119
|
+
var leftRatio = ratioDiff;
|
1120
|
+
var durArr = new Array();
|
1121
|
+
while (!Number.isInteger(1 / leftRatio)) {
|
1122
|
+
var leftRatio = ratioDiff - smallestValue;
|
1123
|
+
durArr.push(1 / smallestValue);
|
1124
|
+
}
|
1125
|
+
durArr.push(1 / leftRatio);
|
1126
|
+
durArr = durArr.reverse();
|
1127
|
+
durArr.forEach(dur => {
|
1128
|
+
var newRest = currentMEI.createElementNS(constants_1.constants._MEINS_, "rest");
|
1129
|
+
newRest.setAttribute("dur", dur.toString());
|
1130
|
+
l.appendChild(newRest);
|
1131
|
+
});
|
1132
|
+
}
|
1133
|
+
//console.log(document.getElementById(l.id), ratioDiff, gcd(ratioDiff), durArr)
|
1134
|
+
}
|
1135
|
+
});
|
1136
|
+
});
|
1137
|
+
});
|
1138
|
+
}
|
1139
|
+
/**
|
1140
|
+
* Replace given id with rest
|
1141
|
+
* @param element element from svg
|
1142
|
+
* @param currentMEI
|
1143
|
+
*/
|
1144
|
+
function replaceWithRest(element, currentMEI) {
|
1145
|
+
var elmei = currentMEI.getElementById(element.id);
|
1146
|
+
//var closestChord: Element = currentMEI .getElementById(element.id).closest("chord")
|
1147
|
+
//if(closestChord !== null){elmei = closestChord}
|
1148
|
+
var dur = elmei.getAttribute("dur");
|
1149
|
+
var dots = elmei.getAttribute("dots");
|
1150
|
+
var newRest = currentMEI.createElementNS(constants_1.constants._MEINS_, "rest");
|
1151
|
+
newRest.setAttribute("dur", dur);
|
1152
|
+
if (dots !== null) {
|
1153
|
+
newRest.setAttribute("dots", dots);
|
1154
|
+
}
|
1155
|
+
elmei.parentElement.insertBefore(newRest, elmei);
|
1156
|
+
elmei.remove();
|
1157
|
+
}
|
1158
|
+
/**
|
1159
|
+
* @deprecated
|
1160
|
+
* Change duration of the following sound events. Elements to change duration are determined by the class to be "marked".
|
1161
|
+
* @param currentMEI Current MEI as Document
|
1162
|
+
* @param additionalElements Elements to be considered to be changed.
|
1163
|
+
* @param refElement Reference Element after which all determined elements (.marked and additionElements) will be changed (e.g. replacing duration during a note insert).
|
1164
|
+
* If no refElement is given, filter the additionalElements to exclude the refElement
|
1165
|
+
* @param marked Consider marked elements
|
1166
|
+
* @returns
|
1167
|
+
*/
|
1168
|
+
function _changeDuration(currentMEI, additionalElements = new Array(), refElement = null) {
|
1169
|
+
var currMeiClone = currentMEI.cloneNode(true);
|
1170
|
+
var changedFlag = "changed";
|
1171
|
+
var multiplier;
|
1172
|
+
var elmei;
|
1173
|
+
var i = refElement === null ? 1 : 0;
|
1174
|
+
for (i; i < additionalElements.length; i++) {
|
1175
|
+
elmei = currentMEI.getElementById(additionalElements[i].id);
|
1176
|
+
var elmeiRatio = getAbsoluteRatio(elmei);
|
1177
|
+
var chord = elmei.closest("chord");
|
1178
|
+
//Dur is attribute of chord and all notes will be changed accordingly
|
1179
|
+
if (chord !== null) {
|
1180
|
+
if (chord.classList.contains(changedFlag)) {
|
1181
|
+
return;
|
1182
|
+
}
|
1183
|
+
else {
|
1184
|
+
elmei = chord;
|
1185
|
+
elmei.classList.add(changedFlag);
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
var dur = parseInt(elmei.getAttribute("dur"));
|
1189
|
+
var dots = parseInt(elmei.getAttribute("dots")); // is NaN if elmei has no dots
|
1190
|
+
if (dur > 0) {
|
1191
|
+
var layerRatio = getAbsoluteRatio(elmei.closest("layer")); // current ratio of layer with already inserted new sound event
|
1192
|
+
var localRatio = getMeterRatioLocal(currentMEI, elmei); //getMeterRatioGlobal(currentMEI )
|
1193
|
+
var danglingRatio = layerRatio - localRatio;
|
1194
|
+
if (danglingRatio > 0) {
|
1195
|
+
var nextElementRatio = getAbsoluteRatio(elmei);
|
1196
|
+
var neNewRatio = nextElementRatio - danglingRatio;
|
1197
|
+
if (neNewRatio > 0) {
|
1198
|
+
var durArr = ratioToDur(neNewRatio);
|
1199
|
+
elmei.setAttribute("dur", durArr[0].toString());
|
1200
|
+
if (durArr[1] > 0) {
|
1201
|
+
elmei.setAttribute("dots", durArr[1].toString());
|
1202
|
+
}
|
1203
|
+
else {
|
1204
|
+
elmei.removeAttribute("dots");
|
1205
|
+
}
|
1206
|
+
}
|
1207
|
+
else {
|
1208
|
+
elmei.remove();
|
1209
|
+
}
|
1210
|
+
}
|
1211
|
+
if ((layerRatio <= localRatio && refElement === null) || (layerRatio < localRatio && refElement !== null)) {
|
1212
|
+
var nextElementRatio = getAbsoluteRatio(elmei);
|
1213
|
+
var neNewRatio;
|
1214
|
+
if (refElement !== null) {
|
1215
|
+
neNewRatio = nextElementRatio - getAbsoluteRatio(refElement);
|
1216
|
+
}
|
1217
|
+
else {
|
1218
|
+
var addRatios = (function (elements) {
|
1219
|
+
var r = 0;
|
1220
|
+
elements.forEach((v, i) => {
|
1221
|
+
if (i > 0) {
|
1222
|
+
r += getAbsoluteRatio(v);
|
1223
|
+
}
|
1224
|
+
});
|
1225
|
+
return r;
|
1226
|
+
});
|
1227
|
+
neNewRatio = nextElementRatio - (layerRatio - addRatios(additionalElements) - getAbsoluteRatio(additionalElements[0]));
|
1228
|
+
}
|
1229
|
+
if (neNewRatio > 0) {
|
1230
|
+
var durArr = ratioToDur(neNewRatio);
|
1231
|
+
elmei.setAttribute("dur", durArr[0].toString());
|
1232
|
+
if (durArr[1] > 0) {
|
1233
|
+
elmei.setAttribute("dots", durArr[1].toString());
|
1234
|
+
}
|
1235
|
+
else {
|
1236
|
+
elmei.removeAttribute("dots");
|
1237
|
+
}
|
1238
|
+
}
|
1239
|
+
else {
|
1240
|
+
elmei.remove();
|
1241
|
+
}
|
1242
|
+
}
|
1243
|
+
}
|
1244
|
+
}
|
1245
|
+
if (!overfillMeasure && elmei != undefined && (elmei === null || elmei === void 0 ? void 0 : elmei.closest("layer")) !== null) {
|
1246
|
+
var newMeasureRatio = getAbsoluteRatio(elmei.closest("layer"));
|
1247
|
+
var localRatio = getMeterRatioLocal(currentMEI, elmei);
|
1248
|
+
if (newMeasureRatio > localRatio) { //getMeterRatioGlobal(currentMEI )){
|
1249
|
+
currentMEI = currMeiClone;
|
1250
|
+
}
|
1251
|
+
}
|
1252
|
+
//clean up after changing durations
|
1253
|
+
currentMEI.querySelectorAll(".changed").forEach(c => c.classList.remove(changedFlag));
|
1254
|
+
cleanUp(currentMEI);
|
1255
|
+
return currentMEI;
|
1256
|
+
}
|
1257
|
+
function addRatios(elements) {
|
1258
|
+
var r = 0;
|
1259
|
+
elements.forEach((v, i) => {
|
1260
|
+
//if(i > 0){
|
1261
|
+
r += getAbsoluteRatio(v);
|
1262
|
+
//}
|
1263
|
+
});
|
1264
|
+
return r;
|
1265
|
+
}
|
1266
|
+
/**
|
1267
|
+
*
|
1268
|
+
* @param currentMEI current used MEI to be changed
|
1269
|
+
* @param additionalElements elements which duration has to be changed which occur after the refElement in the same layer
|
1270
|
+
* @param refElement actual current Element which will be changed in duration
|
1271
|
+
* @param remainRatio remaining ratio of the current layer
|
1272
|
+
* @param meiToReset MEI which can be used to reset the whole process
|
1273
|
+
* @returns
|
1274
|
+
*/
|
1275
|
+
function changeDurationsInLayer(currentMEI, additionalElements = new Array(), refElement = null, remainRatio = null, meiToReset = null) {
|
1276
|
+
var meiCopy = meiToReset || currentMEI.cloneNode(true);
|
1277
|
+
let ms = Array.from(refElement.parentElement.querySelectorAll("note:not(chord note), chord, rest")); // querySelectorAll("note:not(chord note), chord, beam, rest")
|
1278
|
+
var measureSiblings = ms.filter((_, i) => i <= ms.indexOf(refElement));
|
1279
|
+
var ratioUpTpRef = addRatios(measureSiblings);
|
1280
|
+
var refElementRatio = getAbsoluteRatio(refElement);
|
1281
|
+
var remainBarRatio = getMeterRatioLocal(currentMEI, refElement) - ratioUpTpRef;
|
1282
|
+
if (getAbsoluteRatio(refElement.parentElement) === getMeterRatioLocal(currentMEI, refElement)) { // bar has right size,
|
1283
|
+
return currentMEI;
|
1284
|
+
}
|
1285
|
+
if (additionalElements.length > 0) {
|
1286
|
+
var nextNote = additionalElements.shift();
|
1287
|
+
var nnRatio = getAbsoluteRatio(nextNote);
|
1288
|
+
remainRatio = remainRatio || refElementRatio;
|
1289
|
+
var currEl;
|
1290
|
+
var harm;
|
1291
|
+
if (remainRatio < nnRatio) {
|
1292
|
+
var diffRatio = nnRatio - remainRatio;
|
1293
|
+
var nextNoteMEI = currentMEI.getElementById(nextNote.id);
|
1294
|
+
//var dur = ratioToDur(diffRatio)
|
1295
|
+
// currentMEI.getElementById(nextNote.id).setAttribute("dur", dur.shift().toString())
|
1296
|
+
// if (dur.length > 0) {
|
1297
|
+
// currentMEI.getElementById(nextNote.id).setAttribute("dots", dur.shift().toString())
|
1298
|
+
// }
|
1299
|
+
changeDur(nextNoteMEI, diffRatio);
|
1300
|
+
harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
|
1301
|
+
if (harm !== null) {
|
1302
|
+
harm.setAttribute("startid", refElement.id);
|
1303
|
+
}
|
1304
|
+
}
|
1305
|
+
else if (remainRatio === nnRatio) {
|
1306
|
+
currEl = currentMEI.getElementById(nextNote.id);
|
1307
|
+
harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
|
1308
|
+
//if(currEl.tagName === "rest" && harm !== null){
|
1309
|
+
if (harm !== null) {
|
1310
|
+
harm.setAttribute("startid", refElement.id);
|
1311
|
+
}
|
1312
|
+
currEl.remove();
|
1313
|
+
}
|
1314
|
+
else {
|
1315
|
+
currEl = currentMEI.getElementById(nextNote.id);
|
1316
|
+
harm = currentMEI.querySelector('harm[startid="' + nextNote.id + '"]');
|
1317
|
+
//if(currEl.tagName === "rest" && harm !== null){
|
1318
|
+
if (harm !== null) {
|
1319
|
+
harm.setAttribute("startid", refElement.id);
|
1320
|
+
}
|
1321
|
+
remainRatio = remainRatio - nnRatio;
|
1322
|
+
currEl.remove();
|
1323
|
+
changeDurationsInLayer(currentMEI, additionalElements, refElement, remainRatio, meiCopy);
|
1324
|
+
}
|
1325
|
+
}
|
1326
|
+
cleanUp(currentMEI);
|
1327
|
+
return currentMEI;
|
1328
|
+
}
|
1329
|
+
exports.changeDurationsInLayer = changeDurationsInLayer;
|
1330
|
+
/**
|
1331
|
+
* Change duration attributes for given Element for the given ratio.
|
1332
|
+
* Will also change dot attribute if necessary
|
1333
|
+
* @param element
|
1334
|
+
* @param ratio
|
1335
|
+
*/
|
1336
|
+
function changeDur(element, ratio) {
|
1337
|
+
var dur = ratioToDur(ratio);
|
1338
|
+
element.setAttribute("dur", dur.shift().toString());
|
1339
|
+
if (dur.length > 0) {
|
1340
|
+
element.setAttribute("dots", dur.shift().toString());
|
1341
|
+
}
|
1342
|
+
}
|
1343
|
+
exports.changeDur = changeDur;
|
1344
|
+
/**
|
1345
|
+
* Check if elment is overfilling the current layer element. Must provide previous MEI for reference.
|
1346
|
+
* Violate rule: true; follow rules: false
|
1347
|
+
* @param element
|
1348
|
+
* @param currMeiClone
|
1349
|
+
* @returns
|
1350
|
+
*/
|
1351
|
+
function elementIsOverfilling(element, currMeiClone) {
|
1352
|
+
if (!overfillMeasure) {
|
1353
|
+
var newMeasureRatio = getAbsoluteRatio(element.closest("layer"));
|
1354
|
+
var localRatio = getMeterRatioLocal(currMeiClone, element);
|
1355
|
+
if (newMeasureRatio > localRatio) { //getMeterRatioGlobal(currMeiClone)){
|
1356
|
+
return true;
|
1357
|
+
}
|
1358
|
+
}
|
1359
|
+
return false;
|
1360
|
+
}
|
1361
|
+
exports.elementIsOverfilling = elementIsOverfilling;
|
1362
|
+
/**
|
1363
|
+
* Clean up mei after changing values
|
1364
|
+
* @param currentMEI
|
1365
|
+
*/
|
1366
|
+
function cleanUp(currentMEI) {
|
1367
|
+
deleteDefSequences(currentMEI);
|
1368
|
+
reorganizeBeams(currentMEI);
|
1369
|
+
removeEmptyElements(currentMEI);
|
1370
|
+
adjustRests(currentMEI);
|
1371
|
+
redistributeHarms(currentMEI);
|
1372
|
+
}
|
1373
|
+
exports.cleanUp = cleanUp;
|
1374
|
+
/**
|
1375
|
+
* Delete all redundant definition sequences in staffDefs and layers
|
1376
|
+
* @param currentMEI
|
1377
|
+
*/
|
1378
|
+
function deleteDefSequences(currentMEI) {
|
1379
|
+
var staffCount = currentMEI.querySelectorAll("staffDef").length;
|
1380
|
+
for (var i = 0; i < staffCount; i++) {
|
1381
|
+
var n = (i + 1).toString();
|
1382
|
+
var prevElement = null;
|
1383
|
+
var prevShape = null;
|
1384
|
+
var prevLine = null;
|
1385
|
+
currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] clef, staff[n=\"" + n + "\"] clef").forEach(clefElement => {
|
1386
|
+
var shape = clefElement.getAttribute("shape");
|
1387
|
+
var line = clefElement.getAttribute("line");
|
1388
|
+
if (prevElement != null) {
|
1389
|
+
prevShape = prevElement.getAttribute("shape");
|
1390
|
+
prevLine = prevElement.getAttribute("line");
|
1391
|
+
if (prevShape === shape && prevLine === line) {
|
1392
|
+
clefElement.remove();
|
1393
|
+
}
|
1394
|
+
else {
|
1395
|
+
prevElement = clefElement;
|
1396
|
+
}
|
1397
|
+
}
|
1398
|
+
else {
|
1399
|
+
prevElement = clefElement;
|
1400
|
+
}
|
1401
|
+
});
|
1402
|
+
prevElement = null;
|
1403
|
+
var prevSig = null;
|
1404
|
+
currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] keySig, staff[n=\"" + n + "\"] keySig").forEach(sigElement => {
|
1405
|
+
var sig = sigElement.getAttribute("sig");
|
1406
|
+
if (prevElement != null) {
|
1407
|
+
prevSig = prevElement.getAttribute("sig");
|
1408
|
+
if (prevSig === sig) {
|
1409
|
+
sigElement.remove();
|
1410
|
+
}
|
1411
|
+
else {
|
1412
|
+
prevElement = sigElement;
|
1413
|
+
}
|
1414
|
+
}
|
1415
|
+
else {
|
1416
|
+
prevElement = sigElement;
|
1417
|
+
}
|
1418
|
+
});
|
1419
|
+
prevElement = null;
|
1420
|
+
currentMEI.querySelectorAll("staffDef[n=\"" + n + "\"] meterSig, staff[n=\"" + n + "\"] meterSig").forEach(meterElement => {
|
1421
|
+
var count = meterElement.getAttribute("count");
|
1422
|
+
var unit = meterElement.getAttribute("unit");
|
1423
|
+
if (prevElement != null) {
|
1424
|
+
var lastCount = prevElement.getAttribute("count");
|
1425
|
+
var lastUnit = prevElement.getAttribute("unit");
|
1426
|
+
if (lastCount === count && lastUnit === unit) {
|
1427
|
+
meterElement.remove();
|
1428
|
+
}
|
1429
|
+
else {
|
1430
|
+
prevElement = meterElement;
|
1431
|
+
}
|
1432
|
+
}
|
1433
|
+
else {
|
1434
|
+
prevElement = meterElement;
|
1435
|
+
}
|
1436
|
+
});
|
1437
|
+
}
|
1438
|
+
}
|
1439
|
+
function reorganizeBeams(currentMEI) {
|
1440
|
+
// if beams have elements, which shouldn be there
|
1441
|
+
currentMEI.querySelectorAll("beam").forEach(b => {
|
1442
|
+
var beamNotes = Array.from(b.children);
|
1443
|
+
if (!beamNotes.every(c => parseInt(c.getAttribute("dur")) >= 8) && beamNotes.length > 0) {
|
1444
|
+
beamNotes.forEach(n => {
|
1445
|
+
if (parseInt(n.getAttribute("dur")) >= 8) {
|
1446
|
+
if (n.previousElementSibling !== null) {
|
1447
|
+
if (n.previousElementSibling.tagName === "beam") { // check for previous beams to merge with
|
1448
|
+
n.previousElementSibling.appendChild(n);
|
1449
|
+
}
|
1450
|
+
}
|
1451
|
+
else { // else make new beam
|
1452
|
+
var newBeam = currentMEI.createElementNS(constants_1.constants._MEINS_, "beam");
|
1453
|
+
newBeam.setAttribute("id", random_1.uuidv4());
|
1454
|
+
n.parentElement.insertBefore(newBeam, n);
|
1455
|
+
newBeam.append(n);
|
1456
|
+
}
|
1457
|
+
}
|
1458
|
+
});
|
1459
|
+
//set all inner elements outseide of old beam
|
1460
|
+
b.outerHTML = b.innerHTML;
|
1461
|
+
}
|
1462
|
+
});
|
1463
|
+
}
|
1464
|
+
/**
|
1465
|
+
* After manipulating elements in the score, some elements could be empty
|
1466
|
+
* @param currentMEI
|
1467
|
+
*/
|
1468
|
+
function removeEmptyElements(currentMEI) {
|
1469
|
+
Array.from(currentMEI.querySelectorAll("beam")).forEach(b => {
|
1470
|
+
var _a;
|
1471
|
+
if (b.childElementCount === 0) {
|
1472
|
+
(_a = currentMEI.getElementById(b.id)) === null || _a === void 0 ? void 0 : _a.remove();
|
1473
|
+
}
|
1474
|
+
if (b.childElementCount === 1) {
|
1475
|
+
//b.parentElement.insertBefore(b, b.firstChild)
|
1476
|
+
//b.remove()
|
1477
|
+
b.outerHTML = b.innerHTML;
|
1478
|
+
}
|
1479
|
+
var bArr = Array.from(b.children);
|
1480
|
+
if (bArr.every(c => c.tagName === "rest") && bArr.length > 0) {
|
1481
|
+
// Array.from(b.children).forEach(c => {
|
1482
|
+
// b.parentElement.insertBefore(c, b)
|
1483
|
+
// })
|
1484
|
+
// b.remove()
|
1485
|
+
b.outerHTML = b.innerHTML;
|
1486
|
+
}
|
1487
|
+
// Avoids that unvalid rests will be displayed as double full notes
|
1488
|
+
Array.from(currentMEI.querySelectorAll("rest")).forEach(r => {
|
1489
|
+
if (r.getAttribute("dur") === "0" || r.getAttribute("dur") === null) {
|
1490
|
+
r.removeAttribute("dur");
|
1491
|
+
r.removeAttribute("dots");
|
1492
|
+
r.outerHTML = r.outerHTML.replace("rest>", "mRest>");
|
1493
|
+
}
|
1494
|
+
});
|
1495
|
+
Array.from(currentMEI.querySelectorAll("*[xmlns]")).forEach(x => {
|
1496
|
+
var attr = x.getAttribute("xmlns");
|
1497
|
+
if (attr === "" || attr === null || attr == undefined) {
|
1498
|
+
x.removeAttribute("xmlns");
|
1499
|
+
}
|
1500
|
+
});
|
1501
|
+
});
|
1502
|
+
// allow no empty and rest-note element chord elements
|
1503
|
+
Array.from(currentMEI.querySelectorAll("chord")).forEach(c => {
|
1504
|
+
if (c.childElementCount === 0) {
|
1505
|
+
currentMEI.getElementById(c.id).remove();
|
1506
|
+
}
|
1507
|
+
else if (c.childElementCount === 1) {
|
1508
|
+
c.outerHTML = c.innerHTML;
|
1509
|
+
}
|
1510
|
+
else if (c.childElementCount === 2 && c.querySelector("rest") !== null && c.querySelector("note") !== null) {
|
1511
|
+
c.querySelector("note").setAttribute("dur", c.getAttribute("dur"));
|
1512
|
+
c.querySelector("rest").remove();
|
1513
|
+
c.outerHTML = c.innerHTML;
|
1514
|
+
}
|
1515
|
+
});
|
1516
|
+
// Empty harms could be somewhere
|
1517
|
+
Array.from(currentMEI.querySelectorAll("harm")).forEach(h => {
|
1518
|
+
if (h.childElementCount > 0) {
|
1519
|
+
if (h.firstElementChild.childElementCount === 0)
|
1520
|
+
h.remove();
|
1521
|
+
}
|
1522
|
+
else if (h.textContent === "") {
|
1523
|
+
h.remove();
|
1524
|
+
}
|
1525
|
+
});
|
1526
|
+
// remove all xmlns since they are someties empty and also are not parsable sometimes
|
1527
|
+
Array.from(currentMEI.querySelectorAll("*")).forEach(el => {
|
1528
|
+
if (el.tagName.toLowerCase() === "xml")
|
1529
|
+
return;
|
1530
|
+
el.removeAttribute("xmlns");
|
1531
|
+
});
|
1532
|
+
// Array.from(currentMEI .querySelectorAll("measure")).forEach(m => {
|
1533
|
+
// if(m.querySelectorAll("note, chord").length === 0){
|
1534
|
+
// currentMEI .getElementById(m.id).remove()
|
1535
|
+
// }
|
1536
|
+
// })
|
1537
|
+
}
|
1538
|
+
/**
|
1539
|
+
* Apply some additional rules for rests, Elements where added
|
1540
|
+
* @param currentMEI
|
1541
|
+
*/
|
1542
|
+
function adjustRests(currentMEI) {
|
1543
|
+
//mRest and any Element with dur attribute are not allowed in the same layer
|
1544
|
+
currentMEI.querySelectorAll("layer").forEach(l => {
|
1545
|
+
var hasAnyDurAttributes = l.querySelectorAll("*[dur]").length > 0;
|
1546
|
+
var hasTags = l.querySelectorAll("clef, keySig").length > 0;
|
1547
|
+
var hasMrest = l.querySelectorAll("mRest").length > 0;
|
1548
|
+
if (l.children.length === 0 || (hasTags && !hasAnyDurAttributes && !hasMrest)) {
|
1549
|
+
//no layer should be empty, has at least an mRest (therefore: mRests are virtually not deletable)
|
1550
|
+
var newMrest = new mei_template_1.default().createMRest();
|
1551
|
+
l.append(newMrest);
|
1552
|
+
}
|
1553
|
+
else {
|
1554
|
+
Array.from(l.children).forEach(cn => {
|
1555
|
+
if (cn.tagName === "mRest" && hasAnyDurAttributes) {
|
1556
|
+
cn.remove();
|
1557
|
+
}
|
1558
|
+
});
|
1559
|
+
}
|
1560
|
+
});
|
1561
|
+
}
|
1562
|
+
/**
|
1563
|
+
* Give harm new start id if related note was included in chord during process
|
1564
|
+
* @param currentMEI
|
1565
|
+
*/
|
1566
|
+
function redistributeHarms(currentMEI) {
|
1567
|
+
currentMEI.querySelectorAll("harm").forEach(h => {
|
1568
|
+
var startid = h.getAttribute("startid");
|
1569
|
+
if (startid !== null) {
|
1570
|
+
var note = currentMEI.getElementById(startid);
|
1571
|
+
if ((note === null || note === void 0 ? void 0 : note.parentElement.tagName) === "chord")
|
1572
|
+
h.setAttribute("startid", note.parentElement.id);
|
1573
|
+
}
|
1574
|
+
});
|
1575
|
+
}
|
1576
|
+
/**
|
1577
|
+
* Remove tie from all layers if length of layer exceeds global Ratio
|
1578
|
+
* @param currentMEI
|
1579
|
+
*/
|
1580
|
+
function removeTiesFromDoc(currentMEI) {
|
1581
|
+
var globalRatio = getMeterRatioGlobal(currentMEI);
|
1582
|
+
currentMEI.querySelectorAll("layer").forEach(l => {
|
1583
|
+
var layerRatio = getAbsoluteRatio(l);
|
1584
|
+
if (layerRatio > globalRatio) {
|
1585
|
+
var m = l.closest("measure");
|
1586
|
+
m.querySelectorAll("tie").forEach(t => {
|
1587
|
+
var _a;
|
1588
|
+
(_a = l.querySelector(t.getAttribute("endid"))) === null || _a === void 0 ? void 0 : _a.remove();
|
1589
|
+
t.remove();
|
1590
|
+
});
|
1591
|
+
}
|
1592
|
+
});
|
1593
|
+
}
|
1594
|
+
function addMeasure(currentMEI) {
|
1595
|
+
var lastMeasure = Array.from(currentMEI.querySelectorAll("measure")).reverse()[0];
|
1596
|
+
var staffCounts = Array.from(lastMeasure.querySelectorAll("staff")).map(s => { return parseInt(s.getAttribute("n")); });
|
1597
|
+
var staffCount = Math.max.apply(Math, staffCounts);
|
1598
|
+
var layerCounts = Array.from(lastMeasure.querySelectorAll("layer")).map(s => { return parseInt(s.getAttribute("n")); });
|
1599
|
+
var layerCount = Math.max.apply(Math, layerCounts);
|
1600
|
+
var newMeasure = new mei_template_1.default().createMeasure(1, staffCount, layerCount);
|
1601
|
+
lastMeasure.parentElement.append(newMeasure);
|
1602
|
+
var i = 1;
|
1603
|
+
currentMEI.querySelectorAll("measure").forEach(m => {
|
1604
|
+
m.setAttribute("n", i.toString());
|
1605
|
+
i++;
|
1606
|
+
});
|
1607
|
+
newMeasure.setAttribute("id", random_1.uuidv4());
|
1608
|
+
newMeasure.querySelectorAll("*").forEach(el => {
|
1609
|
+
if (el.id === null || el.id === "") {
|
1610
|
+
el.setAttribute("id", random_1.uuidv4());
|
1611
|
+
}
|
1612
|
+
});
|
1613
|
+
cleanUp(currentMEI);
|
1614
|
+
}
|
1615
|
+
exports.addMeasure = addMeasure;
|
1616
|
+
function removeMeasure(currentMEI) {
|
1617
|
+
var measures = Array.from(currentMEI.querySelectorAll("measure")).reverse();
|
1618
|
+
if (measures.length > 1) {
|
1619
|
+
measures[0].remove();
|
1620
|
+
}
|
1621
|
+
else {
|
1622
|
+
measures[0].querySelectorAll("layer").forEach(l => {
|
1623
|
+
l.innerHTML = "";
|
1624
|
+
l.appendChild(currentMEI.createElement("mRest"));
|
1625
|
+
});
|
1626
|
+
}
|
1627
|
+
cleanUp(currentMEI);
|
1628
|
+
}
|
1629
|
+
exports.removeMeasure = removeMeasure;
|
1630
|
+
function addStaff(currentMEI, referenceStaff, relPos) {
|
1631
|
+
var staffNum = referenceStaff.getAttribute("n");
|
1632
|
+
var refn;
|
1633
|
+
var refElement;
|
1634
|
+
currentMEI.querySelectorAll("staff[n=\"" + staffNum + "\"]").forEach(s => {
|
1635
|
+
var _a;
|
1636
|
+
var newStaff = new mei_template_1.default().createStaff(1, 1);
|
1637
|
+
switch (relPos) {
|
1638
|
+
case "above":
|
1639
|
+
refElement = s;
|
1640
|
+
break;
|
1641
|
+
case "below":
|
1642
|
+
refElement = s.nextElementSibling || s;
|
1643
|
+
break;
|
1644
|
+
default:
|
1645
|
+
console.error(relPos, " was never an option");
|
1646
|
+
}
|
1647
|
+
if (relPos === "below" && refElement === s) { // => new staff at the end
|
1648
|
+
s.parentElement.append(newStaff);
|
1649
|
+
}
|
1650
|
+
else {
|
1651
|
+
s.parentElement.insertBefore(newStaff, refElement);
|
1652
|
+
}
|
1653
|
+
//copy elements from the current Staff that have to appear in new staff
|
1654
|
+
var newLayer = newStaff.querySelector("layer");
|
1655
|
+
var copyMeter = (_a = s.querySelector("meterSig")) === null || _a === void 0 ? void 0 : _a.cloneNode(true);
|
1656
|
+
if (copyMeter != undefined && copyMeter !== null) {
|
1657
|
+
newLayer.insertBefore(copyMeter, newLayer.firstChild);
|
1658
|
+
}
|
1659
|
+
refn = (refElement === null || refElement === void 0 ? void 0 : refElement.getAttribute("n")) || staffNum; // s.getAttribute("n")
|
1660
|
+
});
|
1661
|
+
//new StaffDef
|
1662
|
+
var refStaffDef = currentMEI.querySelector("staffDef[n=\"" + refn + "\"]");
|
1663
|
+
var refCopy = refStaffDef.cloneNode(true);
|
1664
|
+
refCopy.querySelectorAll("*[id]").forEach(i => {
|
1665
|
+
i.removeAttribute("id");
|
1666
|
+
});
|
1667
|
+
refStaffDef.parentElement.insertBefore(refCopy, refStaffDef);
|
1668
|
+
currentMEI.querySelectorAll("measure").forEach(m => {
|
1669
|
+
var i = 1;
|
1670
|
+
m.querySelectorAll("staff").forEach(s => {
|
1671
|
+
s.setAttribute("n", i.toString());
|
1672
|
+
i++;
|
1673
|
+
});
|
1674
|
+
});
|
1675
|
+
var i = 1;
|
1676
|
+
currentMEI.querySelectorAll("staffDef").forEach(sd => {
|
1677
|
+
sd.setAttribute("n", i.toString());
|
1678
|
+
i++;
|
1679
|
+
});
|
1680
|
+
cleanUp(currentMEI);
|
1681
|
+
}
|
1682
|
+
exports.addStaff = addStaff;
|
1683
|
+
function removeStaff(currentMEI, referenceStaff, relPos) {
|
1684
|
+
var staff = currentMEI.getElementById(referenceStaff.id);
|
1685
|
+
var staffNum = staff.getAttribute("n");
|
1686
|
+
var refn;
|
1687
|
+
currentMEI.querySelectorAll("staff[n=\"" + staffNum + "\"]").forEach(s => {
|
1688
|
+
switch (relPos) {
|
1689
|
+
case "above":
|
1690
|
+
refn = s.previousElementSibling.getAttribute("n");
|
1691
|
+
s.previousElementSibling.remove();
|
1692
|
+
break;
|
1693
|
+
case "below":
|
1694
|
+
refn = s.nextElementSibling.getAttribute("n");
|
1695
|
+
s.nextElementSibling.remove();
|
1696
|
+
break;
|
1697
|
+
default:
|
1698
|
+
console.error(relPos, " was never an option");
|
1699
|
+
}
|
1700
|
+
});
|
1701
|
+
currentMEI.querySelector("staffDef[n=\"" + refn + "\"]").remove();
|
1702
|
+
currentMEI.querySelectorAll("measure").forEach(m => {
|
1703
|
+
var i = 1;
|
1704
|
+
m.querySelectorAll("staff").forEach(s => {
|
1705
|
+
s.setAttribute("n", i.toString());
|
1706
|
+
i++;
|
1707
|
+
});
|
1708
|
+
});
|
1709
|
+
var i = 1;
|
1710
|
+
currentMEI.querySelectorAll("staffDef").forEach(sd => {
|
1711
|
+
sd.setAttribute("n", i.toString());
|
1712
|
+
i++;
|
1713
|
+
});
|
1714
|
+
cleanUp(currentMEI);
|
1715
|
+
}
|
1716
|
+
exports.removeStaff = removeStaff;
|
1717
|
+
/**
|
1718
|
+
* Create a tuplet out of given Elements.
|
1719
|
+
* Tuplet duration and kind will be derived from number and attributes of the given notes.
|
1720
|
+
* E.g.: Selecting 3 Notes will make a triplet, 4 Notes will make a quadruplet, etc. The after the tuplet the missing durations will be added as rests
|
1721
|
+
* @param meiElements
|
1722
|
+
*/
|
1723
|
+
function createTuplet(meiElements, currentMEI) {
|
1724
|
+
meiElements = meiElements.filter(me => {
|
1725
|
+
if (me != undefined) {
|
1726
|
+
return me.getAttribute("dur") !== null;
|
1727
|
+
}
|
1728
|
+
return;
|
1729
|
+
});
|
1730
|
+
console.log("create tuplets:", meiElements);
|
1731
|
+
var arrCopy = [...meiElements];
|
1732
|
+
var minDurElement = arrCopy.sort((a, b) => parseFloat(b.getAttribute("dur")) - parseFloat(a.getAttribute("dur")))[0];
|
1733
|
+
var ratioTotal = 0;
|
1734
|
+
meiElements.forEach(me => ratioTotal += getAbsoluteRatio(me));
|
1735
|
+
var ratioMinElement = getAbsoluteRatio(minDurElement);
|
1736
|
+
var numMinNotes = (ratioTotal / ratioMinElement);
|
1737
|
+
var numTarget = numMinNotes - 1;
|
1738
|
+
var ratio = numTarget * getAbsoluteRatio(minDurElement);
|
1739
|
+
var [dur, dots] = ratioToDur(ratio);
|
1740
|
+
var tuplet = currentMEI.createElement("tuplet");
|
1741
|
+
tuplet.setAttribute("dur", dur.toString());
|
1742
|
+
if (dots > 0) {
|
1743
|
+
tuplet.setAttribute("dots.ges", dots.toString());
|
1744
|
+
}
|
1745
|
+
tuplet.setAttribute("num", numMinNotes.toString());
|
1746
|
+
tuplet.setAttribute("numbase", numTarget.toString());
|
1747
|
+
currentMEI.querySelector("#" + meiElements[0].id).insertAdjacentElement("beforebegin", tuplet);
|
1748
|
+
meiElements.forEach(me => tuplet.append(me));
|
1749
|
+
// fill missing values with rest
|
1750
|
+
var [durRest, dotsRest] = ratioToDur((numMinNotes * getAbsoluteRatio(minDurElement)) - ratio);
|
1751
|
+
if (ratio < 1) {
|
1752
|
+
if (durRest > 0) {
|
1753
|
+
var rest = currentMEI.createElement("rest");
|
1754
|
+
rest.setAttribute("id", random_1.uuidv4());
|
1755
|
+
rest.setAttribute("dur", durRest.toString());
|
1756
|
+
if (dotsRest > 0) {
|
1757
|
+
rest.setAttribute("dots", dotsRest.toString());
|
1758
|
+
}
|
1759
|
+
tuplet.insertAdjacentElement("afterend", rest);
|
1760
|
+
}
|
1761
|
+
}
|
1762
|
+
}
|
1763
|
+
exports.createTuplet = createTuplet;
|
1764
|
+
/**
|
1765
|
+
* Paste copied ids. First position to which the Elements are copied is the Element according to the refId (= RefElement).
|
1766
|
+
* If multiple staffs are copied, overhanging staffs will be pasted to the staffs below the staff of the RefElement, if definedstaffs exist.
|
1767
|
+
* Else these copiedId will be not pasted.
|
1768
|
+
* @param ids
|
1769
|
+
* @param refId
|
1770
|
+
*/
|
1771
|
+
function paste(ids, refId, currentMEI) {
|
1772
|
+
var _a;
|
1773
|
+
//ordered by staff
|
1774
|
+
var meiElements = new Array();
|
1775
|
+
ids.forEach(id => {
|
1776
|
+
var el = currentMEI.getElementById(id);
|
1777
|
+
//order copiable elements by staff
|
1778
|
+
if (["CHORD", "NOTE", "REST"].includes(el === null || el === void 0 ? void 0 : el.tagName.toUpperCase())) {
|
1779
|
+
if (!(el.tagName.toUpperCase() === "NOTE" && el.closest("chord") !== null)) {
|
1780
|
+
var staff = el.closest("staff");
|
1781
|
+
var num = parseInt(staff.getAttribute("n")) - 1;
|
1782
|
+
if (meiElements[num] == undefined) {
|
1783
|
+
meiElements[num] = new Array();
|
1784
|
+
}
|
1785
|
+
var cel = el.cloneNode(true);
|
1786
|
+
cel.setAttribute("id", random_1.uuidv4());
|
1787
|
+
meiElements[num].push(cel);
|
1788
|
+
}
|
1789
|
+
}
|
1790
|
+
});
|
1791
|
+
var refElement = currentMEI.getElementById(refId);
|
1792
|
+
refElement = (refElement === null || refElement === void 0 ? void 0 : refElement.closest("chord")) || refElement;
|
1793
|
+
var refStaff = refElement === null || refElement === void 0 ? void 0 : refElement.closest("staff");
|
1794
|
+
var refLayer = refElement === null || refElement === void 0 ? void 0 : refElement.closest("layer");
|
1795
|
+
var refMeasure = refElement === null || refElement === void 0 ? void 0 : refElement.closest("measure");
|
1796
|
+
var currentMeasure;
|
1797
|
+
let anyNew;
|
1798
|
+
//console.log(...meiElements)
|
1799
|
+
meiElements.forEach((staff, staffIdx) => {
|
1800
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
1801
|
+
if (refElement === null)
|
1802
|
+
return;
|
1803
|
+
currentMeasure = refElement.closest("measure");
|
1804
|
+
staff.forEach((element, elementIdx) => {
|
1805
|
+
var _a, _b;
|
1806
|
+
if (["NOTE", "REST"].includes(element.tagName.toUpperCase())) {
|
1807
|
+
var newNote = convertToNewNote(element);
|
1808
|
+
newNote.nearestNoteId = refElement.id;
|
1809
|
+
newNote.relPosX = "right";
|
1810
|
+
anyNew = newNote;
|
1811
|
+
}
|
1812
|
+
else if (element.tagName.toUpperCase() === "CHORD") {
|
1813
|
+
var newChord = convertToNewChord(element);
|
1814
|
+
newChord.nearestNoteId = refElement.id;
|
1815
|
+
newChord.relPosX = "right";
|
1816
|
+
anyNew = newChord;
|
1817
|
+
var elementArr = Array.from(element.querySelectorAll("note"));
|
1818
|
+
}
|
1819
|
+
var replace = ((_b = (_a = document.querySelector(".activeContainer")) === null || _a === void 0 ? void 0 : _a.querySelector("#insertToggle")) === null || _b === void 0 ? void 0 : _b.checked) || true;
|
1820
|
+
addToMEI(anyNew, currentMEI, replace);
|
1821
|
+
refElement = convertToElement(anyNew, currentMEI); //element
|
1822
|
+
});
|
1823
|
+
//when changing next staff, refElement musst be staff + 1
|
1824
|
+
var targetStaffN = (_c = (parseInt((_b = (_a = currentMEI.getElementById(refElement.id)) === null || _a === void 0 ? void 0 : _a.closest("staff")) === null || _b === void 0 ? void 0 : _b.getAttribute("n")) + 1)) === null || _c === void 0 ? void 0 : _c.toString();
|
1825
|
+
var refLayerN = (_e = (_d = currentMEI.getElementById(refElement.id)) === null || _d === void 0 ? void 0 : _d.closest("layer")) === null || _e === void 0 ? void 0 : _e.getAttribute("n");
|
1826
|
+
var refMeasureN = (_g = (_f = currentMEI.getElementById(refElement.id)) === null || _f === void 0 ? void 0 : _f.closest("measure")) === null || _g === void 0 ? void 0 : _g.getAttribute("n");
|
1827
|
+
refElement = currentMEI.querySelector("measure[n=\"" + refMeasureN + "\"] > staff[n=\"" + targetStaffN + "\"] > layer[n=\"" + refLayerN + "\"]");
|
1828
|
+
});
|
1829
|
+
//Element gets replaced in all other modes except keymode/textmode
|
1830
|
+
if (!document.querySelector(".activeContainer").classList.contains("textmode") && ((_a = currentMEI.getElementById(refId)) === null || _a === void 0 ? void 0 : _a.tagName) !== "LAYER") {
|
1831
|
+
removeFromMEI([currentMEI.getElementById(refId)], currentMEI);
|
1832
|
+
}
|
1833
|
+
return anyNew === null || anyNew === void 0 ? void 0 : anyNew.id;
|
1834
|
+
}
|
1835
|
+
exports.paste = paste;
|
1836
|
+
/**
|
1837
|
+
* Replace clef in main/ first score definition
|
1838
|
+
* @param targetid
|
1839
|
+
* @param newClef
|
1840
|
+
* @param currentMEI
|
1841
|
+
* @returns
|
1842
|
+
*/
|
1843
|
+
function replaceClefinScoreDef(target, newClef, currentMEI) {
|
1844
|
+
var staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
|
1845
|
+
var staffDefClef = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"] > clef");
|
1846
|
+
staffDefClef.setAttribute("shape", newClef.charAt(0));
|
1847
|
+
staffDefClef.setAttribute("line", mappings_1.clefToLine.get(newClef.charAt(0)));
|
1848
|
+
cleanUp(currentMEI);
|
1849
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1850
|
+
return currentMEI;
|
1851
|
+
}
|
1852
|
+
exports.replaceClefinScoreDef = replaceClefinScoreDef;
|
1853
|
+
/**
|
1854
|
+
* Layer to which a new clef object has to be inserted
|
1855
|
+
* @param targetid Usually a barline before which new clef should stand
|
1856
|
+
* @param newClef Name of new Clef to be inserted
|
1857
|
+
*/
|
1858
|
+
function insertClef(target, newClef, currentMEI) {
|
1859
|
+
var _a, _b;
|
1860
|
+
var targetStaffId = ((_a = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]")) === null || _a === void 0 ? void 0 : _a.id) || ((_b = target.closest(".staff")) === null || _b === void 0 ? void 0 : _b.id);
|
1861
|
+
var targetLayerId = currentMEI.getElementById(targetStaffId).querySelector("layer").id;
|
1862
|
+
currentMEI.getElementById(targetLayerId).querySelectorAll("clef").forEach(c => c.remove());
|
1863
|
+
var clefElement = currentMEI.createElement("clef");
|
1864
|
+
clefElement.setAttribute("id", random_1.uuidv4());
|
1865
|
+
clefElement.setAttribute("shape", newClef.charAt(0));
|
1866
|
+
clefElement.setAttribute("line", mappings_1.clefToLine.get(newClef.charAt(0)));
|
1867
|
+
currentMEI.getElementById(targetLayerId).append(clefElement);
|
1868
|
+
cleanUp(currentMEI);
|
1869
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1870
|
+
return currentMEI;
|
1871
|
+
}
|
1872
|
+
exports.insertClef = insertClef;
|
1873
|
+
function findAttributeRecursive(element, attributeName, currentValue = null) {
|
1874
|
+
var value = currentValue || element.getAttribute(attributeName);
|
1875
|
+
if (value === null) {
|
1876
|
+
Array.from(element.children).forEach(c => {
|
1877
|
+
if (value !== null)
|
1878
|
+
return;
|
1879
|
+
value = findAttributeRecursive(c, attributeName, value);
|
1880
|
+
});
|
1881
|
+
}
|
1882
|
+
console.log(element, value);
|
1883
|
+
return value;
|
1884
|
+
}
|
1885
|
+
/**
|
1886
|
+
* If Key is already defined in scoreDef, replace values
|
1887
|
+
* @param target
|
1888
|
+
* @param newSig
|
1889
|
+
* @param currentMEI
|
1890
|
+
* @returns
|
1891
|
+
*/
|
1892
|
+
function replaceKeyInScoreDef(target, newSig, currentMEI) {
|
1893
|
+
var _a;
|
1894
|
+
var staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
|
1895
|
+
var staffDefSig = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"] > keySig");
|
1896
|
+
if (staffDefSig !== null) {
|
1897
|
+
staffDefSig.setAttribute("sig", mappings_1.keyIdToSig.get(newSig));
|
1898
|
+
}
|
1899
|
+
else {
|
1900
|
+
var newSigElement = new mei_template_1.default().createKeySig("major", mappings_1.keyIdToSig.get(newSig));
|
1901
|
+
(_a = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"]")) === null || _a === void 0 ? void 0 : _a.append(newSigElement);
|
1902
|
+
}
|
1903
|
+
adjustAccids(currentMEI);
|
1904
|
+
cleanUp(currentMEI);
|
1905
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1906
|
+
return currentMEI;
|
1907
|
+
}
|
1908
|
+
exports.replaceKeyInScoreDef = replaceKeyInScoreDef;
|
1909
|
+
/**
|
1910
|
+
* Create a whole new Sig Element and Insert to MEI at given target
|
1911
|
+
* @param target
|
1912
|
+
* @param newSig
|
1913
|
+
* @param currentMEI
|
1914
|
+
* @returns
|
1915
|
+
*/
|
1916
|
+
function insertKey(target, newSig, currentMEI) {
|
1917
|
+
var _a, _b;
|
1918
|
+
console.log("insertKey", target, newSig);
|
1919
|
+
var targetStaff = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]") || target.closest(".staff");
|
1920
|
+
var staffN = targetStaff.getAttribute("n");
|
1921
|
+
var parentMeasure = currentMEI.getElementById(targetStaff.id).closest("measure");
|
1922
|
+
var pmn = parseInt(parentMeasure.getAttribute("n")) + 1;
|
1923
|
+
var targetLayerId = (_a = parentMeasure.parentElement.querySelector("measure[n=\"" + pmn.toString() + "\"] > staff[n=\"" + staffN + "\"] > layer")) === null || _a === void 0 ? void 0 : _a.id;
|
1924
|
+
(_b = currentMEI.getElementById(targetLayerId).querySelectorAll("keySig")) === null || _b === void 0 ? void 0 : _b.forEach(c => c.remove());
|
1925
|
+
var newSigElement = new mei_template_1.default().createKeySig("major", mappings_1.keyIdToSig.get(newSig));
|
1926
|
+
currentMEI.getElementById(targetLayerId).insertBefore(newSigElement, currentMEI.getElementById(targetLayerId).firstElementChild);
|
1927
|
+
adjustAccids(currentMEI);
|
1928
|
+
cleanUp(currentMEI);
|
1929
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1930
|
+
return currentMEI;
|
1931
|
+
}
|
1932
|
+
exports.insertKey = insertKey;
|
1933
|
+
function replaceMeterInScoreDef(target, currentMEI) {
|
1934
|
+
var staffN = document.querySelector(".activeContainer #vrvSVG #" + target.id).closest(".staff").getAttribute("n");
|
1935
|
+
var staffDefMeter = currentMEI.querySelector("staffDef[n=\"" + staffN + "\"]");
|
1936
|
+
var count = document.querySelector(".activeContainer #timeCount").value;
|
1937
|
+
var unit = document.querySelector(".activeContainer #timeUnit").value;
|
1938
|
+
staffDefMeter.setAttribute("meter.count", count);
|
1939
|
+
staffDefMeter.setAttribute("meter.unit", unit);
|
1940
|
+
cleanUp(currentMEI);
|
1941
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1942
|
+
return currentMEI;
|
1943
|
+
}
|
1944
|
+
exports.replaceMeterInScoreDef = replaceMeterInScoreDef;
|
1945
|
+
function insertMeter(target, currentMEI) {
|
1946
|
+
var targetStaff = target.closest(".measure").querySelector(".staff[n=\"" + target.getAttribute("n") + "\"]") || target.closest(".staff");
|
1947
|
+
var parentMeasure = currentMEI.getElementById(targetStaff.id).closest("measure");
|
1948
|
+
var pmn = parseInt(parentMeasure.getAttribute("n")) + 1;
|
1949
|
+
var targetLayers = parentMeasure.parentElement.querySelectorAll("measure[n=\"" + pmn.toString() + "\"] layer");
|
1950
|
+
targetLayers.forEach(tl => {
|
1951
|
+
var _a;
|
1952
|
+
(_a = currentMEI.getElementById(tl.id).querySelectorAll("meterSig")) === null || _a === void 0 ? void 0 : _a.forEach(c => c.remove());
|
1953
|
+
});
|
1954
|
+
var count = document.querySelector(".activeContainer #selectTime #timeCount").value;
|
1955
|
+
var unit = document.querySelector(".activeContainer #selectTime #timeUnit").value;
|
1956
|
+
// change for all layers in given measure
|
1957
|
+
targetLayers.forEach(tl => {
|
1958
|
+
let newMeterElement = new mei_template_1.default().createMeterSig(count, unit); // must be in loop, otherwise same reference gets reassigned every time
|
1959
|
+
currentMEI.getElementById(tl.id).insertBefore(newMeterElement, currentMEI.getElementById(tl.id).firstElementChild);
|
1960
|
+
});
|
1961
|
+
cleanUp(currentMEI);
|
1962
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1963
|
+
return currentMEI;
|
1964
|
+
}
|
1965
|
+
exports.insertMeter = insertMeter;
|
1966
|
+
function insertTempo(target, currentMEI) {
|
1967
|
+
var measure = currentMEI.getElementById(target.id).closest("measure");
|
1968
|
+
var existingTempo = measure.querySelectorAll("tempo");
|
1969
|
+
var sameTempos = Array.from(existingTempo).filter(et => {
|
1970
|
+
var hasSameTimeStamp = parseFloat(et.getAttribute("timeStamp")) === getElementTimestampById(target.id, currentMEI);
|
1971
|
+
var hasSameStartId = et.getAttribute("startId") === target.id;
|
1972
|
+
return hasSameTimeStamp || hasSameStartId;
|
1973
|
+
});
|
1974
|
+
var mmUnit = document.querySelector(".activeContainer #selectTempo #timeCount").value;
|
1975
|
+
var mm = document.querySelector(".activeContainer #selectTempo #timeUnit").value;
|
1976
|
+
measure.appendChild(new mei_template_1.default().createTempo(mm, mmUnit, getElementTimestampById(target.id, currentMEI).toString(), target.id));
|
1977
|
+
sameTempos.forEach(st => st.remove());
|
1978
|
+
cleanUp(currentMEI);
|
1979
|
+
currentMEI = meiConverter.restoreXmlIdTags(currentMEI);
|
1980
|
+
return currentMEI;
|
1981
|
+
}
|
1982
|
+
exports.insertTempo = insertTempo;
|
1983
|
+
/**
|
1984
|
+
* Gets timestamp of element. Computes it, if no such attribute is present for the element
|
1985
|
+
* @param id
|
1986
|
+
* @param currentMEI
|
1987
|
+
* @returns
|
1988
|
+
*/
|
1989
|
+
function getElementTimestampById(id, currentMEI) {
|
1990
|
+
var element = currentMEI.getElementById(id);
|
1991
|
+
var timestamp = element.getAttribute("tstamp");
|
1992
|
+
if (timestamp === null) {
|
1993
|
+
var parentLayer = element.closest("layer");
|
1994
|
+
var count = 0;
|
1995
|
+
var units = parentLayer.querySelectorAll(countableNoteUnitSelector);
|
1996
|
+
for (var i = 0; i < units.length; i++) {
|
1997
|
+
if (units[i].getAttribute("dur") !== null) {
|
1998
|
+
if (units[i].id === id) {
|
1999
|
+
var fraction = 4;
|
2000
|
+
if (currentMEI.querySelector("meterSig") !== null) {
|
2001
|
+
fraction = parseInt(currentMEI.querySelector("meterSig").getAttribute("unit"));
|
2002
|
+
}
|
2003
|
+
timestamp = (count * fraction + 1).toString(); // add 1 to accomodate for shift ratio sum
|
2004
|
+
break;
|
2005
|
+
}
|
2006
|
+
count += getAbsoluteRatio(units[i]);
|
2007
|
+
}
|
2008
|
+
}
|
2009
|
+
}
|
2010
|
+
return parseFloat(timestamp);
|
2011
|
+
}
|
2012
|
+
exports.getElementTimestampById = getElementTimestampById;
|
2013
|
+
/**
|
2014
|
+
* Get Timestamp in specific layer
|
2015
|
+
* @param note
|
2016
|
+
* @returns
|
2017
|
+
*/
|
2018
|
+
function getTimestamp(note) {
|
2019
|
+
var layer = note.closest("layer");
|
2020
|
+
var elements = Array.from(layer.querySelectorAll("*[dur]"));
|
2021
|
+
elements = elements.filter((v, i) => i <= elements.indexOf(note));
|
2022
|
+
var tstamp;
|
2023
|
+
elements.forEach(e => {
|
2024
|
+
var dur = parseInt(e.getAttribute("dur"));
|
2025
|
+
tstamp += 4 / dur;
|
2026
|
+
var dots = e.getAttribute("dots");
|
2027
|
+
var add = dur;
|
2028
|
+
if (dots !== null) {
|
2029
|
+
for (var i = 0; i < parseInt(dots); i++) {
|
2030
|
+
add = add / 2;
|
2031
|
+
tstamp += add;
|
2032
|
+
}
|
2033
|
+
}
|
2034
|
+
});
|
2035
|
+
return tstamp;
|
2036
|
+
}
|
2037
|
+
exports.getTimestamp = getTimestamp;
|
2038
|
+
function setArticulation(currentMEI, artic) {
|
2039
|
+
document.querySelectorAll(".activeContainer #vrvSVG :is(.chord.marked, .note.marked, .note.lastAdded").forEach(note => {
|
2040
|
+
var meiElement = currentMEI.querySelector("#" + note.id);
|
2041
|
+
if (meiElement === null)
|
2042
|
+
return;
|
2043
|
+
if (meiElement.tagName !== "note")
|
2044
|
+
return;
|
2045
|
+
if (meiElement.tagName === "note" && meiElement.parentElement.tagName === "chord") {
|
2046
|
+
meiElement = meiElement.parentElement;
|
2047
|
+
}
|
2048
|
+
if (meiElement.getAttribute("artic") === null || meiElement.getAttribute("artic") !== artic) {
|
2049
|
+
meiElement.setAttribute("artic", artic);
|
2050
|
+
}
|
2051
|
+
else {
|
2052
|
+
meiElement.removeAttribute("artic");
|
2053
|
+
}
|
2054
|
+
});
|
2055
|
+
return currentMEI;
|
2056
|
+
}
|
2057
|
+
exports.setArticulation = setArticulation;
|
2058
|
+
//PRIVATE
|
2059
|
+
function convertToNewNote(element) {
|
2060
|
+
var newNote = {
|
2061
|
+
id: random_1.uuidv4(),
|
2062
|
+
pname: element.getAttribute("pname"),
|
2063
|
+
dur: element.getAttribute("dur"),
|
2064
|
+
dots: element.getAttribute("dots"),
|
2065
|
+
oct: element.getAttribute("oct"),
|
2066
|
+
accid: element.getAttribute("accid") || element.getAttribute("accid.ges"),
|
2067
|
+
rest: element.tagName.toUpperCase() === "REST" ? true : false
|
2068
|
+
};
|
2069
|
+
return newNote;
|
2070
|
+
}
|
2071
|
+
function convertToElement(n, currentMEI) {
|
2072
|
+
var nn;
|
2073
|
+
var newElement;
|
2074
|
+
if (n.hasOwnProperty("pname")) {
|
2075
|
+
nn = n;
|
2076
|
+
newElement = currentMEI.createElement("note");
|
2077
|
+
newElement.setAttribute("pname", nn.pname);
|
2078
|
+
newElement.setAttribute("oct", nn.oct);
|
2079
|
+
newElement.setAttribute("accid", nn.accid);
|
2080
|
+
}
|
2081
|
+
else {
|
2082
|
+
nn = n;
|
2083
|
+
newElement = currentMEI.createElement("chord");
|
2084
|
+
nn.noteElements.forEach(ne => {
|
2085
|
+
newElement.append(convertToElement(ne, currentMEI));
|
2086
|
+
});
|
2087
|
+
}
|
2088
|
+
newElement.setAttribute("id", nn.id);
|
2089
|
+
newElement.setAttribute("dur", nn.dur);
|
2090
|
+
newElement.setAttribute("dots", nn.dots);
|
2091
|
+
return newElement;
|
2092
|
+
}
|
2093
|
+
function convertToNewChord(element) {
|
2094
|
+
var newNotes = Array.from(element.querySelectorAll("note")).map(n => {
|
2095
|
+
return convertToNewNote(n);
|
2096
|
+
});
|
2097
|
+
var newChord = {
|
2098
|
+
id: random_1.uuidv4(),
|
2099
|
+
dur: element.getAttribute("dur"),
|
2100
|
+
dots: element.getAttribute("dots"),
|
2101
|
+
noteElements: newNotes
|
2102
|
+
};
|
2103
|
+
return newChord;
|
2104
|
+
}
|
2105
|
+
function createNewRestElement(dur, dots = undefined) {
|
2106
|
+
var newElem = document.createElementNS(constants_1.constants._MEINS_, "rest");
|
2107
|
+
newElem.setAttribute("dur", dur.toString());
|
2108
|
+
if (dots != undefined)
|
2109
|
+
newElem.setAttribute("dots", dots.toString());
|
2110
|
+
newElem.setAttribute("id", random_1.uuidv4());
|
2111
|
+
return newElem;
|
2112
|
+
}
|