Procházet zdrojové kódy

feat(fingering): draw fingering using Vexflow (left of note), can be disabled by option

add EngravingRule, IOSMDOptions option for draw/renderFingering
demo: OSMD Function Test - All: improve fingering measure (add chord)
sschmidTU před 6 roky
rodič
revize
628562bdc2

+ 1 - 0
demo/index.js

@@ -133,6 +133,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
             drawPartNames: true, // try false
             // drawTitle: false,
             // drawSubtitle: false,
+            drawFingerings: true,
 
             // tupletsBracketed: true,
             // tripletsBracketed: true,

+ 6 - 11
external/vexflow/vexflow.d.ts

@@ -219,6 +219,12 @@ declare namespace Vex {
             public getPosition(): number;
 
             public setPosition(position: number): Modifier;
+
+            public setIndex(index: number): void;
+        }
+
+        export class FretHandFinger extends Modifier {
+            constructor(finger: string);
         }
 
         export class NoteSubGroup extends Modifier {
@@ -226,17 +232,6 @@ declare namespace Vex {
         }
 
         export class StaveModifier extends Modifier {
-            // public static get Position() {
-            //     return {
-            //         LEFT: 1,
-            //         RIGHT: 2,
-            //         ABOVE: 3,
-            //         BELOW: 4,
-            //         BEGIN: 5,
-            //         END: 6,
-            //     };
-            // }
-
             public getPosition(): number;
 
         }

+ 8 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -165,6 +165,7 @@ export class EngravingRules {
     private renderSubtitle: boolean;
     private renderLyricist: boolean;
     private renderInstrumentNames: boolean;
+    private renderFingerings: boolean;
 
     constructor() {
         // global variables
@@ -349,6 +350,7 @@ export class EngravingRules {
         this.renderSubtitle = true;
         this.renderLyricist = true;
         this.renderInstrumentNames = true;
+        this.renderFingerings = true;
 
         this.populateDictionaries();
         try {
@@ -1251,6 +1253,12 @@ export class EngravingRules {
     public set RenderInstrumentNames(value: boolean) {
         this.renderInstrumentNames = value;
     }
+    public get RenderFingerings(): boolean {
+        return this.renderFingerings;
+    }
+    public set RenderFingerings(value: boolean) {
+        this.renderFingerings = value;
+    }
 
     /**
      * This method maps NoteDurations to Distances and DistancesScalingFactors.

+ 18 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -19,7 +19,7 @@ import NoteSubGroup = Vex.Flow.NoteSubGroup;
 import * as log from "loglevel";
 import {unitInPixels} from "./VexFlowMusicSheetDrawer";
 import {Tuplet} from "../../VoiceData/Tuplet";
-import { RepetitionInstructionEnum, RepetitionInstruction, AlignmentType } from "../../VoiceData/Instructions/RepetitionInstruction";
+import {RepetitionInstructionEnum, RepetitionInstruction, AlignmentType} from "../../VoiceData/Instructions/RepetitionInstruction";
 import {SystemLinePosition} from "../SystemLinePosition";
 import {StemDirectionType} from "../../VoiceData/VoiceEntry";
 import {GraphicalVoiceEntry} from "../GraphicalVoiceEntry";
@@ -29,6 +29,8 @@ import {Voice} from "../../VoiceData/Voice";
 import {VexFlowInstantaneousDynamicExpression} from "./VexFlowInstantaneousDynamicExpression";
 import {LinkedVoice} from "../../VoiceData/LinkedVoice";
 import {EngravingRules} from "../EngravingRules";
+import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
+import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
 
 export class VexFlowMeasure extends GraphicalMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -691,6 +693,18 @@ export class VexFlowMeasure extends GraphicalMeasure {
                         vexFlowVoiceEntry.vfStaveNote.addModifier(0, clefModifier);
                     }
                 }
+
+                // add fingering
+                if (voiceEntry.parentVoiceEntry && EngravingRules.Rules.RenderFingerings) {
+                    const technicalInstructions: TechnicalInstruction[] = voiceEntry.parentVoiceEntry.TechnicalInstructions;
+                    for (let i: number = 0; i < technicalInstructions.length; i++) {
+                        const technicalInstruction: TechnicalInstruction = technicalInstructions[i];
+                        const fretFinger: Vex.Flow.FretHandFinger = new Vex.Flow.FretHandFinger(technicalInstruction.value);
+                        fretFinger.setPosition(Vex.Flow.Modifier.Position.LEFT); // could be EngravingRule, though ABOVE doesn't work for chords
+                        vexFlowVoiceEntry.vfStaveNote.addModifier(i, fretFinger);
+                    }
+                }
+
                 this.vfVoices[voice.VoiceId].addTickable(vexFlowVoiceEntry.vfStaveNote);
             }
         }
@@ -728,8 +742,9 @@ export class VexFlowMeasure extends GraphicalMeasure {
             for (const voiceID in gvoices) {
                 if (gvoices.hasOwnProperty(voiceID)) {
                     const vfStaveNote: StemmableNote = (gvoices[voiceID] as VexFlowVoiceEntry).vfStaveNote;
-                    if (gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer !== undefined) {
-                        VexFlowConverter.generateOrnaments(vfStaveNote, gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer);
+                    const ornamentContainer: OrnamentContainer = gvoices[voiceID].notes[0].sourceNote.ParentVoiceEntry.OrnamentContainer;
+                    if (ornamentContainer !== undefined) {
+                        VexFlowConverter.generateOrnaments(vfStaveNote, ornamentContainer);
                     }
                 }
             }

+ 4 - 2
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -26,10 +26,12 @@ export interface IOSMDOptions {
     drawSubtitle?: boolean;
     /** Whether to draw credits (title, composer, arranger, copyright etc., see <credit>. Not yet supported. */ // TODO
     drawCredits?: boolean;
-    /** Whether to draw part (instrument) names. */
-    drawPartNames?: boolean;
     /** Whether to draw the lyricist's name, if given. */
     drawLyricist?: boolean;
+    /** Whether to draw part (instrument) names. */
+    drawPartNames?: boolean;
+    /** Whether to draw fingerings (only left to the note for now). Default true. */
+    drawFingerings?: boolean;
     /** Whether tuplets are labeled with ratio (e.g. 5:2 instead of 5 for quintuplets). Default false. */
     tupletsRatioed?: boolean;
     /** Whether all tuplets should be bracketed (e.g. |--5--| instead of 5). Default false.

+ 8 - 3
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -334,6 +334,8 @@ export class OpenSheetMusicDisplay {
         if (options.disableCursor) {
             this.drawingParameters.drawCursors = false;
         }
+        // alternative to if block: this.drawingsParameters.drawCursors = options.drawCursors !== false. No if, but always sets drawingParameters.
+        // note that every option can be undefined, which doesn't mean the option should be set to false.
         if (options.drawHiddenNotes) {
             this.drawingParameters.drawHiddenNotes = true;
         }
@@ -346,15 +348,18 @@ export class OpenSheetMusicDisplay {
         if (options.drawSubtitle !== undefined) {
             this.drawingParameters.DrawSubtitle = options.drawSubtitle;
         }
-        if (options.drawPartNames !== undefined) {
-            this.drawingParameters.DrawPartNames = options.drawPartNames;
-        }
         if (options.drawLyricist !== undefined) {
             this.drawingParameters.DrawLyricist = options.drawLyricist;
         }
         if (options.drawCredits !== undefined) {
             this.drawingParameters.drawCredits = options.drawCredits;
         }
+        if (options.drawPartNames !== undefined) {
+            this.drawingParameters.DrawPartNames = options.drawPartNames;
+        }
+        if (options.drawFingerings === false) {
+            EngravingRules.Rules.RenderFingerings = false;
+        }
         if (options.defaultColorNoteHead) {
             this.drawingParameters.defaultColorNoteHead = options.defaultColorNoteHead;
         }

+ 48 - 8
test/data/OSMD_function_test_all.xml

@@ -761,7 +761,7 @@
         </note>
       </measure>
     <measure number="15" width="318.48">
-      <note default-x="47.11" default-y="-30.00">
+      <note default-x="110.28" default-y="-30.00">
         <pitch>
           <step>G</step>
           <octave>4</octave>
@@ -772,7 +772,7 @@
         <stem>up</stem>
         <notations>
           <technical>
-            <fingering>0</fingering>
+            <fingering>1</fingering>
             </technical>
           </notations>
         <lyric number="1" default-x="6.22" default-y="-80.00">
@@ -780,7 +780,39 @@
           <text>Fingering</text>
           </lyric>
         </note>
-      <note default-x="122.90" default-y="-35.00">
+      <note default-x="110.28" default-y="-20.00">
+        <chord/>
+        <pitch>
+          <step>B</step>
+          <octave>4</octave>
+          </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <technical>
+            <fingering>2</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="110.28" default-y="-10.00">
+        <chord/>
+        <pitch>
+          <step>D</step>
+          <octave>5</octave>
+          </pitch>
+        <duration>8</duration>
+        <voice>1</voice>
+        <type>half</type>
+        <stem>up</stem>
+        <notations>
+          <technical>
+            <fingering>3</fingering>
+            </technical>
+          </notations>
+        </note>
+      <note default-x="387.67" default-y="-35.00">
         <pitch>
           <step>F</step>
           <octave>4</octave>
@@ -796,7 +828,7 @@
             </technical>
           </notations>
         </note>
-      <note default-x="166.01" default-y="-40.00">
+      <note default-x="545.97" default-y="-40.00">
         <pitch>
           <step>E</step>
           <octave>4</octave>
@@ -812,14 +844,16 @@
             </technical>
           </notations>
         </note>
-      <note default-x="209.11" default-y="-45.00">
+      <note default-x="704.26" default-y="-45.00">
         <pitch>
           <step>D</step>
+          <alter>1</alter>
           <octave>4</octave>
           </pitch>
         <duration>1</duration>
         <voice>1</voice>
         <type>16th</type>
+        <accidental>sharp</accidental>
         <stem>up</stem>
         <beam number="1">begin</beam>
         <beam number="2">begin</beam>
@@ -829,7 +863,7 @@
             </technical>
           </notations>
         </note>
-      <note default-x="236.05" default-y="-40.00">
+      <note default-x="803.20" default-y="-40.00">
         <pitch>
           <step>E</step>
           <octave>4</octave>
@@ -846,7 +880,7 @@
             </technical>
           </notations>
         </note>
-      <note default-x="262.99" default-y="-35.00">
+      <note default-x="902.14" default-y="-35.00">
         <pitch>
           <step>F</step>
           <octave>4</octave>
@@ -863,7 +897,7 @@
             </technical>
           </notations>
         </note>
-      <note default-x="289.93" default-y="-45.00">
+      <note default-x="1001.08" default-y="-45.00">
         <pitch>
           <step>D</step>
           <octave>4</octave>
@@ -871,9 +905,15 @@
         <duration>1</duration>
         <voice>1</voice>
         <type>16th</type>
+        <accidental>natural</accidental>
         <stem>up</stem>
         <beam number="1">end</beam>
         <beam number="2">end</beam>
+        <notations>
+          <technical>
+            <fingering>0</fingering>
+            </technical>
+          </notations>
         </note>
       </measure>
     <measure number="16" width="126.44">