Parcourir la source

merge osmd-public 1.5.7: Add EngravingRules.TempoYSpacing, fix Chrome warning (willReadFrequently), add .nvmrc, etc

see public osmd changelog
sschmidTU il y a 2 ans
Parent
commit
9957ed3a2f

+ 2 - 0
.nvmrc

@@ -0,0 +1,2 @@
+v14
+# v14 is only necessary for MacOS for build issues, see OSMD issue 1239

+ 1 - 2
demo/index.js

@@ -493,6 +493,7 @@ import { TransposeCalculator } from '../src/Plugins/Transpose/TransposeCalculato
             // tripletsBracketed: true,
             // tupletsRatioed: true, // unconventional; renders ratios for tuplets (3:2 instead of 3 for triplets)
         });
+        openSheetMusicDisplay.TransposeCalculator = new TransposeCalculator(); // necessary for using osmd.Sheet.Transpose and osmd.Sheet.Instruments[i].Transpose
         //openSheetMusicDisplay.DrawSkyLine = true;
         //openSheetMusicDisplay.DrawBottomLine = true;
         //openSheetMusicDisplay.setDrawBoundingBox("GraphicalLabel", false);
@@ -555,8 +556,6 @@ import { TransposeCalculator } from '../src/Plugins/Transpose/TransposeCalculato
             // selectSampleOnChange();
         });
         if(transposeBtn && transpose){
-            openSheetMusicDisplay.TransposeCalculator = new TransposeCalculator();
-
             transposeBtn.onclick = function(){
                 var transposeValue = parseInt(transpose.value);
                 openSheetMusicDisplay.Sheet.Transpose = transposeValue;

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "osmd-extended",
-  "version": "1.5.6",
+  "version": "1.5.7",
   "description": "Private / sponsor exclusive OSMD mirror/audio player.",
   "main": "build/opensheetmusicdisplay.min.js",
   "types": "build/dist/src/index.d.ts",

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

@@ -210,6 +210,7 @@ export class EngravingRules {
     public SlurHeightFlattenLongSlursCutoffWidth: number;
     public SlursStartingAtSameStaffEntryYOffset: number;
     public SlurMaximumYControlPointDistance: number;
+    public TempoYSpacing: number;
     public InstantaneousTempoTextHeight: number;
     public ContinuousDynamicTextHeight: number;
     public MoodTextHeight: number;
@@ -607,6 +608,7 @@ export class EngravingRules {
         this.MaximumLyricsElongationFactor = 2.5;
 
         // expressions variables
+        this.TempoYSpacing = 0.0;
         this.InstantaneousTempoTextHeight = 2.3;
         this.ContinuousDynamicTextHeight = 2.3;
         this.MoodTextHeight = 2.3;

+ 6 - 4
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1623,7 +1623,8 @@ export abstract class MusicSheetCalculator {
                              style: FontStyles,
                              placement: PlacementEnum,
                              fontHeight: number,
-                             textAlignment: TextAlignmentEnum = TextAlignmentEnum.CenterBottom): GraphicalLabel {
+                             textAlignment: TextAlignmentEnum = TextAlignmentEnum.CenterBottom,
+                             yPadding: number = 0): GraphicalLabel {
         const label: Label = new Label(combinedString, textAlignment);
         label.fontStyle = style;
         label.fontHeight = fontHeight;
@@ -1656,9 +1657,9 @@ export abstract class MusicSheetCalculator {
         let drawingHeight: number;
         const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
         if (placement === PlacementEnum.Below) {
-            drawingHeight = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
+            drawingHeight = skyBottomLineCalculator.getBottomLineMaxInRange(left, right) + yPadding;
         } else {
-            drawingHeight = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
+            drawingHeight = skyBottomLineCalculator.getSkyLineMinInRange(left, right) - yPadding;
         }
 
         // set RelativePosition
@@ -1745,7 +1746,8 @@ export abstract class MusicSheetCalculator {
                                                                        multiTempoExpression.getFontstyleOfFirstEntry(),
                                                                        entry.Expression.Placement,
                                                                        this.rules.UnknownTextHeight,
-                                                                       textAlignment);
+                                                                       textAlignment,
+                                                                       this.rules.TempoYSpacing);
 
                 if (entry.Expression instanceof InstantaneousTempoExpression) {
                     //already added?

+ 1 - 0
src/MusicalScore/Instrument.ts

@@ -17,6 +17,7 @@ export class Instrument extends InstrumentalGroup {
 
     /** Transposition halftones for this instrument only.
      *  This is additive to osmd.Sheet.Transpose (MusicSheet).
+     *  osmd.TransposeCaculator needs to be defined/created for this to take effect. (just set it with new TransposeCalculator())
      * You need to call osmd.updateGraphic() before the next render() (assuming this is set after load()).
      */
     public Transpose: number = 0;

+ 7 - 2
src/MusicalScore/MusicSheet.ts

@@ -39,12 +39,12 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         this.MusicPartManager = new MusicPartManager(this);
         this.hasBPMInfo = false;
     }
-    public static defaultTitle: string = "[no title given]";
+    public static defaultTitle: string = "defaultTitle";
 
     public userStartTempoInBPM: number;
     public pageWidth: number;
 
-    private idString: string = "random idString, not initialized";
+    private idString: string = "uninitialized";
     private sourceMeasures: SourceMeasure[] = [];
     private repetitions: Repetition[] = [];
     private dynListStaves: DynamicsContainer[][] = [];
@@ -479,6 +479,11 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
     public get Transpose(): number {
         return this.transpose;
     }
+    /** Sets the number of halftones for transposition.
+     * E.g. +1 halftone will transpose Eb major to E major.
+     * also see Instrument.Transpose (e.g. osmd.Sheet.Instruments[0].Transpose will additionally transpose this instrument only)
+     * osmd.TransposeCaculator needs to be defined/created for this to take effect. (just set it with new TransposeCalculator())
+     */
     public set Transpose(value: number) {
         this.transpose = value;
     }

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

@@ -862,7 +862,7 @@ export class InstrumentReader {
           } catch (ex) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefLineError",
-              "Invalid clef line given -> using default clef line."
+              "Invalid clef line. Using default."
             );
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
             line = 2;
@@ -877,7 +877,7 @@ export class InstrumentReader {
             if (!ClefInstruction.isSupportedClef(clefEnum)) {
               errorMsg = ITextTranslation.translateText(
                 "ReaderErrorMessages/ClefError",
-                "Unsupported clef found -> using default clef."
+                "Unsupported clef. Using default."
               );
               this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
               clefEnum = ClefEnum.G;
@@ -889,7 +889,7 @@ export class InstrumentReader {
           } catch (e) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefError",
-              "Invalid clef found -> using default clef."
+              "Invalid clef. Using default."
             );
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
             clefEnum = ClefEnum.G;
@@ -905,7 +905,7 @@ export class InstrumentReader {
           } catch (e) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefOctaveError",
-              "Invalid clef octave found -> using default clef octave."
+              "Invalid clef octave. Using default."
             );
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
             clefOctaveOffset = 0;
@@ -922,7 +922,7 @@ export class InstrumentReader {
           } catch (err) {
             errorMsg = ITextTranslation.translateText(
               "ReaderErrorMessages/ClefError",
-              "Invalid clef found -> using default clef."
+              "Invalid clef. Using default."
             );
             this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
             staffNumber = 1;
@@ -943,7 +943,7 @@ export class InstrumentReader {
         } catch (ex) {
           errorMsg = ITextTranslation.translateText(
             "ReaderErrorMessages/KeyError",
-            "Invalid key found -> set to default."
+            "Invalid key. Set to default."
           );
           this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
           key = 0;
@@ -962,7 +962,7 @@ export class InstrumentReader {
         } catch (ex) {
           errorMsg = ITextTranslation.translateText(
             "ReaderErrorMessages/KeyError",
-            "Invalid key found -> set to default."
+            "Invalid key/mode. Set to default."
           );
           this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
           keyEnum = KeyEnum.major;
@@ -1040,7 +1040,7 @@ export class InstrumentReader {
             denom = parseInt(attrNode.element("time").element("beat-type").value, 10);
           }
         } catch (ex) {
-          errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm found -> set to default.");
+          errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm. Set to default.");
           this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
           num = 4;
           denom = 4;
@@ -1330,7 +1330,7 @@ export class InstrumentReader {
          directionStaffNumber = parseInt(staffNode.value, 10);
        } catch (ex) {
          const errorMsg: string = ITextTranslation.translateText(
-           "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number -> set to default."
+           "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number. Set to default."
          );
          this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
          directionStaffNumber = 1;

+ 2 - 2
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -129,7 +129,7 @@ export class ExpressionReader {
                 }
             } catch (ex) {
                 const errorMsg: string = ITextTranslation.translateText(  "ReaderErrorMessages/ExpressionPlacementError",
-                                                                          "Invalid expression placement -> set to default.");
+                                                                          "Invalid expression placement. Set to default.");
                 log.debug("ExpressionReader.readExpressionParameters", errorMsg, ex);
                 this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                 this.placement = PlacementEnum.Below;
@@ -277,7 +277,7 @@ export class ExpressionReader {
                 octaveStaffNumber = parseInt(staffNode.value, 10);
             } catch (ex) {
                 const errorMsg: string = ITextTranslation.translateText(  "ReaderErrorMessages/OctaveShiftStaffError",
-                                                                          "Invalid octave shift staff number -> set to default");
+                                                                          "Invalid octave shift staff number. Set to default");
                 this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
                 octaveStaffNumber = 1;
                 log.debug("ExpressionReader.addOctaveShift", errorMsg, ex);

+ 1 - 1
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -35,7 +35,7 @@ import { DynamicsCalculator } from "../MusicalScore/ScoreIO/MusicSymbolModules/D
  * After the constructor, use load() and render() to load and render a MusicXML file.
  */
 export class OpenSheetMusicDisplay {
-    private version: string = "1.5.6-audio-extended"; // getter: this.Version
+    private version: string = "1.5.7-audio-extended"; // getter: this.Version
     // at release, bump version and change to -release, afterwards to -dev again
 
     /**

+ 4 - 2
src/VexFlowPatch/readme.txt

@@ -33,8 +33,10 @@ open group to get SVG group+class for key signature
 
 pedalmarking.js (custom addition):
 Add rendering options for pedals that break across systems.
-clef.js (custom addition):
-open group to get SVG group+class for clef
+
+renderer.js (vexflow4: need to check if possible):
+CanvasContext: getContext(): use willReadFrequently option for marginal performance potential,
+and for preventing chrome warning (#1242)
 
 stave.js (merged/fixed vexflow 4):
 prevent a bug where a modifier width is NaN, leading to a VexFlow error (fixed vexflow 4)

+ 179 - 0
src/VexFlowPatch/src/renderer.js

@@ -0,0 +1,179 @@
+// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
+//
+// ## Description
+// Support for different rendering contexts: Canvas, Raphael
+
+import { CanvasContext } from './canvascontext';
+import { RaphaelContext } from './raphaelcontext';
+import { SVGContext } from './svgcontext';
+import { Vex } from './vex';
+
+let lastContext = null;
+
+export class Renderer {
+  static get Backends() {
+    return {
+      CANVAS: 1,
+      RAPHAEL: 2,
+      SVG: 3,
+      VML: 4,
+    };
+  }
+
+  // End of line types
+  static get LineEndType() {
+    return {
+      NONE: 1, // No leg
+      UP: 2,   // Upward leg
+      DOWN: 3, // Downward leg
+    };
+  }
+
+  // Set this to true if you're using VexFlow inside a runtime
+  // that does not allow modifiying canvas objects. There is a small
+  // performance degradation due to the extra indirection.
+  static get USE_CANVAS_PROXY() {
+    return false;
+  }
+
+  static get lastContext() {
+    return lastContext;
+  }
+  static set lastContext(ctx) {
+    lastContext = ctx;
+  }
+
+  static buildContext(elementId, backend, width, height, background) {
+    const renderer = new Renderer(elementId, backend);
+    if (width && height) {
+      renderer.resize(width, height);
+    }
+
+    if (!background) background = '#FFF';
+    const ctx = renderer.getContext();
+    ctx.setBackgroundFillStyle(background);
+    Renderer.lastContext = ctx;
+    return ctx;
+  }
+
+  static getCanvasContext(elementId, width, height, background) {
+    return Renderer.buildContext(elementId, Renderer.Backends.CANVAS, width, height, background);
+  }
+
+  static getRaphaelContext(elementId, width, height, background) {
+    return Renderer.buildContext(elementId, Renderer.Backends.RAPHAEL, width, height, background);
+  }
+
+  static getSVGContext(elementId, width, height, background) {
+    return Renderer.buildContext(elementId, Renderer.Backends.SVG, width, height, background);
+  }
+
+  static bolsterCanvasContext(ctx) {
+    if (Renderer.USE_CANVAS_PROXY) {
+      return new CanvasContext(ctx);
+    }
+
+    const methodNames = [
+      'clear', 'setFont', 'setRawFont', 'setFillStyle', 'setBackgroundFillStyle',
+      'setStrokeStyle', 'setShadowColor', 'setShadowBlur', 'setLineWidth',
+      'setLineCap', 'setLineDash', 'openGroup', 'closeGroup', 'getGroup',
+    ];
+
+    ctx.vexFlowCanvasContext = ctx;
+
+    methodNames.forEach(methodName => {
+      ctx[methodName] = ctx[methodName] || CanvasContext.prototype[methodName];
+    });
+
+    return ctx;
+  }
+
+  // Draw a dashed line (horizontal, vertical or diagonal
+  // dashPattern = [3,3] draws a 3 pixel dash followed by a three pixel space.
+  // setting the second number to 0 draws a solid line.
+  static drawDashedLine(context, fromX, fromY, toX, toY, dashPattern) {
+    context.beginPath();
+
+    const dx = toX - fromX;
+    const dy = toY - fromY;
+    const angle = Math.atan2(dy, dx);
+    let x = fromX;
+    let y = fromY;
+    context.moveTo(fromX, fromY);
+    let idx = 0;
+    let draw = true;
+    while (!((dx < 0 ? x <= toX : x >= toX) && (dy < 0 ? y <= toY : y >= toY))) {
+      const dashLength = dashPattern[idx++ % dashPattern.length];
+      const nx = x + (Math.cos(angle) * dashLength);
+      x = dx < 0 ? Math.max(toX, nx) : Math.min(toX, nx);
+      const ny = y + (Math.sin(angle) * dashLength);
+      y = dy < 0 ? Math.max(toY, ny) : Math.min(toY, ny);
+      if (draw) {
+        context.lineTo(x, y);
+      } else {
+        context.moveTo(x, y);
+      }
+      draw = !draw;
+    }
+
+    context.closePath();
+    context.stroke();
+  }
+
+  constructor(elementId, backend) {
+    this.elementId = elementId;
+    if (!this.elementId) {
+      throw new Vex.RERR('BadArgument', 'Invalid id for renderer.');
+    }
+
+    this.element = document.getElementById(elementId);
+    if (!this.element) this.element = elementId;
+
+    // Verify backend and create context
+    this.ctx = null;
+    this.paper = null;
+    this.backend = backend;
+    if (this.backend === Renderer.Backends.CANVAS) {
+      // Create context.
+      if (!this.element.getContext) {
+        throw new Vex.RERR('BadElement', `Can't get canvas context from element: ${elementId}`);
+      }
+      // VexFlowPatch: add willReadFrequently attribute for getImageData() performance
+      this.ctx = Renderer.bolsterCanvasContext(this.element.getContext('2d', {willReadFrequently: true}));
+    } else if (this.backend === Renderer.Backends.RAPHAEL) {
+      this.ctx = new RaphaelContext(this.element);
+    } else if (this.backend === Renderer.Backends.SVG) {
+      this.ctx = new SVGContext(this.element);
+    } else {
+      throw new Vex.RERR('InvalidBackend', `No support for backend: ${this.backend}`);
+    }
+  }
+
+  resize(width, height) {
+    if (this.backend === Renderer.Backends.CANVAS) {
+      if (!this.element.getContext) {
+        throw new Vex.RERR(
+          'BadElement', `Can't get canvas context from element: ${this.elementId}`
+        );
+      }
+      [width, height] = CanvasContext.SanitizeCanvasDims(width, height);
+
+      const devicePixelRatio = window.devicePixelRatio || 1;
+
+      this.element.width = width * devicePixelRatio;
+      this.element.height = height * devicePixelRatio;
+      this.element.style.width = width + 'px';
+      this.element.style.height = height + 'px';
+
+      // VexFlowPatch: add willReadFrequently attribute for getImageData() performance
+      this.ctx = Renderer.bolsterCanvasContext(this.element.getContext('2d', {willReadFrequently: true}));
+      this.ctx.scale(devicePixelRatio, devicePixelRatio);
+    } else {
+      this.ctx.resize(width, height);
+    }
+
+    return this;
+  }
+
+  getContext() { return this.ctx; }
+}