Преглед на файлове

Merge branch 'develop' into feat/spacing/compact-merge-develop

# Conflicts:
#	src/MusicalScore/Graphical/EngravingRules.ts
#	src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

(forgot to commit/push this, did this a while ago)
sschmid преди 5 години
родител
ревизия
fe0e4030e2

+ 9 - 1
src/MusicalScore/Graphical/EngravingRules.ts

@@ -27,6 +27,7 @@ export class EngravingRules {
     private pageTopMargin: number;
     private pageTopMarginNarrow: number;
     private pageBottomMargin: number;
+    private pageBottomExtraWhiteSpace: number; // experimental. extra white space that wil be added below the sheet
     private pageLeftMargin: number;
     private pageRightMargin: number;
     private titleTopDistance: number;
@@ -263,6 +264,7 @@ export class EngravingRules {
         this.pageTopMargin = 5.0;
         this.pageTopMarginNarrow = 0.0; // for compact mode
         this.pageBottomMargin = 5.0;
+        this.pageBottomExtraWhiteSpace = 0.0; // experimental.
         this.pageLeftMargin = 5.0;
         this.pageRightMargin = 5.0;
         this.titleTopDistance = 9.0;
@@ -451,7 +453,7 @@ export class EngravingRules {
         this.metronomeMarksDrawn = true;
         this.metronomeMarkXShift = -6; // our unit, is taken * unitInPixels
         this.metronomeMarkYShift = -0.5;
-        this.softmaxFactorVexFlow = 15; // seems like the sweet spot. Vexflow default is 100.
+        this.softmaxFactorVexFlow = 15; // only applies to Vexflow 3.x. 15 seems like the sweet spot. Vexflow default is 100.
         // if too high, score gets too big, especially half notes. with half note quarter quarter, the quarters get squeezed.
         // if too low, smaller notes aren't positioned correctly.
 
@@ -590,6 +592,12 @@ export class EngravingRules {
     public set PageBottomMargin(value: number) {
         this.pageBottomMargin = value;
     }
+    public get PageBottomExtraWhiteSpace(): number {
+        return this.pageBottomExtraWhiteSpace;
+    }
+    public set PageBottomExtraWhiteSpace(value: number) {
+        this.pageBottomExtraWhiteSpace = value;
+    }
     public get PageLeftMargin(): number {
         return this.pageLeftMargin;
     }

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

@@ -343,7 +343,7 @@ export abstract class MusicSheetCalculator {
      */
     protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
         const staffLine: StaffLine = musicSystem.StaffLines[0];
-        let previousLabelMeasureNumber: number;
+        let previousLabelMeasureNumber: number = staffLine.Measures[0].MeasureNumber;
         let labelOffsetX: number = 0;
         for (const measure of staffLine.Measures) {
             if (measure.MeasureNumber === 0 || measure.MeasureNumber === 1) {

+ 19 - 0
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -1140,6 +1140,25 @@ export class MusicSystemBuilder {
         if (timesPageCouldntFitSingleSystem > 0) {
             console.log(`total amount of pages that couldn't fit a single music system: ${timesPageCouldntFitSingleSystem} of ${currentPage.PageNumber}`);
         }
+        if (this.rules.PageBottomExtraWhiteSpace > 0 && this.graphicalMusicSheet.MusicPages.length === 1) {
+            // experimental, not used unless the EngravingRule is set to > 0 (default 0)
+
+            // calculate last page's bounding box, otherwise it uses this.rules.PageHeight which is 10001
+            currentPage.PositionAndShape.calculateBoundingBox();
+            // TODO currently bugged with PageFormat A3. this squeezes lyrics and notes (with A3 Landscape). why?
+            //   for this reason, the extra white space should currently only be used with the Endless PageFormat,
+            //   and using EngravingRules.PageBottomExtraWhiteSpace should be considered experimental.
+
+            // add this.rules.PageBottomMargin
+            const pageBottomMarginBB: BoundingBox = new BoundingBox(currentPage, currentPage.PositionAndShape, false);
+            // pageBottomMarginBB.RelativePosition.x = 0;
+            pageBottomMarginBB.RelativePosition.y = currentPage.PositionAndShape.BorderMarginBottom;
+            // pageBottomMarginBB.BorderBottom = this.rules.PageBottomMargin;
+            pageBottomMarginBB.BorderBottom = this.rules.PageBottomExtraWhiteSpace;
+            pageBottomMarginBB.calculateBoundingBox();
+            currentPage.PositionAndShape.calculateBoundingBox();
+        }
+
     }
 
     /**

+ 0 - 3
src/MusicalScore/Graphical/SkyBottomLineCalculator.ts

@@ -1,6 +1,3 @@
-/* tslint:disable no-unused-variable */
-//FIXME: Enble tslint again when all functions are implemented and in use!
-
 import { EngravingRules } from "./EngravingRules";
 import { StaffLine } from "./StaffLine";
 import { PointF2D } from "../../Common/DataObjects/PointF2D";

+ 41 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -58,6 +58,10 @@ export class VexFlowConverter {
     public static duration(fraction: Fraction, isTuplet: boolean): string {
       const dur: number = fraction.RealValue;
 
+      if (dur === 2) { // Breve
+        return "1/2";
+      }
+      // TODO consider long (dur=4) and maxima (dur=8), though Vexflow doesn't seem to support them
       if (dur >= 1) {
           return "w";
       } else if (dur < 1 && dur >= 0.5) {
@@ -528,15 +532,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 +573,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;
     }

+ 2 - 13
src/MusicalScore/Graphical/VexFlow/VexFlowMultiRestMeasure.ts

@@ -36,22 +36,11 @@ export class VexFlowMultiRestMeasure extends VexFlowMeasure {
 
         this.resetLayout();
 
-        // type note: Vex.Flow.MultiMeasureRest is not in the DefinitelyTyped definitions yet.
         this.multiRestElement = new Vex.Flow.MultiMeasureRest(sourceMeasure.multipleRestMeasures, {
             // number_line: 3
         });
     }
 
-
-    /**
-     * This method is called after the StaffEntriesScaleFactor has been set.
-     * Here the final x-positions of the staff entries have to be set.
-     * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth)
-     */
-    public layoutSymbols(): void {
-        // vexflow does the x-layout
-    }
-
     /**
      * Draw this measure on a VexFlow CanvasContext
      * @param ctx
@@ -71,14 +60,14 @@ export class VexFlowMultiRestMeasure extends VexFlowMeasure {
     }
 
     public format(): void {
-        // return
+        // like most of the following methods, not necessary / can be simplified for MultiRestMeasure.
     }
 
     /**
      * Returns all the voices that are present in this measure
      */
     public getVoicesWithinMeasure(): Voice[] {
-        return [];
+        return []; // we should still return a list here, not undefined, i guess.
     }
 
     /**

+ 10 - 1
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -285,6 +285,7 @@ export class InstrumentReader {
 
           // check Tremolo
           let tremoloStrokes: number = 0;
+          let vibratoStrokes: boolean = false;
           if (notationsNode) {
             const ornamentsNode: IXmlElement = notationsNode.element("ornaments");
             if (ornamentsNode) {
@@ -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

+ 1 - 0
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,

+ 15 - 4
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;

+ 15 - 1
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;
+    }
 }

+ 3 - 1
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 {

+ 227 - 0
test/data/tabs_bend_and_release.musicxml

@@ -0,0 +1,227 @@
+<?xml version="1.0"?>
+<score-partwise version="3.1">
+    <work>
+        <work-title>bend release</work-title>
+    </work>
+    <identification>
+        <encoding>
+            <software>www.guitartabcreator.com</software>
+            <encoding-date>2020-06-14</encoding-date>
+            <supports attribute="new-system" element="print" type="yes" value="yes" />
+            <supports attribute="new-page" element="print" type="yes" value="yes" />
+            <supports element="accidental" type="yes" />
+            <supports element="beam" type="yes" />
+            <supports element="stem" type="yes" />
+        </encoding>
+    </identification>
+    <defaults>
+        <scaling>
+            <millimeters>6.35</millimeters>
+            <tenths>40</tenths>
+        </scaling>
+        <page-layout>
+            <page-height>1760</page-height>
+            <page-width>1360</page-width>
+            <page-margins type="both">
+                <left-margin>80</left-margin>
+                <right-margin>80</right-margin>
+                <top-margin>80</top-margin>
+                <bottom-margin>80</bottom-margin>
+            </page-margins>
+        </page-layout>
+        <system-layout>
+            <system-margins>
+                <left-margin>0</left-margin>
+                <right-margin>0</right-margin>
+            </system-margins>
+            <system-distance>173</system-distance>
+            <top-system-distance>68</top-system-distance>
+        </system-layout>
+        <staff-layout>
+            <staff-distance>67</staff-distance>
+        </staff-layout>
+        <appearance>
+            <line-width type="stem">0.8333</line-width>
+            <line-width type="beam">5</line-width>
+            <line-width type="staff">1.25</line-width>
+            <line-width type="light barline">1.4583</line-width>
+            <line-width type="heavy barline">5</line-width>
+            <line-width type="leger">1.875</line-width>
+            <line-width type="ending">1.4583</line-width>
+            <line-width type="wedge">0.9375</line-width>
+            <line-width type="enclosure">1.45843</line-width>
+            <line-width type="tuplet bracket">1.4583</line-width>
+            <note-size type="grace">50</note-size>
+            <note-size type="cue">50</note-size>
+            <distance type="hyphen">60</distance>
+            <distance type="beam">8</distance>
+        </appearance>
+        <music-font font-size="18" font-family="Maestro,engraved" />
+        <word-font font-size="9" font-family="Times New Roman" />
+    </defaults>
+    <part-list>
+        <score-part id="P1">
+            <part-name print-object="no">Guitar</part-name>
+            <part-abbreviation print-object="no">Gtr.</part-abbreviation>
+            <score-instrument id="P1-I1">
+                <instrument-name>Acoustic Guitar (steel)</instrument-name>
+                <instrument-sound>pluck.guitar</instrument-sound>
+            </score-instrument>
+            <midi-instrument id="P1-I1">
+                <midi-channel>1</midi-channel>
+                <midi-program>26</midi-program>
+                <volume>80</volume>
+                <pan>0</pan>
+            </midi-instrument>
+        </score-part>
+    </part-list>
+    <part id="P1">
+        <measure number="1">
+            <print>
+                <page-layout>
+                    <page-height>1850</page-height>
+                    <page-width>1310</page-width>
+                    <page-margins>
+                        <left-margin>80</left-margin>
+                        <right-margin>727</right-margin>
+                        <top-margin>80</top-margin>
+                        <bottom-margin>80</bottom-margin>
+                    </page-margins>
+                </page-layout>
+                <system-layout>
+                    <system-margins>
+                        <left-margin>68</left-margin>
+                        <right-margin>0</right-margin>
+                    </system-margins>
+                    <top-system-distance>187</top-system-distance>
+                </system-layout>
+                <measure-number>system</measure-number>
+            </print>
+            <attributes>
+                <divisions>2</divisions>
+                <key>
+                    <fifths>0</fifths>
+                    <mode>major</mode>
+                </key>
+                <time>
+                    <beats>4</beats>
+                    <beat-type>4</beat-type>
+                </time>
+                <staves>1</staves>
+                <clef number="1">
+                    <sign>TAB</sign>
+                    <line>5</line>
+                </clef>
+                <transpose>
+                    <diatonic>0</diatonic>
+                    <chromatic>0</chromatic>
+                    <octave-change>-1</octave-change>
+                </transpose>
+                <staff-details number="1">
+                    <staff-lines>6</staff-lines>
+                    <staff-tuning line="1">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="2">
+                        <tuning-step>A</tuning-step>
+                        <tuning-octave>2</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="3">
+                        <tuning-step>D</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="4">
+                        <tuning-step>G</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="5">
+                        <tuning-step>B</tuning-step>
+                        <tuning-octave>3</tuning-octave>
+                    </staff-tuning>
+                    <staff-tuning line="6">
+                        <tuning-step>E</tuning-step>
+                        <tuning-octave>4</tuning-octave>
+                    </staff-tuning>
+                </staff-details>
+            </attributes>
+            <sound tempo="120" />
+            <note>
+                <pitch>
+                    <step>E</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>2</fret>
+                        <bend>
+                            <bend-alter>4</bend-alter>
+                        </bend>
+                        <bend>
+                            <bend-alter>2</bend-alter>
+                            <release></release>
+                        </bend>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>3</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>F</step>
+                    <octave>3</octave>
+                    <alter>1</alter>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>4</fret>
+                    </technical>
+                </notations>
+            </note>
+            <note>
+                <pitch>
+                    <step>G</step>
+                    <octave>3</octave>
+                </pitch>
+                <duration>2</duration>
+                <voice>1</voice>
+                <type>quarter</type>
+                <stem>none</stem>
+                <staff>1</staff>
+                <notations>
+                    <technical>
+                        <string>4</string>
+                        <fret>5</fret>
+                    </technical>
+                </notations>
+            </note>
+        </measure>
+    </part>
+</score-partwise>