Browse Source

merge osmd-public: fix repeat end start collision (:||:), tempo parsing, null errors, MultiMeasure generation

tempo parsing: in ExpressionReader.ts
sschmid 3 years ago
parent
commit
62851abade

+ 3 - 0
src/Common/FileIO/Mxl.ts

@@ -1,5 +1,6 @@
 import { IXmlElement } from "./Xml";
 import JSZip from "jszip";
+import log from "loglevel";
 
 /**
  * Some helper methods to handle MXL files.
@@ -63,6 +64,8 @@ export class MXLHelper {
                 return zip.file("META-INF/container.xml").async("text");
             },
             (err: any) => {
+                // log jszip error. for some reason this isn't done in OSMD where this method is used.
+                log.error(err);
                 throw err;
             }
         ).then(

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

@@ -215,6 +215,7 @@ export class EngravingRules {
     public MultipleRestMeasureDefaultWidth: number;
     public DistanceBetweenVerticalSystemLines: number;
     public DistanceBetweenDotAndLine: number;
+    public RepeatEndStartPadding: number;
     public OctaveShiftLineWidth: number;
     public OctaveShiftVerticalLineLength: number;
     public GraceLineWidth: number;
@@ -540,9 +541,10 @@ export class EngravingRules {
         this.SystemThinLineWidth = 0.12;
         this.SystemBoldLineWidth = EngravingRules.unit / 2.0;
         this.SystemRepetitionEndingLineWidth = 0.12;
-        this.SystemDotWidth = EngravingRules.unit / 5.0;
+        this.SystemDotWidth = EngravingRules.unit / 2.0;
         this.DistanceBetweenVerticalSystemLines = 0.35;
         this.DistanceBetweenDotAndLine = 0.7;
+        this.RepeatEndStartPadding = 2.0; // set to 0.0 to restore old padding/width with :||: measures
         this.OctaveShiftLineWidth = 0.12;
         this.OctaveShiftVerticalLineLength = EngravingRules.unit;
         this.GraceLineWidth = this.StaffLineWidth * this.GraceNoteScalingFactor;

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

@@ -968,6 +968,9 @@ export class GraphicalMusicSheet {
     }*/
 
     public GetGraphicalFromSourceStaffEntry(sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
+        if (!sourceStaffEntry.VerticalContainerParent.ParentMeasure?.VerticalMeasureList) {
+            return undefined;
+        }
         const graphicalMeasure: GraphicalMeasure = sourceStaffEntry.VerticalContainerParent.ParentMeasure.VerticalMeasureList
             [sourceStaffEntry.ParentStaff.idInMusicSheet];
         return graphicalMeasure.findGraphicalStaffEntryFromTimestamp(sourceStaffEntry.Timestamp);

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

@@ -246,8 +246,8 @@ export class MusicSystemBuilder {
         if (systemMeasures.length >= 1) {
             const measures: GraphicalMeasure[] =
                 this.currentSystemParams.currentSystem.GraphicalMeasures[this.currentSystemParams.currentSystem.GraphicalMeasures.length - 1];
-            const measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
             let diff: number = 0.0;
+            const measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
             if (measureParams.endLine === SystemLinesEnum.DotsBoldBoldDots) {
                 measureParams.endLine = SystemLinesEnum.DotsThinBold;
                 diff = measures[0].getLineWidth(SystemLinesEnum.DotsBoldBoldDots) / 2 - measures[0].getLineWidth(SystemLinesEnum.DotsThinBold);

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

@@ -153,8 +153,8 @@ export class VexFlowMeasure extends GraphicalMeasure {
             case SystemLinesEnum.BoldThinDots:
             case SystemLinesEnum.DotsThinBold:
                 return 10.0 / unitInPixels;
-            case SystemLinesEnum.DotsBoldBoldDots:
-                return 10.0 / unitInPixels;
+            case SystemLinesEnum.DotsBoldBoldDots: // :||: = repeat ends, another repeat starts in next measure
+                return 10.0 / unitInPixels + this.rules.RepeatEndStartPadding;
             default:
                 return 0;
         }
@@ -666,7 +666,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
             for (const ve of voice.VoiceEntries) {
                 for (const note of ve.Notes) {
                     const gNote: VexFlowGraphicalNote = this.rules.GNote(note) as VexFlowGraphicalNote;
-                    if (!gNote.vfnote) { // can happen were invisible, then multi rest measure. TODO fix multi rest measure not removed
+                    if (!gNote?.vfnote) { // can happen were invisible, then multi rest measure. TODO fix multi rest measure not removed
                         return;
                     }
                     const vfnote: Vex.Flow.StemmableNote = gNote.vfnote[0];

+ 1 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowVoiceEntry.ts

@@ -107,7 +107,7 @@ export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
                     }
                 }
                 if (colorBeam) {
-                    if (vfStaveNote.beam?.setStyle) {
+                    if (vfStaveNote?.beam?.setStyle) {
                         vfStaveNote.beam.setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor});
                     }
                 }

+ 2 - 2
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -463,8 +463,8 @@ export class InstrumentReader {
           if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
             this.saveClefInstructionAtEndOfMeasure();
           }
-          const staffDetailsNode: IXmlElement = xmlNode.element("staff-details");
-          if (staffDetailsNode) {
+          const staffDetailsNodes: IXmlElement[] = xmlNode.elements("staff-details"); // there can be multiple, even if redundant. see #1041
+          for (const staffDetailsNode of staffDetailsNodes) {
             const staffLinesNode: IXmlElement = staffDetailsNode.element("staff-lines");
             if (staffLinesNode) {
               let staffNumber: number = 1;

+ 4 - 3
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -153,7 +153,7 @@ export class ExpressionReader {
             const tempoAttr: IXmlAttribute = n.attribute("tempo");
             const dynAttr: IXmlAttribute = n.attribute("dynamics");
             if (tempoAttr) {
-                const match: string[] = tempoAttr.value.match(/\d+/);
+                const match: string[] = tempoAttr.value.match(/^(\d+\.?\d{0,9}|\.\d{1,9})$/);
                 this.soundTempo = match !== undefined ? parseInt(match[0], 10) : 100;
                 currentMeasure.TempoInBPM = this.soundTempo;
                 if (this.musicSheet.DefaultStartTempoInBpm === 0) {
@@ -184,7 +184,7 @@ export class ExpressionReader {
                 if (useCurrentFractionForPositioning) {
                     this.directionTimestamp = Fraction.createFromFraction(inSourceMeasureCurrentFraction);
                 }
-                const bpmNumber: number = parseInt(bpm.value, 10);
+                const bpmNumber: number = parseFloat(bpm.value);
                 this.createNewTempoExpressionIfNeeded(currentMeasure);
                 const instantaneousTempoExpression: InstantaneousTempoExpression =
                     new InstantaneousTempoExpression(undefined,
@@ -195,7 +195,8 @@ export class ExpressionReader {
                                                      true);
                 instantaneousTempoExpression.parentMeasure = currentMeasure;
                 this.soundTempo = bpmNumber;
-                currentMeasure.TempoInBPM = this.soundTempo;
+                // make sure to take dotted beats into account
+                currentMeasure.TempoInBPM = this.soundTempo * (dotted?1.5:1);
                 if (this.musicSheet.DefaultStartTempoInBpm === 0) {
                     this.musicSheet.DefaultStartTempoInBpm = this.soundTempo;
                 }

+ 6 - 0
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -611,9 +611,15 @@ export class SourceMeasure {
                 if (!staffEntry || !staffEntry.ParentStaff.ParentInstrument.Visible) {
                     continue; // ignore notes in invisible instruments (instruments not shown)
                 }
+                if (staffEntry.ChordContainers.length > 0) {
+                    return false;
+                }
                 if (staffEntry.ParentStaff.hasLyrics) {
                     visibleLyrics = true;
                 }
+                if (this.firstRepetitionInstructions.length > 0 || this.lastRepetitionInstructions.length > 0) {
+                    return false;
+                }
                 for (const voiceEntry of staffEntry.VoiceEntries) {
                     for (const note of voiceEntry.Notes) {
                         if (!note.isRest()) {

+ 1 - 1
src/OpenSheetMusicDisplay/Cursor.ts

@@ -228,8 +228,8 @@ export class Cursor implements IPlaybackListener {
     bottomStaffline.PositionAndShape.RelativePosition.y + bottomStaffline.StaffHeight;
     height = endY - y;
 
-    const measurePositionAndShape: BoundingBox = this.graphic.findGraphicalMeasure(iterator.CurrentMeasureIndex, 0).PositionAndShape;
     // Update the graphical cursor
+    const measurePositionAndShape: BoundingBox = this.graphic.findGraphicalMeasure(iterator.CurrentMeasureIndex, 0).PositionAndShape;
     this.updateWidthAndStyle(measurePositionAndShape, x, y, height);
 
     if (this.openSheetMusicDisplay.FollowCursor) {

BIN
test/data/test_chord_symbols_multirest.mxl