Repository: opensheetmusicdisplay/opensheetmusicdisplay
Pull request #811 (2020-06-25 19:34:23)

fix branch name
git fetch and git pull created an error because feat/tabs existed beforehand, apparently, and feat/tabs/bend-release makes tabs a directory instead of a branch now.

-> deleate feat/tabs/bend-release, keep feat/tab/bend-release


Patch:
diff --git a/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts b/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
index 16d95805..4eee36d4 100644
--- a/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
+++ b/src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
@@ -528,15 +528,36 @@ export class VexFlowConverter {
      */
     public static CreateTabNote(gve: GraphicalVoiceEntry): Vex.Flow.TabNote {
         const tabPositions: {str: number, fret: number}[] = [];
+        const tabPhrases: { type: number, text: string, width: number }[] = [];
         const frac: Fraction = gve.notes[0].graphicalNoteLength;
         const isTuplet: boolean = gve.notes[0].sourceNote.NoteTuplet !== undefined;
         let duration: string = VexFlowConverter.duration(frac, isTuplet);
         let numDots: number = 0;
+        let tabVibrato: boolean = false;
         for (const note of gve.notes) {
             const tabNote: TabNote = note.sourceNote as TabNote;
             const tabPosition: {str: number, fret: number} = {str: tabNote.StringNumber, fret: tabNote.FretNumber};
             tabPositions.push(tabPosition);
+            tabNote.BendArray.forEach( function( bend: {bendalter: number, direction: string} ): void {
+                let phraseText: string;
+                const phraseStep: number = bend.bendalter - tabPosition.fret;
+                if (phraseStep > 1) {
+                    phraseText = "Full";
+                } else if (phraseStep === 1) {
+                    phraseText = "1/2";
+                } else {
+                    phraseText = "1/4";
+                }
+                if (bend.direction === "up") {
+                    tabPhrases.push({type: Vex.Flow.Bend.UP, text: phraseText, width: 10});
+                } else {
+                    tabPhrases.push({type: Vex.Flow.Bend.DOWN, text: phraseText, width: 10});
+                }
+            });
 
+            if (tabNote.VibratoStroke) {
+                tabVibrato = true;
+            }
             if (numDots < note.numberOfDots) {
                 numDots = note.numberOfDots;
             }
@@ -548,6 +569,22 @@ export class VexFlowConverter {
             duration: duration,
             positions: tabPositions,
         });
+        tabPhrases.forEach(function(phrase: { type: number, text: string, width: number }): void {
+            if (phrase.type === Vex.Flow.Bend.UP) {
+                vfnote.addModifier (new Vex.Flow.Bend(phrase.text, false));
+            } else {
+                vfnote.addModifier (new Vex.Flow.Bend(phrase.text, true));
+            }
+        });
+        // does not work well to add phrases as array
+        /*
+        if (tabPhrases.length > 0) {
+           vfnote.addModifier (new Vex.Flow.Bend(undefined, undefined, tabPhrases), 1);
+        }
+        */
+        if (tabVibrato) {
+            vfnote.addModifier(new Vex.Flow.Vibrato());
+        }
 
         return vfnote;
     }
diff --git a/src/MusicalScore/ScoreIO/InstrumentReader.ts b/src/MusicalScore/ScoreIO/InstrumentReader.ts
index 286d1d37..9fb3db89 100644
--- a/src/MusicalScore/ScoreIO/InstrumentReader.ts
+++ b/src/MusicalScore/ScoreIO/InstrumentReader.ts
@@ -286,6 +286,7 @@ export class InstrumentReader {
           // check Tremolo
           let tremoloStrokes: number = 0;
           if (notationsNode) {
+            let vibratoStrokes: boolean = false;
             const ornamentsNode: IXmlElement = notationsNode.element("ornaments");
             if (ornamentsNode) {
               const tremoloNode: IXmlElement = ornamentsNode.element("tremolo");
@@ -299,6 +300,13 @@ export class InstrumentReader {
                 }
                 // TODO implement type "start". Vexflow doesn't have tremolo beams yet though (shorter than normal beams)
               }
+              const vibratoNode: IXmlElement = ornamentsNode.element("wavy-line");
+              if (vibratoNode !== undefined) {
+                const vibratoType: Attr = vibratoNode.attribute("type");
+                if (vibratoType && vibratoType.value === "start") {
+                  vibratoStrokes = true;
+                }
+              }
             }
           }
 
@@ -379,7 +387,8 @@ export class InstrumentReader {
             this.currentStaffEntry, this.currentMeasure,
             measureStartAbsoluteTimestamp,
             this.maxTieNoteFraction, isChord, guitarPro,
-            printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml
+            printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml,
+            vibratoStrokes
           );
 
           // notationsNode created further up for multiple checks
diff --git a/src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts b/src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
index 94ce64a2..2ec72c55 100644
--- a/src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
+++ b/src/MusicalScore/ScoreIO/MusicSymbolModules/ArticulationReader.ts
@@ -99,6 +99,7 @@ export class ArticulationReader {
       [xmlElement: string]: ArticulationEnum;
     }
     const xmlElementToArticulationEnum: XMLElementToArticulationEnum = {
+      "bend": ArticulationEnum.bend,
       "down-bow": ArticulationEnum.downbow,
       "open-string": ArticulationEnum.naturalharmonic,
       "snap-pizzicato": ArticulationEnum.snappizzicato,
diff --git a/src/MusicalScore/ScoreIO/VoiceGenerator.ts b/src/MusicalScore/ScoreIO/VoiceGenerator.ts
index 704ef7eb..581969b2 100644
--- a/src/MusicalScore/ScoreIO/VoiceGenerator.ts
+++ b/src/MusicalScore/ScoreIO/VoiceGenerator.ts
@@ -109,7 +109,7 @@ export class VoiceGenerator {
               parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
               measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean,
               printObject: boolean, isCueNote: boolean, stemDirectionXml: StemDirectionType, tremoloStrokes: number,
-              stemColorXml: string, noteheadColorXml: string): Note {
+              stemColorXml: string, noteheadColorXml: string, vibratoStrokes: boolean): Note {
     this.currentStaffEntry = parentStaffEntry;
     this.currentMeasure = parentMeasure;
     //log.debug("read called:", restNote);
@@ -117,7 +117,7 @@ export class VoiceGenerator {
       this.currentNote = restNote
         ? this.addRestNote(noteDuration, noteTypeXml, printObject, isCueNote, noteheadColorXml)
         : this.addSingleNote(noteNode, noteDuration, noteTypeXml, typeDuration, normalNotes, chord, guitarPro,
-                             printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml);
+                             printObject, isCueNote, stemDirectionXml, tremoloStrokes, stemColorXml, noteheadColorXml, vibratoStrokes);
       // read lyrics
       const lyricElements: IXmlElement[] = noteNode.elements("lyric");
       if (this.lyricsReader !== undefined && lyricElements) {
@@ -346,7 +346,7 @@ export class VoiceGenerator {
   private addSingleNote(node: IXmlElement, noteDuration: Fraction, noteTypeXml: NoteType, typeDuration: Fraction,
                         normalNotes: number, chord: boolean, guitarPro: boolean,
                         printObject: boolean, isCueNote: boolean, stemDirectionXml: StemDirectionType, tremoloStrokes: number,
-                        stemColorXml: string, noteheadColorXml: string): Note {
+                        stemColorXml: string, noteheadColorXml: string, vibratoStrokes: boolean): Note {
     //log.debug("addSingleNote called");
     let noteAlter: number = 0;
     let noteAccidental: AccidentalEnum = AccidentalEnum.NONE;
@@ -440,6 +440,7 @@ export class VoiceGenerator {
     let note: Note = undefined;
     let stringNumber: number = -1;
     let fretNumber: number = -1;
+    const bends: {bendalter: number, direction: string}[] = [];
     // check for guitar tabs:
     const notationNode: IXmlElement = node.element("notations");
     if (notationNode) {
@@ -453,6 +454,16 @@ export class VoiceGenerator {
         if (fretNode) {
           fretNumber = parseInt(fretNode.value, 10);
         }
+        const bendElementsArr: IXmlElement[] = technicalNode.elements("bend");
+        bendElementsArr.forEach(function (bend: IXmlElement): void {
+            const bendalterNote: IXmlElement = bend.element("bend-alter");
+            const releaseNode: IXmlElement = bend.element("release");
+            if (releaseNode !== undefined) {
+              bends.push({bendalter: parseInt (bendalterNote.value, 10), direction: "down"});
+            } else {
+              bends.push({bendalter: parseInt (bendalterNote.value, 10), direction: "up"});
+            }
+          });
       }
     }
 
@@ -461,7 +472,7 @@ export class VoiceGenerator {
       note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
     } else {
       // create TabNote
-      note = new TabNote(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch, stringNumber, fretNumber);
+      note = new TabNote(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch, stringNumber, fretNumber, bends, vibratoStrokes);
     }
 
     note.TypeLength = typeDuration;
diff --git a/src/MusicalScore/VoiceData/TabNote.ts b/src/MusicalScore/VoiceData/TabNote.ts
index 01d9865b..7e95f3cd 100644
--- a/src/MusicalScore/VoiceData/TabNote.ts
+++ b/src/MusicalScore/VoiceData/TabNote.ts
@@ -5,14 +5,20 @@ import { SourceStaffEntry } from "./SourceStaffEntry";
 import { Pitch } from "../../Common/DataObjects/Pitch";
 
 export class TabNote extends Note {
-    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch, stringNumber: number, fretNumber: number) {
+    constructor(voiceEntry: VoiceEntry, parentStaffEntry: SourceStaffEntry, length: Fraction, pitch: Pitch,
+                stringNumber: number, fretNumber: number, bendArray: { bendalter: number, direction: string }[],
+                vibratoStroke: boolean) {
         super(voiceEntry, parentStaffEntry, length, pitch);
         this.stringNumber = stringNumber;
         this.fretNumber = fretNumber;
+        this.bendArray = bendArray;
+        this.vibratoStroke = vibratoStroke;
     }
 
     private stringNumber: number;
     private fretNumber: number;
+    private bendArray: { bendalter: number, direction: string }[];
+    private vibratoStroke: boolean;
 
     public get StringNumber(): number {
         return this.stringNumber;
@@ -21,4 +27,12 @@ export class TabNote extends Note {
     public get FretNumber(): number {
         return this.fretNumber;
     }
+
+    public get BendArray(): { bendalter: number, direction: string }[] {
+        return this.bendArray;
+    }
+
+    public get VibratoStroke(): boolean {
+        return this.vibratoStroke;
+    }
 }
diff --git a/src/MusicalScore/VoiceData/VoiceEntry.ts b/src/MusicalScore/VoiceData/VoiceEntry.ts
index 5bc749b5..436e0611 100644
--- a/src/MusicalScore/VoiceData/VoiceEntry.ts
+++ b/src/MusicalScore/VoiceData/VoiceEntry.ts
@@ -172,6 +172,7 @@ export class VoiceEntry {
             case ArticulationEnum.snappizzicato:
             case ArticulationEnum.upbow:
             case ArticulationEnum.downbow:
+            case ArticulationEnum.bend:
                 return true;
             default:
                 return false;
@@ -400,7 +401,8 @@ export enum ArticulationEnum {
     stress,
     unstress,
     detachedlegato,
-    otherarticulation
+    otherarticulation,
+    bend
 }
 
 export enum StemDirectionType {


Test patch:
diff --git a/test/Util/generateImages_browserless.js b/test/Util/generateImages_browserless.js
index c72d26a9..3b640a92 100644
--- a/test/Util/generateImages_browserless.js
+++ b/test/Util/generateImages_browserless.js
@@ -6,11 +6,9 @@
 
   This can be used to generate PNGs from OSMD without a browser.
   It's also used with the visual regression test system in
-  `tools/visual_regression.sh`
-  (see package.json, used with npm run generate:blessed and generate:current, then test:visual).
+  `tools/visual_regression.sh`.
 
-  Note: this script needs to "fake" quite a few browser elements, like window, document, and a Canvas HTMLElement,
-  which otherwise are missing in pure nodejs, causing errors in OSMD.
+  Note: this script needs to "fake" quite a few browser elements, like window, document, and a Canvas HTMLElement.
   For that it needs the canvas package installed.
   There are also some hacks needed to set the container size (offsetWidth) correctly.