Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor #4042

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 41 additions & 108 deletions js/mxml.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,43 @@
/* exported saveMxmlOutput */

saveMxmlOutput = (logo) => {
// temporary until I get more things sorted out
const ignore = ["voice two", "voice one", "one voice"];
let res = "";
let indent = 0;

const add = (str) => {
for (let i = 0; i < indent; i++) {
res += " ";
}
res += str + "\n";
res += " ".repeat(indent) + str + "\n";
};

const addDirection = (type) => {
add('<direction placement="above">');
indent++;
add("<direction-type>");
indent++;
add(`<wedge type="${type}"/>`);
indent--;
add("</direction-type>");
indent--;
add("</direction>");
};

const addMeasureAttributes = (measure, div, beats, beatType) => {
add(`<measure number="${measure}"> <attributes> <divisions>${div}</divisions> <key> <fifths>0</fifths> </key> <time> <beats>${beats}</beats> <beat-type>${beatType}</beat-type> </time> <clef> <sign>G</sign> <line>2</line> </clef> </attributes>`);
};

add("<?xml version='1.0' encoding='UTF-8'?>");
add(
'<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">'
);
add('<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">');
add('<score-partwise version="3.1">');
indent++;
add("<part-list>");
indent++;

// Why is logo.notation.notationStaging an object and not an array?
Object.keys(logo.notation.notationStaging).forEach((voice) => {
if (logo.notation.notationStaging[voice].length === 0) {
return;
}
if (logo.notation.notationStaging[voice].length === 0) return;
voiceNum = parseInt(voice) + 1;
add('<score-part id="P' + voiceNum + '">');
add(`<score-part id="P${voiceNum}">`);
indent++;
add("<part-name> Voice #" + voiceNum + " </part-name>");
add(`<part-name> Voice #${voiceNum} </part-name>`);
indent--;
add("</score-part>");
});
Expand All @@ -51,71 +58,35 @@ saveMxmlOutput = (logo) => {
indent--;

Object.keys(logo.notation.notationStaging).forEach((voice) => {
if (logo.notation.notationStaging[voice].length === 0) {
return;
}
if (logo.notation.notationStaging[voice].length === 0) return;
voiceNum = parseInt(voice) + 1;
indent++;
add('<part id="P' + voiceNum + '">');
add(`<part id="P${voiceNum}">`);
indent++;

// assume 4/4 time, 32 divisions bc smallest note is 1/32
// key is C by default
let currMeasure = 1;
let divisions = 32;
let beats = 4;
let beatType = 4;
let beatsChanged = false;
let newDivisions = -1;
let newBeats = -1;
let newBeatType = -1;
let openedMeasureTag = false;
let queuedTempo = null;
let firstMeasure = true;
let currMeasure = 1, divisions = 32, beats = 4, beatType = 4;
let beatsChanged = false, newDivisions = -1, newBeats = -1, newBeatType = -1;
let openedMeasureTag = false, queuedTempo = null, firstMeasure = true;
indent++;
let divisionsLeft = divisions;
const notes = logo.notation.notationStaging[voice];

for (let i = 0; i < notes.length; i += 1) {
// obj = [note, duration, dotCount, tupletValue, roundDown, insideChord, staccato]
for (let i = 0; i < notes.length; i++) {
const obj = notes[i];
if (["tie", "begin slur", "end slur"].includes(obj)) {
continue;
}
if (["tie", "begin slur", "end slur"].includes(obj) || ignore.includes(obj)) continue;

if (ignore.includes(obj)) {
continue;
}

// ignore key
if (obj === "key") {
i += 2;
continue;
}

if (obj === "begin crescendo") {
add('<direction placement="above">');
indent++;
add("<direction-type>");
indent++;
add('<wedge type="crescendo"/>');
indent--;
add("</direction-type>");
indent--;
add("</direction>");
addDirection("crescendo");
continue;
}

if (obj === "begin decrescendo") {
add('<direction placement="above">');
indent++;
add("<direction-type>");
indent++;
add('<wedge type="diminuendo"/>');
indent--;
add("</direction-type>");
indent--;
add("</direction>");
addDirection("diminuendo");
continue;
}

Expand All @@ -136,11 +107,10 @@ saveMxmlOutput = (logo) => {
const bpm = notes[i + 1];
const beatMeasure = notes[i + 2];
const bpmAdjusted = Math.floor(bpm * (4 / beatMeasure));

if (openedMeasureTag) {
add('<sound tempo="' + bpmAdjusted + '"/>');
add(`<sound tempo="${bpmAdjusted}"/>`);
} else {
queuedTempo = '<sound tempo="' + bpmAdjusted + '"/>';
queuedTempo = `<sound tempo="${bpmAdjusted}"/>`;
}
i += 2;
continue;
Expand All @@ -149,23 +119,19 @@ saveMxmlOutput = (logo) => {
if (obj === "meter") {
newBeats = notes[i + 1];
newBeatType = notes[i + 2];

// divisions per beat == how many 1/32 notes fit in one beat?
newDivisions = newBeats * (1 / newBeatType / (1 / 32));
i += 2;
beatsChanged = true;
continue;
}

// We only add </chord> tag to the non-first elements in a chord
let isChordNote = false;
for (const p of obj[0]) {
let dur = 32 / obj[1];
for (let j = 0; j < obj[2]; j++) dur += dur / 2;

if (divisionsLeft < dur && !isChordNote) {
if (openedMeasureTag) {
// throw "big chungus";
add("</measure>");
currMeasure++;
divisionsLeft = divisions;
Expand All @@ -176,37 +142,17 @@ saveMxmlOutput = (logo) => {
if (!isChordNote) {
if (divisionsLeft === divisions) {
if (firstMeasure) {
add(
'<measure number="' +
currMeasure +
'"> <attributes> <divisions>' +
divisions +
"</divisions> <key> <fifths>0</fifths> </key> <time> <beats>" +
beats +
"</beats> <beat-type>" +
beatType +
"</beat-type> </time> <clef> <sign>G</sign> <line>2</line> </clef> </attributes>"
);
addMeasureAttributes(currMeasure, divisions, beats, beatType);
firstMeasure = false;
} else if (beatsChanged) {
beats = newBeats;
beatType = newBeatType;
divisions = newDivisions;
divisionsLeft = divisions;
add(
'<measure number="' +
currMeasure +
'"> <attributes> <divisions>' +
newDivisions +
"</divisions> <key> <fifths>0</fifths> </key> <time> <beats>" +
newBeats +
"</beats> <beat-type>" +
newBeatType +
"</beat-type> </time> <clef> <sign>G</sign> <line>2</line> </clef> </attributes>"
);
addMeasureAttributes(currMeasure, newDivisions, newBeats, newBeatType);
beatsChanged = false;
} else {
add('<measure number="' + currMeasure + '">');
add(`<measure number="${currMeasure}">`);
}
openedMeasureTag = true;
if (queuedTempo !== null) {
Expand All @@ -217,15 +163,7 @@ saveMxmlOutput = (logo) => {
divisionsLeft -= dur;
}

let alter;

if (p[1] === "\u266d") {
alter = -1; // flat
} else if (p[1] === "\u266F") {
alter = 1; // sharp
} else {
alter = 0; // no accidental
}
let alter = p[1] === "\u266d" ? -1 : p[1] === "\u266F" ? 1 : 0;

add("<note>");
indent++;
Expand All @@ -236,16 +174,14 @@ saveMxmlOutput = (logo) => {
} else {
add("<pitch>");
indent++;
add("<step>" + p[0] + "</step>");
if (alter != 0) {
add("<alter>" + alter + "</alter>");
}
add("<octave>" + p[p.length - 1] + "</octave>");
add(`<step>${p[0]}</step>`);
if (alter !== 0) add(`<alter>${alter}</alter>`);
add(`<octave>${p[p.length - 1]}</octave>`);
indent--;
add("</pitch>");
}

add("<duration>" + dur + "</duration>");
add(`<duration>${dur}</duration>`);
if (notes[i + 1] === "tie") {
add('<tie type="start"/>');
} else if (notes[i - 1] === "tie") {
Expand All @@ -257,9 +193,7 @@ saveMxmlOutput = (logo) => {
indent++;
add("<articulations>");
indent++;
if (obj[6]) {
add('<staccato placement="below"/>');
}
if (obj[6]) add('<staccato placement="below"/>');
indent--;
add("</articulations>");
indent--;
Expand Down Expand Up @@ -296,7 +230,6 @@ saveMxmlOutput = (logo) => {
});
add("</score-partwise>");

// Filter voices
let mi = 1e5;
for (let i = 0; i < res.length - 1; i++) {
if ((res[i] === "P" || res[i] === "#") && "123456789".includes(res[i + 1])) {
Expand Down
Loading