Ver Fonte

feat(invisible notes): grace notes can be invisible. Invisible notes are parsed again, with invisible (printObject) flag

sschmidTU há 6 anos atrás
pai
commit
ccf860eb47

+ 13 - 0
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1100,10 +1100,23 @@ export abstract class MusicSheetCalculator {
                                openTuplets: Tuplet[], openBeams: Beam[],
                                octaveShiftValue: OctaveEnum, linkedNotes: Note[] = undefined,
                                sourceStaffEntry: SourceStaffEntry = undefined): OctaveEnum {
+        let voiceEntryHasPrintableNotes: boolean = false;
+        for (const note of voiceEntry.Notes) {
+            if (note.PrintObject) {
+                voiceEntryHasPrintableNotes = true;
+                break;
+            }
+        }
+        if (!voiceEntryHasPrintableNotes) {
+            return; // do not create a GraphicalVoiceEntry without graphical notes in it, will cause problems
+        }
         this.calculateStemDirectionFromVoices(voiceEntry);
         const gve: GraphicalVoiceEntry = graphicalStaffEntry.findOrCreateGraphicalVoiceEntry(voiceEntry);
         for (let idx: number = 0, len: number = voiceEntry.Notes.length; idx < len; ++idx) {
             const note: Note = voiceEntry.Notes[idx];
+            if (note === undefined || !note.PrintObject) {
+                continue;
+            }
             if (sourceStaffEntry !== undefined && sourceStaffEntry.Link !== undefined && linkedNotes !== undefined && linkedNotes.indexOf(note) > -1) {
                 continue;
             }

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

@@ -625,11 +625,18 @@ export class VexFlowMeasure extends GraphicalMeasure {
                     }
                     continue;
                 }
-                (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
+                if (gve.notes[0].sourceNote.PrintObject) {
+                    (gve as VexFlowVoiceEntry).vfStaveNote = VexFlowConverter.StaveNote(gve);
+                } else {
+                    graceGVoiceEntriesBefore = []; // if note is not rendered, its grace notes might need to be removed
+                    continue;
+                }
                 if (graceGVoiceEntriesBefore.length > 0) {
                     const graceNotes: Vex.Flow.GraceNote[] = [];
                     for (let i: number = 0; i < graceGVoiceEntriesBefore.length; i++) {
-                        graceNotes.push(VexFlowConverter.StaveNote(graceGVoiceEntriesBefore[i]));
+                        if (graceGVoiceEntriesBefore[i].notes[0].sourceNote.PrintObject) {
+                            graceNotes.push(VexFlowConverter.StaveNote(graceGVoiceEntriesBefore[i]));
+                        }
                     }
                     const graceNoteGroup: Vex.Flow.GraceNoteGroup = new Vex.Flow.GraceNoteGroup(graceNotes, graceSlur);
                     (gve as VexFlowVoiceEntry).vfStaveNote.addModifier(0, graceNoteGroup.beamNotes());

+ 6 - 8
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -132,15 +132,13 @@ export class InstrumentReader {
       const xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
       for (const xmlNode of xmlMeasureListArr) {
         if (xmlNode.name === "note") {
+          let printObject: boolean = true;
           if (xmlNode.hasAttributes && xmlNode.attribute("print-object") &&
               xmlNode.attribute("print-object").value === "no") {
-            // TODO handle invisible notes. should be parsed, optionally displayed via DrawingParameter.
-            // if (xmlNode.attribute("print-spacing")) {
-            //   if (xmlNode.attribute("print-spacing").value === "yes" {
-            //     // give spacing for invisible notes even when not displayed
-            //   else {
-            //     // don't print and don't give spacing. might still need to parse to keep measure model intact
-            continue; // TODO for now, just skip invisible notes. remove continue when implementing display invisible notes option
+              printObject = false; // note will not be rendered, but still parsed for Playback etc.
+              // if (xmlNode.attribute("print-spacing")) {
+              //   if (xmlNode.attribute("print-spacing").value === "yes" {
+              //     // TODO give spacing for invisible notes even when not displayed. might be hard with Vexflow formatting
           }
           let noteStaff: number = 1;
           if (this.instrument.Staves.length > 1) {
@@ -266,7 +264,7 @@ export class InstrumentReader {
             xmlNode, noteDuration, restNote,
             this.currentStaffEntry, this.currentMeasure,
             measureStartAbsoluteTimestamp,
-            this.maxTieNoteFraction, isChord, guitarPro
+            this.maxTieNoteFraction, isChord, guitarPro, printObject
           );
 
           const notationsNode: IXmlElement = xmlNode.element("notations");

+ 10 - 5
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -99,18 +99,20 @@ export class VoiceGenerator {
    * @param maxTieNoteFraction
    * @param chord
    * @param guitarPro
+   * @param printObject whether the note should be rendered (true) or invisible (false)
    * @returns {Note}
    */
   public read(noteNode: IXmlElement, noteDuration: Fraction, restNote: boolean,
               parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
-              measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean): Note {
+              measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean,
+              printObject: boolean = true): Note {
     this.currentStaffEntry = parentStaffEntry;
     this.currentMeasure = parentMeasure;
     //log.debug("read called:", restNote);
     try {
       this.currentNote = restNote
-        ? this.addRestNote(noteDuration)
-        : this.addSingleNote(noteNode, noteDuration, chord, guitarPro);
+        ? this.addRestNote(noteDuration, printObject)
+        : this.addSingleNote(noteNode, noteDuration, chord, guitarPro, printObject);
       // read lyrics
       const lyricElements: IXmlElement[] = noteNode.elements("lyric");
       if (this.lyricsReader !== undefined && lyricElements !== undefined) {
@@ -294,7 +296,8 @@ export class VoiceGenerator {
    * @param guitarPro
    * @returns {Note}
    */
-  private addSingleNote(node: IXmlElement, noteDuration: Fraction, chord: boolean, guitarPro: boolean): Note {
+  private addSingleNote(node: IXmlElement, noteDuration: Fraction, chord: boolean, guitarPro: boolean,
+                        printObject: boolean = true): Note {
     //log.debug("addSingleNote called");
     let noteAlter: number = 0;
     let noteAccidental: AccidentalEnum = AccidentalEnum.NONE;
@@ -387,6 +390,7 @@ export class VoiceGenerator {
     const pitch: Pitch = new Pitch(noteStep, noteOctave, noteAccidental);
     const noteLength: Fraction = Fraction.createFromFraction(noteDuration);
     const note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
+    note.PrintObject = printObject;
     note.PlaybackInstrumentId = playbackInstrumentId;
     if (noteHeadShapeXml !== undefined && noteHeadShapeXml !== "normal") {
       note.NoteHead = new NoteHead(note, noteHeadShapeXml, noteHeadFilledXml);
@@ -404,9 +408,10 @@ export class VoiceGenerator {
    * @param divisions
    * @returns {Note}
    */
-  private addRestNote(noteDuration: Fraction): Note {
+  private addRestNote(noteDuration: Fraction, printObject: boolean = true): Note {
     const restFraction: Fraction = Fraction.createFromFraction(noteDuration);
     const restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
+    restNote.PrintObject = printObject;
     this.currentVoiceEntry.Notes.push(restNote);
     if (this.openBeam !== undefined) {
       this.openBeam.ExtendedNoteList.push(restNote);

+ 9 - 0
src/MusicalScore/VoiceData/Note.ts

@@ -45,6 +45,8 @@ export class Note {
     private slurs: Slur[] = [];
     private playbackInstrumentId: string = undefined;
     private noteHead: NoteHead = undefined;
+    /** States whether the note should be displayed. False if xmlNode.attribute("print-object").value = "no". */
+    private printObject: boolean = true;
 
 
     public get ParentVoiceEntry(): VoiceEntry {
@@ -104,6 +106,13 @@ export class Note {
     public get NoteHead(): NoteHead {
         return this.noteHead;
     }
+    public get PrintObject(): boolean {
+        return this.printObject;
+    }
+
+    public set PrintObject(value: boolean) {
+        this.printObject = value;
+    }
 
     public isRest(): boolean {
         return this.Pitch === undefined;