@coderline/alphatab 1.8.0-alpha.1669 → 1.8.0-alpha.1671

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.8.0-alpha.1669 (develop, build 1669)
2
+ * alphaTab v1.8.0-alpha.1671 (develop, build 1671)
3
3
  *
4
4
  * Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -203,9 +203,9 @@ class AlphaTabError extends Error {
203
203
  * @internal
204
204
  */
205
205
  class VersionInfo {
206
- static version = '1.8.0-alpha.1669';
207
- static date = '2026-01-08T02:24:31.439Z';
208
- static commit = 'f7c00b0b8db32011293f475bf5e930d21c1903ac';
206
+ static version = '1.8.0-alpha.1671';
207
+ static date = '2026-01-10T02:20:48.787Z';
208
+ static commit = '1edf83d58fe31a924adb1980bc4a529aed07dad4';
209
209
  static print(print) {
210
210
  print(`alphaTab ${VersionInfo.version}`);
211
211
  print(`commit: ${VersionInfo.commit}`);
@@ -1315,6 +1315,23 @@ var NotationElement;
1315
1315
  * The key signature for numbered notation staff.
1316
1316
  */
1317
1317
  NotationElement[NotationElement["EffectNumberedNotationKeySignature"] = 51] = "EffectNumberedNotationKeySignature";
1318
+ /**
1319
+ * The fretboard numbers shown in chord diagrams.
1320
+ */
1321
+ NotationElement[NotationElement["ChordDiagramFretboardNumbers"] = 52] = "ChordDiagramFretboardNumbers";
1322
+ /**
1323
+ * The bar numbers.
1324
+ */
1325
+ NotationElement[NotationElement["BarNumber"] = 53] = "BarNumber";
1326
+ /**
1327
+ * The repeat count indicator shown above the thick bar line to describe
1328
+ * how many repeats should be played.
1329
+ */
1330
+ NotationElement[NotationElement["RepeatCount"] = 54] = "RepeatCount";
1331
+ /**
1332
+ * The slurs shown on bend effects within the score staff.
1333
+ */
1334
+ NotationElement[NotationElement["ScoreBendSlur"] = 55] = "ScoreBendSlur";
1318
1335
  })(NotationElement || (NotationElement = {}));
1319
1336
  /**
1320
1337
  * The notation settings control how various music notation elements are shown and behaving
@@ -2174,6 +2191,17 @@ class Bar {
2174
2191
  * Gets or sets the type of key signature (major/minor)
2175
2192
  */
2176
2193
  keySignatureType = KeySignatureType.Major;
2194
+ /**
2195
+ * How bar numbers should be displayed.
2196
+ * If specified, overrides the value from the stylesheet on score level.
2197
+ */
2198
+ barNumberDisplay;
2199
+ /**
2200
+ * The shortest duration contained across beats in this bar.
2201
+ * @internal
2202
+ * @json_ignore
2203
+ */
2204
+ shortestDuration = Duration.DoubleWhole;
2177
2205
  /**
2178
2206
  * The bar line to draw on the left side of the bar with an "automatic" type resolved to the actual one.
2179
2207
  * @param isFirstOfSystem Whether the bar is the first one in the system.
@@ -2245,12 +2273,16 @@ class Bar {
2245
2273
  this._filledVoices.add(0);
2246
2274
  this._isEmpty = true;
2247
2275
  this._isRestOnly = true;
2276
+ this.shortestDuration = Duration.DoubleWhole;
2248
2277
  for (let i = 0, j = this.voices.length; i < j; i++) {
2249
2278
  const voice = this.voices[i];
2250
2279
  voice.finish(settings, sharedDataBag);
2251
2280
  if (!voice.isEmpty) {
2252
2281
  this._isEmpty = false;
2253
2282
  this._filledVoices.add(i);
2283
+ if (voice.shortestDuration > this.shortestDuration) {
2284
+ this.shortestDuration = voice.shortestDuration;
2285
+ }
2254
2286
  }
2255
2287
  if (!voice.isRestOnly) {
2256
2288
  this._isRestOnly = false;
@@ -2347,6 +2379,116 @@ var TripletFeel;
2347
2379
  TripletFeel[TripletFeel["Scottish8th"] = 6] = "Scottish8th";
2348
2380
  })(TripletFeel || (TripletFeel = {}));
2349
2381
 
2382
+ /**
2383
+ * Defines the custom beaming rules which define how beats are beamed together or split apart
2384
+ * during the automatic beaming when displayed.
2385
+ * @json
2386
+ * @json_strict
2387
+ * @public
2388
+ *
2389
+ * @remarks
2390
+ * The beaming logic works like this:
2391
+ *
2392
+ * The time axis of the bar is sliced into even chunks. The chunk-size is defined by the respective group definition.
2393
+ * Within these chunks groups can then be placed spanning 1 or more chunks.
2394
+ *
2395
+ * If beats start within the same "group" they are beamed together.
2396
+ */
2397
+ class BeamingRules {
2398
+ _singleGroupKey;
2399
+ /**
2400
+ * The the group for a given "longest duration" within the bar.
2401
+ * @remarks
2402
+ * The map key is the duration to which the bar will be sliced into.
2403
+ * The map value defines the "groups" placed within the sliced.
2404
+ */
2405
+ groups = new Map();
2406
+ /**
2407
+ * @internal
2408
+ * @json_ignore
2409
+ */
2410
+ uniqueId = '';
2411
+ /**
2412
+ * @internal
2413
+ * @json_ignore
2414
+ */
2415
+ timeSignatureNumerator = 0;
2416
+ /**
2417
+ * @internal
2418
+ * @json_ignore
2419
+ */
2420
+ timeSignatureDenominator = 0;
2421
+ /**
2422
+ * @internal
2423
+ */
2424
+ static createSimple(timeSignatureNumerator, timeSignatureDenominator, duration, groups) {
2425
+ const r = new BeamingRules();
2426
+ r.timeSignatureNumerator = timeSignatureNumerator;
2427
+ r.timeSignatureDenominator = timeSignatureDenominator;
2428
+ r.groups.set(duration, groups);
2429
+ r.finish();
2430
+ return r;
2431
+ }
2432
+ /**
2433
+ * @internal
2434
+ */
2435
+ findRule(shortestDuration) {
2436
+ // fast path: one rule -> take it
2437
+ const singleGroupKey = this._singleGroupKey;
2438
+ if (singleGroupKey) {
2439
+ return [singleGroupKey, this.groups.get(singleGroupKey)];
2440
+ }
2441
+ if (shortestDuration < Duration.Quarter) {
2442
+ return [shortestDuration, []];
2443
+ }
2444
+ // first search shorter
2445
+ let durationValue = shortestDuration;
2446
+ do {
2447
+ const duration = durationValue;
2448
+ if (this.groups.has(duration)) {
2449
+ return [duration, this.groups.get(duration)];
2450
+ }
2451
+ durationValue = durationValue * 2;
2452
+ } while (durationValue <= Duration.TwoHundredFiftySixth);
2453
+ // then longer
2454
+ durationValue = shortestDuration / 2;
2455
+ do {
2456
+ const duration = durationValue;
2457
+ if (this.groups.has(duration)) {
2458
+ return [duration, this.groups.get(duration)];
2459
+ }
2460
+ durationValue = durationValue / 2;
2461
+ } while (durationValue > Duration.Half);
2462
+ return [shortestDuration, []];
2463
+ }
2464
+ /**
2465
+ * @internal
2466
+ */
2467
+ finish() {
2468
+ let uniqueId = `${this.timeSignatureNumerator}_${this.timeSignatureDenominator}`;
2469
+ for (const [k, v] of this.groups) {
2470
+ uniqueId += `__${k}`;
2471
+ // trim of 0s at the end of the group
2472
+ let lastZero = v.length;
2473
+ for (let i = v.length - 1; i >= 0; i--) {
2474
+ if (v[i] === 0) {
2475
+ lastZero = i;
2476
+ }
2477
+ else {
2478
+ break;
2479
+ }
2480
+ }
2481
+ if (lastZero < v.length) {
2482
+ v.splice(lastZero, v.length - lastZero);
2483
+ }
2484
+ uniqueId += `_${v.join('_')}`;
2485
+ if (this.groups.size === 1) {
2486
+ this._singleGroupKey = k;
2487
+ }
2488
+ }
2489
+ this.uniqueId = uniqueId;
2490
+ }
2491
+ }
2350
2492
  /**
2351
2493
  * The MasterBar stores information about a bar which affects
2352
2494
  * all tracks.
@@ -2464,6 +2606,16 @@ class MasterBar {
2464
2606
  * Gets or sets whether this is bar has a common time signature.
2465
2607
  */
2466
2608
  timeSignatureCommon = false;
2609
+ /**
2610
+ * Defines the custom beaming rules which should be applied to this bar and all bars following.
2611
+ */
2612
+ beamingRules;
2613
+ /**
2614
+ * The actual (custom) beaming rules to use for this bar if any were specified.
2615
+ * @json_ignore
2616
+ * @internal
2617
+ */
2618
+ actualBeamingRules;
2467
2619
  /**
2468
2620
  * Gets or sets whether the bar indicates a free time playing.
2469
2621
  */
@@ -2596,6 +2748,32 @@ class MasterBar {
2596
2748
  }
2597
2749
  this.syncPoints.push(syncPoint);
2598
2750
  }
2751
+ finish(sharedDataBag) {
2752
+ let beamingRules = this.beamingRules;
2753
+ if (beamingRules) {
2754
+ beamingRules.timeSignatureNumerator = this.timeSignatureNumerator;
2755
+ beamingRules.timeSignatureDenominator = this.timeSignatureDenominator;
2756
+ beamingRules.finish();
2757
+ }
2758
+ if (this.index > 0) {
2759
+ this.start = this.previousMasterBar.start + this.previousMasterBar.calculateDuration();
2760
+ // clear out equal rules to reduce memory consumption.
2761
+ const previousRules = sharedDataBag.has('beamingRules')
2762
+ ? sharedDataBag.get('beamingRules')
2763
+ : undefined;
2764
+ if (previousRules && previousRules.uniqueId === beamingRules?.uniqueId) {
2765
+ this.beamingRules = undefined;
2766
+ beamingRules = previousRules;
2767
+ }
2768
+ else if (!beamingRules) {
2769
+ beamingRules = previousRules;
2770
+ }
2771
+ }
2772
+ this.actualBeamingRules = beamingRules;
2773
+ if (this.beamingRules) {
2774
+ sharedDataBag.set('beamingRules', beamingRules);
2775
+ }
2776
+ }
2599
2777
  }
2600
2778
 
2601
2779
  /**
@@ -2668,6 +2846,25 @@ var TrackNameOrientation;
2668
2846
  */
2669
2847
  TrackNameOrientation[TrackNameOrientation["Vertical"] = 1] = "Vertical";
2670
2848
  })(TrackNameOrientation || (TrackNameOrientation = {}));
2849
+ /**
2850
+ * How bar numbers are displayed
2851
+ * @public
2852
+ */
2853
+ var BarNumberDisplay;
2854
+ (function (BarNumberDisplay) {
2855
+ /**
2856
+ * Show bar numbers on all bars.
2857
+ */
2858
+ BarNumberDisplay[BarNumberDisplay["AllBars"] = 0] = "AllBars";
2859
+ /**
2860
+ * Show bar numbers on the first bar of every system.
2861
+ */
2862
+ BarNumberDisplay[BarNumberDisplay["FirstOfSystem"] = 1] = "FirstOfSystem";
2863
+ /**
2864
+ * Hide all bar numbers
2865
+ */
2866
+ BarNumberDisplay[BarNumberDisplay["Hide"] = 2] = "Hide";
2867
+ })(BarNumberDisplay || (BarNumberDisplay = {}));
2671
2868
  /**
2672
2869
  * This class represents the rendering stylesheet.
2673
2870
  * It contains settings which control the display of the score when rendered.
@@ -2761,6 +2958,10 @@ class RenderStylesheet {
2761
2958
  * {@link hideEmptyStaves}
2762
2959
  */
2763
2960
  showSingleStaffBrackets = false;
2961
+ /**
2962
+ * How bar numbers should be displayed.
2963
+ */
2964
+ barNumberDisplay = BarNumberDisplay.AllBars;
2764
2965
  }
2765
2966
 
