|
|
|
|
|
|
|
|
var DynamicDecoration = require('./elements/dynamic-decoration'); |
|
|
var CrescendoElem = require('./elements/crescendo-element'); |
|
|
var GlissandoElem = require('./elements/glissando-element'); |
|
|
var glyphs = require('./glyphs'); |
|
|
var RelativeElement = require('./elements/relative-element'); |
|
|
var TieElem = require('./elements/tie-element'); |
|
|
|
|
|
var Decoration = function Decoration() { |
|
|
this.startDiminuendoX = undefined; |
|
|
this.startCrescendoX = undefined; |
|
|
this.minTop = 12; |
|
|
this.minBottom = 0; |
|
|
}; |
|
|
|
|
|
var closeDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove) { |
|
|
var yPos; |
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
if (decoration[i] === "staccato" || decoration[i] === "tenuto" || (decoration[i] === "accent" && !accentAbove)) { |
|
|
var symbol = "scripts." + decoration[i]; |
|
|
if (decoration[i] === "accent") symbol = "scripts.sforzato"; |
|
|
if (yPos === undefined) |
|
|
yPos = (dir === "down") ? pitch + 2 : minPitch - 2; |
|
|
else |
|
|
yPos = (dir === "down") ? yPos + 2 : yPos - 2; |
|
|
if (decoration[i] === "accent") { |
|
|
|
|
|
if (dir === "up") yPos--; |
|
|
else yPos++; |
|
|
} else { |
|
|
|
|
|
switch (yPos) { |
|
|
case 2: |
|
|
case 4: |
|
|
case 6: |
|
|
case 8: |
|
|
case 10: |
|
|
if (dir === "up") yPos--; |
|
|
else yPos++; |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (pitch > 9) yPos++; |
|
|
var deltaX = width / 2; |
|
|
if (glyphs.getSymbolAlign(symbol) !== "center") { |
|
|
deltaX -= (glyphs.getSymbolWidth(symbol) / 2); |
|
|
} |
|
|
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), yPos)); |
|
|
} |
|
|
if (decoration[i] === "slide" && abselem.heads[0]) { |
|
|
var yPos2 = abselem.heads[0].pitch; |
|
|
yPos2 -= 2; |
|
|
var blank1 = new RelativeElement("", -roomtaken - 15, 0, yPos2 - 1); |
|
|
var blank2 = new RelativeElement("", -roomtaken - 5, 0, yPos2 + 1); |
|
|
abselem.addFixedX(blank1); |
|
|
abselem.addFixedX(blank2); |
|
|
voice.addOther(new TieElem({ anchor1: blank1, anchor2: blank2, fixedY: true })); |
|
|
} |
|
|
} |
|
|
if (yPos === undefined) |
|
|
yPos = pitch; |
|
|
|
|
|
return { above: yPos, below: abselem.bottom }; |
|
|
}; |
|
|
|
|
|
var volumeDecoration = function (voice, decoration, abselem, positioning) { |
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
switch (decoration[i]) { |
|
|
case "p": |
|
|
case "mp": |
|
|
case "pp": |
|
|
case "ppp": |
|
|
case "pppp": |
|
|
case "f": |
|
|
case "ff": |
|
|
case "fff": |
|
|
case "ffff": |
|
|
case "sfz": |
|
|
case "mf": |
|
|
var elem = new DynamicDecoration(abselem, decoration[i], positioning); |
|
|
voice.addOther(elem); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
var compoundDecoration = function (decoration, pitch, width, abselem, dir) { |
|
|
function highestPitch() { |
|
|
if (abselem.heads.length === 0) |
|
|
return 10; |
|
|
var pitch = abselem.heads[0].pitch; |
|
|
for (var i = 1; i < abselem.heads.length; i++) |
|
|
pitch = Math.max(pitch, abselem.heads[i].pitch); |
|
|
return pitch; |
|
|
} |
|
|
function lowestPitch() { |
|
|
if (abselem.heads.length === 0) |
|
|
return 2; |
|
|
var pitch = abselem.heads[0].pitch; |
|
|
for (var i = 1; i < abselem.heads.length; i++) |
|
|
pitch = Math.min(pitch, abselem.heads[i].pitch); |
|
|
return pitch; |
|
|
} |
|
|
function compoundDecoration(symbol, count) { |
|
|
var placement = (dir === 'down') ? lowestPitch() + 1 : highestPitch() + 9; |
|
|
if (dir !== 'down' && count === 1) |
|
|
placement--; |
|
|
var deltaX = width / 2; |
|
|
deltaX += (dir === 'down') ? -5 : 3; |
|
|
for (var i = 0; i < count; i++) { |
|
|
placement -= 1; |
|
|
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), placement)); |
|
|
} |
|
|
} |
|
|
|
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
switch (decoration[i]) { |
|
|
case "/": compoundDecoration("flags.ugrace", 1); break; |
|
|
case "//": compoundDecoration("flags.ugrace", 2); break; |
|
|
case "///": compoundDecoration("flags.ugrace", 3); break; |
|
|
case "////": compoundDecoration("flags.ugrace", 4); break; |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
var stackedDecoration = function (decoration, width, abselem, yPos, positioning, minTop, minBottom, accentAbove) { |
|
|
function incrementPlacement(placement, height) { |
|
|
if (placement === 'above') |
|
|
yPos.above += height; |
|
|
else |
|
|
yPos.below -= height; |
|
|
} |
|
|
function getPlacement(placement) { |
|
|
var y; |
|
|
if (placement === 'above') { |
|
|
y = yPos.above; |
|
|
if (y < minTop) |
|
|
y = minTop; |
|
|
} else { |
|
|
y = yPos.below; |
|
|
if (y > minBottom) |
|
|
y = minBottom; |
|
|
} |
|
|
return y; |
|
|
} |
|
|
function textDecoration(text, placement, anchor) { |
|
|
var y = getPlacement(placement); |
|
|
var textFudge = 2; |
|
|
var textHeight = 5; |
|
|
|
|
|
abselem.addFixedX(new RelativeElement(text, width / 2, 0, y + textFudge, { type: "decoration", klass: 'ornament', thickness: 3, anchor: anchor })); |
|
|
|
|
|
incrementPlacement(placement, textHeight); |
|
|
} |
|
|
function symbolDecoration(symbol, placement) { |
|
|
var deltaX = width / 2; |
|
|
if (glyphs.getSymbolAlign(symbol) !== "center") { |
|
|
deltaX -= (glyphs.getSymbolWidth(symbol) / 2); |
|
|
} |
|
|
var height = glyphs.symbolHeightInPitches(symbol) + 1; |
|
|
var y = getPlacement(placement); |
|
|
y = (placement === 'above') ? y + height / 2 : y - height / 2; |
|
|
abselem.addFixedX(new RelativeElement(symbol, deltaX, glyphs.getSymbolWidth(symbol), y, { klass: 'ornament', thickness: glyphs.symbolHeightInPitches(symbol), position: placement })); |
|
|
|
|
|
incrementPlacement(placement, height); |
|
|
} |
|
|
|
|
|
var symbolList = { |
|
|
"+": "scripts.stopped", |
|
|
"open": "scripts.open", |
|
|
"snap": "scripts.snap", |
|
|
"wedge": "scripts.wedge", |
|
|
"thumb": "scripts.thumb", |
|
|
"shortphrase": "scripts.shortphrase", |
|
|
"mediumphrase": "scripts.mediumphrase", |
|
|
"longphrase": "scripts.longphrase", |
|
|
"trill": "scripts.trill", |
|
|
"roll": "scripts.roll", |
|
|
"irishroll": "scripts.roll", |
|
|
"marcato": "scripts.umarcato", |
|
|
"dmarcato": "scripts.dmarcato", |
|
|
"umarcato": "scripts.umarcato", |
|
|
"turn": "scripts.turn", |
|
|
"uppermordent": "scripts.prall", |
|
|
"pralltriller": "scripts.prall", |
|
|
"mordent": "scripts.mordent", |
|
|
"lowermordent": "scripts.mordent", |
|
|
"downbow": "scripts.downbow", |
|
|
"upbow": "scripts.upbow", |
|
|
"fermata": "scripts.ufermata", |
|
|
"invertedfermata": "scripts.dfermata", |
|
|
"breath": ",", |
|
|
"coda": "scripts.coda", |
|
|
"segno": "scripts.segno" |
|
|
}; |
|
|
|
|
|
var hasOne = false; |
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
switch (decoration[i]) { |
|
|
case "0": |
|
|
case "1": |
|
|
case "2": |
|
|
case "3": |
|
|
case "4": |
|
|
case "5": |
|
|
case "D.C.": |
|
|
case "D.S.": |
|
|
textDecoration(decoration[i], positioning, 'middle'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "D.C.alcoda": |
|
|
textDecoration("D.C. al coda", positioning, 'end'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "D.C.alfine": |
|
|
textDecoration("D.C. al fine", positioning, 'end'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "D.S.alcoda": |
|
|
textDecoration("D.S. al coda", positioning, 'end'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "D.S.alfine": |
|
|
textDecoration("D.S. al fine", positioning, 'end'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "fine": |
|
|
textDecoration("FINE", positioning, 'middle'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "+": |
|
|
case "open": |
|
|
case "snap": |
|
|
case "wedge": |
|
|
case "thumb": |
|
|
case "shortphrase": |
|
|
case "mediumphrase": |
|
|
case "longphrase": |
|
|
case "trill": |
|
|
case "roll": |
|
|
case "irishroll": |
|
|
case "marcato": |
|
|
case "dmarcato": |
|
|
case "turn": |
|
|
case "uppermordent": |
|
|
case "pralltriller": |
|
|
case "mordent": |
|
|
case "lowermordent": |
|
|
case "downbow": |
|
|
case "upbow": |
|
|
case "fermata": |
|
|
case "breath": |
|
|
case "umarcato": |
|
|
case "coda": |
|
|
case "segno": |
|
|
symbolDecoration(symbolList[decoration[i]], positioning); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "invertedfermata": |
|
|
symbolDecoration(symbolList[decoration[i]], 'below'); |
|
|
hasOne = true; |
|
|
break; |
|
|
case "mark": |
|
|
abselem.klass = "mark"; |
|
|
break; |
|
|
case "accent": |
|
|
if (accentAbove) { |
|
|
symbolDecoration("scripts.sforzato", positioning); |
|
|
hasOne = true; |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
return hasOne; |
|
|
}; |
|
|
|
|
|
function leftDecoration(decoration, abselem, roomtaken) { |
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
switch (decoration[i]) { |
|
|
case "arpeggio": |
|
|
|
|
|
|
|
|
|
|
|
for (var j = abselem.abcelem.minpitch - 1; j <= abselem.abcelem.maxpitch; j += 2) { |
|
|
abselem.addExtra( |
|
|
new RelativeElement( |
|
|
"scripts.arpeggio", |
|
|
-glyphs.getSymbolWidth("scripts.arpeggio") * 2 - roomtaken, |
|
|
0, |
|
|
j + 2, |
|
|
{ klass: 'ornament', thickness: glyphs.symbolHeightInPitches("scripts.arpeggio") } |
|
|
) |
|
|
); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, positioning) { |
|
|
var diminuendo; |
|
|
var crescendo; |
|
|
var glissando; |
|
|
for (var i = 0; i < decoration.length; i++) { |
|
|
switch (decoration[i]) { |
|
|
case "diminuendo(": |
|
|
this.startDiminuendoX = abselem; |
|
|
diminuendo = undefined; |
|
|
break; |
|
|
case "diminuendo)": |
|
|
diminuendo = { start: this.startDiminuendoX, stop: abselem }; |
|
|
this.startDiminuendoX = undefined; |
|
|
break; |
|
|
case "crescendo(": |
|
|
this.startCrescendoX = abselem; |
|
|
crescendo = undefined; |
|
|
break; |
|
|
case "crescendo)": |
|
|
crescendo = { start: this.startCrescendoX, stop: abselem }; |
|
|
this.startCrescendoX = undefined; |
|
|
break; |
|
|
case '~(': |
|
|
case "glissando(": |
|
|
this.startGlissandoX = abselem; |
|
|
glissando = undefined; |
|
|
break; |
|
|
case '~)': |
|
|
case "glissando)": |
|
|
glissando = { start: this.startGlissandoX, stop: abselem }; |
|
|
this.startGlissandoX = undefined; |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (diminuendo) { |
|
|
voice.addOther(new CrescendoElem(diminuendo.start, diminuendo.stop, ">", positioning)); |
|
|
} |
|
|
if (crescendo) { |
|
|
voice.addOther(new CrescendoElem(crescendo.start, crescendo.stop, "<", positioning)); |
|
|
} |
|
|
if (glissando) { |
|
|
voice.addOther(new GlissandoElem(glissando.start, glissando.stop)); |
|
|
} |
|
|
}; |
|
|
|
|
|
Decoration.prototype.createDecoration = function (voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, positioning, hasVocals, accentAbove) { |
|
|
if (!positioning) |
|
|
positioning = { ornamentPosition: 'above', volumePosition: hasVocals ? 'above' : 'below', dynamicPosition: hasVocals ? 'above' : 'below' }; |
|
|
|
|
|
volumeDecoration(voice, decoration, abselem, positioning.volumePosition); |
|
|
this.dynamicDecoration(voice, decoration, abselem, positioning.dynamicPosition); |
|
|
compoundDecoration(decoration, pitch, width, abselem, dir); |
|
|
|
|
|
|
|
|
var yPos = closeDecoration(voice, decoration, pitch, width, abselem, roomtaken, dir, minPitch, accentAbove); |
|
|
|
|
|
|
|
|
yPos.above = Math.max(yPos.above, this.minTop); |
|
|
yPos.below = Math.min(yPos.below, minPitch); |
|
|
var hasOne = stackedDecoration(decoration, width, abselem, yPos, positioning.ornamentPosition, this.minTop, minPitch, accentAbove); |
|
|
|
|
|
|
|
|
|
|
|
leftDecoration(decoration, abselem, roomtaken); |
|
|
}; |
|
|
|
|
|
module.exports = Decoration; |
|
|
|