소스 검색

fix articulations: read placement from XML. refactor articulations, create Articulation class

instead of just handling articulationEnums,
there is now an Articulation class, which includes the articulation and placement enums.

fixes part of #887
sschmidTU 4 년 전
부모
커밋
b022c423b3

+ 3 - 3
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -16,7 +16,7 @@ import { Beam } from "../VoiceData/Beam";
 import { OctaveEnum } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import { VoiceEntry, StemDirectionType } from "../VoiceData/VoiceEntry";
 import { OrnamentContainer } from "../VoiceData/OrnamentContainer";
-import { ArticulationEnum } from "../VoiceData/VoiceEntry";
+import { Articulation } from "../VoiceData/Articulation";
 import { Tuplet } from "../VoiceData/Tuplet";
 import { MusicSystem } from "./MusicSystem";
 import { GraphicalTie } from "./GraphicalTie";
@@ -365,7 +365,7 @@ export abstract class MusicSheetCalculator {
         throw new Error("abstract, not implemented");
     }
 
-    protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
+    protected handleVoiceEntryArticulations(articulations: Articulation[],
                                             voiceEntry: VoiceEntry,
                                             staffEntry: GraphicalStaffEntry): void {
         throw new Error("abstract, not implemented");
@@ -966,7 +966,7 @@ export abstract class MusicSheetCalculator {
         return;
     }
 
-    protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+    protected layoutArticulationMarks(articulations: Articulation[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
         return;
     }
 

+ 9 - 2
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -29,6 +29,7 @@ import { ArpeggioType } from "../../VoiceData/Arpeggio";
 import { TabNote } from "../../VoiceData/TabNote";
 import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
 import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
+import { Articulation } from "../../VoiceData/Articulation";
 
 /**
  * Helper class, which contains static methods which actually convert
@@ -445,7 +446,7 @@ export class VexFlowConverter {
         return vfnote;
     }
 
-    public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: ArticulationEnum[]): void {
+    public static generateArticulations(vfnote: Vex.Flow.StemmableNote, articulations: Articulation[]): void {
         if (!vfnote || vfnote.getAttribute("type") === "GhostNote") {
             return;
         }
@@ -459,7 +460,8 @@ export class VexFlowConverter {
         for (const articulation of articulations) {
             // tslint:disable-next-line:switch-default
             let vfArt: Vex.Flow.Articulation = undefined;
-            switch (articulation) {
+            const articulationEnum: ArticulationEnum = articulation.articulationEnum;
+            switch (articulationEnum) {
                 case ArticulationEnum.accent: {
                     vfArt = new Vex.Flow.Articulation("a>");
                     break;
@@ -520,6 +522,11 @@ export class VexFlowConverter {
                     break;
                 }
             }
+            if (articulation.placement === PlacementEnum.Above) {
+                vfArtPosition = Vex.Flow.Modifier.Position.ABOVE;
+            } else {
+                vfArtPosition = Vex.Flow.Modifier.Position.BELOW;
+            }
             if (vfArt) {
                 vfArt.setPosition(vfArtPosition);
                 (vfnote as StaveNote).addModifier(0, vfArt);

+ 3 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -16,7 +16,7 @@ import { OctaveEnum, OctaveShift } from "../../VoiceData/Expressions/ContinuousE
 import { Fraction } from "../../../Common/DataObjects/Fraction";
 import { LyricWord } from "../../VoiceData/Lyrics/LyricsWord";
 import { OrnamentContainer } from "../../VoiceData/OrnamentContainer";
-import { ArticulationEnum } from "../../VoiceData/VoiceEntry";
+import { Articulation } from "../../VoiceData/Articulation";
 import { Tuplet } from "../../VoiceData/Tuplet";
 import { VexFlowMeasure } from "./VexFlowMeasure";
 import { VexFlowTextMeasurer } from "./VexFlowTextMeasurer";
@@ -458,7 +458,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * @param voiceEntry
    * @param graphicalStaffEntry
    */
-  protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+  protected layoutArticulationMarks(articulations: Articulation[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
     // uncomment this when implementing:
     // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);
 
@@ -927,7 +927,7 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
    * @param voiceEntry
    * @param graphicalStaffEntry
    */
-  protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
+  protected handleVoiceEntryArticulations(articulations: Articulation[],
                                           voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
     // uncomment this when implementing:
     // let vfse: VexFlowStaffEntry = (graphicalStaffEntry as VexFlowStaffEntry);

+ 24 - 9
src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts

@@ -5,6 +5,7 @@ import {TechnicalInstruction, TechnicalInstructionType} from "../../VoiceData/In
 import {OrnamentContainer, OrnamentEnum} from "../../VoiceData/OrnamentContainer";
 import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {AccidentalEnum} from "../../../Common/DataObjects/Pitch";
+import { Articulation } from "../../VoiceData/Articulation";
 export class ArticulationReader {
 
   private getAccEnumFromString(input: string): AccidentalEnum {
@@ -50,25 +51,30 @@ export class ArticulationReader {
           name = name.replace("-", "");
           let articulationEnum: ArticulationEnum = ArticulationEnum[name];
           if (VoiceEntry.isSupportedArticulation(articulationEnum)) {
+            let placement: PlacementEnum = PlacementEnum.Above;
+            if (childNode.attribute("placement")?.value === "below") {
+              placement = PlacementEnum.Below;
+            }
+            const newArticulation: Articulation = new Articulation(articulationEnum, placement);
             // staccato should be first // necessary?
             if (name === "staccato") {
               if (currentVoiceEntry.Articulations.length > 0 &&
-                currentVoiceEntry.Articulations[0] !== ArticulationEnum.staccato) {
-                currentVoiceEntry.Articulations.splice(0, 0, articulationEnum); // TODO can't this overwrite another articulation?
+                currentVoiceEntry.Articulations[0].articulationEnum !== ArticulationEnum.staccato) {
+                currentVoiceEntry.Articulations.splice(0, 0, newArticulation); // TODO can't this overwrite another articulation?
               }
             }
             if (name === "strongaccent") { // see name.replace("-", "") above
               const marcatoType: string = childNode?.attribute("type")?.value;
               if (marcatoType === "up") {
-                articulationEnum = ArticulationEnum.marcatoup;
+                newArticulation.articulationEnum = ArticulationEnum.marcatoup;
               } else if (marcatoType === "down") {
-                articulationEnum = ArticulationEnum.marcatodown;
+                newArticulation.articulationEnum = ArticulationEnum.marcatodown;
               }
             }
 
             // don't add the same articulation twice
-            if (currentVoiceEntry.Articulations.indexOf(articulationEnum) === -1) {
-              currentVoiceEntry.Articulations.push(articulationEnum);
+            if (!currentVoiceEntry.hasArticulation(newArticulation)) {
+              currentVoiceEntry.Articulations.push(newArticulation);
             }
           }
         } catch (ex) {
@@ -93,8 +99,12 @@ export class ArticulationReader {
         articulationEnum = ArticulationEnum.invertedfermata;
       }
     }
+    let placement: PlacementEnum = PlacementEnum.Above;
+    if (xmlNode.attribute("placement")?.value === "below") {
+      placement = PlacementEnum.Below;
+    }
     // add to VoiceEntry
-    currentVoiceEntry.Articulations.push(articulationEnum);
+    currentVoiceEntry.Articulations.push(new Articulation(articulationEnum, placement));
   }
 
   /**
@@ -123,8 +133,13 @@ export class ArticulationReader {
       const articulationEnum: ArticulationEnum = xmlElementToArticulationEnum[xmlArticulation];
       const node: IXmlElement = xmlNode.element(xmlArticulation);
       if (node) {
-        if (currentVoiceEntry.Articulations.indexOf(articulationEnum) === -1) {
-          currentVoiceEntry.Articulations.push(articulationEnum);
+        let placement: PlacementEnum = PlacementEnum.Above;
+        if (node.attribute("placement").value === "below") {
+          placement = PlacementEnum.Below;
+        }
+        const newArticulation: Articulation = new Articulation(articulationEnum, placement);
+        if (!currentVoiceEntry.hasArticulation(newArticulation)) {
+          currentVoiceEntry.Articulations.push(newArticulation);
         }
       }
     }

+ 16 - 0
src/MusicalScore/VoiceData/Articulation.ts

@@ -0,0 +1,16 @@
+import { PlacementEnum } from "./Expressions/AbstractExpression";
+import { ArticulationEnum } from "./VoiceEntry";
+
+export class Articulation {
+    public placement: PlacementEnum = PlacementEnum.Above;
+    public articulationEnum: ArticulationEnum;
+
+    constructor(articulationEnum: ArticulationEnum, placement: PlacementEnum = PlacementEnum.Above) {
+        this.articulationEnum = articulationEnum;
+        this.placement = placement;
+    }
+
+    public Equals(otherArticulation: Articulation): boolean {
+        return otherArticulation.articulationEnum === this.articulationEnum && otherArticulation.placement === this.placement;
+    }
+}

+ 17 - 8
src/MusicalScore/VoiceData/VoiceEntry.ts

@@ -12,6 +12,7 @@ import {AccidentalEnum} from "../../Common/DataObjects/Pitch";
 import { Dictionary } from "typescript-collections";
 import {Arpeggio} from "./Arpeggio";
 import { SourceMeasure } from "./SourceMeasure";
+import { Articulation } from "./Articulation";
 
 /**
  * A [[VoiceEntry]] contains the notes in a voice at a timestamp.
@@ -45,7 +46,7 @@ export class VoiceEntry {
     private graceAfterMainNote: boolean;
     private graceNoteSlash: boolean;
     private graceSlur: boolean; // TODO grace slur system could be refined to be non-binary
-    private articulations: ArticulationEnum[] = [];
+    private articulations: Articulation[] = [];
     private technicalInstructions: TechnicalInstruction[] = [];
     private lyricsEntries: Dictionary<number, LyricsEntry> = new Dictionary<number, LyricsEntry>();
     /** The Arpeggio consisting of this VoiceEntry's notes. Undefined if no arpeggio exists. */
@@ -99,7 +100,7 @@ export class VoiceEntry {
     public set GraceSlur(value: boolean) {
         this.graceSlur = value;
     }
-    public get Articulations(): ArticulationEnum[] {
+    public get Articulations(): Articulation[] {
         return this.articulations;
     }
     public get TechnicalInstructions(): TechnicalInstruction[] {
@@ -155,6 +156,14 @@ export class VoiceEntry {
         this.stemColor = value;
     }
 
+    public hasArticulation(articulation: Articulation) {
+        for (const existingArticulation of this.articulations) {
+            if (existingArticulation.Equals(articulation)) {
+                return true;
+            }
+        }
+        return false;
+    }
     public static isSupportedArticulation(articulation: ArticulationEnum): boolean {
         switch (articulation) {
             case ArticulationEnum.accent:
@@ -194,16 +203,16 @@ export class VoiceEntry {
         return false;
     }
     public isStaccato(): boolean {
-        for (let idx: number = 0, len: number = this.Articulations.length; idx < len; ++idx) {
-            const articulation: ArticulationEnum = this.Articulations[idx];
-            if (articulation === ArticulationEnum.staccato) { return true; }
+        for (const articulation of this.Articulations) {
+            if (articulation.articulationEnum === ArticulationEnum.staccato) {
+                return true;
+            }
         }
         return false;
     }
     public isAccent(): boolean {
-        for (let idx: number = 0, len: number = this.Articulations.length; idx < len; ++idx) {
-            const articulation: ArticulationEnum = this.Articulations[idx];
-            if (articulation === ArticulationEnum.accent || articulation === ArticulationEnum.strongaccent) {
+        for (const articulation of this.Articulations) {
+            if (articulation.articulationEnum === ArticulationEnum.accent || articulation.articulationEnum === ArticulationEnum.strongaccent) {
                 return true;
             }
         }