|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
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; |
|
|
|