Bladeren bron

feat(tempo): Save measures' BPM (#558)

* Save measures' BPM and close #557

* Slight improvements, to handle cases where no BPM is mentioned.

* Fixed tiny confusion between `userStartTempoInBPM` and `userStartTempoInBPM`

* Added support for tempo that appears [in] `metronome`
Soulaymen Chouri 5 jaren geleden
bovenliggende
commit
cf199ad3b1

+ 15 - 0
src/MusicalScore/MusicSheet.ts

@@ -41,6 +41,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         this.pageWidth = 120;
         // create MusicPartManager
         this.MusicPartManager = new MusicPartManager(this);
+        this.hasBPMInfo = false;
     }
     public static defaultTitle: string = "[kein Titel]";
 
@@ -77,6 +78,11 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     // (*) private musicSheetParameterObject: MusicSheetParameterObject = undefined;
     private engravingRules: EngravingRules;
     // (*) private musicSheetParameterChangedDelegate: MusicSheetParameterChangedDelegate;
+    /*
+     * The BPM info is present in the sheet, if it is set to false, means each measure's
+     * BPM was set to its value, 120
+     */
+    private hasBPMInfo: boolean;
 
     /**
      * Get the global index within the music sheet for this staff.
@@ -225,6 +231,15 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public set SelectionEnd(value: Fraction) {
         this.selectionEnd = value;
     }
+
+    public set HasBPMInfo(value: boolean) {
+        this.hasBPMInfo = value;
+    }
+
+    public get HasBPMInfo(): boolean {
+        return this.hasBPMInfo;
+    }
+
     // (*) public get MusicSheetParameterObject(): MusicSheetParameterObject {
     //    return this.musicSheetParameterObject;
     //}

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

@@ -502,9 +502,17 @@ export class InstrumentReader {
          const reader: ExpressionReader = this.expressionReaders[i];
          if (reader !== undefined) {
            reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
-      }
+          }
         }
       }
+
+      // if this is the first measure and no BPM info found, we set it to 120
+      // next measures will automatically inherit that value
+      if (!this.musicSheet.HasBPMInfo) {
+        this.currentMeasure.TempoInBPM = 120;
+      } else if (currentMeasure.TempoInBPM === 0) {
+        this.currentMeasure.TempoInBPM = this.previousMeasure.TempoInBPM;
+      }
     } catch (e) {
       if (divisionsException) {
         throw new MusicSheetReadingException(e.Message);

+ 3 - 0
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -183,6 +183,9 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
          afterSheetReadingModule.calculate(this.musicSheet);
         }
 
+        this.musicSheet.DefaultStartTempoInBpm = this.musicSheet.SourceMeasures[0].TempoInBPM;
+        this.musicSheet.userStartTempoInBPM = this.musicSheet.userStartTempoInBPM || this.musicSheet.DefaultStartTempoInBpm;
+
         return this.musicSheet;
     }
 

+ 5 - 0
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -142,6 +142,8 @@ export class ExpressionReader {
             if (tempoAttr) {
                 const match: string[] = tempoAttr.value.match(/\d+/);
                 this.soundTempo = match !== undefined ? parseInt(match[0], 10) : 100;
+                currentMeasure.TempoInBPM = this.soundTempo;
+                this.musicSheet.HasBPMInfo = true;
                 isTempoInstruction = true;
             }
             if (dynAttr) {
@@ -174,6 +176,9 @@ export class ExpressionReader {
                                                      bpmNumber,
                                                      this.currentMultiTempoExpression,
                                                      true);
+                this.soundTempo = bpmNumber;
+                currentMeasure.TempoInBPM = this.soundTempo;
+                this.musicSheet.HasBPMInfo = true;
                 instantaneousTempoExpression.dotted = dotted;
                 instantaneousTempoExpression.beatUnit = beatUnit.value;
                 this.currentMultiTempoExpression.addExpression(instantaneousTempoExpression, "");

+ 9 - 1
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -32,6 +32,7 @@ export class SourceMeasure {
         this.endingBarStyle = "";
         this.firstInstructionsStaffEntries = new Array(completeNumberOfStaves);
         this.lastInstructionsStaffEntries = new Array(completeNumberOfStaves);
+        this.TempoInBPM = 0;
         for (let i: number = 0; i < completeNumberOfStaves; i++) {
             this.graphicalMeasureErrors.push(false);
             this.staffLinkedExpressions.push([]);
@@ -65,7 +66,7 @@ export class SourceMeasure {
     private lastInstructionsStaffEntries: SourceStaffEntry[];
     private firstRepetitionInstructions: RepetitionInstruction[] = [];
     private lastRepetitionInstructions: RepetitionInstruction[] = [];
-
+    private tempoInBPM: number;
     private verticalMeasureList: GraphicalMeasure[]; // useful, see GraphicalMusicSheet.GetGraphicalFromSourceStaffEntry
 
     public get MeasureNumber(): number {
@@ -167,6 +168,13 @@ export class SourceMeasure {
         this.verticalMeasureList = value;
     }
 
+    public get TempoInBPM(): number {
+        return this.tempoInBPM;
+    }
+
+    public set TempoInBPM(value: number) {
+        this.tempoInBPM = value;
+    }
     /**
      * Check at the given timestamp if a VerticalContainer exists, if not creates a new, timestamp-ordered one,
      * and at the given index, if a [[SourceStaffEntry]] exists, and if not, creates a new one.