2766
2967
  /**
@@ -2923,6 +3124,12 @@ let Voice$1 = class Voice {
2923
3124
  get isRestOnly() {
2924
3125
  return this._isRestOnly;
2925
3126
  }
3127
+ /**
3128
+ * The shortest duration contained across beats in this bar.
3129
+ * @internal
3130
+ * @json_ignore
3131
+ */
3132
+ shortestDuration = Duration.DoubleWhole;
2926
3133
  insertBeat(after, newBeat) {
2927
3134
  newBeat.nextBeat = after.nextBeat;
2928
3135
  if (newBeat.nextBeat) {
@@ -3016,6 +3223,7 @@ let Voice$1 = class Voice {
3016
3223
  }
3017
3224
  let currentDisplayTick = 0;
3018
3225
  let currentPlaybackTick = 0;
3226
+ this.shortestDuration = Duration.DoubleWhole;
3019
3227
  for (let i = 0; i < this.beats.length; i++) {
3020
3228
  const beat = this.beats[i];
3021
3229
  beat.index = i;
@@ -3024,6 +3232,9 @@ let Voice$1 = class Voice {
3024
3232
  // we need to first steal the duration from the right beat
3025
3233
  // and place the grace beats correctly
3026
3234
  if (beat.graceType === GraceType.None) {
3235
+ if (beat.duration > this.shortestDuration) {
3236
+ this.shortestDuration = beat.duration;
3237
+ }
3027
3238
  if (beat.graceGroup) {
3028
3239
  const firstGraceBeat = beat.graceGroup.beats[0];
3029
3240
  const lastGraceBeat = beat.graceGroup.beats[beat.graceGroup.beats.length - 1];
@@ -3484,7 +3695,7 @@ class Score {
3484
3695
  bar.previousMasterBar.nextMasterBar = bar;
3485
3696
  // NOTE: this will not work on anacrusis. Correct anacrusis durations are only working
3486
3697
  // when there are beats with playback positions already computed which requires full finish
3487
- // chicken-egg problem here. temporarily forcing anacrusis length here to 0,
3698
+ // chicken-egg problem here. temporarily forcing anacrusis length here to 0,
3488
3699
  // .finish() will correct these times
3489
3700
  bar.start =
3490
3701
  bar.previousMasterBar.start +
@@ -3548,9 +3759,7 @@ class Score {
3548
3759
  }
3549
3760
  // fixup masterbar starts to handle anacrusis lengths
3550
3761
  for (const mb of this.masterBars) {
3551
- if (mb.index > 0) {
3552
- mb.start = mb.previousMasterBar.start + mb.previousMasterBar.calculateDuration();
3553
- }
3762
+ mb.finish(sharedDataBag);
3554
3763
  }
3555
3764
  }
3556
3765
  /**
@@ -8432,6 +8641,12 @@ class AlphaTex1EnumMappings {
8432
8641
  ['buzzroll', 1]
8433
8642
  ]);
8434
8643
  static tremoloPickingStyleReversed = AlphaTex1EnumMappings._reverse(AlphaTex1EnumMappings.tremoloPickingStyle);
8644
+ static barNumberDisplay = new Map([
8645
+ ['allbars', 0],
8646
+ ['firstofsystem', 1],
8647
+ ['hide', 2]
8648
+ ]);
8649
+ static barNumberDisplayReversed = AlphaTex1EnumMappings._reverse(AlphaTex1EnumMappings.barNumberDisplay);
8435
8650
  static keySignaturesMinorReversed = new Map([
8436
8651
  [-7, 'abminor'],
8437
8652
  [-6, 'ebminor'],
@@ -8626,7 +8841,8 @@ class AlphaTex1LanguageDefinitions {
8626
8841
  ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]],
8627
8842
  ['hideemptystaves', null],
8628
8843
  ['hideemptystavesinfirstsystem', null],
8629
- ['showsinglestaffbrackets', null]
8844
+ ['showsinglestaffbrackets', null],
8845
+ ['defaultbarnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]]
8630
8846
  ]);
8631
8847
  static staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([
8632
8848
  ['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]],
@@ -8911,7 +9127,17 @@ class AlphaTex1LanguageDefinitions {
8911
9127
  ['sph', [[[[16], 2]]]],
8912
9128
  ['spu', [[[[16], 2]]]],
8913
9129
  ['db', null],
8914
- ['voicemode', [[[[10, 17], 0, ['staffwise', 'barwise']]]]]
9130
+ ['voicemode', [[[[10, 17], 0, ['staffwise', 'barwise']]]]],
9131
+ ['barnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]],
9132
+ [
9133
+ 'beaming',
9134
+ [
9135
+ [
9136
+ [[16], 0],
9137
+ [[16], 5]
9138
+ ]
9139
+ ]
9140
+ ]
8915
9141
  ]);
8916
9142
  static metaDataProperties = AlphaTex1LanguageDefinitions._metaProps([
8917
9143
  [
@@ -8969,6 +9195,7 @@ class AlphaTex1LanguageDefinitions {
8969
9195
  ['hideemptystaves', null],
8970
9196
  ['hideemptystavesinfirstsystem', null],
8971
9197
  ['showsinglestaffbrackets', null],
9198
+ ['defaultbarnumberdisplay', null],
8972
9199
  [
8973
9200
  'tuning',
8974
9201
  [
@@ -9025,7 +9252,9 @@ class AlphaTex1LanguageDefinitions {
9025
9252
  ['sph', null],
9026
9253
  ['spu', null],
9027
9254
  ['db', null],
9028
- ['voicemode', null]
9255
+ ['voicemode', null],
9256
+ ['barnumberdisplay', null],
9257
+ ['beaming', null]
9029
9258
  ]);
9030
9259
  static metaDataSignatures = [
9031
9260
  AlphaTex1LanguageDefinitions.scoreMetaDataSignatures,
@@ -12740,6 +12969,7 @@ var BeamDirection;
12740
12969
  */
12741
12970
  class AlphaTex1LanguageHandler {
12742
12971
  static instance = new AlphaTex1LanguageHandler();
12972
+ static _timeSignatureDenominators = new Set([1, 2, 4, 8, 16, 32, 64, 128]);
12743
12973
  applyScoreMetaData(importer, score, metaData) {
12744
12974
  const result = this._checkArgumentTypes(importer, [AlphaTex1LanguageDefinitions.scoreMetaDataSignatures], metaData, metaData.tag.tag.text.toLowerCase(), metaData.arguments);
12745
12975
  if (result !== undefined) {
@@ -12876,6 +13106,13 @@ class AlphaTex1LanguageHandler {
12876
13106
  case 'showsinglestaffbrackets':
12877
13107
  score.stylesheet.showSingleStaffBrackets = true;
12878
13108
  return ApplyNodeResult.Applied;
13109
+ case 'defaultbarnumberdisplay':
13110
+ const barNumberDisplay = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'bar number display', AlphaTex1EnumMappings.barNumberDisplay);
13111
+ if (barNumberDisplay === undefined) {
13112
+ return ApplyNodeResult.NotAppliedSemanticError;
13113
+ }
13114
+ score.stylesheet.barNumberDisplay = barNumberDisplay;
13115
+ return ApplyNodeResult.Applied;
12879
13116
  default:
12880
13117
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
12881
13118
  }
@@ -13154,8 +13391,29 @@ class AlphaTex1LanguageHandler {
13154
13391
  case 'ts':
13155
13392
  switch (metaData.arguments.arguments[0].nodeType) {
13156
13393
  case AlphaTexNodeType.Number:
13157
- bar.masterBar.timeSignatureNumerator = metaData.arguments.arguments[0].value;
13158
- bar.masterBar.timeSignatureDenominator = metaData.arguments.arguments[1].value;
13394
+ bar.masterBar.timeSignatureNumerator =
13395
+ metaData.arguments.arguments[0].value | 0;
13396
+ if (bar.masterBar.timeSignatureNumerator < 1 || bar.masterBar.timeSignatureNumerator > 32) {
13397
+ importer.addSemanticDiagnostic({
13398
+ code: AlphaTexDiagnosticCode.AT211,
13399
+ message: `Value is out of valid range. Allowed range: 1-32, Actual Value: ${bar.masterBar.timeSignatureNumerator}`,
13400
+ start: metaData.arguments.arguments[0].start,
13401
+ end: metaData.arguments.arguments[0].end,
13402
+ severity: AlphaTexDiagnosticsSeverity.Error
13403
+ });
13404
+ }
13405
+ bar.masterBar.timeSignatureDenominator =
13406
+ metaData.arguments.arguments[1].value | 0;
13407
+ if (!AlphaTex1LanguageHandler._timeSignatureDenominators.has(bar.masterBar.timeSignatureDenominator)) {
13408
+ const valueList = Array.from(AlphaTex1LanguageHandler._timeSignatureDenominators).join(', ');
13409
+ importer.addSemanticDiagnostic({
13410
+ code: AlphaTexDiagnosticCode.AT211,
13411
+ message: `Value is out of valid range. Allowed range: ${valueList}, Actual Value: ${bar.masterBar.timeSignatureDenominator}`,
13412
+ start: metaData.arguments.arguments[0].start,
13413
+ end: metaData.arguments.arguments[0].end,
13414
+ severity: AlphaTexDiagnosticsSeverity.Error
13415
+ });
13416
+ }
13159
13417
  break;
13160
13418
  case AlphaTexNodeType.Ident:
13161
13419
  case AlphaTexNodeType.String:
@@ -13178,6 +13436,8 @@ class AlphaTex1LanguageHandler {
13178
13436
  break;
13179
13437
  }
13180
13438
  return ApplyNodeResult.Applied;
13439
+ case 'beaming':
13440
+ return this._parseBeamingRule(importer, metaData, bar.masterBar);
13181
13441
  case 'ks':
13182
13442
  const keySignature = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'key signature', AlphaTex1EnumMappings.keySignature);
13183
13443
  if (keySignature === undefined) {
@@ -13367,10 +13627,64 @@ class AlphaTex1LanguageHandler {
13367
13627
  bar.masterBar.isDoubleBar = true;
13368
13628
  bar.barLineRight = BarLineStyle.LightLight;
13369
13629
  return ApplyNodeResult.Applied;
13630
+ case 'barnumberdisplay':
13631
+ const barNumberDisplay = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'bar number display', AlphaTex1EnumMappings.barNumberDisplay);
13632
+ if (barNumberDisplay === undefined) {
13633
+ return ApplyNodeResult.NotAppliedSemanticError;
13634
+ }
13635
+ bar.barNumberDisplay = barNumberDisplay;
13636
+ return ApplyNodeResult.Applied;
13370
13637
  default:
13371
13638
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
13372
13639
  }
13373
13640
  }
13641
+ _parseBeamingRule(importer, metaData, masterBar) {
13642
+ let duration = Duration.Eighth;
13643
+ const groupSizes = [];
13644
+ const durationValue = metaData.arguments.arguments[0].value;
13645
+ switch (durationValue) {
13646
+ case 4:
13647
+ duration = Duration.QuadrupleWhole;
13648
+ break;
13649
+ case 8:
13650
+ duration = Duration.Eighth;
13651
+ break;
13652
+ case 16:
13653
+ duration = Duration.Sixteenth;
13654
+ break;
13655
+ case 32:
13656
+ duration = Duration.ThirtySecond;
13657
+ break;
13658
+ default:
13659
+ importer.addSemanticDiagnostic({
13660
+ code: AlphaTexDiagnosticCode.AT209,
13661
+ message: `Value is out of valid range. Allowed range: 4,8,16 or 32, Actual Value: ${durationValue}`,
13662
+ severity: AlphaTexDiagnosticsSeverity.Error,
13663
+ start: metaData.arguments.arguments[0].start,
13664
+ end: metaData.arguments.arguments[0].end
13665
+ });
13666
+ return ApplyNodeResult.NotAppliedSemanticError;
13667
+ }
13668
+ for (let i = 1; i < metaData.arguments.arguments.length; i++) {
13669
+ const groupSize = metaData.arguments.arguments[i].value;
13670
+ if (groupSize < 1) {
13671
+ importer.addSemanticDiagnostic({
13672
+ code: AlphaTexDiagnosticCode.AT209,
13673
+ message: `Value is out of valid range. Allowed range: >0, Actual Value: ${durationValue}`,
13674
+ severity: AlphaTexDiagnosticsSeverity.Error,
13675
+ start: metaData.arguments.arguments[i].start,
13676
+ end: metaData.arguments.arguments[i].end
13677
+ });
13678
+ return ApplyNodeResult.NotAppliedSemanticError;
13679
+ }
13680
+ groupSizes.push(metaData.arguments.arguments[i].value);
13681
+ }
13682
+ if (!masterBar.beamingRules) {
13683
+ masterBar.beamingRules = new BeamingRules();
13684
+ }
13685
+ masterBar.beamingRules.groups.set(duration, groupSizes);
13686
+ return ApplyNodeResult.Applied;
13687
+ }
13374
13688
  static _handleAccidentalMode(importer, args) {
13375
13689
  const accidentalMode = AlphaTex1LanguageHandler._parseEnumValue(importer, args, 'accidental mode', AlphaTex1EnumMappings.alphaTexAccidentalMode);
13376
13690
  if (accidentalMode === undefined) {
@@ -14734,6 +15048,9 @@ class AlphaTex1LanguageHandler {
14734
15048
  if (stylesheet.showSingleStaffBrackets) {
14735
15049
  nodes.push(Atnf.meta('showSingleStaffBrackets'));
14736
15050
  }
15051
+ if (stylesheet.barNumberDisplay !== BarNumberDisplay.AllBars) {
15052
+ nodes.push(Atnf.identMeta('defaultBarNumberDisplay', BarNumberDisplay[stylesheet.barNumberDisplay]));
15053
+ }
14737
15054
  // Unsupported:
14738
15055
  // 'globaldisplaychorddiagramsontop',
14739
15056
  // 'pertrackchorddiagramsontop',
@@ -14867,6 +15184,9 @@ class AlphaTex1LanguageHandler {
14867
15184
  }
14868
15185
  ];
14869
15186
  }
15187
+ if (bar.barNumberDisplay !== undefined) {
15188
+ nodes.push(Atnf.identMeta('barNumberDisplay', BarNumberDisplay[bar.barNumberDisplay]));
15189
+ }
14870
15190
  return nodes;
14871
15191
  }
14872
15192
  static _buildStaffMetaDataNodes(nodes, staff) {
@@ -14957,6 +15277,15 @@ class AlphaTex1LanguageHandler {
14957
15277
  ])));
14958
15278
  }
14959
15279
  }
15280
+ if (masterBar.beamingRules) {
15281
+ for (const [k, v] of masterBar.beamingRules.groups) {
15282
+ const args = Atnf.args([Atnf.number(k)], true);
15283
+ for (const i of v) {
15284
+ args.arguments.push(Atnf.number(i));
15285
+ }
15286
+ nodes.push(Atnf.meta('beaming', args));
15287
+ }
15288
+ }
14960
15289
  if ((masterBar.index > 0 && masterBar.tripletFeel !== masterBar.previousMasterBar?.tripletFeel) ||
14961
15290
  (masterBar.index === 0 && masterBar.tripletFeel !== TripletFeel.NoTripletFeel)) {
14962
15291
  nodes.push(Atnf.identMeta('tf', AlphaTex1EnumMappings.tripletFeelReversed.get(masterBar.tripletFeel)));
@@ -19535,6 +19864,10 @@ class Gp3To5Importer extends ScoreImporter {
19535
19864
  this.readTrack();
19536
19865
  }
19537
19866
  }
19867
+ /**
19868
+ * Guitar Pro 3-6 changes to a bass clef if any string tuning is below B2;
19869
+ */
19870
+ static _bassClefTuningThreshold = ModelUtils.parseTuning('B2').realValue;
19538
19871
  readTrack() {
19539
19872
  const newTrack = new Track();
19540
19873
  newTrack.ensureStaveCount(1);
@@ -19615,11 +19948,11 @@ class Gp3To5Importer extends ScoreImporter {
19615
19948
  this.data.readByte();
19616
19949
  // `12` for all tunings which have bass clefs
19617
19950
  const clefMode = IOHelper.readInt32LE(this.data);
19618
- if (clefMode === 12) {
19619
- this._clefsPerTrack.set(index, Clef.F4);
19951
+ if (clefMode === 12 || tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) {
19952
+ this._clefsPerTrack.set(newTrack.index, Clef.F4);
19620
19953
  }
19621
19954
  else {
19622
- this._clefsPerTrack.set(index, Clef.G2);
19955
+ this._clefsPerTrack.set(newTrack.index, Clef.G2);
19623
19956
  }
19624
19957
  // Unknown, no UI setting seem to affect this
19625
19958
  IOHelper.readInt32LE(this.data);
@@ -19651,11 +19984,11 @@ class Gp3To5Importer extends ScoreImporter {
19651
19984
  }
19652
19985
  }
19653
19986
  else {
19654
- if (GeneralMidi.isBass(newTrack.playbackInfo.program)) {
19655
- this._clefsPerTrack.set(index, Clef.F4);
19987
+ if (tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) {
19988
+ this._clefsPerTrack.set(newTrack.index, Clef.F4);
19656
19989
  }
19657
19990
  else {
19658
- this._clefsPerTrack.set(index, Clef.G2);
19991
+ this._clefsPerTrack.set(newTrack.index, Clef.G2);
19659
19992
  }
19660
19993
  }
19661
19994
  }
@@ -20929,6 +21262,19 @@ class BinaryStylesheet {
20929
21262
  ModelUtils.getOrCreateHeaderFooterStyle(score, ScoreSubElement.CopyrightSecondLine).isVisible =
20930
21263
  value;
20931
21264
  break;
21265
+ case 'System/barIndexDrawType':
21266
+ switch (value) {
21267
+ case 0:
21268
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.AllBars;
21269
+ break;
21270
+ case 1:
21271
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.FirstOfSystem;
21272
+ break;
21273
+ case 2:
21274
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.Hide;
21275
+ break;
21276
+ }
21277
+ break;
20932
21278
  }
20933
21279
  }
20934
21280
  }
@@ -21118,6 +21464,17 @@ class BinaryStylesheet {
21118
21464
  }
21119
21465
  }
21120
21466
  }
21467
+ switch (score.stylesheet.barNumberDisplay) {
21468
+ case BarNumberDisplay.AllBars:
21469
+ binaryStylesheet.addValue('System/barIndexDrawType', 0, DataType.Integer);
21470
+ break;
21471
+ case BarNumberDisplay.FirstOfSystem:
21472
+ binaryStylesheet.addValue('System/barIndexDrawType', 1, DataType.Integer);
21473
+ break;
21474
+ case BarNumberDisplay.Hide:
21475
+ binaryStylesheet.addValue('System/barIndexDrawType', 2, DataType.Integer);
21476
+ break;
21477
+ }
21121
21478
  const writer = ByteBuffer.withCapacity(128);
21122
21479
  binaryStylesheet.writeTo(writer);
21123
21480
  return writer.toArray();
@@ -22929,6 +23286,8 @@ class GpifParser {
22929
23286
  }
22930
23287
  }
22931
23288
  _parseMasterBarXProperties(masterBar, node) {
23289
+ let beamingRuleDuration = Number.NaN;
23290
+ let beamingRuleGroups = undefined;
22932
23291
  for (const c of node.childElements()) {
22933
23292
  switch (c.localName) {
22934
23293
  case 'XProperty':
@@ -22937,10 +23296,32 @@ class GpifParser {
22937
23296
  case '1124073984':
22938
23297
  masterBar.displayScale = GpifParser._parseFloatSafe(c.findChildElement('Double')?.innerText, 1);
22939
23298
  break;
23299
+ case '1124139010':
23300
+ beamingRuleDuration = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
23301
+ break;
23302
+ default:
23303
+ const idNumeric = GpifParser._parseIntSafe(id, 0);
23304
+ if (idNumeric >= 1124139264 && idNumeric <= 1124139295) {
23305
+ const groupIndex = idNumeric - 1124139264;
23306
+ const groupSize = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
23307
+ if (beamingRuleGroups === undefined) {
23308
+ beamingRuleGroups = [];
23309
+ }
23310
+ while (beamingRuleGroups.length < groupIndex + 1) {
23311
+ beamingRuleGroups.push(0);
23312
+ }
23313
+ beamingRuleGroups[groupIndex] = groupSize;
23314
+ }
23315
+ break;
22940
23316
  }
22941
23317
  break;
22942
23318
  }
22943
23319
  }
23320
+ if (!Number.isNaN(beamingRuleDuration) && beamingRuleGroups) {
23321
+ const rules = new BeamingRules();
23322
+ rules.groups.set(beamingRuleDuration, beamingRuleGroups);
23323
+ masterBar.beamingRules = rules;
23324
+ }
22944
23325
  }
22945
23326
  _parseBeatProperties(node, beat) {
22946
23327
  let isWhammy = false;
@@ -24824,6 +25205,8 @@ class MusicXmlImporter extends ScoreImporter {
24824
25205
  _idToTrackInfo = new Map();
24825
25206
  _indexToTrackInfo = new Map();
24826
25207
  _staffToContext = new Map();
25208
+ _currentBarNumberDisplayPart;
25209
+ _currentBarNumberDisplayBar;
24827
25210
  _divisionsPerQuarterNote = 1;
24828
25211
  _currentDynamics = DynamicValue.F;
24829
25212
  get name() {
@@ -25437,20 +25820,29 @@ class MusicXmlImporter extends ScoreImporter {
25437
25820
  break;
25438
25821
  }
25439
25822
  }
25823
+ this._currentBarNumberDisplayPart = undefined;
25440
25824
  }
25441
25825
  _parsePartwiseMeasure(element, track, index) {
25442
25826
  const masterBar = this._getOrCreateMasterBar(element, index);
25443
- this._parsePartMeasure(element, masterBar, track);
25827
+ const implicit = element.attributes.get('implicit') === 'yes';
25828
+ this._parsePartMeasure(element, masterBar, track, implicit, true);
25829
+ this._currentBarNumberDisplayBar = undefined;
25444
25830
  }
25445
25831
  _parseTimewiseMeasure(element, index) {
25446
25832
  const masterBar = this._getOrCreateMasterBar(element, index);
25833
+ const implicit = element.attributes.get('implicit') === 'yes';
25447
25834
  for (const c of element.childElements()) {
25448
25835
  switch (c.localName) {
25449
25836
  case 'part':
25450
- this._parseTimewisePart(c, masterBar);
25837
+ this._parseTimewisePart(c, masterBar, implicit);
25838
+ this._currentBarNumberDisplayPart = undefined;
25839
+ break;
25840
+ case 'print':
25841
+ this._parsePrint(c, masterBar, undefined, true);
25451
25842
  break;
25452
25843
  }
25453
25844
  }
25845
+ this._currentBarNumberDisplayBar = undefined;
25454
25846
  }
25455
25847
  _getOrCreateMasterBar(element, index) {
25456
25848
  const implicit = element.attributes.get('implicit') === 'yes';
@@ -25469,13 +25861,13 @@ class MusicXmlImporter extends ScoreImporter {
25469
25861
  const masterBar = this._score.masterBars[index];
25470
25862
  return masterBar;
25471
25863
  }
25472
- _parseTimewisePart(element, masterBar) {
25864
+ _parseTimewisePart(element, masterBar, implicit) {
25473
25865
  const id = element.attributes.get('id');
25474
25866
  if (!id || !this._idToTrackInfo.has(id)) {
25475
25867
  return;
25476
25868
  }
25477
25869
  const track = this._idToTrackInfo.get(id).track;
25478
- this._parsePartMeasure(element, masterBar, track);
25870
+ this._parsePartMeasure(element, masterBar, track, implicit, false);
25479
25871
  }
25480
25872
  // current measure state
25481
25873
  /**
@@ -25487,7 +25879,7 @@ class MusicXmlImporter extends ScoreImporter {
25487
25879
  * to access the current voice/staff (e.g. on rests when we don't have notes)
25488
25880
  */
25489
25881
  _lastBeat = null;
25490
- _parsePartMeasure(element, masterBar, track) {
25882
+ _parsePartMeasure(element, masterBar, track, implicit, isPartwise) {
25491
25883
  this._musicalPosition = 0;
25492
25884
  this._lastBeat = null;
25493
25885
  masterBar.alternateEndings = this._nextMasterBarRepeatEnding;
@@ -25514,7 +25906,7 @@ class MusicXmlImporter extends ScoreImporter {
25514
25906
  break;
25515
25907
  // case 'figured-bass': Not supported
25516
25908
  case 'print':
25517
- this._parsePrint(c, masterBar, track);
25909
+ this._parsePrint(c, masterBar, track, true);
25518
25910
  break;
25519
25911
  case 'sound':
25520
25912
  this._parseSound(c, masterBar, track);
@@ -25535,16 +25927,51 @@ class MusicXmlImporter extends ScoreImporter {
25535
25927
  this._applySimileMarks(masterBar, track);
25536
25928
  // initial empty staff and voice (if no other elements created something already)
25537
25929
  const staff = this._getOrCreateStaff(track, 0);
25538
- this._getOrCreateBar(staff, masterBar);
25930
+ const bar = this._getOrCreateBar(staff, masterBar);
25931
+ if (implicit) {
25932
+ bar.barNumberDisplay = BarNumberDisplay.Hide;
25933
+ }
25934
+ else if (isPartwise) {
25935
+ bar.barNumberDisplay = this._currentBarNumberDisplayBar ?? this._currentBarNumberDisplayPart;
25936
+ }
25937
+ else {
25938
+ bar.barNumberDisplay = this._currentBarNumberDisplayPart ?? this._currentBarNumberDisplayBar;
25939
+ }
25539
25940
  // clear measure attribute
25540
25941
  this._keyAllStaves = null;
25541
25942
  }
25542
- _parsePrint(element, masterBar, track) {
25543
- if (element.getAttribute('new-system', 'no') === 'yes') {
25544
- track.addLineBreaks(masterBar.index);
25943
+ _parsePrint(element, masterBar, track, isMeasurePrint) {
25944
+ if (track !== undefined) {
25945
+ if (element.getAttribute('new-system', 'no') === 'yes') {
25946
+ track.addLineBreaks(masterBar.index);
25947
+ }
25948
+ else if (element.getAttribute('new-page', 'no') === 'yes') {
25949
+ track.addLineBreaks(masterBar.index);
25950
+ }
25545
25951
  }
25546
- else if (element.getAttribute('new-page', 'no') === 'yes') {
25547
- track.addLineBreaks(masterBar.index);
25952
+ let newDisplay = undefined;
25953
+ for (const c of element.childElements()) {
25954
+ switch (c.localName) {
25955
+ case 'measure-numbering':
25956
+ switch (c.innerText) {
25957
+ case 'none':
25958
+ newDisplay = BarNumberDisplay.Hide;
25959
+ break;
25960
+ case 'measure':
25961
+ newDisplay = BarNumberDisplay.AllBars;
25962
+ break;
25963
+ case 'system':
25964
+ newDisplay = BarNumberDisplay.FirstOfSystem;
25965
+ break;
25966
+ }
25967
+ break;
25968
+ }
25969
+ }
25970
+ if (isMeasurePrint) {
25971
+ this._currentBarNumberDisplayBar = newDisplay;
25972
+ }
25973
+ else {
25974
+ this._currentBarNumberDisplayPart = newDisplay;
25548
25975
  }
25549
25976
  }
25550
25977
  _applySimileMarks(masterBar, track) {
@@ -26654,7 +27081,7 @@ class MusicXmlImporter extends ScoreImporter {
26654
27081
  if (unit !== null && perMinute > 0) {
26655
27082
  const tempoAutomation = new Automation();
26656
27083
  tempoAutomation.type = AutomationType.Tempo;
26657
- tempoAutomation.value = (perMinute * (unit / 4)) | 0;
27084
+ tempoAutomation.value = perMinute * (unit / 4);
26658
27085
  tempoAutomation.ratioPosition = ratioPosition;
26659
27086
  if (!this._hasSameTempo(masterBar, tempoAutomation)) {
26660
27087
  masterBar.tempoAutomations.push(tempoAutomation);
@@ -29583,7 +30010,7 @@ class MidiFileSequencer {
29583
30010
  }
29584
30011
  if (mEvent.type === MidiEventType.TempoChange) {
29585
30012
  const meta = mEvent;
29586
- bpm = meta.beatsPerMinute;
30013
+ bpm = MidiFileSequencer._sanitizeBpm(meta.beatsPerMinute);
29587
30014
  state.tempoChanges.push(new MidiFileSequencerTempoChange(bpm, absTick, absTime));
29588
30015
  metronomeLengthInMillis = metronomeLengthInTicks * (60000.0 / (bpm * midiFile.division));
29589
30016
  }
@@ -29701,7 +30128,7 @@ class MidiFileSequencer {
29701
30128
  }
29702
30129
  else {
29703
30130
  const previousSyncPoint = syncPoints[i - 1];
29704
- previousModifiedTempo = previousSyncPoint.syncBpm;
30131
+ previousModifiedTempo = MidiFileSequencer._sanitizeBpm(previousSyncPoint.syncBpm);
29705
30132
  previousMillisecondOffset = previousSyncPoint.syncTime;
29706
30133
  previousTick = previousSyncPoint.synthTick;
29707
30134
  }
@@ -29725,7 +30152,7 @@ class MidiFileSequencer {
29725
30152
  syncPoint.syncTime = interpolatedMillisecondOffset;
29726
30153
  syncPoint.syncBpm = previousModifiedTempo;
29727
30154
  }
29728
- bpm = state.tempoChanges[tempoChangeIndex].bpm;
30155
+ bpm = MidiFileSequencer._sanitizeBpm(state.tempoChanges[tempoChangeIndex].bpm);
29729
30156
  tempoChangeIndex++;
29730
30157
  }
29731
30158
  deltaTick = p.synthTick - absTick;
@@ -29746,10 +30173,13 @@ class MidiFileSequencer {
29746
30173
  this._updateCurrentTempo(state, timePosition);
29747
30174
  const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
29748
30175
  const timeDiff = timePosition - lastTempoChange.time;
29749
- const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
30176
+ const ticks = (timeDiff / (60000.0 / (MidiFileSequencer._sanitizeBpm(lastTempoChange.bpm) * state.division))) | 0;
29750
30177
  // we add 1 for possible rounding errors.(floating point issuses)
29751
30178
  return lastTempoChange.ticks + ticks + 1;
29752
30179
  }
30180
+ static _sanitizeBpm(bpm) {
30181
+ return Math.max(bpm, 1); // prevent <0 bpms. Doesn't make sense and can cause endless loops
30182
+ }
29753
30183
  currentUpdateCurrentTempo(timePosition) {
29754
30184
  this._updateCurrentTempo(this._mainState, timePosition * this.playbackSpeed);
29755
30185
  }
@@ -36427,6 +36857,43 @@ class JsonHelper {
36427
36857
  }
36428
36858
  }
36429
36859
 
36860
+ /**
36861
+ * @internal
36862
+ */
36863
+ class BeamingRulesSerializer {
36864
+ static fromJson(obj, m) {
36865
+ if (!m) {
36866
+ return;
36867
+ }
36868
+ JsonHelper.forEach(m, (v, k) => BeamingRulesSerializer.setProperty(obj, k, v));
36869
+ }
36870
+ static toJson(obj) {
36871
+ if (!obj) {
36872
+ return null;
36873
+ }
36874
+ const o = new Map();
36875
+ {
36876
+ const m = new Map();
36877
+ o.set("groups", m);
36878
+ for (const [k, v] of obj.groups) {
36879
+ m.set(k.toString(), v);
36880
+ }
36881
+ }
36882
+ return o;
36883
+ }
36884
+ static setProperty(obj, property, v) {
36885
+ switch (property) {
36886
+ case "groups":
36887
+ obj.groups = new Map();
36888
+ JsonHelper.forEach(v, (v, k) => {
36889
+ obj.groups.set(JsonHelper.parseEnum(k, Duration), v);
36890
+ });
36891
+ return true;
36892
+ }
36893
+ return false;
36894
+ }
36895
+ }
36896
+
36430
36897
  /**
36431
36898
  * @internal
36432
36899
  */
@@ -36605,6 +37072,9 @@ class MasterBarSerializer {
36605
37072
  o.set("timesignaturenumerator", obj.timeSignatureNumerator);
36606
37073
  o.set("timesignaturedenominator", obj.timeSignatureDenominator);
36607
37074
  o.set("timesignaturecommon", obj.timeSignatureCommon);
37075
+ if (obj.beamingRules) {
37076
+ o.set("beamingrules", BeamingRulesSerializer.toJson(obj.beamingRules));
37077
+ }
36608
37078
  o.set("isfreetime", obj.isFreeTime);
36609
37079
  o.set("tripletfeel", obj.tripletFeel);
36610
37080
  if (obj.section) {
@@ -36657,6 +37127,15 @@ class MasterBarSerializer {
36657
37127
  case "timesignaturecommon":
36658
37128
  obj.timeSignatureCommon = v;
36659
37129
  return true;
37130
+ case "beamingrules":
37131
+ if (v) {
37132
+ obj.beamingRules = new BeamingRules();
37133
+ BeamingRulesSerializer.fromJson(obj.beamingRules, v);
37134
+ }
37135
+ else {
37136
+ obj.beamingRules = undefined;
37137
+ }
37138
+ return true;
36660
37139
  case "isfreetime":
36661
37140
  obj.isFreeTime = v;
36662
37141
  return true;
@@ -37481,6 +37960,7 @@ class BarSerializer {
37481
37960
  o.set("barlineright", obj.barLineRight);
37482
37961
  o.set("keysignature", obj.keySignature);
37483
37962
  o.set("keysignaturetype", obj.keySignatureType);
37963
+ o.set("barnumberdisplay", obj.barNumberDisplay);
37484
37964
  if (obj.style) {
37485
37965
  o.set("style", BarStyleSerializer.toJson(obj.style));
37486
37966
  }
@@ -37534,6 +38014,9 @@ class BarSerializer {
37534
38014
  case "keysignaturetype":
37535
38015
  obj.keySignatureType = JsonHelper.parseEnum(v, KeySignatureType);
37536
38016
  return true;
38017
+ case "barnumberdisplay":
38018
+ obj.barNumberDisplay = JsonHelper.parseEnum(v, BarNumberDisplay);
38019
+ return true;
37537
38020
  case "style":
37538
38021
  if (v) {
37539
38022
  obj.style = new BarStyle();
@@ -38028,6 +38511,7 @@ class RenderStylesheetSerializer {
38028
38511
  o.set("hideemptystaves", obj.hideEmptyStaves);
38029
38512
  o.set("hideemptystavesinfirstsystem", obj.hideEmptyStavesInFirstSystem);
38030
38513
  o.set("showsinglestaffbrackets", obj.showSingleStaffBrackets);
38514
+ o.set("barnumberdisplay", obj.barNumberDisplay);
38031
38515
  return o;
38032
38516
  }
38033
38517
  static setProperty(obj, property, v) {
@@ -38098,6 +38582,9 @@ class RenderStylesheetSerializer {
38098
38582
  case "showsinglestaffbrackets":
38099
38583
  obj.showSingleStaffBrackets = v;
38100
38584
  return true;
38585
+ case "barnumberdisplay":
38586
+ obj.barNumberDisplay = JsonHelper.parseEnum(v, BarNumberDisplay);
38587
+ return true;
38101
38588
  }
38102
38589
  return false;
38103
38590
  }
@@ -41829,25 +42316,20 @@ class RenderingResourcesSerializer {
41829
42316
  const o = new Map();
41830
42317
  o.set("smuflfontfamilyname", obj.smuflFontFamilyName);
41831
42318
  o.set("engravingsettings", EngravingSettingsSerializer.toJson(obj.engravingSettings));
41832
- o.set("copyrightfont", Font.toJson(obj.copyrightFont));
41833
- o.set("titlefont", Font.toJson(obj.titleFont));
41834
- o.set("subtitlefont", Font.toJson(obj.subTitleFont));
41835
- o.set("wordsfont", Font.toJson(obj.wordsFont));
41836
- o.set("effectfont", Font.toJson(obj.effectFont));
41837
- o.set("timerfont", Font.toJson(obj.timerFont));
41838
- o.set("directionsfont", Font.toJson(obj.directionsFont));
41839
- o.set("fretboardnumberfont", Font.toJson(obj.fretboardNumberFont));
42319
+ {
42320
+ const m = new Map();
42321
+ o.set("elementfonts", m);
42322
+ for (const [k, v] of obj.elementFonts) {
42323
+ m.set(k.toString(), Font.toJson(v));
42324
+ }
42325
+ }
41840
42326
  o.set("numberednotationfont", Font.toJson(obj.numberedNotationFont));
41841
42327
  o.set("numberednotationgracefont", Font.toJson(obj.numberedNotationGraceFont));
41842
42328
  o.set("tablaturefont", Font.toJson(obj.tablatureFont));
41843
42329
  o.set("gracefont", Font.toJson(obj.graceFont));
41844
42330
  o.set("stafflinecolor", Color.toJson(obj.staffLineColor));
41845
42331
  o.set("barseparatorcolor", Color.toJson(obj.barSeparatorColor));
41846
- o.set("barnumberfont", Font.toJson(obj.barNumberFont));
41847
42332
  o.set("barnumbercolor", Color.toJson(obj.barNumberColor));
41848
- o.set("fingeringfont", Font.toJson(obj.fingeringFont));
41849
- o.set("inlinefingeringfont", Font.toJson(obj.inlineFingeringFont));
41850
- o.set("markerfont", Font.toJson(obj.markerFont));
41851
42333
  o.set("mainglyphcolor", Color.toJson(obj.mainGlyphColor));
41852
42334
  o.set("secondaryglyphcolor", Color.toJson(obj.secondaryGlyphColor));
41853
42335
  o.set("scoreinfocolor", Color.toJson(obj.scoreInfoColor));
@@ -41858,29 +42340,10 @@ class RenderingResourcesSerializer {
41858
42340
  case "smuflfontfamilyname":
41859
42341
  obj.smuflFontFamilyName = v;
41860
42342
  return true;
41861
- case "copyrightfont":
41862
- obj.copyrightFont = Font.fromJson(v);
41863
- return true;
41864
- case "titlefont":
41865
- obj.titleFont = Font.fromJson(v);
41866
- return true;
41867
- case "subtitlefont":
41868
- obj.subTitleFont = Font.fromJson(v);
41869
- return true;
41870
- case "wordsfont":
41871
- obj.wordsFont = Font.fromJson(v);
41872
- return true;
41873
- case "effectfont":
41874
- obj.effectFont = Font.fromJson(v);
41875
- return true;
41876
- case "timerfont":
41877
- obj.timerFont = Font.fromJson(v);
41878
- return true;
41879
- case "directionsfont":
41880
- obj.directionsFont = Font.fromJson(v);
41881
- return true;
41882
- case "fretboardnumberfont":
41883
- obj.fretboardNumberFont = Font.fromJson(v);
42343
+ case "elementfonts":
42344
+ JsonHelper.forEach(v, (v, k) => {
42345
+ obj.elementFonts.set(JsonHelper.parseEnum(k, NotationElement), Font.fromJson(v));
42346
+ });
41884
42347
  return true;
41885
42348
  case "numberednotationfont":
41886
42349
  obj.numberedNotationFont = Font.fromJson(v);
@@ -41900,21 +42363,9 @@ class RenderingResourcesSerializer {
41900
42363
  case "barseparatorcolor":
41901
42364
  obj.barSeparatorColor = Color.fromJson(v);
41902
42365
  return true;
41903
- case "barnumberfont":
41904
- obj.barNumberFont = Font.fromJson(v);
41905
- return true;
41906
42366
  case "barnumbercolor":
41907
42367
  obj.barNumberColor = Color.fromJson(v);
41908
42368
  return true;
41909
- case "fingeringfont":
41910
- obj.fingeringFont = Font.fromJson(v);
41911
- return true;
41912
- case "inlinefingeringfont":
41913
- obj.inlineFingeringFont = Font.fromJson(v);
41914
- return true;
41915
- case "markerfont":
41916
- obj.markerFont = Font.fromJson(v);
41917
- return true;
41918
42369
  case "mainglyphcolor":
41919
42370
  obj.mainGlyphColor = Color.fromJson(v);
41920
42371
  return true;
@@ -41971,6 +42422,46 @@ var StaveProfile;
41971
42422
  class RenderingResources {
41972
42423
  static _sansFont = 'Arial, sans-serif';
41973
42424
  static _serifFont = 'Georgia, serif';
42425
+ static _effectFont = new Font(RenderingResources._serifFont, 12, FontStyle.Italic);
42426
+ /**
42427
+ * The default fonts for notation elements if not specified by the user.
42428
+ */
42429
+ static defaultFonts = new Map([
42430
+ [NotationElement.ScoreTitle, new Font(RenderingResources._serifFont, 32, FontStyle.Plain)],
42431
+ [NotationElement.ScoreSubTitle, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42432
+ [NotationElement.ScoreArtist, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42433
+ [NotationElement.ScoreAlbum, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42434
+ [NotationElement.ScoreWords, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42435
+ [NotationElement.ScoreMusic, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42436
+ [NotationElement.ScoreWordsAndMusic, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42437
+ [NotationElement.ScoreCopyright, new Font(RenderingResources._sansFont, 12, FontStyle.Plain, FontWeight.Bold)],
42438
+ [NotationElement.EffectBeatTimer, new Font(RenderingResources._serifFont, 12, FontStyle.Plain)],
42439
+ [NotationElement.EffectDirections, new Font(RenderingResources._serifFont, 14, FontStyle.Plain)],
42440
+ [NotationElement.ChordDiagramFretboardNumbers, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42441
+ [NotationElement.EffectFingering, new Font(RenderingResources._serifFont, 14, FontStyle.Plain)],
42442
+ [NotationElement.EffectMarker, new Font(RenderingResources._serifFont, 14, FontStyle.Plain, FontWeight.Bold)],
42443
+ [NotationElement.EffectCapo, RenderingResources._effectFont],
42444
+ [NotationElement.EffectFreeTime, RenderingResources._effectFont],
42445
+ [NotationElement.EffectLyrics, RenderingResources._effectFont],
42446
+ [NotationElement.EffectTap, RenderingResources._effectFont],
42447
+ [NotationElement.ChordDiagrams, RenderingResources._effectFont],
42448
+ [NotationElement.EffectChordNames, RenderingResources._effectFont],
42449
+ [NotationElement.EffectText, RenderingResources._effectFont],
42450
+ [NotationElement.EffectPalmMute, RenderingResources._effectFont],
42451
+ [NotationElement.EffectLetRing, RenderingResources._effectFont],
42452
+ [NotationElement.EffectBeatBarre, RenderingResources._effectFont],
42453
+ [NotationElement.EffectTripletFeel, RenderingResources._effectFont],
42454
+ [NotationElement.EffectHarmonics, RenderingResources._effectFont],
42455
+ [NotationElement.EffectPickSlide, RenderingResources._effectFont],
42456
+ [NotationElement.GuitarTuning, RenderingResources._effectFont],
42457
+ [NotationElement.EffectRasgueado, RenderingResources._effectFont],
42458
+ [NotationElement.EffectWhammyBar, RenderingResources._effectFont],
42459
+ [NotationElement.TrackNames, RenderingResources._effectFont],
42460
+ [NotationElement.RepeatCount, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42461
+ [NotationElement.BarNumber, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42462
+ [NotationElement.ScoreBendSlur, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42463
+ [NotationElement.EffectAlternateEndings, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)]
42464
+ ]);
41974
42465
  /**
41975
42466
  * The name of the SMuFL Font to use for rendering music symbols.
41976
42467
  *
@@ -41997,50 +42488,168 @@ class RenderingResources {
41997
42488
  * The font to use for displaying the songs copyright information in the header of the music sheet.
41998
42489
  * @defaultValue `bold 12px Arial, sans-serif`
41999
42490
  * @since 0.9.6
42491
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreCopyright}
42492
+ */
42493
+ get copyrightFont() {
42494
+ return this.elementFonts.get(NotationElement.ScoreCopyright);
42495
+ }
42496
+ /**
42497
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreCopyright}
42000
42498
  */
42001
- copyrightFont = new Font(RenderingResources._sansFont, 12, FontStyle.Plain, FontWeight.Bold);
42499
+ set copyrightFont(value) {
42500
+ this.elementFonts.set(NotationElement.ScoreCopyright, value);
42501
+ }
42002
42502
  /**
42003
42503
  * The font to use for displaying the songs title in the header of the music sheet.
42004
42504
  * @defaultValue `32px Georgia, serif`
42005
42505
  * @since 0.9.6
42506
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreTitle}
42507
+ */
42508
+ get titleFont() {
42509
+ return this.elementFonts.get(NotationElement.ScoreTitle);
42510
+ }
42511
+ /**
42512
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreTitle}
42006
42513
  */
42007
- titleFont = new Font(RenderingResources._serifFont, 32, FontStyle.Plain);
42514
+ set titleFont(value) {
42515
+ this.elementFonts.set(NotationElement.ScoreTitle, value);
42516
+ }
42008
42517
  /**
42009
42518
  * The font to use for displaying the songs subtitle in the header of the music sheet.
42010
42519
  * @defaultValue `20px Georgia, serif`
42011
42520
  * @since 0.9.6
42521
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreSubTitle}
42012
42522
  */
42013
- subTitleFont = new Font(RenderingResources._serifFont, 20, FontStyle.Plain);
42523
+ get subTitleFont() {
42524
+ return this.elementFonts.get(NotationElement.ScoreSubTitle);
42525
+ }
42526
+ /**
42527
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreSubTitle}
42528
+ */
42529
+ set subTitleFont(value) {
42530
+ this.elementFonts.set(NotationElement.ScoreSubTitle, value);
42531
+ }
42014
42532
  /**
42015
42533
  * The font to use for displaying the lyrics information in the header of the music sheet.
42016
42534
  * @defaultValue `15px Arial, sans-serif`
42017
42535
  * @since 0.9.6
42536
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreWords}
42018
42537
  */
42019
- wordsFont = new Font(RenderingResources._serifFont, 15, FontStyle.Plain);
42538
+ get wordsFont() {
42539
+ return this.elementFonts.get(NotationElement.ScoreWords);
42540
+ }
42020
42541
  /**
42021
- * The font to use for displaying certain effect related elements in the music sheet.
42022
- * @defaultValue `italic 12px Georgia, serif`
42023
- * @since 0.9.6
42542
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreWords}
42024
42543
  */
42025
- effectFont = new Font(RenderingResources._serifFont, 12, FontStyle.Italic);
42544
+ set wordsFont(value) {
42545
+ this.elementFonts.set(NotationElement.ScoreWords, value);
42546
+ }
42026
42547
  /**
42027
42548
  * The font to use for displaying beat time information in the music sheet.
42028
42549
  * @defaultValue `12px Georgia, serif`
42029
42550
  * @since 1.4.0
42551
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectBeatTimer}
42030
42552
  */
42031
- timerFont = new Font(RenderingResources._serifFont, 12, FontStyle.Plain);
42553
+ get timerFont() {
42554
+ return this.elementFonts.get(NotationElement.EffectBeatTimer);
42555
+ }
42556
+ /**
42557
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectBeatTimer}
42558
+ */
42559
+ set timerFont(value) {
42560
+ this.elementFonts.set(NotationElement.EffectBeatTimer, value);
42561
+ }
42032
42562
  /**
42033
42563
  * The font to use for displaying the directions texts.
42034
42564
  * @defaultValue `14px Georgia, serif`
42035
42565
  * @since 1.4.0
42566
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectDirections}
42036
42567
  */
42037
- directionsFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain);
42568
+ get directionsFont() {
42569
+ return this.elementFonts.get(NotationElement.EffectDirections);
42570
+ }
42571
+ /**
42572
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectDirections}
42573
+ */
42574
+ set directionsFont(value) {
42575
+ this.elementFonts.set(NotationElement.EffectDirections, value);
42576
+ }
42038
42577
  /**
42039
42578
  * The font to use for displaying the fretboard numbers in chord diagrams.
42040
42579
  * @defaultValue `11px Arial, sans-serif`
42041
42580
  * @since 0.9.6
42581
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ChordDiagramFretboardNumbers}
42042
42582
  */
42043
- fretboardNumberFont = new Font(RenderingResources._sansFont, 11, FontStyle.Plain);
42583
+ get fretboardNumberFont() {
42584
+ return this.elementFonts.get(NotationElement.ChordDiagramFretboardNumbers);
42585
+ }
42586
+ /**
42587
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ChordDiagramFretboardNumbers}
42588
+ */
42589
+ set fretboardNumberFont(value) {
42590
+ this.elementFonts.set(NotationElement.ChordDiagramFretboardNumbers, value);
42591
+ }
42592
+ /**
42593
+ * Unused, see deprecation note.
42594
+ * @defaultValue `14px Georgia, serif`
42595
+ * @since 0.9.6
42596
+ * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42597
+ * @json_ignore
42598
+ */
42599
+ fingeringFont = RenderingResources._effectFont;
42600
+ /**
42601
+ * Unused, see deprecation note.
42602
+ * @defaultValue `12px Georgia, serif`
42603
+ * @since 1.4.0
42604
+ * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42605
+ * @json_ignore
42606
+ */
42607
+ inlineFingeringFont = RenderingResources._effectFont;
42608
+ /**
42609
+ * The font to use for section marker labels shown above the music sheet.
42610
+ * @defaultValue `bold 14px Georgia, serif`
42611
+ * @since 0.9.6
42612
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectMarker}
42613
+ */
42614
+ get markerFont() {
42615
+ return this.elementFonts.get(NotationElement.EffectMarker);
42616
+ }
42617
+ /**
42618
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectMarker}
42619
+ */
42620
+ set markerFont(value) {
42621
+ this.elementFonts.set(NotationElement.EffectMarker, value);
42622
+ }
42623
+ /**
42624
+ * Ununsed, see deprecation note.
42625
+ * @defaultValue `italic 12px Georgia, serif`
42626
+ * @since 0.9.6
42627
+ * @deprecated use {@link elementFonts} with the respective
42628
+ * @json_ignore
42629
+ */
42630
+ effectFont = RenderingResources._effectFont;
42631
+ /**
42632
+ * The font to use for displaying the bar numbers above the music sheet.
42633
+ * @defaultValue `11px Arial, sans-serif`
42634
+ * @since 0.9.6
42635
+ * @deprecated use {@link elementFonts} with {@link NotationElement.BarNumber}
42636
+ */
42637
+ get barNumberFont() {
42638
+ return this.elementFonts.get(NotationElement.BarNumber);
42639
+ }
42640
+ /**
42641
+ * @deprecated use {@link elementFonts} with {@link NotationElement.BarNumber}
42642
+ */
42643
+ set barNumberFont(value) {
42644
+ this.elementFonts.set(NotationElement.BarNumber, value);
42645
+ }
42646
+ // NOTE: the main staff fonts are still own properties.
42647
+ /**
42648
+ * The fonts used by individual elements. Check {@link defaultFonts} for the elements which have custom fonts.
42649
+ * Removing fonts from this map can lead to unexpected side effects and errors. Only update it with new values.
42650
+ * @json_immutable
42651
+ */
42652
+ elementFonts = new Map();
42044
42653
  /**
42045
42654
  * The font to use for displaying the numbered music notation in the music sheet.
42046
42655
  * @defaultValue `14px Arial, sans-serif`
@@ -42077,38 +42686,12 @@ class RenderingResources {
42077
42686
  * @since 0.9.6
42078
42687
  */
42079
42688
  barSeparatorColor = new Color(34, 34, 17, 0xff);
42080
- /**
42081
- * The font to use for displaying the bar numbers above the music sheet.
42082
- * @defaultValue `11px Arial, sans-serif`
42083
- * @since 0.9.6
42084
- */
42085
- barNumberFont = new Font(RenderingResources._sansFont, 11, FontStyle.Plain);
42086
42689
  /**
42087
42690
  * The color to use for displaying the bar numbers above the music sheet.
42088
42691
  * @defaultValue `rgb(200, 0, 0)`
42089
42692
  * @since 0.9.6
42090
42693
  */
42091
42694
  barNumberColor = new Color(200, 0, 0, 0xff);
42092
- /**
42093
- * The font to use for displaying finger information in the music sheet.
42094
- * @defaultValue `14px Georgia, serif`
42095
- * @since 0.9.6
42096
- * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42097
- */
42098
- fingeringFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain);
42099
- /**
42100
- * The font to use for displaying finger information when inline into the music sheet.
42101
- * @defaultValue `12px Georgia, serif`
42102
- * @since 1.4.0
42103
- * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42104
- */
42105
- inlineFingeringFont = new Font(RenderingResources._serifFont, 12, FontStyle.Plain);
42106
- /**
42107
- * The font to use for section marker labels shown above the music sheet.
42108
- * @defaultValue `bold 14px Georgia, serif`
42109
- * @since 0.9.6
42110
- */
42111
- markerFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain, FontWeight.Bold);
42112
42695
  /**
42113
42696
  * The color to use for music notation elements of the primary voice.
42114
42697
  * @defaultValue `rgb(0, 0, 0)`
@@ -42127,28 +42710,50 @@ class RenderingResources {
42127
42710
  * @since 0.9.6
42128
42711
  */
42129
42712
  scoreInfoColor = new Color(0, 0, 0, 0xff);
42713
+ constructor() {
42714
+ for (const [k, v] of RenderingResources.defaultFonts) {
42715
+ this.elementFonts.set(k, v.withSize(v.size));
42716
+ }
42717
+ }
42130
42718
  /**
42131
42719
  * @internal
42132
42720
  * @param element
42133
42721
  */
42134
42722
  getFontForElement(element) {
42723
+ let notationElement = NotationElement.ScoreWords;
42135
42724
  switch (element) {
42136
42725
  case ScoreSubElement.Title:
42137
- return this.titleFont;
42726
+ notationElement = NotationElement.ScoreTitle;
42727
+ break;
42138
42728
  case ScoreSubElement.SubTitle:
42729
+ notationElement = NotationElement.ScoreSubTitle;
42730
+ break;
42139
42731
  case ScoreSubElement.Artist:
42732
+ notationElement = NotationElement.ScoreArtist;
42733
+ break;
42140
42734
  case ScoreSubElement.Album:
42141
- return this.subTitleFont;
42735
+ notationElement = NotationElement.ScoreAlbum;
42736
+ break;
42142
42737
  case ScoreSubElement.Words:
42738
+ notationElement = NotationElement.ScoreWords;
42739
+ break;
42143
42740
  case ScoreSubElement.Music:
42741
+ notationElement = NotationElement.ScoreMusic;
42742
+ break;
42144
42743
  case ScoreSubElement.WordsAndMusic:
42145
- case ScoreSubElement.Transcriber:
42146
- return this.wordsFont;
42744
+ notationElement = NotationElement.ScoreWordsAndMusic;
42745
+ break;
42147
42746
  case ScoreSubElement.Copyright:
42148
42747
  case ScoreSubElement.CopyrightSecondLine:
42149
- return this.copyrightFont;
42748
+ notationElement = NotationElement.ScoreCopyright;
42749
+ break;
42750
+ default:
42751
+ notationElement = NotationElement.ScoreWords;
42752
+ break;
42150
42753
  }
42151
- return this.wordsFont;
42754
+ return this.elementFonts.has(notationElement)
42755
+ ? this.elementFonts.get(notationElement)
42756
+ : RenderingResources.defaultFonts.get(NotationElement.ScoreWords);
42152
42757
  }
42153
42758
  }
42154
42759
 
@@ -42453,6 +43058,13 @@ class DisplaySettings {
42453
43058
  * @defaultValue `5`
42454
43059
  */
42455
43060
  trackStaffPaddingBetween = 5;
43061
+ /**
43062
+ * The additional padding to apply between multiple lyric lines.
43063
+ * @since 1.8.0
43064
+ * @category Display
43065
+ * @defaultValue `5`
43066
+ */
43067
+ lyricLinesPaddingBetween = 5;
42456
43068
  /**
42457
43069
  * The mode used to arrange staves and systems.
42458
43070
  * @since 1.3.0
@@ -42563,6 +43175,7 @@ class DisplaySettingsSerializer {
42563
43175
  o.set("staffpaddingleft", obj.staffPaddingLeft);
42564
43176
  o.set("effectbandpaddingbottom", obj.effectBandPaddingBottom);
42565
43177
  o.set("trackstaffpaddingbetween", obj.trackStaffPaddingBetween);
43178
+ o.set("lyriclinespaddingbetween", obj.lyricLinesPaddingBetween);
42566
43179
  o.set("systemslayoutmode", obj.systemsLayoutMode);
42567
43180
  return o;
42568
43181
  }
@@ -42649,6 +43262,9 @@ class DisplaySettingsSerializer {
42649
43262
  case "trackstaffpaddingbetween":
42650
43263
  obj.trackStaffPaddingBetween = v;
42651
43264
  return true;
43265
+ case "lyriclinespaddingbetween":
43266
+ obj.lyricLinesPaddingBetween = v;
43267
+ return true;
42652
43268
  case "systemslayoutmode":
42653
43269
  obj.systemsLayoutMode = JsonHelper.parseEnum(v, SystemsLayoutMode);
42654
43270
  return true;
@@ -55357,23 +55973,20 @@ class BrowserUiFacade {
55357
55973
  this._contents = '';
55358
55974
  const element = api.container;
55359
55975
  if (settings.core.tex) {
55360
- this._contents = element.element.innerText;
55976
+ this._contents = element.element.textContent;
55361
55977
  element.element.innerText = '';
55362
55978
  }
55363
55979
  this._createStyleElements(settings);
55364
55980
  this._file = settings.core.file;
55365
55981
  }
55366
55982
  _setupFontCheckers(settings) {
55367
- this._registerFontChecker(settings.display.resources.copyrightFont);
55368
- this._registerFontChecker(settings.display.resources.effectFont);
55983
+ for (const font of settings.display.resources.elementFonts.values()) {
55984
+ this._registerFontChecker(font);
55985
+ }
55369
55986
  this._registerFontChecker(settings.display.resources.graceFont);
55370
- this._registerFontChecker(settings.display.resources.markerFont);
55371
55987
  this._registerFontChecker(settings.display.resources.tablatureFont);
55372
- this._registerFontChecker(settings.display.resources.titleFont);
55373
- this._registerFontChecker(settings.display.resources.wordsFont);
55374
- this._registerFontChecker(settings.display.resources.barNumberFont);
55375
- this._registerFontChecker(settings.display.resources.fretboardNumberFont);
55376
- this._registerFontChecker(settings.display.resources.subTitleFont);
55988
+ this._registerFontChecker(settings.display.resources.numberedNotationFont);
55989
+ this._registerFontChecker(settings.display.resources.numberedNotationGraceFont);
55377
55990
  }
55378
55991
  _registerFontChecker(font) {
55379
55992
  if (!this._fontCheckers.has(font.families.join(', '))) {
@@ -57210,7 +57823,9 @@ class AlternateEndingsGlyph extends EffectGlyph {
57210
57823
  }
57211
57824
  doLayout() {
57212
57825
  super.doLayout();
57213
- this.height = this.renderer.resources.wordsFont.size + this.renderer.smuflMetrics.alternateEndingsPadding * 2;
57826
+ this.height =
57827
+ this.renderer.resources.elementFonts.get(NotationElement.EffectAlternateEndings).size +
57828
+ this.renderer.smuflMetrics.alternateEndingsPadding * 2;
57214
57829
  let endingsStrings = '';
57215
57830
  for (let i = 0, j = this._endings.length; i < j; i++) {
57216
57831
  endingsStrings += this._endings[i] + 1;
@@ -57246,7 +57861,7 @@ class AlternateEndingsGlyph extends EffectGlyph {
57246
57861
  const baseline = canvas.textBaseline;
57247
57862
  canvas.textBaseline = TextBaseline.Top;
57248
57863
  const res = this.renderer.resources;
57249
- canvas.font = res.wordsFont;
57864
+ canvas.font = res.elementFonts.get(NotationElement.EffectAlternateEndings);
57250
57865
  canvas.fillText(this._endingsString, cx + this.x + this.renderer.smuflMetrics.alternateEndingsPadding, cy + this.y + this.renderer.smuflMetrics.alternateEndingsPadding);
57251
57866
  canvas.textBaseline = baseline;
57252
57867
  }
@@ -57388,10 +58003,12 @@ class LineRangedGlyph extends GroupedEffectGlyph {
57388
58003
  _label;
57389
58004
  _dashed;
57390
58005
  _labelWidth = 0;
57391
- constructor(label, dashed = true) {
58006
+ _fontElement;
58007
+ constructor(label, fontElement, dashed = true) {
57392
58008
  super(BeatXPosition.OnNotes);
57393
58009
  this._label = label;
57394
58010
  this._dashed = dashed;
58011
+ this._fontElement = fontElement;
57395
58012
  }
57396
58013
  doLayout() {
57397
58014
  if (this.renderer.settings.notation.extendLineEffectsToBeatEnd) {
@@ -57399,14 +58016,14 @@ class LineRangedGlyph extends GroupedEffectGlyph {
57399
58016
  this.forceGroupedRendering = true;
57400
58017
  }
57401
58018
  super.doLayout();
57402
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.effectFont;
58019
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(this._fontElement);
57403
58020
  const size = this.renderer.scoreRenderer.canvas.measureText(this._label);
57404
58021
  this.height = size.height;
57405
58022
  this._labelWidth = size.width;
57406
58023
  }
57407
58024
  paintNonGrouped(cx, cy, canvas) {
57408
58025
  const res = this.renderer.resources;
57409
- canvas.font = res.effectFont;
58026
+ canvas.font = res.elementFonts.get(this._fontElement);
57410
58027
  const b = canvas.textBaseline;
57411
58028
  canvas.textBaseline = TextBaseline.Middle;
57412
58029
  canvas.fillText(this._label, cx + this.x - this._labelWidth / 2, cy + this.y + this.height / 2);
@@ -57467,7 +58084,7 @@ class BeatBarreEffectInfo extends EffectInfo {
57467
58084
  break;
57468
58085
  }
57469
58086
  barre += `B ${BeatBarreEffectInfo.toRoman(beat.barreFret)}`;
57470
- return new LineRangedGlyph(barre, false);
58087
+ return new LineRangedGlyph(barre, NotationElement.EffectBeatBarre, false);
57471
58088
  }
57472
58089
  static _romanLetters = new Map([
57473
58090
  // ['M', 1000],
@@ -57517,7 +58134,7 @@ class BeatTimerGlyph extends EffectGlyph {
57517
58134
  const seconds = ((this._timer - minutes * 60000) / 1000) | 0;
57518
58135
  this._text = `${minutes}:${seconds.toString().padStart(2, '0')}`;
57519
58136
  const c = this.renderer.scoreRenderer.canvas;
57520
- c.font = this.renderer.resources.timerFont;
58137
+ c.font = this.renderer.resources.elementFonts.get(NotationElement.EffectBeatTimer);
57521
58138
  const size = c.measureText(this._text);
57522
58139
  this._textHeight = c.font.size + this.renderer.smuflMetrics.beatTimerPadding * 2;
57523
58140
  this._textWidth = size.width + this.renderer.smuflMetrics.beatTimerPadding * 2;
@@ -57529,7 +58146,7 @@ class BeatTimerGlyph extends EffectGlyph {
57529
58146
  const f = canvas.font;
57530
58147
  const b = canvas.textBaseline;
57531
58148
  const a = canvas.textAlign;
57532
- canvas.font = this.renderer.resources.timerFont;
58149
+ canvas.font = this.renderer.resources.elementFonts.get(NotationElement.EffectBeatTimer);
57533
58150
  canvas.textBaseline = TextBaseline.Middle;
57534
58151
  canvas.textAlign = TextAlign.Center;
57535
58152
  canvas.fillText(this._text, cx + this.x, cy + this.y + this.height / 2);
@@ -57638,7 +58255,7 @@ class CapoEffectInfo extends EffectInfo {
57638
58255
  return beat.index === 0 && beat.voice.bar.index === 0 && beat.voice.bar.staff.capo !== 0;
57639
58256
  }
57640
58257
  createNewGlyph(renderer, beat) {
57641
- return new TextGlyph(0, 0, `Capo. fret ${beat.voice.bar.staff.capo}`, renderer.resources.effectFont, TextAlign.Left);
58258
+ return new TextGlyph(0, 0, `Capo. fret ${beat.voice.bar.staff.capo}`, renderer.resources.elementFonts.get(NotationElement.EffectCapo), TextAlign.Left);
57642
58259
  }
57643
58260
  canExpand(_from, _to) {
57644
58261
  return false;
@@ -57655,31 +58272,41 @@ class ChordDiagramGlyph extends EffectGlyph {
57655
58272
  _fretRow = 0;
57656
58273
  _firstFretSpacing = 0;
57657
58274
  _center;
57658
- constructor(x, y, chord, center = false) {
58275
+ _fontElement;
58276
+ constructor(x, y, chord, fontElement, center = false) {
57659
58277
  super(x, y);
57660
58278
  this._chord = chord;
57661
58279
  this._center = center;
58280
+ this._fontElement = fontElement;
57662
58281
  }
57663
58282
  doLayout() {
57664
58283
  super.doLayout();
57665
58284
  const res = this.renderer.resources;
57666
- this._textRow = res.effectFont.size * 1.5;
57667
- this._fretRow = res.effectFont.size * 1.5;
57668
- if (this._chord.firstFret > 1) {
57669
- this._firstFretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
57670
- }
57671
- else {
57672
- this._firstFretSpacing = 0;
57673
- }
57674
- this.height =
57675
- this._textRow +
58285
+ const font = res.elementFonts.get(this._fontElement);
58286
+ this._textRow = font.size * 1.5;
58287
+ this._fretRow = font.size * 1.5;
58288
+ this.height = this._textRow;
58289
+ this.width = 2 * this.renderer.smuflMetrics.chordDiagramPaddingX;
58290
+ if (this.renderer.settings.notation.isNotationElementVisible(NotationElement.ChordDiagramFretboardNumbers)) {
58291
+ if (this._chord.firstFret > 1) {
58292
+ this._firstFretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
58293
+ }
58294
+ else {
58295
+ this._firstFretSpacing = 0;
58296
+ }
58297
+ this.height +=
57676
58298
  this._fretRow +
57677
- ChordDiagramGlyph._frets * this.renderer.smuflMetrics.chordDiagramFretSpacing +
57678
- 2 * this.renderer.smuflMetrics.chordDiagramPaddingY;
57679
- this.width =
57680
- this._firstFretSpacing +
57681
- (this._chord.strings.length - 1) * this.renderer.smuflMetrics.chordDiagramStringSpacing +
57682
- 2 * this.renderer.smuflMetrics.chordDiagramPaddingX;
58299
+ ChordDiagramGlyph._frets * this.renderer.smuflMetrics.chordDiagramFretSpacing +
58300
+ 2 * this.renderer.smuflMetrics.chordDiagramPaddingY;
58301
+ this.width +=
58302
+ this._firstFretSpacing +
58303
+ (this._chord.strings.length - 1) * this.renderer.smuflMetrics.chordDiagramStringSpacing;
58304
+ }
58305
+ else if (this._chord.showName) {
58306
+ const canvas = this.renderer.scoreRenderer.canvas;
58307
+ canvas.font = font;
58308
+ this.width += canvas.measureText(this._chord.name).width;
58309
+ }
57683
58310
  }
57684
58311
  paint(cx, cy, canvas) {
57685
58312
  cx += this.x + this.renderer.smuflMetrics.chordDiagramPaddingX + this._firstFretSpacing;
@@ -57687,25 +58314,35 @@ class ChordDiagramGlyph extends EffectGlyph {
57687
58314
  if (this._center) {
57688
58315
  cx -= this.width / 2;
57689
58316
  }
57690
- const stringSpacing = this.renderer.smuflMetrics.chordDiagramStringSpacing;
57691
- const fretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
57692
58317
  const res = this.renderer.resources;
57693
58318
  const lineWidth = res.engravingSettings.chordDiagramLineWidth;
57694
58319
  const w = this.width - 2 * this.renderer.smuflMetrics.chordDiagramPaddingX - this._firstFretSpacing + lineWidth;
57695
- const circleHeight = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardFilledCircle);
57696
- const circleTopOffset = res.engravingSettings.glyphTop.get(MusicFontSymbol.FretboardFilledCircle);
57697
- const xTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardX) / 2;
57698
- const oTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardO) / 2;
57699
58320
  const align = canvas.textAlign;
57700
58321
  const baseline = canvas.textBaseline;
57701
- canvas.font = res.effectFont;
58322
+ const font = res.elementFonts.get(this._fontElement);
58323
+ canvas.font = font;
57702
58324
  canvas.textAlign = TextAlign.Center;
57703
58325
  canvas.textBaseline = TextBaseline.Top;
57704
58326
  if (this._chord.showName) {
57705
- canvas.fillText(this._chord.name, cx + w / 2, cy + res.effectFont.size / 2);
58327
+ canvas.fillText(this._chord.name, cx + w / 2, cy + font.size / 2);
57706
58328
  }
58329
+ if (this.renderer.settings.notation.isNotationElementVisible(NotationElement.ChordDiagramFretboardNumbers)) {
58330
+ this._paintFretboard(cx, cy, canvas, w);
58331
+ }
58332
+ canvas.textAlign = align;
58333
+ canvas.textBaseline = baseline;
58334
+ }
58335
+ _paintFretboard(cx, cy, canvas, w) {
57707
58336
  cy += this._textRow;
57708
- canvas.font = res.fretboardNumberFont;
58337
+ const res = this.renderer.resources;
58338
+ const stringSpacing = this.renderer.smuflMetrics.chordDiagramStringSpacing;
58339
+ const fretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
58340
+ const circleHeight = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardFilledCircle);
58341
+ const circleTopOffset = res.engravingSettings.glyphTop.get(MusicFontSymbol.FretboardFilledCircle);
58342
+ const xTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardX) / 2;
58343
+ const oTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardO) / 2;
58344
+ const lineWidth = res.engravingSettings.chordDiagramLineWidth;
58345
+ canvas.font = res.elementFonts.get(NotationElement.ChordDiagramFretboardNumbers);
57709
58346
  canvas.textBaseline = TextBaseline.Middle;
57710
58347
  for (let i = 0; i < this._chord.strings.length; i++) {
57711
58348
  const x = cx + i * stringSpacing;
@@ -57768,8 +58405,6 @@ class ChordDiagramGlyph extends EffectGlyph {
57768
58405
  const xRight = cx + (this._chord.strings.length - strings[0] - 1) * stringSpacing;
57769
58406
  canvas.fillRect(xLeft, y - circleHeight / 2, xRight - xLeft, circleHeight);
57770
58407
  }
57771
- canvas.textAlign = align;
57772
- canvas.textBaseline = baseline;
57773
58408
  }
57774
58409
  }
57775
58410
 
@@ -57795,8 +58430,8 @@ class ChordsEffectInfo extends EffectInfo {
57795
58430
  createNewGlyph(renderer, beat) {
57796
58431
  const showDiagram = beat.voice.bar.staff.track.score.stylesheet.globalDisplayChordDiagramsInScore;
57797
58432
  return showDiagram
57798
- ? new ChordDiagramGlyph(0, 0, beat.chord, true)
57799
- : new TextGlyph(0, 0, beat.chord.name, renderer.resources.effectFont, TextAlign.Center);
58433
+ ? new ChordDiagramGlyph(0, 0, beat.chord, NotationElement.EffectChordNames, true)
58434
+ : new TextGlyph(0, 0, beat.chord.name, renderer.resources.elementFonts.get(NotationElement.EffectChordNames), TextAlign.Center);
57800
58435
  }
57801
58436
  canExpand(_from, _to) {
57802
58437
  return false;
@@ -57907,14 +58542,14 @@ class JumpDirectionGlyph extends Glyph {
57907
58542
  }
57908
58543
  doLayout() {
57909
58544
  const c = this.renderer.scoreRenderer.canvas;
57910
- c.font = this.renderer.resources.directionsFont;
58545
+ c.font = this.renderer.resources.elementFonts.get(NotationElement.EffectDirections);
57911
58546
  this.height = c.measureText(this._text).height;
57912
58547
  }
57913
58548
  paint(cx, cy, canvas) {
57914
58549
  const font = canvas.font;
57915
58550
  const baseline = canvas.textBaseline;
57916
58551
  const align = canvas.textAlign;
57917
- canvas.font = this.renderer.resources.directionsFont;
58552
+ canvas.font = this.renderer.resources.elementFonts.get(NotationElement.EffectDirections);
57918
58553
  canvas.textBaseline = TextBaseline.Middle;
57919
58554
  canvas.textAlign = TextAlign.Right;
57920
58555
  canvas.fillText(this._text, cx + this.x, cy + this.y + this.height / 2);
@@ -58810,7 +59445,7 @@ class FreeTimeEffectInfo extends EffectInfo {
58810
59445
  (masterBar.index === 0 || masterBar.isFreeTime !== masterBar.previousMasterBar.isFreeTime));
58811
59446
  }
58812
59447
  createNewGlyph(renderer, _beat) {
58813
- return new TextGlyph(0, 0, 'Free time', renderer.resources.effectFont, TextAlign.Left);
59448
+ return new TextGlyph(0, 0, 'Free time', renderer.resources.elementFonts.get(NotationElement.EffectFreeTime), TextAlign.Left);
58814
59449
  }
58815
59450
  canExpand(_from, _to) {
58816
59451
  return true;
@@ -58948,7 +59583,7 @@ class HarmonicsEffectInfo extends NoteEffectInfoBase {
58948
59583
  return EffectBarGlyphSizing.GroupedOnBeat;
58949
59584
  }
58950
59585
  createNewGlyph(_renderer, _beat) {
58951
- return new LineRangedGlyph(HarmonicsEffectInfo.harmonicToString(this._harmonicType));
59586
+ return new LineRangedGlyph(HarmonicsEffectInfo.harmonicToString(this._harmonicType), NotationElement.EffectHarmonics);
58952
59587
  }
58953
59588
  static harmonicToString(type) {
58954
59589
  switch (type) {
@@ -59021,7 +59656,7 @@ class LetRingEffectInfo extends EffectInfo {
59021
59656
  return EffectBarGlyphSizing.GroupedOnBeat;
59022
59657
  }
59023
59658
  createNewGlyph(_renderer, _beat) {
59024
- return new LineRangedGlyph('LetRing');
59659
+ return new LineRangedGlyph('LetRing', NotationElement.EffectLetRing);
59025
59660
  }
59026
59661
  canExpand(_from, _to) {
59027
59662
  return true;
@@ -59033,6 +59668,7 @@ class LetRingEffectInfo extends EffectInfo {
59033
59668
  */
59034
59669
  class LyricsGlyph extends EffectGlyph {
59035
59670
  _lines;
59671
+ _linePositions = [];
59036
59672
  font;
59037
59673
  textAlign;
59038
59674
  constructor(x, y, lines, font, textAlign = TextAlign.Center) {
@@ -59043,7 +59679,17 @@ class LyricsGlyph extends EffectGlyph {
59043
59679
  }
59044
59680
  doLayout() {
59045
59681
  super.doLayout();
59046
- this.height = this.font.size * this._lines.length;
59682
+ const lineSpacing = this.renderer.settings.display.lyricLinesPaddingBetween;
59683
+ const canvas = this.renderer.scoreRenderer.canvas;
59684
+ canvas.font = this.font;
59685
+ let y = 0;
59686
+ for (const line of this._lines) {
59687
+ this._linePositions.push(y);
59688
+ const size = canvas.measureText(line.length > 0 ? line : ' ');
59689
+ y += size.height + lineSpacing;
59690
+ }
59691
+ y -= lineSpacing;
59692
+ this.height = y;
59047
59693
  }
59048
59694
  paint(cx, cy, canvas) {
59049
59695
  canvas.font = this.font;
@@ -59051,7 +59697,7 @@ class LyricsGlyph extends EffectGlyph {
59051
59697
  canvas.textAlign = this.textAlign;
59052
59698
  for (let i = 0; i < this._lines.length; i++) {
59053
59699
  if (this._lines[i]) {
59054
- canvas.fillText(this._lines[i], cx + this.x, cy + this.y + i * this.font.size);
59700
+ canvas.fillText(this._lines[i], cx + this.x, cy + this.y + this._linePositions[i]);
59055
59701
  }
59056
59702
  }
59057
59703
  canvas.textAlign = old;
@@ -59078,7 +59724,7 @@ class LyricsEffectInfo extends EffectInfo {
59078
59724
  return !!beat.lyrics;
59079
59725
  }
59080
59726
  createNewGlyph(renderer, beat) {
59081
- return new LyricsGlyph(0, 0, beat.lyrics, renderer.resources.effectFont, TextAlign.Center);
59727
+ return new LyricsGlyph(0, 0, beat.lyrics, renderer.resources.elementFonts.get(NotationElement.EffectLyrics), TextAlign.Center);
59082
59728
  }
59083
59729
  canExpand(_from, _to) {
59084
59730
  return true;
@@ -59110,7 +59756,7 @@ class MarkerEffectInfo extends EffectInfo {
59110
59756
  createNewGlyph(renderer, beat) {
59111
59757
  return new TextGlyph(0, 0, !beat.voice.bar.masterBar.section.marker
59112
59758
  ? beat.voice.bar.masterBar.section.text
59113
- : `[${beat.voice.bar.masterBar.section.marker}] ${beat.voice.bar.masterBar.section.text}`, renderer.resources.markerFont, TextAlign.Left);
59759
+ : `[${beat.voice.bar.masterBar.section.marker}] ${beat.voice.bar.masterBar.section.text}`, renderer.resources.elementFonts.get(NotationElement.EffectMarker), TextAlign.Left);
59114
59760
  }
59115
59761
  canExpand(_from, _to) {
59116
59762
  return true;
@@ -59549,7 +60195,7 @@ class PalmMuteEffectInfo extends NoteEffectInfoBase {
59549
60195
  return EffectBarGlyphSizing.GroupedOnBeat;
59550
60196
  }
59551
60197
  createNewGlyph(_renderer, _beat) {
59552
- return new LineRangedGlyph('P.M.');
60198
+ return new LineRangedGlyph('P.M.', NotationElement.EffectPalmMute);
59553
60199
  }
59554
60200
  }
59555
60201
 
@@ -59567,7 +60213,7 @@ class PickSlideEffectInfo extends NoteEffectInfoBase {
59567
60213
  return EffectBarGlyphSizing.GroupedOnBeat;
59568
60214
  }
59569
60215
  createNewGlyph(_renderer, _beat) {
59570
- return new LineRangedGlyph('P.S.');
60216
+ return new LineRangedGlyph('P.S.', NotationElement.EffectPickSlide);
59571
60217
  }
59572
60218
  }
59573
60219
 
@@ -59642,7 +60288,7 @@ class RasgueadoEffectInfo extends EffectInfo {
59642
60288
  return EffectBarGlyphSizing.GroupedOnBeat;
59643
60289
  }
59644
60290
  createNewGlyph(_renderer, _beat) {
59645
- return new LineRangedGlyph('rasg.');
60291
+ return new LineRangedGlyph('rasg.', NotationElement.EffectRasgueado);
59646
60292
  }
59647
60293
  canExpand(_from, _to) {
59648
60294
  return true;
@@ -61148,6 +61794,7 @@ class BeamingHelperDrawInfo {
61148
61794
  class BeamingHelper {
61149
61795
  _staff;
61150
61796
  _renderer;
61797
+ _beamingRuleLookup;
61151
61798
  voice = null;
61152
61799
  beats = [];
61153
61800
  shortestDuration = Duration.QuadrupleWhole;
@@ -61181,10 +61828,11 @@ class BeamingHelper {
61181
61828
  static beatHasFlag(beat) {
61182
61829
  return (!beat.deadSlapped && !beat.isRest && (beat.duration > Duration.Quarter || beat.graceType !== GraceType.None));
61183
61830
  }
61184
- constructor(staff, renderer) {
61831
+ constructor(staff, renderer, beamingRuleLookup) {
61185
61832
  this._staff = staff;
61186
61833
  this._renderer = renderer;
61187
61834
  this.beats = [];
61835
+ this._beamingRuleLookup = beamingRuleLookup;
61188
61836
  }
61189
61837
  alignWithBeats() {
61190
61838
  for (const v of this.drawingInfos.values()) {
@@ -61239,7 +61887,7 @@ class BeamingHelper {
61239
61887
  switch (this.beats[this.beats.length - 1].beamingMode) {
61240
61888
  case BeatBeamingMode.Auto:
61241
61889
  case BeatBeamingMode.ForceSplitOnSecondaryToNext:
61242
- add = BeamingHelper._canJoin(this.beats[this.beats.length - 1], beat);
61890
+ add = this._canJoin(this.beats[this.beats.length - 1], beat);
61243
61891
  break;
61244
61892
  case BeatBeamingMode.ForceSplitToNext:
61245
61893
  add = false;
@@ -61311,8 +61959,7 @@ class BeamingHelper {
61311
61959
  this._highestNoteCompareValueInHelper = highestValueForNote;
61312
61960
  }
61313
61961
  }
61314
- // TODO: Check if this beaming is really correct, I'm not sure if we are connecting beats correctly
61315
- static _canJoin(b1, b2) {
61962
+ _canJoin(b1, b2) {
61316
61963
  // is this a voice we can join with?
61317
61964
  if (!b1 ||
61318
61965
  !b2 ||
@@ -61350,19 +61997,10 @@ class BeamingHelper {
61350
61997
  return true;
61351
61998
  }
61352
61999
  }
61353
- // TODO: create more rules for automatic beaming
61354
- let divisionLength = MidiUtils.QuarterTime;
61355
- switch (m1.masterBar.timeSignatureDenominator) {
61356
- case 8:
61357
- if (m1.masterBar.timeSignatureNumerator % 3 === 0) {
61358
- divisionLength += (MidiUtils.QuarterTime / 2) | 0;
61359
- }
61360
- break;
61361
- }
61362
- // check if they are on the same division
61363
- const division1 = ((divisionLength + start1) / divisionLength) | 0 | 0;
61364
- const division2 = ((divisionLength + start2) / divisionLength) | 0 | 0;
61365
- return division1 === division2;
62000
+ // check if they are on the same group as per rule definitions
62001
+ const groupId1 = this._beamingRuleLookup.calculateGroupIndex(start1);
62002
+ const groupId2 = this._beamingRuleLookup.calculateGroupIndex(start2);
62003
+ return groupId1 === groupId2;
61366
62004
  }
61367
62005
  static _canJoinDuration(d) {
61368
62006
  switch (d) {
@@ -61529,6 +62167,61 @@ class BarCollisionHelper {
61529
62167
  }
61530
62168
  }
61531
62169
 
62170
+ /**
62171
+ * @internal
62172
+ */
62173
+ class BeamingRuleLookup {
62174
+ _division = 0;
62175
+ _slots = [];
62176
+ _barDuration;
62177
+ constructor(barDuration, division, slots) {
62178
+ this._division = division;
62179
+ this._slots = slots;
62180
+ this._barDuration = barDuration;
62181
+ }
62182
+ calculateGroupIndex(beatStartTime) {
62183
+ // no slots -> all have their own group based (use the start time as index)
62184
+ if (this._slots.length === 0) {
62185
+ return beatStartTime;
62186
+ }
62187
+ // rollover within the bar.
62188
+ beatStartTime = beatStartTime % this._barDuration;
62189
+ const slotIndex = Math.floor(beatStartTime / this._division);
62190
+ return this._slots[slotIndex];
62191
+ }
62192
+ static build(masterBar, ruleDuration, ruleGroups) {
62193
+ const totalDuration = masterBar.calculateDuration(false);
62194
+ const division = MidiUtils.toTicks(ruleDuration);
62195
+ const slotCount = totalDuration / division;
62196
+ // should only happen in case of improper data.
62197
+ if (slotCount < 0 || ruleGroups.length === 0) {
62198
+ return new BeamingRuleLookup(0, 0, []);
62199
+ }
62200
+ let groupIndex = 0;
62201
+ let remainingSlots = ruleGroups[groupIndex];
62202
+ const slots = [];
62203
+ for (let i = 0; i < slotCount; i++) {
62204
+ if (groupIndex < ruleGroups.length) {
62205
+ slots.push(groupIndex);
62206
+ remainingSlots--;
62207
+ if (remainingSlots <= 0) {
62208
+ groupIndex++;
62209
+ if (groupIndex < ruleGroups.length) {
62210
+ remainingSlots = ruleGroups[groupIndex];
62211
+ }
62212
+ }
62213
+ }
62214
+ else {
62215
+ // no groups defined for the remaining slots: all slots are treated
62216
+ // as unjoined
62217
+ slots.push(groupIndex);
62218
+ groupIndex++;
62219
+ }
62220
+ }
62221
+ return new BeamingRuleLookup(totalDuration, division, slots);
62222
+ }
62223
+ }
62224
+
61532
62225
  /**
61533
62226
  * @internal
61534
62227
  */
@@ -61545,6 +62238,19 @@ class BarHelpers {
61545
62238
  initialize() {
61546
62239
  const barRenderer = this._renderer;
61547
62240
  const bar = this._renderer.bar;
62241
+ const masterBar = bar.masterBar;
62242
+ const beamingRules = masterBar.actualBeamingRules ?? BarHelpers._findOrBuildDefaultBeamingRules(masterBar);
62243
+ const rule = beamingRules.findRule(bar.shortestDuration);
62244
+ // NOTE: moste rules have only one group definition, so its better to reuse the unique id
62245
+ // than compute a potentially shorter id here.
62246
+ const key = `beaming_${beamingRules.uniqueId}_${rule[0]}`;
62247
+ let beamingRuleLookup = this._renderer.scoreRenderer.layout.beamingRuleLookups.has(key)
62248
+ ? this._renderer.scoreRenderer.layout.beamingRuleLookups.get(key)
62249
+ : undefined;
62250
+ if (!beamingRuleLookup) {
62251
+ beamingRuleLookup = BeamingRuleLookup.build(masterBar, rule[0], rule[1]);
62252
+ this._renderer.scoreRenderer.layout.beamingRuleLookups.set(key, beamingRuleLookup);
62253
+ }
61548
62254
  let currentBeamHelper = null;
61549
62255
  let currentGraceBeamHelper = null;
61550
62256
  for (let i = 0, j = bar.voices.length; i < j; i++) {
@@ -61570,7 +62276,7 @@ class BarHelpers {
61570
62276
  helperForBeat.finish();
61571
62277
  }
61572
62278
  // if not possible, create the next beaming helper
61573
- helperForBeat = new BeamingHelper(bar.staff, barRenderer);
62279
+ helperForBeat = new BeamingHelper(bar.staff, barRenderer, beamingRuleLookup);
61574
62280
  helperForBeat.preferredBeamDirection = this.preferredBeamDirection;
61575
62281
  helperForBeat.checkBeat(b);
61576
62282
  if (b.graceType !== GraceType.None) {
@@ -61593,6 +62299,69 @@ class BarHelpers {
61593
62299
  currentGraceBeamHelper = null;
61594
62300
  }
61595
62301
  }
62302
+ static _defaultBeamingRules;
62303
+ static _findOrBuildDefaultBeamingRules(masterBar) {
62304
+ let defaultBeamingRules = BarHelpers._defaultBeamingRules;
62305
+ if (!defaultBeamingRules) {
62306
+ defaultBeamingRules = new Map([
62307
+ BeamingRules.createSimple(2, 16, Duration.Sixteenth, [1, 1]),
62308
+ BeamingRules.createSimple(1, 8, Duration.Eighth, [1]),
62309
+ BeamingRules.createSimple(1, 4, Duration.Quarter, [1]),
62310
+ BeamingRules.createSimple(3, 16, Duration.Sixteenth, [3]),
62311
+ BeamingRules.createSimple(4, 16, Duration.Sixteenth, [2, 2]),
62312
+ BeamingRules.createSimple(2, 8, Duration.Eighth, [1, 1]),
62313
+ BeamingRules.createSimple(5, 16, Duration.Sixteenth, [3, 2]),
62314
+ BeamingRules.createSimple(6, 16, Duration.Sixteenth, [3, 3]),
62315
+ BeamingRules.createSimple(3, 8, Duration.Eighth, [3]),
62316
+ BeamingRules.createSimple(4, 8, Duration.Eighth, [2, 2]),
62317
+ BeamingRules.createSimple(2, 4, Duration.Quarter, [1, 1]),
62318
+ BeamingRules.createSimple(9, 16, Duration.Sixteenth, [3, 3, 3]),
62319
+ BeamingRules.createSimple(5, 8, Duration.Eighth, [3, 2]),
62320
+ BeamingRules.createSimple(12, 16, Duration.Sixteenth, [3, 3, 3, 3]),
62321
+ BeamingRules.createSimple(6, 8, Duration.Eighth, [3, 3, 3]),
62322
+ BeamingRules.createSimple(3, 4, Duration.Quarter, [1, 1, 1]),
62323
+ BeamingRules.createSimple(7, 8, Duration.Eighth, [4, 3]),
62324
+ BeamingRules.createSimple(8, 8, Duration.Eighth, [3, 3, 2]),
62325
+ BeamingRules.createSimple(4, 4, Duration.Quarter, [1, 1, 1, 1]),
62326
+ BeamingRules.createSimple(9, 8, Duration.Eighth, [3, 3, 3]),
62327
+ BeamingRules.createSimple(10, 8, Duration.Eighth, [4, 3, 3]),
62328
+ BeamingRules.createSimple(5, 4, Duration.Quarter, [1, 1, 1, 1, 1]),
62329
+ BeamingRules.createSimple(12, 8, Duration.Eighth, [3, 3, 3, 3]),
62330
+ BeamingRules.createSimple(6, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1]),
62331
+ BeamingRules.createSimple(15, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3]),
62332
+ BeamingRules.createSimple(8, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1, 1, 1]),
62333
+ BeamingRules.createSimple(18, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3])
62334
+ ].map(r => [`${r.timeSignatureNumerator}_${r.timeSignatureDenominator}`, r]));
62335
+ BarHelpers._defaultBeamingRules = defaultBeamingRules;
62336
+ }
62337
+ const key = `${masterBar.timeSignatureNumerator}_${masterBar.timeSignatureDenominator}`;
62338
+ if (defaultBeamingRules.has(key)) {
62339
+ return defaultBeamingRules.get(key);
62340
+ }
62341
+ // NOTE: this is the old alphaTab logic how we used to beamed bars.
62342
+ // we either group in quarters, or in 3x8ths depending on the key signature
62343
+ let divisionLength = MidiUtils.QuarterTime;
62344
+ switch (masterBar.timeSignatureDenominator) {
62345
+ case 8:
62346
+ if (masterBar.timeSignatureNumerator % 3 === 0) {
62347
+ divisionLength += (MidiUtils.QuarterTime / 2) | 0;
62348
+ }
62349
+ break;
62350
+ }
62351
+ const numberOfDivisions = Math.ceil(masterBar.calculateDuration(false) / divisionLength);
62352
+ const notesPerDivision = (divisionLength / MidiUtils.QuarterTime) * 2;
62353
+ const fallback = new BeamingRules();
62354
+ const groups = [];
62355
+ for (let i = 0; i < numberOfDivisions; i++) {
62356
+ groups.push(notesPerDivision);
62357
+ }
62358
+ fallback.groups.set(Duration.Eighth, groups);
62359
+ fallback.timeSignatureNumerator = masterBar.timeSignatureNumerator;
62360
+ fallback.timeSignatureDenominator = masterBar.timeSignatureDenominator;
62361
+ fallback.finish();
62362
+ defaultBeamingRules.set(key, fallback);
62363
+ return fallback;
62364
+ }
61596
62365
  getBeamingHelperForBeat(beat) {
61597
62366
  return this._beamHelperLookup.has(beat.id) ? this._beamHelperLookup.get(beat.id) : undefined;
61598
62367
  }
@@ -63250,12 +64019,12 @@ class TapEffectInfo extends EffectInfo {
63250
64019
  createNewGlyph(renderer, beat) {
63251
64020
  const res = renderer.resources;
63252
64021
  if (beat.slap) {
63253
- return new TextGlyph(0, 0, 'S', res.effectFont, TextAlign.Center);
64022
+ return new TextGlyph(0, 0, 'S', res.elementFonts.get(NotationElement.EffectTap), TextAlign.Center);
63254
64023
  }
63255
64024
  if (beat.pop) {
63256
- return new TextGlyph(0, 0, 'P', res.effectFont, TextAlign.Center);
64025
+ return new TextGlyph(0, 0, 'P', res.elementFonts.get(NotationElement.EffectTap), TextAlign.Center);
63257
64026
  }
63258
- return new TextGlyph(0, 0, 'T', res.effectFont, TextAlign.Center);
64027
+ return new TextGlyph(0, 0, 'T', res.elementFonts.get(NotationElement.EffectTap), TextAlign.Center);
63259
64028
  }
63260
64029
  canExpand(_from, _to) {
63261
64030
  return true;
@@ -63284,7 +64053,7 @@ class BarTempoGlyph extends EffectGlyph {
63284
64053
  for (const automation of this._tempoAutomations) {
63285
64054
  let x = cx + this.renderer.getRatioPositionX(automation.ratioPosition);
63286
64055
  const res = this.renderer.resources;
63287
- canvas.font = res.markerFont;
64056
+ canvas.font = res.elementFonts.get(NotationElement.EffectMarker);
63288
64057
  const notePosY = cy +
63289
64058
  this.y +
63290
64059
  this.height +
@@ -63362,7 +64131,7 @@ class TextEffectInfo extends EffectInfo {
63362
64131
  return !!beat.text;
63363
64132
  }
63364
64133
  createNewGlyph(renderer, beat) {
63365
- return new TextGlyph(0, 0, beat.text, renderer.resources.effectFont, TextAlign.Left);
64134
+ return new TextGlyph(0, 0, beat.text, renderer.resources.elementFonts.get(NotationElement.EffectText), TextAlign.Left);
63366
64135
  }
63367
64136
  canExpand(_from, _to) {
63368
64137
  return true;
@@ -63487,7 +64256,7 @@ class TripletFeelGlyph extends EffectGlyph {
63487
64256
  const textY = cy + this.height;
63488
64257
  const b = canvas.textBaseline;
63489
64258
  canvas.textBaseline = TextBaseline.Bottom;
63490
- canvas.font = this.renderer.resources.effectFont;
64259
+ canvas.font = this.renderer.resources.elementFonts.get(NotationElement.EffectTripletFeel);
63491
64260
  canvas.fillText('(', cx, textY);
63492
64261
  cx += canvas.measureText('( ').width;
63493
64262
  cx = this._drawGroup(cx, noteY + this._tupletHeight, canvas, leftNotes);
@@ -63729,7 +64498,7 @@ class WhammyBarEffectInfo extends EffectInfo {
63729
64498
  return beat.hasWhammyBar;
63730
64499
  }
63731
64500
  createNewGlyph(_renderer, _beat) {
63732
- return new LineRangedGlyph('w/bar');
64501
+ return new LineRangedGlyph('w/bar', NotationElement.EffectWhammyBar);
63733
64502
  }
63734
64503
  canExpand(_from, _to) {
63735
64504
  return true;
@@ -63881,7 +64650,7 @@ class RowContainerGlyph extends GlyphGroup {
63881
64650
  class ChordDiagramContainerGlyph extends RowContainerGlyph {
63882
64651
  addChord(chord) {
63883
64652
  if (chord.strings.length > 0) {
63884
- const chordDiagram = new ChordDiagramGlyph(0, 0, chord);
64653
+ const chordDiagram = new ChordDiagramGlyph(0, 0, chord, NotationElement.ChordDiagrams);
63885
64654
  chordDiagram.renderer = this.renderer;
63886
64655
  chordDiagram.doLayout();
63887
64656
  this.glyphs.push(chordDiagram);
@@ -63945,7 +64714,7 @@ class TuningGlyph extends GlyphGroup {
63945
64714
  this.height = 0;
63946
64715
  // Track name
63947
64716
  if (this._trackLabel.length > 0) {
63948
- const trackName = new TextGlyph(0, this.height, this._trackLabel, res.effectFont, TextAlign.Left);
64717
+ const trackName = new TextGlyph(0, this.height, this._trackLabel, res.elementFonts.get(NotationElement.GuitarTuning), TextAlign.Left);
63949
64718
  trackName.renderer = this.renderer;
63950
64719
  trackName.doLayout();
63951
64720
  this.height += trackName.height;
@@ -63953,7 +64722,7 @@ class TuningGlyph extends GlyphGroup {
63953
64722
  }
63954
64723
  // Name
63955
64724
  if (tuning.name.length > 0) {
63956
- const tuningName = new TextGlyph(0, this.height, tuning.name, res.effectFont, TextAlign.Left);
64725
+ const tuningName = new TextGlyph(0, this.height, tuning.name, res.elementFonts.get(NotationElement.GuitarTuning), TextAlign.Left);
63957
64726
  tuningName.renderer = this.renderer;
63958
64727
  tuningName.doLayout();
63959
64728
  this.height += tuningName.height;
@@ -63961,7 +64730,7 @@ class TuningGlyph extends GlyphGroup {
63961
64730
  }
63962
64731
  const circleScale = this.renderer.smuflMetrics.tuningGlyphCircleNumberScale;
63963
64732
  const circleHeight = this.renderer.smuflMetrics.glyphHeights.get(MusicFontSymbol.GuitarString0) * circleScale;
63964
- this.renderer.scoreRenderer.canvas.font = res.effectFont;
64733
+ this.renderer.scoreRenderer.canvas.font = res.elementFonts.get(NotationElement.GuitarTuning);
63965
64734
  const stringColumnWidth = (circleHeight + this.renderer.scoreRenderer.canvas.measureText(' = Gb').width) *
63966
64735
  res.engravingSettings.tuningGlyphStringColumnScale;
63967
64736
  this.width = Math.max(this.renderer.scoreRenderer.canvas.measureText(this._trackLabel).width, Math.max(this.renderer.scoreRenderer.canvas.measureText(tuning.name).width, 2 * stringColumnWidth));
@@ -63975,7 +64744,7 @@ class TuningGlyph extends GlyphGroup {
63975
64744
  const symbol = (MusicFontSymbol.GuitarString0 + (i + 1));
63976
64745
  this.addGlyph(new MusicFontGlyph(currentX, currentY + circleHeight, circleScale, symbol));
63977
64746
  const str = ` = ${Tuning.getTextForTuning(tuning.tunings[i], false)}`;
63978
- this.addGlyph(new TextGlyph(currentX + circleHeight, currentY + circleHeight / 2, str, res.effectFont, TextAlign.Left, TextBaseline.Middle));
64747
+ this.addGlyph(new TextGlyph(currentX + circleHeight, currentY + circleHeight / 2, str, res.elementFonts.get(NotationElement.GuitarTuning), TextAlign.Left, TextBaseline.Middle));
63979
64748
  currentY += circleHeight + this.renderer.smuflMetrics.tuningGlyphStringRowPadding;
63980
64749
  const bottomY = currentY;
63981
64750
  if (this.height < bottomY) {
@@ -65027,7 +65796,7 @@ class StaffSystem {
65027
65796
  let hasAnyTrackName = false;
65028
65797
  if (shouldRender) {
65029
65798
  const canvas = this.layout.renderer.canvas;
65030
- const res = settings.display.resources.effectFont;
65799
+ const res = settings.display.resources.elementFonts.get(NotationElement.TrackNames);
65031
65800
  canvas.font = res;
65032
65801
  for (const t of tracks) {
65033
65802
  let trackNameText = '';
@@ -65181,7 +65950,7 @@ class StaffSystem {
65181
65950
  // Draw track names
65182
65951
  const settings = this.layout.renderer.settings;
65183
65952
  const hasTrackName = this.layout.renderer.settings.notation.isNotationElementVisible(NotationElement.TrackNames);
65184
- canvas.font = res.effectFont;
65953
+ canvas.font = res.elementFonts.get(NotationElement.TrackNames);
65185
65954
  if (hasTrackName) {
65186
65955
  const stylesheet = this.layout.renderer.score.stylesheet;
65187
65956
  const trackNamePolicy = this.layout.renderer.tracks.length === 1
@@ -65525,6 +66294,7 @@ class ScoreLayout {
65525
66294
  this.renderer = renderer;
65526
66295
  }
65527
66296
  slurRegistry = new SlurRegistry();
66297
+ beamingRuleLookups = new Map();
65528
66298
  resize() {
65529
66299
  this._lazyPartials.clear();
65530
66300
  this.slurRegistry.clear();
@@ -65533,6 +66303,7 @@ class ScoreLayout {
65533
66303
  layoutAndRender() {
65534
66304
  this._lazyPartials.clear();
65535
66305
  this.slurRegistry.clear();
66306
+ this.beamingRuleLookups.clear();
65536
66307
  this._barRendererLookup.clear();
65537
66308
  this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
65538
66309
  const score = this.renderer.score;
@@ -65871,7 +66642,10 @@ class ScoreLayout {
65871
66642
  const msg = 'rendered by alphaTab';
65872
66643
  const resources = this.renderer.settings.display.resources;
65873
66644
  const size = 12;
65874
- const font = Font.withFamilyList(resources.copyrightFont.families, size, FontStyle.Plain, FontWeight.Bold);
66645
+ const fontFamilies = resources.elementFonts.has(NotationElement.ScoreCopyright)
66646
+ ? resources.elementFonts.get(NotationElement.ScoreCopyright).families
66647
+ : resources.tablatureFont.families;
66648
+ const font = Font.withFamilyList(fontFamilies, size, FontStyle.Plain, FontWeight.Bold);
65875
66649
  const fakeBarRenderer = new BarRendererBase(this.renderer, this.renderer.tracks[0].staves[0].bars[0]);
65876
66650
  const glyph = new TextGlyph(0, 0, msg, font, TextAlign.Center, undefined, resources.mainGlyphColor);
65877
66651
  glyph.renderer = fakeBarRenderer;
@@ -66784,13 +67558,7 @@ class BarNumberGlyph extends Glyph {
66784
67558
  this._number = `${num} `;
66785
67559
  }
66786
67560
  doLayout() {
66787
- // TODO: activate this and update paddings accordingly.
66788
- // if (!this.renderer.staff!.isFirstInSystem) {
66789
- // this.width = 0;
66790
- // this.height = 0;
66791
- // return;
66792
- // }
66793
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.barNumberFont;
67561
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(NotationElement.BarNumber);
66794
67562
  const size = this.renderer.scoreRenderer.canvas.measureText(this._number);
66795
67563
  this.width = size.width;
66796
67564
  this.height = size.height;
@@ -66804,7 +67572,7 @@ class BarNumberGlyph extends Glyph {
66804
67572
  try {
66805
67573
  const res = this.renderer.resources;
66806
67574
  const baseline = canvas.textBaseline;
66807
- canvas.font = res.barNumberFont;
67575
+ canvas.font = res.elementFonts.get(NotationElement.BarNumber);
66808
67576
  canvas.textBaseline = TextBaseline.Top;
66809
67577
  canvas.fillText(this._number, cx + this.x, cy + this.y);
66810
67578
  canvas.textBaseline = baseline;
@@ -67903,7 +68671,7 @@ class RepeatCountGlyph extends Glyph {
67903
68671
  this._count = count;
67904
68672
  }
67905
68673
  doLayout() {
67906
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.barNumberFont;
68674
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(NotationElement.RepeatCount);
67907
68675
  const size = this.renderer.scoreRenderer.canvas.measureText(`x${this._count}`);
67908
68676
  this.width = 0; // do not account width
67909
68677
  this.height = size.height;
@@ -67914,7 +68682,7 @@ class RepeatCountGlyph extends Glyph {
67914
68682
  try {
67915
68683
  const res = this.renderer.resources;
67916
68684
  const oldAlign = canvas.textAlign;
67917
- canvas.font = res.barNumberFont;
68685
+ canvas.font = res.elementFonts.get(NotationElement.RepeatCount);
67918
68686
  canvas.textAlign = TextAlign.Right;
67919
68687
  const s = `x${this._count}`;
67920
68688
  const w = canvas.measureText(s).width / 1.5;
@@ -68039,11 +68807,12 @@ class LineBarRenderer extends BarRendererBase {
68039
68807
  }
68040
68808
  createStartSpacing() {
68041
68809
  if (this._startSpacing) {
68042
- return;
68810
+ return false;
68043
68811
  }
68044
68812
  const padding = this.index === 0 ? this.settings.display.firstStaffPaddingLeft : this.settings.display.staffPaddingLeft;
68045
68813
  this.addPreBeatGlyph(new SpacingGlyph(0, 0, padding));
68046
68814
  this._startSpacing = true;
68815
+ return true;
68047
68816
  }
68048
68817
  paintTuplets(cx, cy, canvas, beatElement, bracketsAsArcs = false) {
68049
68818
  for (const v of this.voiceContainer.voiceDrawOrder) {
@@ -68385,16 +69154,45 @@ class LineBarRenderer extends BarRendererBase {
68385
69154
  super.createPreBeatGlyphs();
68386
69155
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
68387
69156
  this.createLinePreBeatGlyphs();
69157
+ let hasSpaceAfterStartGlyphs = false;
68388
69158
  if (this.index === 0) {
68389
- this.createStartSpacing();
69159
+ hasSpaceAfterStartGlyphs = this.createStartSpacing();
69160
+ }
69161
+ if (this.shouldCreateBarNumber()) {
69162
+ this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69163
+ }
69164
+ else if (!hasSpaceAfterStartGlyphs) {
69165
+ this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
69166
+ }
69167
+ }
69168
+ shouldCreateBarNumber() {
69169
+ let display = BarNumberDisplay.AllBars;
69170
+ if (!this.settings.notation.isNotationElementVisible(NotationElement.BarNumber)) {
69171
+ display = BarNumberDisplay.Hide;
69172
+ }
69173
+ else if (this.bar.barNumberDisplay !== undefined) {
69174
+ display = this.bar.barNumberDisplay;
69175
+ }
69176
+ else {
69177
+ display = this.bar.staff.track.score.stylesheet.barNumberDisplay;
69178
+ }
69179
+ switch (display) {
69180
+ case BarNumberDisplay.AllBars:
69181
+ return true;
69182
+ case BarNumberDisplay.FirstOfSystem:
69183
+ return this.isFirstOfStaff;
69184
+ case BarNumberDisplay.Hide:
69185
+ return false;
68390
69186
  }
68391
- this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69187
+ return true;
68392
69188
  }
68393
69189
  createPostBeatGlyphs() {
68394
69190
  super.createPostBeatGlyphs();
68395
69191
  const lastBar = this.lastBar;
68396
69192
  this.addPostBeatGlyph(new BarLineGlyph(true, this.bar.staff.track.score.stylesheet.extendBarLines));
68397
- if (lastBar.masterBar.isRepeatEnd && lastBar.masterBar.repeatCount > 2) {
69193
+ if (lastBar.masterBar.isRepeatEnd &&
69194
+ lastBar.masterBar.repeatCount > 2 &&
69195
+ this.settings.notation.isNotationElementVisible(NotationElement.RepeatCount)) {
68398
69196
  this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getLineHeight(-0.5), this.bar.masterBar.repeatCount));
68399
69197
  }
68400
69198
  }
@@ -68958,8 +69756,13 @@ class NumberedBarRenderer extends LineBarRenderer {
68958
69756
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
68959
69757
  }
68960
69758
  this.createLinePreBeatGlyphs();
68961
- this.createStartSpacing();
68962
- this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69759
+ const hasSpaceAfterStartGlyphs = this.createStartSpacing();
69760
+ if (this.shouldCreateBarNumber()) {
69761
+ this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69762
+ }
69763
+ else if (!hasSpaceAfterStartGlyphs) {
69764
+ this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
69765
+ }
68963
69766
  }
68964
69767
  createLinePreBeatGlyphs() {
68965
69768
  if (this._isOnlyNumbered &&
@@ -71507,7 +72310,7 @@ class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph {
71507
72310
  if (note.bendStyle === BendStyle.Gradual) {
71508
72311
  const res = this.renderer.resources;
71509
72312
  const c = this.renderer.scoreRenderer.canvas;
71510
- c.font = res.barNumberFont; // see note in paint()
72313
+ c.font = res.elementFonts.get(NotationElement.ScoreBendSlur);
71511
72314
  slurHeight += c.measureText('grad.').height;
71512
72315
  }
71513
72316
  if (slurHeight > maxSlurHeight) {
@@ -71607,11 +72410,16 @@ class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph {
71607
72410
  this._notes.sort((a, b) => {
71608
72411
  return b.displayValue - a.displayValue;
71609
72412
  });
72413
+ // draw slurs
72414
+ if (this.renderer.settings.notation.isNotationElementVisible(NotationElement.ScoreBendSlur)) {
72415
+ this._paintSlurs(cx, cy, canvas, startNoteRenderer, startX, middleX, endBeatX);
72416
+ }
72417
+ }
72418
+ _paintSlurs(cx, cy, canvas, startNoteRenderer, startX, middleX, endBeatX) {
71610
72419
  const directionBeat = this._beat.graceType === GraceType.BendGrace ? this._beat.nextBeat : this._beat;
71611
72420
  let direction = this._notes.length === 1 ? this.getTieDirection(directionBeat, startNoteRenderer) : BeamDirection.Up;
71612
72421
  const noteHeadHeight = this.renderer.smuflMetrics.glyphHeights.get(MusicFontSymbol.NoteheadBlack);
71613
- // draw slurs
71614
- canvas.font = this.renderer.resources.barNumberFont; // we have never have set it explicitly, that's the current state
72422
+ canvas.font = this.renderer.resources.elementFonts.get(NotationElement.ScoreBendSlur);
71615
72423
  for (let i = 0; i < this._notes.length; i++) {
71616
72424
  const note = this._notes[i];
71617
72425
  const _ = ElementStyleHelper.note(canvas, NoteSubElement.StandardNotationEffects, note);
@@ -77683,6 +78491,9 @@ class GpifWriter {
77683
78491
  key.addElement('Sharps').innerText = 'Sharps';
77684
78492
  masterBarNode.addElement('Time').innerText =
77685
78493
  `${masterBar.timeSignatureNumerator}/${masterBar.timeSignatureDenominator}`;
78494
+ if (masterBar.actualBeamingRules) {
78495
+ this._writeBarXProperties(masterBarNode, masterBar);
78496
+ }
77686
78497
  if (masterBar.isFreeTime) {
77687
78498
  masterBarNode.addElement('FreeTime');
77688
78499
  }
@@ -77792,6 +78603,29 @@ class GpifWriter {
77792
78603
  }
77793
78604
  this._writeFermatas(masterBarNode, masterBar);
77794
78605
  }
78606
+ _writeBarXProperties(masterBarNode, masterBar) {
78607
+ const properties = masterBarNode.addElement('XProperties');
78608
+ const beamingRules = masterBar.actualBeamingRules;
78609
+ if (beamingRules) {
78610
+ // prefer 8th note rule (that's what GP mostly has)
78611
+ const rule = beamingRules.findRule(Duration.Eighth);
78612
+ // NOTE: it's not clear if guitar pro supports quarter rules
78613
+ // for that case we better convert this to an "8th" note rule.
78614
+ let durationProp = rule[0];
78615
+ let groupSizeFactor = 1;
78616
+ if (rule[0] === Duration.Quarter) {
78617
+ durationProp = 8;
78618
+ groupSizeFactor = 2;
78619
+ }
78620
+ this._writeSimpleXPropertyNode(properties, '1124139010', 'Int', durationProp.toString());
78621
+ const startGroupid = 1124139264;
78622
+ let i = 0;
78623
+ while (i < rule[1].length) {
78624
+ this._writeSimpleXPropertyNode(properties, (startGroupid + i).toString(), 'Int', (rule[1][i] * groupSizeFactor).toString());
78625
+ i++;
78626
+ }
78627
+ }
78628
+ }
77795
78629
  _writeFermatas(parent, masterBar) {
77796
78630
  const fermataCount = masterBar.fermata?.size ?? 0;
77797
78631
  if (fermataCount === 0) {
@@ -79740,6 +80574,7 @@ const _barrel$3 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty(
79740
80574
  BackingTrack,
79741
80575
  Bar,
79742
80576
  get BarLineStyle () { return BarLineStyle; },
80577
+ get BarNumberDisplay () { return BarNumberDisplay; },
79743
80578
  BarStyle,
79744
80579
  get BarSubElement () { return BarSubElement; },
79745
80580
  get BarreShape () { return BarreShape; },