|
@@ -712,8 +712,7 @@ export class VoiceGenerator {
|
|
|
if (bracketAttr && bracketAttr.value === "yes") {
|
|
|
bracketed = true;
|
|
|
}
|
|
|
- const placementAttr: Attr = tupletNode.attribute("placement");
|
|
|
- const placementBelow: boolean = placementAttr && placementAttr.value === "below";
|
|
|
+
|
|
|
const type: Attr = tupletNode.attribute("type");
|
|
|
if (type && type.value === "start") {
|
|
|
let tupletNumber: number = 1;
|
|
@@ -733,7 +732,17 @@ export class VoiceGenerator {
|
|
|
|
|
|
}
|
|
|
const tuplet: Tuplet = new Tuplet(tupletLabelNumber, bracketed);
|
|
|
- tuplet.tupletLabelNumberPlacement = placementBelow ? PlacementEnum.Below : PlacementEnum.Above;
|
|
|
+ //Default to above
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ //If we ever encounter a placement attribute for this tuplet, should override.
|
|
|
+ //Even previous placement attributes for the tuplet
|
|
|
+ const placementAttr: Attr = tupletNode.attribute("placement");
|
|
|
+ if (placementAttr) {
|
|
|
+ if (placementAttr.value === "below") {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ }
|
|
|
+ tuplet.PlacementFromXml = true;
|
|
|
+ }
|
|
|
if (this.tupletDict[tupletNumber]) {
|
|
|
delete this.tupletDict[tupletNumber];
|
|
|
if (Object.keys(this.tupletDict).length === 0) {
|
|
@@ -756,9 +765,39 @@ export class VoiceGenerator {
|
|
|
}
|
|
|
const tuplet: Tuplet = this.tupletDict[tupletNumber];
|
|
|
if (tuplet) {
|
|
|
+ const placementAttr: Attr = tupletNode.attribute("placement");
|
|
|
+ if (placementAttr) {
|
|
|
+ if (placementAttr.value === "below") {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ } else {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ tuplet.PlacementFromXml = true;
|
|
|
+ }
|
|
|
const subnotelist: Note[] = [];
|
|
|
subnotelist.push(this.currentNote);
|
|
|
tuplet.Notes.push(subnotelist);
|
|
|
+ //If our placement hasn't been from XML, check all the notes in the tuplet
|
|
|
+ //Search for the first non-rest and use it's stem direction
|
|
|
+ if (!tuplet.PlacementFromXml) {
|
|
|
+ let foundNonRest: boolean = false;
|
|
|
+ for (const subList of tuplet.Notes) {
|
|
|
+ for (const note of subList) {
|
|
|
+ if (!note.isRest()) {
|
|
|
+ if(note.StemDirectionXml === StemDirectionType.Down) {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ } else {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ foundNonRest = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (foundNonRest) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
|
|
|
this.currentNote.NoteTuplet = tuplet;
|
|
|
delete this.tupletDict[tupletNumber];
|
|
@@ -785,9 +824,6 @@ export class VoiceGenerator {
|
|
|
if (bracketAttr && bracketAttr.value === "yes") {
|
|
|
bracketed = true;
|
|
|
}
|
|
|
- const placementAttr: Attr = n.attribute("placement");
|
|
|
- const placementBelow: boolean = placementAttr && placementAttr.value === "below";
|
|
|
-
|
|
|
if (type === "start") {
|
|
|
let tupletLabelNumber: number = 0;
|
|
|
let timeModNode: IXmlElement = node.element("time-modification");
|
|
@@ -812,7 +848,20 @@ export class VoiceGenerator {
|
|
|
let tuplet: Tuplet = this.tupletDict[tupletnumber];
|
|
|
if (!tuplet) {
|
|
|
tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber, bracketed);
|
|
|
- tuplet.tupletLabelNumberPlacement = placementBelow ? PlacementEnum.Below : PlacementEnum.Above;
|
|
|
+ //Default to above
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ //If we ever encounter a placement attribute for this tuplet, should override.
|
|
|
+ //Even previous placement attributes for the tuplet
|
|
|
+ const placementAttr: Attr = n.attribute("placement");
|
|
|
+ if (placementAttr) {
|
|
|
+ if (placementAttr.value === "below") {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ } else {
|
|
|
+ //Just in case
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ tuplet.PlacementFromXml = true;
|
|
|
}
|
|
|
const subnotelist: Note[] = [];
|
|
|
subnotelist.push(this.currentNote);
|
|
@@ -826,9 +875,39 @@ export class VoiceGenerator {
|
|
|
}
|
|
|
const tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
|
|
|
if (tuplet) {
|
|
|
+ const placementAttr: Attr = n.attribute("placement");
|
|
|
+ if (placementAttr) {
|
|
|
+ if (placementAttr.value === "below") {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ } else {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ tuplet.PlacementFromXml = true;
|
|
|
+ }
|
|
|
const subnotelist: Note[] = [];
|
|
|
subnotelist.push(this.currentNote);
|
|
|
tuplet.Notes.push(subnotelist);
|
|
|
+ //If our placement hasn't been from XML, check all the notes in the tuplet
|
|
|
+ //Search for the first non-rest and use it's stem direction
|
|
|
+ if (!tuplet.PlacementFromXml) {
|
|
|
+ let foundNonRest: boolean = false;
|
|
|
+ for (const subList of tuplet.Notes) {
|
|
|
+ for (const note of subList) {
|
|
|
+ if (!note.isRest()) {
|
|
|
+ if(note.StemDirectionXml === StemDirectionType.Down) {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Below;
|
|
|
+ } else {
|
|
|
+ tuplet.tupletLabelNumberPlacement = PlacementEnum.Above;
|
|
|
+ }
|
|
|
+ foundNonRest = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (foundNonRest) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
|
|
|
this.currentNote.NoteTuplet = tuplet;
|
|
|
if (Object.keys(this.tupletDict).length === 0) {
|
|
@@ -889,6 +968,26 @@ export class VoiceGenerator {
|
|
|
if (tieNodeList.length === 1) {
|
|
|
const tieNode: IXmlElement = tieNodeList[0];
|
|
|
if (tieNode !== undefined && tieNode.attributes()) {
|
|
|
+ let tieDirection: PlacementEnum = PlacementEnum.NotYetDefined;
|
|
|
+ // read tie direction/placement from XML
|
|
|
+ const placementAttr: IXmlAttribute = tieNode.attribute("placement");
|
|
|
+ if (placementAttr) {
|
|
|
+ if (placementAttr.value === "above") {
|
|
|
+ tieDirection = PlacementEnum.Above;
|
|
|
+ } else if (placementAttr.value === "below") {
|
|
|
+ tieDirection = PlacementEnum.Below;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // tie direction also be given like this:
|
|
|
+ const orientationAttr: IXmlAttribute = tieNode.attribute("orientation");
|
|
|
+ if (orientationAttr) {
|
|
|
+ if (orientationAttr.value === "over") {
|
|
|
+ tieDirection = PlacementEnum.Above;
|
|
|
+ } else if (orientationAttr.value === "under") {
|
|
|
+ tieDirection = PlacementEnum.Below;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const type: string = tieNode.attribute("type").value;
|
|
|
try {
|
|
|
if (type === "start") {
|
|
@@ -900,7 +999,7 @@ export class VoiceGenerator {
|
|
|
const tie: Tie = new Tie(this.currentNote, tieType);
|
|
|
this.openTieDict[newTieNumber] = tie;
|
|
|
tie.TieNumber = newTieNumber;
|
|
|
- this.setTieDirections();
|
|
|
+ tie.TieDirection = tieDirection;
|
|
|
} else if (type === "stop") {
|
|
|
const tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
|
|
|
const tie: Tie = this.openTieDict[tieNumber];
|
|
@@ -925,28 +1024,6 @@ export class VoiceGenerator {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // TODO do same for slurs, optimize.
|
|
|
- /** Sets the directions of open ties: up for the top one, down for the others. */
|
|
|
- private setTieDirections(): void {
|
|
|
- const tieKeys: string[] = Object.keys(this.openTieDict);
|
|
|
- let highestNote: Note = undefined;
|
|
|
- for (const tieKey of tieKeys) {
|
|
|
- const tie: Tie = this.openTieDict[tieKey];
|
|
|
- const tieNote: Note = tie.Notes[0];
|
|
|
- if (!highestNote || tieNote.Pitch.OperatorFundamentalGreaterThan(highestNote.Pitch)) {
|
|
|
- highestNote = tieNote;
|
|
|
- }
|
|
|
- }
|
|
|
- for (const tieKey of tieKeys) {
|
|
|
- const tie: Tie = this.openTieDict[tieKey];
|
|
|
- if (tie.Notes[0] === highestNote) {
|
|
|
- tie.TieDirection = PlacementEnum.Above;
|
|
|
- } else {
|
|
|
- tie.TieDirection = PlacementEnum.Below;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Find the next free int (starting from 0) to use as key in TieDict.
|
|
|
* @returns {number}
|