UNPKG

abcjs

Version:

Renderer for abc music notation

458 lines (436 loc) 14.6 kB
// abc_vertical_lint.js: Analyzes the vertical position of the output object. //This file takes as input the output structure of the writing routine and lists the vertical position of all the elements. /*globals toString */ var verticalLint = function(tunes) { "use strict"; function fixed(digits, zero, num) { if (num === undefined) return "undefined"; if (typeof num !== "number") return num; var str = num.toFixed(digits); if (str.indexOf(zero) > 0) return str.split('.')[0]; while (str.length && str[str.length-1] === '0') str = str.substr(0,str.length-1); return str; } function fixed1(num) { return fixed(1, ".0", num); } function fixed2(num) { return fixed(2, ".00", num); } function fixed4(num) { return fixed(4, ".0000", num); } function formatY(obj) { return "y= ( " + fixed1(obj.bottom) + ' , ' + fixed1(obj.top) + " )"; } function formatX(obj) { return " x=" + fixed1(obj.x) + ' w=' + fixed1(obj.width); } function formatArrayStart(tabs, i) { return tabs + i + ": "; } function collectBrace(brace) { var ret; if (brace) { ret = []; for (var b1 = 0; b1 < brace.length; b1++) { var bracket = { x: brace[b1].x, top: fixed1(brace[b1].startY), bottom: fixed1(brace[b1].endY) }; if (brace[b1].header) bracket.header = brace[b1].header; ret.push(bracket); } } return ret; } function getType(obj) { if (obj.$type.indexOf('staff-extra') >= 0) { if (obj.elem.length === 1) { switch(obj.elem[0]) { case "symbol clefs.G": return "Treble Clef"; case "symbol clefs.F": return "Bass Clef"; case "symbol clefs.C": return "C Clef"; case "symbol timesig.common": return "Time Common"; case "symbol timesig.cut": return "Time Cut"; case "symbol timesig.imperfectum": return "Time Imperfectum"; case "symbol timesig.imperfectum2": return "Time Imperfectum 2"; case "symbol timesig.perfectum": return "Time Perfectum"; case "symbol timesig.perfectum2": return "Time Perfectum 2"; case "symbol clefs.perc": return "Percussion Clef"; } } else if (obj.elem.length === 2) { switch(obj.elem[0]) { case "symbol clefs.G": if (obj.elem[1] === 'symbol 8') return "Treble Clef 8"; else if (obj.elem[1].indexOf("barNumber") === 0) return "Treble Clef [bar=" + obj.elem[1].substring(10) + "]"; break; case "symbol clefs.F": if (obj.elem[1] === 'symbol 8') return "Bass Clef 8"; break; case "symbol clefs.C": if (obj.elem[1] === 'symbol 8') return "C Clef 8"; break; } var left = obj.elem[0].replace("symbol ", ""); var right = obj.elem[1].replace("symbol ", ""); left = parseInt(left,10); right = parseInt(right,10); if (!isNaN(left) && !isNaN(right)) return "Time Sig " + left + "/" + right; } if (obj.elem[0] === "symbol accidentals.sharp" || obj.elem[0] === "symbol accidentals.flat" || obj.elem[0] === "symbol accidentals.nat" || obj.elem[0] === "symbol accidentals.halfsharp" || obj.elem[0] === "symbol accidentals.halfflat") return "Key Sig " + obj.elem.join(" ").replace(/symbol/g,""); if (obj.elem.length > 0) { if (obj.elem[0] === 'symbol 1' || obj.elem[0] === 'symbol 2' || obj.elem[0] === 'symbol 3' || obj.elem[0] === 'symbol 4' || obj.elem[0] === 'symbol 5' || obj.elem[0] === 'symbol 6' || obj.elem[0] === 'symbol 7' || obj.elem[0] === 'symbol 8' || obj.elem[0] === 'symbol 9') return "Time Sig (odd): " + obj.elem.join('').replace(/symbol /g,""); } } else if (obj.$type.indexOf("note") === 0 || obj.$type.indexOf("rest") === 0) { return obj.$type + " " + fixed4(obj.duration) + ' ' + obj.elem.join(" ").replace(/symbol /g,"").replace(/\n/g, "\\n"); } else if (obj.$type === 'bar') return "Bar"; else if (obj.$type === 'part') return obj.elem[0]; else if (obj.$type === 'tempo') return "Tempo " + obj.elem[0]; return "unknown"; } function formatStaffs(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = "\n"; for (i = 0; i < arr.length; i++) { var obj = arr[i]; str += formatArrayStart(tabs, i) + formatY(obj) + "\n"; } return tabs + str; } function formatVoices(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = "\n"; for (i = 0; i < arr.length; i++) { var obj = arr[i]; str += formatArrayStart(tabs, i) + formatY(obj); str += formatElements(obj.voiceChildren, indent+1); if (obj.otherChildren.length > 0) str += formatOtherChildren(obj.otherChildren, indent+1); if (obj.beams.length > 0) str += formatBeams(obj.beams, indent+1); } return tabs + str; } function formatClasses(obj) { if (obj.classes) return " " + obj.classes; return ''; } function formatElements(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = "\n"; for (i = 0; i < arr.length; i++) { var obj = arr[i]; var type = getType(obj); if (type === "unknown") str += formatArrayStart(tabs, i) + "\n" + formatObject(obj, indent) + formatClasses(obj) + "\n"; else str += formatArrayStart(tabs, i) + type + ' ' + formatY(obj) + formatX(obj) + formatClasses(obj) + "\n"; } return tabs + str; } function formatOtherChildren(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = tabs + "Other Children:\n"; for (i = 0; i < arr.length; i++) { str += formatArrayStart(tabs, i); var keys = Object.keys(arr[i].params); keys = keys.sort(); var params = ""; for (var j = 0; j < keys.length; j++) params += keys[j] + ": " + arr[i].params[keys[j]] + " "; str += arr[i].type + " (" + params + ")" + formatClasses(arr[i].params) + "\n"; } return str + "\n"; } function formatBeams(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = tabs + "Beams:\n"; for (i = 0; i < arr.length; i++) { str += formatArrayStart(tabs, i); var keys = Object.keys(arr[i].params); keys = keys.sort(); var params = ""; for (var j = 0; j < keys.length; j++) params += keys[j] + ": " + arr[i].params[keys[j]] + " "; str += arr[i].type + " (" + params + ")\n"; } return str + "\n"; } function formatArray(arr, indent) { var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; var str = " [\n"; for (i = 0; i < arr.length; i++) { var obj = arr[i]; if (typeof obj === "string" || typeof obj === "number") str += tabs + i + ": " + obj + "\n"; else str += tabs + i + ":\n" + formatObject(obj, indent+1) +"\n"; } return tabs + str + tabs + "]"; } function formatObject(obj, indent) { var str = []; var tabs = ""; for (var i = 0; i < indent; i++) tabs += "\t"; if (typeof obj === "string" || typeof obj === "number") return tabs + obj; for (var key in obj) { if (obj.hasOwnProperty(key)) { var value = obj[key]; var prefix = tabs + key + ": "; if (key === 'staffs') str.push(prefix + formatStaffs(value, indent+1)); else if (key === 'voiceChildren') str.push(prefix + formatElements(value, indent+1)); else if (toString.call(value) === "[object Array]") str.push(prefix + formatArray(value, indent+1)); else if (typeof value === "object") str.push(prefix + formatObject(value, indent+1)); else if (typeof value === "number") str.push(prefix + fixed1(value)); else str.push(prefix + value); } } str = str.sort(); return str.join("\n"); } function addHeader(obj) { if (obj.header) return " " + obj.header; return ""; } function formatLine(line, lineNum) { var str = ""; str += "Line: " + lineNum + ": (" + fixed1(line.height) + ")\n"; if (line.brace) { for (var i = 0; i < line.brace.length; i++) str += "brace: " + fixed1(line.brace[i].x) + " " + formatY(line.brace[i]) + addHeader(line.brace[i]) + "\n"; } if (line.bracket) { for (var i2 = 0; i2 < line.bracket.length; i2++) str += "bracket: " + fixed1(line.bracket[i2].x) + " " + formatY(line.bracket[i2]) + addHeader(line.bracket[i2]) + "\n"; } str += "staffs: " + formatStaffs(line.staffs, 1); str += "voices: " + formatVoices(line.voices, 1); return str; } function setSpecialY(obj) { var ret = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { if (obj[ret]) ret[key] = obj[ret]; } } return ret; } function extractPositioningInfo(staffGroup, lineNum) { var ret = { height: staffGroup.height, minSpace: staffGroup.minspace, spacingUnits: staffGroup.spacingunits, width: staffGroup.w, startX: staffGroup.startX, staffs: [], voices: [] }; ret.brace = collectBrace(staffGroup.brace); ret.bracket = collectBrace(staffGroup.bracket); for (var i = 0; i < staffGroup.staffs.length; i++) { var staff = staffGroup.staffs[i]; ret.staffs.push({bottom: fixed1(staff.bottom), top: fixed1(staff.top), specialY: setSpecialY(staff) }); } for (i = 0; i < staffGroup.voices.length; i++) { var voice = staffGroup.voices[i]; var obj = { bottom: fixed1(voice.bottom), top: fixed1(voice.top), specialY: setSpecialY(voice), width: fixed1(voice.w), startX: voice.startX, voiceChildren: [], otherChildren: [], beams: [] }; for (var j = 0; j < voice.children.length; j++) { var child = voice.children[j]; var type = child.type; var classes = []; if (child.elemset) { for (var jj = 0; jj < child.elemset.length; jj++) { var cl = child.elemset[jj].classList; if (cl && cl.length > 0) { for (var jjj = 0; jjj < cl.length; jjj++) classes.push(cl[jjj]); } } } if (type === 'note' || type === 'rest') { if (child.abcelem.pitches) { var pitches = []; for (var ii = 0; ii < child.abcelem.pitches.length; ii++) pitches.push(child.abcelem.pitches[ii].verticalPos); type += "(" + pitches.join(',') + ")"; } else { var r = "rest"; switch (child.abcelem.rest.type) { case "invisible": r += "/inv"; break; case "spacer": r += "/sp"; break; } type = "note(" + r + ")"; } if (child.abcelem.lyric && child.abcelem.lyric.length > 0) type += " " + child.abcelem.lyric[0].syllable; } var obj2 = { $type: type, bottom: fixed1(child.bottom), top: fixed1(child.top), specialY: setSpecialY(child), minSpacing: child.minspacing, duration: child.duration, width: child.w, x: child.x }; if (classes.length > 0) obj2.classes = classes.join(" "); obj2.elem = []; if (type === 'tempo') { var tempo = child.children[0].tempo; var tempoNote = child.children[0].note; var arr = []; if (tempo.preString) arr.push(tempo.preString); else arr.push('*'); arr.push(tempo.duration); if (tempo.postString) arr.push(tempo.postString); else arr.push('*'); if (tempoNote) { arr.push(' note(' + fixed1(tempoNote.top) + ',' + fixed1(tempoNote.bottom) + ',' + fixed1(tempoNote.x) + ',' + fixed1(tempoNote.w) + ')'); } obj2.elem.push(arr.join(' ')); } else if (child.children.length) { for (var k = 0; k < child.children.length; k++) { var str = child.children[k].type; if (child.children[k].c) str += " " + child.children[k].c; obj2.elem.push(str); } } obj.voiceChildren.push(obj2); } for (j = 0; j < voice.otherchildren.length; j++) { var otherChild = voice.otherchildren[j]; var className = otherChild.constructor.name; var ch = { type: className, params: {} }; if (className === "String") ch.params.value = otherChild; else { for (var key in otherChild) { if (otherChild.hasOwnProperty(key)) { var value = otherChild[key]; if (value === null) ch.params[key] = "null"; else if (value === className && key === 'type') ; else if (key === "elemset") { var cls = []; for (var j2 = 0; j2 < otherChild.elemset.length; j2++) { var c2 = otherChild.elemset[j2].classList; if (c2 && c2.length > 0) { for (var j3 = 0; j3 < c2.length; j3++) cls.push(c2[j3]); } } ch.params.classes = cls.join(" "); } else if (typeof value === "object") { var obj3 = value.constructor.name + '['; switch (value.constructor.name) { case "AbsoluteElement": obj3 += value.type; break; case "RelativeElement": obj3 += value.pitch; break; case "Array": obj3 += value.length; break; default: console.log(value.constructor.name); ch.params[key] = value.constructor.name; break; } ch.params[key] = obj3 +']'; } else if (typeof value === "number") ch.params[key] = fixed1(value); else ch.params[key] = value; } } } obj.otherChildren.push(ch); } for (j = 0; j < voice.beams.length; j++) { var beam = voice.beams[j]; var className2 = beam.constructor.name; var ch2 = { type: className2, params: {} }; if (className2 === "String") ch2.params.value = beam; else { for (var key2 in beam) { if (beam.hasOwnProperty(key2)) { var value2 = beam[key2]; if (typeof value2 === "object") ch2.params[key2] = "object"; else ch2.params[key2] = value2; } } } obj.beams.push(ch2); } // TODO: there is also extra[], heads[], elemset[], and right[] to parse. ret.voices.push(obj); } return formatLine(ret, lineNum); } function extractText(label, arr) { var items = []; items.push(label); for (var i = 0; i < arr.length; i++) { var item = arr[i]; items.push("\t" + JSON.stringify(item)); } items.push(""); return items.join("\n"); } var positioning = []; for (var i = 0; i < tunes.length; i++) { var tune = tunes[i]; if (tune.topText && tune.topText.rows.length > 0) positioning.push(extractText("Top Text", tune.topText.rows)); for (var j = 0; j < tune.lines.length; j++) { var line = tune.lines[j]; if (line.staffGroup) positioning.push(extractPositioningInfo(tune.lines[j].staffGroup, j)); else positioning.push(JSON.stringify(line)); } } if (tune.bottomText && tune.bottomText.rows.length > 0) positioning.push(extractText("Bottom Text", tune.bottomText.rows)); return positioning; }; module.exports = verticalLint;