@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.
package/dist/alphaTab.js CHANGED
@@ -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
  *
@@ -209,9 +209,9 @@
209
209
  * @internal
210
210
  */
211
211
  class VersionInfo {
212
- static version = '1.8.0-alpha.1669';
213
- static date = '2026-01-08T02:24:31.439Z';
214
- static commit = 'f7c00b0b8db32011293f475bf5e930d21c1903ac';
212
+ static version = '1.8.0-alpha.1671';
213
+ static date = '2026-01-10T02:20:48.787Z';
214
+ static commit = '1edf83d58fe31a924adb1980bc4a529aed07dad4';
215
215
  static print(print) {
216
216
  print(`alphaTab ${VersionInfo.version}`);
217
217
  print(`commit: ${VersionInfo.commit}`);
@@ -1321,6 +1321,23 @@
1321
1321
  * The key signature for numbered notation staff.
1322
1322
  */
1323
1323
  NotationElement[NotationElement["EffectNumberedNotationKeySignature"] = 51] = "EffectNumberedNotationKeySignature";
1324
+ /**
1325
+ * The fretboard numbers shown in chord diagrams.
1326
+ */
1327
+ NotationElement[NotationElement["ChordDiagramFretboardNumbers"] = 52] = "ChordDiagramFretboardNumbers";
1328
+ /**
1329
+ * The bar numbers.
1330
+ */
1331
+ NotationElement[NotationElement["BarNumber"] = 53] = "BarNumber";
1332
+ /**
1333
+ * The repeat count indicator shown above the thick bar line to describe
1334
+ * how many repeats should be played.
1335
+ */
1336
+ NotationElement[NotationElement["RepeatCount"] = 54] = "RepeatCount";
1337
+ /**
1338
+ * The slurs shown on bend effects within the score staff.
1339
+ */
1340
+ NotationElement[NotationElement["ScoreBendSlur"] = 55] = "ScoreBendSlur";
1324
1341
  })(exports.NotationElement || (exports.NotationElement = {}));
1325
1342
  /**
1326
1343
  * The notation settings control how various music notation elements are shown and behaving
@@ -2180,6 +2197,17 @@
2180
2197
  * Gets or sets the type of key signature (major/minor)
2181
2198
  */
2182
2199
  keySignatureType = KeySignatureType.Major;
2200
+ /**
2201
+ * How bar numbers should be displayed.
2202
+ * If specified, overrides the value from the stylesheet on score level.
2203
+ */
2204
+ barNumberDisplay;
2205
+ /**
2206
+ * The shortest duration contained across beats in this bar.
2207
+ * @internal
2208
+ * @json_ignore
2209
+ */
2210
+ shortestDuration = Duration.DoubleWhole;
2183
2211
  /**
2184
2212
  * The bar line to draw on the left side of the bar with an "automatic" type resolved to the actual one.
2185
2213
  * @param isFirstOfSystem Whether the bar is the first one in the system.
@@ -2251,12 +2279,16 @@
2251
2279
  this._filledVoices.add(0);
2252
2280
  this._isEmpty = true;
2253
2281
  this._isRestOnly = true;
2282
+ this.shortestDuration = Duration.DoubleWhole;
2254
2283
  for (let i = 0, j = this.voices.length; i < j; i++) {
2255
2284
  const voice = this.voices[i];
2256
2285
  voice.finish(settings, sharedDataBag);
2257
2286
  if (!voice.isEmpty) {
2258
2287
  this._isEmpty = false;
2259
2288
  this._filledVoices.add(i);
2289
+ if (voice.shortestDuration > this.shortestDuration) {
2290
+ this.shortestDuration = voice.shortestDuration;
2291
+ }
2260
2292
  }
2261
2293
  if (!voice.isRestOnly) {
2262
2294
  this._isRestOnly = false;
@@ -2353,6 +2385,116 @@
2353
2385
  TripletFeel[TripletFeel["Scottish8th"] = 6] = "Scottish8th";
2354
2386
  })(TripletFeel || (TripletFeel = {}));
2355
2387
 
2388
+ /**
2389
+ * Defines the custom beaming rules which define how beats are beamed together or split apart
2390
+ * during the automatic beaming when displayed.
2391
+ * @json
2392
+ * @json_strict
2393
+ * @public
2394
+ *
2395
+ * @remarks
2396
+ * The beaming logic works like this:
2397
+ *
2398
+ * The time axis of the bar is sliced into even chunks. The chunk-size is defined by the respective group definition.
2399
+ * Within these chunks groups can then be placed spanning 1 or more chunks.
2400
+ *
2401
+ * If beats start within the same "group" they are beamed together.
2402
+ */
2403
+ class BeamingRules {
2404
+ _singleGroupKey;
2405
+ /**
2406
+ * The the group for a given "longest duration" within the bar.
2407
+ * @remarks
2408
+ * The map key is the duration to which the bar will be sliced into.
2409
+ * The map value defines the "groups" placed within the sliced.
2410
+ */
2411
+ groups = new Map();
2412
+ /**
2413
+ * @internal
2414
+ * @json_ignore
2415
+ */
2416
+ uniqueId = '';
2417
+ /**
2418
+ * @internal
2419
+ * @json_ignore
2420
+ */
2421
+ timeSignatureNumerator = 0;
2422
+ /**
2423
+ * @internal
2424
+ * @json_ignore
2425
+ */
2426
+ timeSignatureDenominator = 0;
2427
+ /**
2428
+ * @internal
2429
+ */
2430
+ static createSimple(timeSignatureNumerator, timeSignatureDenominator, duration, groups) {
2431
+ const r = new BeamingRules();
2432
+ r.timeSignatureNumerator = timeSignatureNumerator;
2433
+ r.timeSignatureDenominator = timeSignatureDenominator;
2434
+ r.groups.set(duration, groups);
2435
+ r.finish();
2436
+ return r;
2437
+ }
2438
+ /**
2439
+ * @internal
2440
+ */
2441
+ findRule(shortestDuration) {
2442
+ // fast path: one rule -> take it
2443
+ const singleGroupKey = this._singleGroupKey;
2444
+ if (singleGroupKey) {
2445
+ return [singleGroupKey, this.groups.get(singleGroupKey)];
2446
+ }
2447
+ if (shortestDuration < Duration.Quarter) {
2448
+ return [shortestDuration, []];
2449
+ }
2450
+ // first search shorter
2451
+ let durationValue = shortestDuration;
2452
+ do {
2453
+ const duration = durationValue;
2454
+ if (this.groups.has(duration)) {
2455
+ return [duration, this.groups.get(duration)];
2456
+ }
2457
+ durationValue = durationValue * 2;
2458
+ } while (durationValue <= Duration.TwoHundredFiftySixth);
2459
+ // then longer
2460
+ durationValue = shortestDuration / 2;
2461
+ do {
2462
+ const duration = durationValue;
2463
+ if (this.groups.has(duration)) {
2464
+ return [duration, this.groups.get(duration)];
2465
+ }
2466
+ durationValue = durationValue / 2;
2467
+ } while (durationValue > Duration.Half);
2468
+ return [shortestDuration, []];
2469
+ }
2470
+ /**
2471
+ * @internal
2472
+ */
2473
+ finish() {
2474
+ let uniqueId = `${this.timeSignatureNumerator}_${this.timeSignatureDenominator}`;
2475
+ for (const [k, v] of this.groups) {
2476
+ uniqueId += `__${k}`;
2477
+ // trim of 0s at the end of the group
2478
+ let lastZero = v.length;
2479
+ for (let i = v.length - 1; i >= 0; i--) {
2480
+ if (v[i] === 0) {
2481
+ lastZero = i;
2482
+ }
2483
+ else {
2484
+ break;
2485
+ }
2486
+ }
2487
+ if (lastZero < v.length) {
2488
+ v.splice(lastZero, v.length - lastZero);
2489
+ }
2490
+ uniqueId += `_${v.join('_')}`;
2491
+ if (this.groups.size === 1) {
2492
+ this._singleGroupKey = k;
2493
+ }
2494
+ }
2495
+ this.uniqueId = uniqueId;
2496
+ }
2497
+ }
2356
2498
  /**
2357
2499
  * The MasterBar stores information about a bar which affects
2358
2500
  * all tracks.
@@ -2470,6 +2612,16 @@
2470
2612
  * Gets or sets whether this is bar has a common time signature.
2471
2613
  */
2472
2614
  timeSignatureCommon = false;
2615
+ /**
2616
+ * Defines the custom beaming rules which should be applied to this bar and all bars following.
2617
+ */
2618
+ beamingRules;
2619
+ /**
2620
+ * The actual (custom) beaming rules to use for this bar if any were specified.
2621
+ * @json_ignore
2622
+ * @internal
2623
+ */
2624
+ actualBeamingRules;
2473
2625
  /**
2474
2626
  * Gets or sets whether the bar indicates a free time playing.
2475
2627
  */
@@ -2602,6 +2754,32 @@
2602
2754
  }
2603
2755
  this.syncPoints.push(syncPoint);
2604
2756
  }
2757
+ finish(sharedDataBag) {
2758
+ let beamingRules = this.beamingRules;
2759
+ if (beamingRules) {
2760
+ beamingRules.timeSignatureNumerator = this.timeSignatureNumerator;
2761
+ beamingRules.timeSignatureDenominator = this.timeSignatureDenominator;
2762
+ beamingRules.finish();
2763
+ }
2764
+ if (this.index > 0) {
2765
+ this.start = this.previousMasterBar.start + this.previousMasterBar.calculateDuration();
2766
+ // clear out equal rules to reduce memory consumption.
2767
+ const previousRules = sharedDataBag.has('beamingRules')
2768
+ ? sharedDataBag.get('beamingRules')
2769
+ : undefined;
2770
+ if (previousRules && previousRules.uniqueId === beamingRules?.uniqueId) {
2771
+ this.beamingRules = undefined;
2772
+ beamingRules = previousRules;
2773
+ }
2774
+ else if (!beamingRules) {
2775
+ beamingRules = previousRules;
2776
+ }
2777
+ }
2778
+ this.actualBeamingRules = beamingRules;
2779
+ if (this.beamingRules) {
2780
+ sharedDataBag.set('beamingRules', beamingRules);
2781
+ }
2782
+ }
2605
2783
  }
2606
2784
 
2607
2785
  /**
@@ -2674,6 +2852,25 @@
2674
2852
  */
2675
2853
  TrackNameOrientation[TrackNameOrientation["Vertical"] = 1] = "Vertical";
2676
2854
  })(TrackNameOrientation || (TrackNameOrientation = {}));
2855
+ /**
2856
+ * How bar numbers are displayed
2857
+ * @public
2858
+ */
2859
+ var BarNumberDisplay;
2860
+ (function (BarNumberDisplay) {
2861
+ /**
2862
+ * Show bar numbers on all bars.
2863
+ */
2864
+ BarNumberDisplay[BarNumberDisplay["AllBars"] = 0] = "AllBars";
2865
+ /**
2866
+ * Show bar numbers on the first bar of every system.
2867
+ */
2868
+ BarNumberDisplay[BarNumberDisplay["FirstOfSystem"] = 1] = "FirstOfSystem";
2869
+ /**
2870
+ * Hide all bar numbers
2871
+ */
2872
+ BarNumberDisplay[BarNumberDisplay["Hide"] = 2] = "Hide";
2873
+ })(BarNumberDisplay || (BarNumberDisplay = {}));
2677
2874
  /**
2678
2875
  * This class represents the rendering stylesheet.
2679
2876
  * It contains settings which control the display of the score when rendered.
@@ -2767,6 +2964,10 @@
2767
2964
  * {@link hideEmptyStaves}
2768
2965
  */
2769
2966
  showSingleStaffBrackets = false;
2967
+ /**
2968
+ * How bar numbers should be displayed.
2969
+ */
2970
+ barNumberDisplay = BarNumberDisplay.AllBars;
2770
2971
  }
2771
2972
 
2772
2973
  /**
@@ -2929,6 +3130,12 @@
2929
3130
  get isRestOnly() {
2930
3131
  return this._isRestOnly;
2931
3132
  }
3133
+ /**
3134
+ * The shortest duration contained across beats in this bar.
3135
+ * @internal
3136
+ * @json_ignore
3137
+ */
3138
+ shortestDuration = Duration.DoubleWhole;
2932
3139
  insertBeat(after, newBeat) {
2933
3140
  newBeat.nextBeat = after.nextBeat;
2934
3141
  if (newBeat.nextBeat) {
@@ -3022,6 +3229,7 @@
3022
3229
  }
3023
3230
  let currentDisplayTick = 0;
3024
3231
  let currentPlaybackTick = 0;
3232
+ this.shortestDuration = Duration.DoubleWhole;
3025
3233
  for (let i = 0; i < this.beats.length; i++) {
3026
3234
  const beat = this.beats[i];
3027
3235
  beat.index = i;
@@ -3030,6 +3238,9 @@
3030
3238
  // we need to first steal the duration from the right beat
3031
3239
  // and place the grace beats correctly
3032
3240
  if (beat.graceType === GraceType.None) {
3241
+ if (beat.duration > this.shortestDuration) {
3242
+ this.shortestDuration = beat.duration;
3243
+ }
3033
3244
  if (beat.graceGroup) {
3034
3245
  const firstGraceBeat = beat.graceGroup.beats[0];
3035
3246
  const lastGraceBeat = beat.graceGroup.beats[beat.graceGroup.beats.length - 1];
@@ -3490,7 +3701,7 @@
3490
3701
  bar.previousMasterBar.nextMasterBar = bar;
3491
3702
  // NOTE: this will not work on anacrusis. Correct anacrusis durations are only working
3492
3703
  // when there are beats with playback positions already computed which requires full finish
3493
- // chicken-egg problem here. temporarily forcing anacrusis length here to 0,
3704
+ // chicken-egg problem here. temporarily forcing anacrusis length here to 0,
3494
3705
  // .finish() will correct these times
3495
3706
  bar.start =
3496
3707
  bar.previousMasterBar.start +
@@ -3554,9 +3765,7 @@
3554
3765
  }
3555
3766
  // fixup masterbar starts to handle anacrusis lengths
3556
3767
  for (const mb of this.masterBars) {
3557
- if (mb.index > 0) {
3558
- mb.start = mb.previousMasterBar.start + mb.previousMasterBar.calculateDuration();
3559
- }
3768
+ mb.finish(sharedDataBag);
3560
3769
  }
3561
3770
  }
3562
3771
  /**
@@ -8438,6 +8647,12 @@
8438
8647
  ['buzzroll', 1]
8439
8648
  ]);
8440
8649
  static tremoloPickingStyleReversed = AlphaTex1EnumMappings._reverse(AlphaTex1EnumMappings.tremoloPickingStyle);
8650
+ static barNumberDisplay = new Map([
8651
+ ['allbars', 0],
8652
+ ['firstofsystem', 1],
8653
+ ['hide', 2]
8654
+ ]);
8655
+ static barNumberDisplayReversed = AlphaTex1EnumMappings._reverse(AlphaTex1EnumMappings.barNumberDisplay);
8441
8656
  static keySignaturesMinorReversed = new Map([
8442
8657
  [-7, 'abminor'],
8443
8658
  [-6, 'ebminor'],
@@ -8632,7 +8847,8 @@
8632
8847
  ['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]],
8633
8848
  ['hideemptystaves', null],
8634
8849
  ['hideemptystavesinfirstsystem', null],
8635
- ['showsinglestaffbrackets', null]
8850
+ ['showsinglestaffbrackets', null],
8851
+ ['defaultbarnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]]
8636
8852
  ]);
8637
8853
  static staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([
8638
8854
  ['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]],
@@ -8917,7 +9133,17 @@
8917
9133
  ['sph', [[[[16], 2]]]],
8918
9134
  ['spu', [[[[16], 2]]]],
8919
9135
  ['db', null],
8920
- ['voicemode', [[[[10, 17], 0, ['staffwise', 'barwise']]]]]
9136
+ ['voicemode', [[[[10, 17], 0, ['staffwise', 'barwise']]]]],
9137
+ ['barnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]],
9138
+ [
9139
+ 'beaming',
9140
+ [
9141
+ [
9142
+ [[16], 0],
9143
+ [[16], 5]
9144
+ ]
9145
+ ]
9146
+ ]
8921
9147
  ]);
8922
9148
  static metaDataProperties = AlphaTex1LanguageDefinitions._metaProps([
8923
9149
  [
@@ -8975,6 +9201,7 @@
8975
9201
  ['hideemptystaves', null],
8976
9202
  ['hideemptystavesinfirstsystem', null],
8977
9203
  ['showsinglestaffbrackets', null],
9204
+ ['defaultbarnumberdisplay', null],
8978
9205
  [
8979
9206
  'tuning',
8980
9207
  [
@@ -9031,7 +9258,9 @@
9031
9258
  ['sph', null],
9032
9259
  ['spu', null],
9033
9260
  ['db', null],
9034
- ['voicemode', null]
9261
+ ['voicemode', null],
9262
+ ['barnumberdisplay', null],
9263
+ ['beaming', null]
9035
9264
  ]);
9036
9265
  static metaDataSignatures = [
9037
9266
  AlphaTex1LanguageDefinitions.scoreMetaDataSignatures,
@@ -12746,6 +12975,7 @@
12746
12975
  */
12747
12976
  class AlphaTex1LanguageHandler {
12748
12977
  static instance = new AlphaTex1LanguageHandler();
12978
+ static _timeSignatureDenominators = new Set([1, 2, 4, 8, 16, 32, 64, 128]);
12749
12979
  applyScoreMetaData(importer, score, metaData) {
12750
12980
  const result = this._checkArgumentTypes(importer, [AlphaTex1LanguageDefinitions.scoreMetaDataSignatures], metaData, metaData.tag.tag.text.toLowerCase(), metaData.arguments);
12751
12981
  if (result !== undefined) {
@@ -12882,6 +13112,13 @@
12882
13112
  case 'showsinglestaffbrackets':
12883
13113
  score.stylesheet.showSingleStaffBrackets = true;
12884
13114
  return ApplyNodeResult.Applied;
13115
+ case 'defaultbarnumberdisplay':
13116
+ const barNumberDisplay = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'bar number display', AlphaTex1EnumMappings.barNumberDisplay);
13117
+ if (barNumberDisplay === undefined) {
13118
+ return ApplyNodeResult.NotAppliedSemanticError;
13119
+ }
13120
+ score.stylesheet.barNumberDisplay = barNumberDisplay;
13121
+ return ApplyNodeResult.Applied;
12885
13122
  default:
12886
13123
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
12887
13124
  }
@@ -13160,8 +13397,29 @@
13160
13397
  case 'ts':
13161
13398
  switch (metaData.arguments.arguments[0].nodeType) {
13162
13399
  case AlphaTexNodeType.Number:
13163
- bar.masterBar.timeSignatureNumerator = metaData.arguments.arguments[0].value;
13164
- bar.masterBar.timeSignatureDenominator = metaData.arguments.arguments[1].value;
13400
+ bar.masterBar.timeSignatureNumerator =
13401
+ metaData.arguments.arguments[0].value | 0;
13402
+ if (bar.masterBar.timeSignatureNumerator < 1 || bar.masterBar.timeSignatureNumerator > 32) {
13403
+ importer.addSemanticDiagnostic({
13404
+ code: AlphaTexDiagnosticCode.AT211,
13405
+ message: `Value is out of valid range. Allowed range: 1-32, Actual Value: ${bar.masterBar.timeSignatureNumerator}`,
13406
+ start: metaData.arguments.arguments[0].start,
13407
+ end: metaData.arguments.arguments[0].end,
13408
+ severity: AlphaTexDiagnosticsSeverity.Error
13409
+ });
13410
+ }
13411
+ bar.masterBar.timeSignatureDenominator =
13412
+ metaData.arguments.arguments[1].value | 0;
13413
+ if (!AlphaTex1LanguageHandler._timeSignatureDenominators.has(bar.masterBar.timeSignatureDenominator)) {
13414
+ const valueList = Array.from(AlphaTex1LanguageHandler._timeSignatureDenominators).join(', ');
13415
+ importer.addSemanticDiagnostic({
13416
+ code: AlphaTexDiagnosticCode.AT211,
13417
+ message: `Value is out of valid range. Allowed range: ${valueList}, Actual Value: ${bar.masterBar.timeSignatureDenominator}`,
13418
+ start: metaData.arguments.arguments[0].start,
13419
+ end: metaData.arguments.arguments[0].end,
13420
+ severity: AlphaTexDiagnosticsSeverity.Error
13421
+ });
13422
+ }
13165
13423
  break;
13166
13424
  case AlphaTexNodeType.Ident:
13167
13425
  case AlphaTexNodeType.String:
@@ -13184,6 +13442,8 @@
13184
13442
  break;
13185
13443
  }
13186
13444
  return ApplyNodeResult.Applied;
13445
+ case 'beaming':
13446
+ return this._parseBeamingRule(importer, metaData, bar.masterBar);
13187
13447
  case 'ks':
13188
13448
  const keySignature = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'key signature', AlphaTex1EnumMappings.keySignature);
13189
13449
  if (keySignature === undefined) {
@@ -13373,10 +13633,64 @@
13373
13633
  bar.masterBar.isDoubleBar = true;
13374
13634
  bar.barLineRight = BarLineStyle.LightLight;
13375
13635
  return ApplyNodeResult.Applied;
13636
+ case 'barnumberdisplay':
13637
+ const barNumberDisplay = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'bar number display', AlphaTex1EnumMappings.barNumberDisplay);
13638
+ if (barNumberDisplay === undefined) {
13639
+ return ApplyNodeResult.NotAppliedSemanticError;
13640
+ }
13641
+ bar.barNumberDisplay = barNumberDisplay;
13642
+ return ApplyNodeResult.Applied;
13376
13643
  default:
13377
13644
  return ApplyNodeResult.NotAppliedUnrecognizedMarker;
13378
13645
  }
13379
13646
  }
13647
+ _parseBeamingRule(importer, metaData, masterBar) {
13648
+ let duration = Duration.Eighth;
13649
+ const groupSizes = [];
13650
+ const durationValue = metaData.arguments.arguments[0].value;
13651
+ switch (durationValue) {
13652
+ case 4:
13653
+ duration = Duration.QuadrupleWhole;
13654
+ break;
13655
+ case 8:
13656
+ duration = Duration.Eighth;
13657
+ break;
13658
+ case 16:
13659
+ duration = Duration.Sixteenth;
13660
+ break;
13661
+ case 32:
13662
+ duration = Duration.ThirtySecond;
13663
+ break;
13664
+ default:
13665
+ importer.addSemanticDiagnostic({
13666
+ code: AlphaTexDiagnosticCode.AT209,
13667
+ message: `Value is out of valid range. Allowed range: 4,8,16 or 32, Actual Value: ${durationValue}`,
13668
+ severity: AlphaTexDiagnosticsSeverity.Error,
13669
+ start: metaData.arguments.arguments[0].start,
13670
+ end: metaData.arguments.arguments[0].end
13671
+ });
13672
+ return ApplyNodeResult.NotAppliedSemanticError;
13673
+ }
13674
+ for (let i = 1; i < metaData.arguments.arguments.length; i++) {
13675
+ const groupSize = metaData.arguments.arguments[i].value;
13676
+ if (groupSize < 1) {
13677
+ importer.addSemanticDiagnostic({
13678
+ code: AlphaTexDiagnosticCode.AT209,
13679
+ message: `Value is out of valid range. Allowed range: >0, Actual Value: ${durationValue}`,
13680
+ severity: AlphaTexDiagnosticsSeverity.Error,
13681
+ start: metaData.arguments.arguments[i].start,
13682
+ end: metaData.arguments.arguments[i].end
13683
+ });
13684
+ return ApplyNodeResult.NotAppliedSemanticError;
13685
+ }
13686
+ groupSizes.push(metaData.arguments.arguments[i].value);
13687
+ }
13688
+ if (!masterBar.beamingRules) {
13689
+ masterBar.beamingRules = new BeamingRules();
13690
+ }
13691
+ masterBar.beamingRules.groups.set(duration, groupSizes);
13692
+ return ApplyNodeResult.Applied;
13693
+ }
13380
13694
  static _handleAccidentalMode(importer, args) {
13381
13695
  const accidentalMode = AlphaTex1LanguageHandler._parseEnumValue(importer, args, 'accidental mode', AlphaTex1EnumMappings.alphaTexAccidentalMode);
13382
13696
  if (accidentalMode === undefined) {
@@ -14740,6 +15054,9 @@
14740
15054
  if (stylesheet.showSingleStaffBrackets) {
14741
15055
  nodes.push(Atnf.meta('showSingleStaffBrackets'));
14742
15056
  }
15057
+ if (stylesheet.barNumberDisplay !== BarNumberDisplay.AllBars) {
15058
+ nodes.push(Atnf.identMeta('defaultBarNumberDisplay', BarNumberDisplay[stylesheet.barNumberDisplay]));
15059
+ }
14743
15060
  // Unsupported:
14744
15061
  // 'globaldisplaychorddiagramsontop',
14745
15062
  // 'pertrackchorddiagramsontop',
@@ -14873,6 +15190,9 @@
14873
15190
  }
14874
15191
  ];
14875
15192
  }
15193
+ if (bar.barNumberDisplay !== undefined) {
15194
+ nodes.push(Atnf.identMeta('barNumberDisplay', BarNumberDisplay[bar.barNumberDisplay]));
15195
+ }
14876
15196
  return nodes;
14877
15197
  }
14878
15198
  static _buildStaffMetaDataNodes(nodes, staff) {
@@ -14963,6 +15283,15 @@
14963
15283
  ])));
14964
15284
  }
14965
15285
  }
15286
+ if (masterBar.beamingRules) {
15287
+ for (const [k, v] of masterBar.beamingRules.groups) {
15288
+ const args = Atnf.args([Atnf.number(k)], true);
15289
+ for (const i of v) {
15290
+ args.arguments.push(Atnf.number(i));
15291
+ }
15292
+ nodes.push(Atnf.meta('beaming', args));
15293
+ }
15294
+ }
14966
15295
  if ((masterBar.index > 0 && masterBar.tripletFeel !== masterBar.previousMasterBar?.tripletFeel) ||
14967
15296
  (masterBar.index === 0 && masterBar.tripletFeel !== TripletFeel.NoTripletFeel)) {
14968
15297
  nodes.push(Atnf.identMeta('tf', AlphaTex1EnumMappings.tripletFeelReversed.get(masterBar.tripletFeel)));
@@ -19541,6 +19870,10 @@
19541
19870
  this.readTrack();
19542
19871
  }
19543
19872
  }
19873
+ /**
19874
+ * Guitar Pro 3-6 changes to a bass clef if any string tuning is below B2;
19875
+ */
19876
+ static _bassClefTuningThreshold = ModelUtils.parseTuning('B2').realValue;
19544
19877
  readTrack() {
19545
19878
  const newTrack = new Track();
19546
19879
  newTrack.ensureStaveCount(1);
@@ -19621,11 +19954,11 @@
19621
19954
  this.data.readByte();
19622
19955
  // `12` for all tunings which have bass clefs
19623
19956
  const clefMode = IOHelper.readInt32LE(this.data);
19624
- if (clefMode === 12) {
19625
- this._clefsPerTrack.set(index, Clef.F4);
19957
+ if (clefMode === 12 || tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) {
19958
+ this._clefsPerTrack.set(newTrack.index, Clef.F4);
19626
19959
  }
19627
19960
  else {
19628
- this._clefsPerTrack.set(index, Clef.G2);
19961
+ this._clefsPerTrack.set(newTrack.index, Clef.G2);
19629
19962
  }
19630
19963
  // Unknown, no UI setting seem to affect this
19631
19964
  IOHelper.readInt32LE(this.data);
@@ -19657,11 +19990,11 @@
19657
19990
  }
19658
19991
  }
19659
19992
  else {
19660
- if (GeneralMidi.isBass(newTrack.playbackInfo.program)) {
19661
- this._clefsPerTrack.set(index, Clef.F4);
19993
+ if (tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) {
19994
+ this._clefsPerTrack.set(newTrack.index, Clef.F4);
19662
19995
  }
19663
19996
  else {
19664
- this._clefsPerTrack.set(index, Clef.G2);
19997
+ this._clefsPerTrack.set(newTrack.index, Clef.G2);
19665
19998
  }
19666
19999
  }
19667
20000
  }
@@ -20935,6 +21268,19 @@
20935
21268
  ModelUtils.getOrCreateHeaderFooterStyle(score, ScoreSubElement.CopyrightSecondLine).isVisible =
20936
21269
  value;
20937
21270
  break;
21271
+ case 'System/barIndexDrawType':
21272
+ switch (value) {
21273
+ case 0:
21274
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.AllBars;
21275
+ break;
21276
+ case 1:
21277
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.FirstOfSystem;
21278
+ break;
21279
+ case 2:
21280
+ score.stylesheet.barNumberDisplay = BarNumberDisplay.Hide;
21281
+ break;
21282
+ }
21283
+ break;
20938
21284
  }
20939
21285
  }
20940
21286
  }
@@ -21124,6 +21470,17 @@
21124
21470
  }
21125
21471
  }
21126
21472
  }
21473
+ switch (score.stylesheet.barNumberDisplay) {
21474
+ case BarNumberDisplay.AllBars:
21475
+ binaryStylesheet.addValue('System/barIndexDrawType', 0, DataType.Integer);
21476
+ break;
21477
+ case BarNumberDisplay.FirstOfSystem:
21478
+ binaryStylesheet.addValue('System/barIndexDrawType', 1, DataType.Integer);
21479
+ break;
21480
+ case BarNumberDisplay.Hide:
21481
+ binaryStylesheet.addValue('System/barIndexDrawType', 2, DataType.Integer);
21482
+ break;
21483
+ }
21127
21484
  const writer = ByteBuffer.withCapacity(128);
21128
21485
  binaryStylesheet.writeTo(writer);
21129
21486
  return writer.toArray();
@@ -22935,6 +23292,8 @@
22935
23292
  }
22936
23293
  }
22937
23294
  _parseMasterBarXProperties(masterBar, node) {
23295
+ let beamingRuleDuration = Number.NaN;
23296
+ let beamingRuleGroups = undefined;
22938
23297
  for (const c of node.childElements()) {
22939
23298
  switch (c.localName) {
22940
23299
  case 'XProperty':
@@ -22943,10 +23302,32 @@
22943
23302
  case '1124073984':
22944
23303
  masterBar.displayScale = GpifParser._parseFloatSafe(c.findChildElement('Double')?.innerText, 1);
22945
23304
  break;
23305
+ case '1124139010':
23306
+ beamingRuleDuration = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
23307
+ break;
23308
+ default:
23309
+ const idNumeric = GpifParser._parseIntSafe(id, 0);
23310
+ if (idNumeric >= 1124139264 && idNumeric <= 1124139295) {
23311
+ const groupIndex = idNumeric - 1124139264;
23312
+ const groupSize = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
23313
+ if (beamingRuleGroups === undefined) {
23314
+ beamingRuleGroups = [];
23315
+ }
23316
+ while (beamingRuleGroups.length < groupIndex + 1) {
23317
+ beamingRuleGroups.push(0);
23318
+ }
23319
+ beamingRuleGroups[groupIndex] = groupSize;
23320
+ }
23321
+ break;
22946
23322
  }
22947
23323
  break;
22948
23324
  }
22949
23325
  }
23326
+ if (!Number.isNaN(beamingRuleDuration) && beamingRuleGroups) {
23327
+ const rules = new BeamingRules();
23328
+ rules.groups.set(beamingRuleDuration, beamingRuleGroups);
23329
+ masterBar.beamingRules = rules;
23330
+ }
22950
23331
  }
22951
23332
  _parseBeatProperties(node, beat) {
22952
23333
  let isWhammy = false;
@@ -24830,6 +25211,8 @@
24830
25211
  _idToTrackInfo = new Map();
24831
25212
  _indexToTrackInfo = new Map();
24832
25213
  _staffToContext = new Map();
25214
+ _currentBarNumberDisplayPart;
25215
+ _currentBarNumberDisplayBar;
24833
25216
  _divisionsPerQuarterNote = 1;
24834
25217
  _currentDynamics = DynamicValue.F;
24835
25218
  get name() {
@@ -25443,20 +25826,29 @@
25443
25826
  break;
25444
25827
  }
25445
25828
  }
25829
+ this._currentBarNumberDisplayPart = undefined;
25446
25830
  }
25447
25831
  _parsePartwiseMeasure(element, track, index) {
25448
25832
  const masterBar = this._getOrCreateMasterBar(element, index);
25449
- this._parsePartMeasure(element, masterBar, track);
25833
+ const implicit = element.attributes.get('implicit') === 'yes';
25834
+ this._parsePartMeasure(element, masterBar, track, implicit, true);
25835
+ this._currentBarNumberDisplayBar = undefined;
25450
25836
  }
25451
25837
  _parseTimewiseMeasure(element, index) {
25452
25838
  const masterBar = this._getOrCreateMasterBar(element, index);
25839
+ const implicit = element.attributes.get('implicit') === 'yes';
25453
25840
  for (const c of element.childElements()) {
25454
25841
  switch (c.localName) {
25455
25842
  case 'part':
25456
- this._parseTimewisePart(c, masterBar);
25843
+ this._parseTimewisePart(c, masterBar, implicit);
25844
+ this._currentBarNumberDisplayPart = undefined;
25845
+ break;
25846
+ case 'print':
25847
+ this._parsePrint(c, masterBar, undefined, true);
25457
25848
  break;
25458
25849
  }
25459
25850
  }
25851
+ this._currentBarNumberDisplayBar = undefined;
25460
25852
  }
25461
25853
  _getOrCreateMasterBar(element, index) {
25462
25854
  const implicit = element.attributes.get('implicit') === 'yes';
@@ -25475,13 +25867,13 @@
25475
25867
  const masterBar = this._score.masterBars[index];
25476
25868
  return masterBar;
25477
25869
  }
25478
- _parseTimewisePart(element, masterBar) {
25870
+ _parseTimewisePart(element, masterBar, implicit) {
25479
25871
  const id = element.attributes.get('id');
25480
25872
  if (!id || !this._idToTrackInfo.has(id)) {
25481
25873
  return;
25482
25874
  }
25483
25875
  const track = this._idToTrackInfo.get(id).track;
25484
- this._parsePartMeasure(element, masterBar, track);
25876
+ this._parsePartMeasure(element, masterBar, track, implicit, false);
25485
25877
  }
25486
25878
  // current measure state
25487
25879
  /**
@@ -25493,7 +25885,7 @@
25493
25885
  * to access the current voice/staff (e.g. on rests when we don't have notes)
25494
25886
  */
25495
25887
  _lastBeat = null;
25496
- _parsePartMeasure(element, masterBar, track) {
25888
+ _parsePartMeasure(element, masterBar, track, implicit, isPartwise) {
25497
25889
  this._musicalPosition = 0;
25498
25890
  this._lastBeat = null;
25499
25891
  masterBar.alternateEndings = this._nextMasterBarRepeatEnding;
@@ -25520,7 +25912,7 @@
25520
25912
  break;
25521
25913
  // case 'figured-bass': Not supported
25522
25914
  case 'print':
25523
- this._parsePrint(c, masterBar, track);
25915
+ this._parsePrint(c, masterBar, track, true);
25524
25916
  break;
25525
25917
  case 'sound':
25526
25918
  this._parseSound(c, masterBar, track);
@@ -25541,16 +25933,51 @@
25541
25933
  this._applySimileMarks(masterBar, track);
25542
25934
  // initial empty staff and voice (if no other elements created something already)
25543
25935
  const staff = this._getOrCreateStaff(track, 0);
25544
- this._getOrCreateBar(staff, masterBar);
25936
+ const bar = this._getOrCreateBar(staff, masterBar);
25937
+ if (implicit) {
25938
+ bar.barNumberDisplay = BarNumberDisplay.Hide;
25939
+ }
25940
+ else if (isPartwise) {
25941
+ bar.barNumberDisplay = this._currentBarNumberDisplayBar ?? this._currentBarNumberDisplayPart;
25942
+ }
25943
+ else {
25944
+ bar.barNumberDisplay = this._currentBarNumberDisplayPart ?? this._currentBarNumberDisplayBar;
25945
+ }
25545
25946
  // clear measure attribute
25546
25947
  this._keyAllStaves = null;
25547
25948
  }
25548
- _parsePrint(element, masterBar, track) {
25549
- if (element.getAttribute('new-system', 'no') === 'yes') {
25550
- track.addLineBreaks(masterBar.index);
25949
+ _parsePrint(element, masterBar, track, isMeasurePrint) {
25950
+ if (track !== undefined) {
25951
+ if (element.getAttribute('new-system', 'no') === 'yes') {
25952
+ track.addLineBreaks(masterBar.index);
25953
+ }
25954
+ else if (element.getAttribute('new-page', 'no') === 'yes') {
25955
+ track.addLineBreaks(masterBar.index);
25956
+ }
25551
25957
  }
25552
- else if (element.getAttribute('new-page', 'no') === 'yes') {
25553
- track.addLineBreaks(masterBar.index);
25958
+ let newDisplay = undefined;
25959
+ for (const c of element.childElements()) {
25960
+ switch (c.localName) {
25961
+ case 'measure-numbering':
25962
+ switch (c.innerText) {
25963
+ case 'none':
25964
+ newDisplay = BarNumberDisplay.Hide;
25965
+ break;
25966
+ case 'measure':
25967
+ newDisplay = BarNumberDisplay.AllBars;
25968
+ break;
25969
+ case 'system':
25970
+ newDisplay = BarNumberDisplay.FirstOfSystem;
25971
+ break;
25972
+ }
25973
+ break;
25974
+ }
25975
+ }
25976
+ if (isMeasurePrint) {
25977
+ this._currentBarNumberDisplayBar = newDisplay;
25978
+ }
25979
+ else {
25980
+ this._currentBarNumberDisplayPart = newDisplay;
25554
25981
  }
25555
25982
  }
25556
25983
  _applySimileMarks(masterBar, track) {
@@ -26660,7 +27087,7 @@
26660
27087
  if (unit !== null && perMinute > 0) {
26661
27088
  const tempoAutomation = new Automation();
26662
27089
  tempoAutomation.type = AutomationType.Tempo;
26663
- tempoAutomation.value = (perMinute * (unit / 4)) | 0;
27090
+ tempoAutomation.value = perMinute * (unit / 4);
26664
27091
  tempoAutomation.ratioPosition = ratioPosition;
26665
27092
  if (!this._hasSameTempo(masterBar, tempoAutomation)) {
26666
27093
  masterBar.tempoAutomations.push(tempoAutomation);
@@ -29589,7 +30016,7 @@
29589
30016
  }
29590
30017
  if (mEvent.type === MidiEventType.TempoChange) {
29591
30018
  const meta = mEvent;
29592
- bpm = meta.beatsPerMinute;
30019
+ bpm = MidiFileSequencer._sanitizeBpm(meta.beatsPerMinute);
29593
30020
  state.tempoChanges.push(new MidiFileSequencerTempoChange(bpm, absTick, absTime));
29594
30021
  metronomeLengthInMillis = metronomeLengthInTicks * (60000.0 / (bpm * midiFile.division));
29595
30022
  }
@@ -29707,7 +30134,7 @@
29707
30134
  }
29708
30135
  else {
29709
30136
  const previousSyncPoint = syncPoints[i - 1];
29710
- previousModifiedTempo = previousSyncPoint.syncBpm;
30137
+ previousModifiedTempo = MidiFileSequencer._sanitizeBpm(previousSyncPoint.syncBpm);
29711
30138
  previousMillisecondOffset = previousSyncPoint.syncTime;
29712
30139
  previousTick = previousSyncPoint.synthTick;
29713
30140
  }
@@ -29731,7 +30158,7 @@
29731
30158
  syncPoint.syncTime = interpolatedMillisecondOffset;
29732
30159
  syncPoint.syncBpm = previousModifiedTempo;
29733
30160
  }
29734
- bpm = state.tempoChanges[tempoChangeIndex].bpm;
30161
+ bpm = MidiFileSequencer._sanitizeBpm(state.tempoChanges[tempoChangeIndex].bpm);
29735
30162
  tempoChangeIndex++;
29736
30163
  }
29737
30164
  deltaTick = p.synthTick - absTick;
@@ -29752,10 +30179,13 @@
29752
30179
  this._updateCurrentTempo(state, timePosition);
29753
30180
  const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
29754
30181
  const timeDiff = timePosition - lastTempoChange.time;
29755
- const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
30182
+ const ticks = (timeDiff / (60000.0 / (MidiFileSequencer._sanitizeBpm(lastTempoChange.bpm) * state.division))) | 0;
29756
30183
  // we add 1 for possible rounding errors.(floating point issuses)
29757
30184
  return lastTempoChange.ticks + ticks + 1;
29758
30185
  }
30186
+ static _sanitizeBpm(bpm) {
30187
+ return Math.max(bpm, 1); // prevent <0 bpms. Doesn't make sense and can cause endless loops
30188
+ }
29759
30189
  currentUpdateCurrentTempo(timePosition) {
29760
30190
  this._updateCurrentTempo(this._mainState, timePosition * this.playbackSpeed);
29761
30191
  }
@@ -36433,6 +36863,43 @@
36433
36863
  }
36434
36864
  }
36435
36865
 
36866
+ /**
36867
+ * @internal
36868
+ */
36869
+ class BeamingRulesSerializer {
36870
+ static fromJson(obj, m) {
36871
+ if (!m) {
36872
+ return;
36873
+ }
36874
+ JsonHelper.forEach(m, (v, k) => BeamingRulesSerializer.setProperty(obj, k, v));
36875
+ }
36876
+ static toJson(obj) {
36877
+ if (!obj) {
36878
+ return null;
36879
+ }
36880
+ const o = new Map();
36881
+ {
36882
+ const m = new Map();
36883
+ o.set("groups", m);
36884
+ for (const [k, v] of obj.groups) {
36885
+ m.set(k.toString(), v);
36886
+ }
36887
+ }
36888
+ return o;
36889
+ }
36890
+ static setProperty(obj, property, v) {
36891
+ switch (property) {
36892
+ case "groups":
36893
+ obj.groups = new Map();
36894
+ JsonHelper.forEach(v, (v, k) => {
36895
+ obj.groups.set(JsonHelper.parseEnum(k, Duration), v);
36896
+ });
36897
+ return true;
36898
+ }
36899
+ return false;
36900
+ }
36901
+ }
36902
+
36436
36903
  /**
36437
36904
  * @internal
36438
36905
  */
@@ -36611,6 +37078,9 @@
36611
37078
  o.set("timesignaturenumerator", obj.timeSignatureNumerator);
36612
37079
  o.set("timesignaturedenominator", obj.timeSignatureDenominator);
36613
37080
  o.set("timesignaturecommon", obj.timeSignatureCommon);
37081
+ if (obj.beamingRules) {
37082
+ o.set("beamingrules", BeamingRulesSerializer.toJson(obj.beamingRules));
37083
+ }
36614
37084
  o.set("isfreetime", obj.isFreeTime);
36615
37085
  o.set("tripletfeel", obj.tripletFeel);
36616
37086
  if (obj.section) {
@@ -36663,6 +37133,15 @@
36663
37133
  case "timesignaturecommon":
36664
37134
  obj.timeSignatureCommon = v;
36665
37135
  return true;
37136
+ case "beamingrules":
37137
+ if (v) {
37138
+ obj.beamingRules = new BeamingRules();
37139
+ BeamingRulesSerializer.fromJson(obj.beamingRules, v);
37140
+ }
37141
+ else {
37142
+ obj.beamingRules = undefined;
37143
+ }
37144
+ return true;
36666
37145
  case "isfreetime":
36667
37146
  obj.isFreeTime = v;
36668
37147
  return true;
@@ -37487,6 +37966,7 @@
37487
37966
  o.set("barlineright", obj.barLineRight);
37488
37967
  o.set("keysignature", obj.keySignature);
37489
37968
  o.set("keysignaturetype", obj.keySignatureType);
37969
+ o.set("barnumberdisplay", obj.barNumberDisplay);
37490
37970
  if (obj.style) {
37491
37971
  o.set("style", BarStyleSerializer.toJson(obj.style));
37492
37972
  }
@@ -37540,6 +38020,9 @@
37540
38020
  case "keysignaturetype":
37541
38021
  obj.keySignatureType = JsonHelper.parseEnum(v, KeySignatureType);
37542
38022
  return true;
38023
+ case "barnumberdisplay":
38024
+ obj.barNumberDisplay = JsonHelper.parseEnum(v, BarNumberDisplay);
38025
+ return true;
37543
38026
  case "style":
37544
38027
  if (v) {
37545
38028
  obj.style = new BarStyle();
@@ -38034,6 +38517,7 @@
38034
38517
  o.set("hideemptystaves", obj.hideEmptyStaves);
38035
38518
  o.set("hideemptystavesinfirstsystem", obj.hideEmptyStavesInFirstSystem);
38036
38519
  o.set("showsinglestaffbrackets", obj.showSingleStaffBrackets);
38520
+ o.set("barnumberdisplay", obj.barNumberDisplay);
38037
38521
  return o;
38038
38522
  }
38039
38523
  static setProperty(obj, property, v) {
@@ -38104,6 +38588,9 @@
38104
38588
  case "showsinglestaffbrackets":
38105
38589
  obj.showSingleStaffBrackets = v;
38106
38590
  return true;
38591
+ case "barnumberdisplay":
38592
+ obj.barNumberDisplay = JsonHelper.parseEnum(v, BarNumberDisplay);
38593
+ return true;
38107
38594
  }
38108
38595
  return false;
38109
38596
  }
@@ -41835,25 +42322,20 @@
41835
42322
  const o = new Map();
41836
42323
  o.set("smuflfontfamilyname", obj.smuflFontFamilyName);
41837
42324
  o.set("engravingsettings", EngravingSettingsSerializer.toJson(obj.engravingSettings));
41838
- o.set("copyrightfont", Font.toJson(obj.copyrightFont));
41839
- o.set("titlefont", Font.toJson(obj.titleFont));
41840
- o.set("subtitlefont", Font.toJson(obj.subTitleFont));
41841
- o.set("wordsfont", Font.toJson(obj.wordsFont));
41842
- o.set("effectfont", Font.toJson(obj.effectFont));
41843
- o.set("timerfont", Font.toJson(obj.timerFont));
41844
- o.set("directionsfont", Font.toJson(obj.directionsFont));
41845
- o.set("fretboardnumberfont", Font.toJson(obj.fretboardNumberFont));
42325
+ {
42326
+ const m = new Map();
42327
+ o.set("elementfonts", m);
42328
+ for (const [k, v] of obj.elementFonts) {
42329
+ m.set(k.toString(), Font.toJson(v));
42330
+ }
42331
+ }
41846
42332
  o.set("numberednotationfont", Font.toJson(obj.numberedNotationFont));
41847
42333
  o.set("numberednotationgracefont", Font.toJson(obj.numberedNotationGraceFont));
41848
42334
  o.set("tablaturefont", Font.toJson(obj.tablatureFont));
41849
42335
  o.set("gracefont", Font.toJson(obj.graceFont));
41850
42336
  o.set("stafflinecolor", Color.toJson(obj.staffLineColor));
41851
42337
  o.set("barseparatorcolor", Color.toJson(obj.barSeparatorColor));
41852
- o.set("barnumberfont", Font.toJson(obj.barNumberFont));
41853
42338
  o.set("barnumbercolor", Color.toJson(obj.barNumberColor));
41854
- o.set("fingeringfont", Font.toJson(obj.fingeringFont));
41855
- o.set("inlinefingeringfont", Font.toJson(obj.inlineFingeringFont));
41856
- o.set("markerfont", Font.toJson(obj.markerFont));
41857
42339
  o.set("mainglyphcolor", Color.toJson(obj.mainGlyphColor));
41858
42340
  o.set("secondaryglyphcolor", Color.toJson(obj.secondaryGlyphColor));
41859
42341
  o.set("scoreinfocolor", Color.toJson(obj.scoreInfoColor));
@@ -41864,29 +42346,10 @@
41864
42346
  case "smuflfontfamilyname":
41865
42347
  obj.smuflFontFamilyName = v;
41866
42348
  return true;
41867
- case "copyrightfont":
41868
- obj.copyrightFont = Font.fromJson(v);
41869
- return true;
41870
- case "titlefont":
41871
- obj.titleFont = Font.fromJson(v);
41872
- return true;
41873
- case "subtitlefont":
41874
- obj.subTitleFont = Font.fromJson(v);
41875
- return true;
41876
- case "wordsfont":
41877
- obj.wordsFont = Font.fromJson(v);
41878
- return true;
41879
- case "effectfont":
41880
- obj.effectFont = Font.fromJson(v);
41881
- return true;
41882
- case "timerfont":
41883
- obj.timerFont = Font.fromJson(v);
41884
- return true;
41885
- case "directionsfont":
41886
- obj.directionsFont = Font.fromJson(v);
41887
- return true;
41888
- case "fretboardnumberfont":
41889
- obj.fretboardNumberFont = Font.fromJson(v);
42349
+ case "elementfonts":
42350
+ JsonHelper.forEach(v, (v, k) => {
42351
+ obj.elementFonts.set(JsonHelper.parseEnum(k, exports.NotationElement), Font.fromJson(v));
42352
+ });
41890
42353
  return true;
41891
42354
  case "numberednotationfont":
41892
42355
  obj.numberedNotationFont = Font.fromJson(v);
@@ -41906,21 +42369,9 @@
41906
42369
  case "barseparatorcolor":
41907
42370
  obj.barSeparatorColor = Color.fromJson(v);
41908
42371
  return true;
41909
- case "barnumberfont":
41910
- obj.barNumberFont = Font.fromJson(v);
41911
- return true;
41912
42372
  case "barnumbercolor":
41913
42373
  obj.barNumberColor = Color.fromJson(v);
41914
42374
  return true;
41915
- case "fingeringfont":
41916
- obj.fingeringFont = Font.fromJson(v);
41917
- return true;
41918
- case "inlinefingeringfont":
41919
- obj.inlineFingeringFont = Font.fromJson(v);
41920
- return true;
41921
- case "markerfont":
41922
- obj.markerFont = Font.fromJson(v);
41923
- return true;
41924
42375
  case "mainglyphcolor":
41925
42376
  obj.mainGlyphColor = Color.fromJson(v);
41926
42377
  return true;
@@ -41977,6 +42428,46 @@
41977
42428
  class RenderingResources {
41978
42429
  static _sansFont = 'Arial, sans-serif';
41979
42430
  static _serifFont = 'Georgia, serif';
42431
+ static _effectFont = new Font(RenderingResources._serifFont, 12, FontStyle.Italic);
42432
+ /**
42433
+ * The default fonts for notation elements if not specified by the user.
42434
+ */
42435
+ static defaultFonts = new Map([
42436
+ [exports.NotationElement.ScoreTitle, new Font(RenderingResources._serifFont, 32, FontStyle.Plain)],
42437
+ [exports.NotationElement.ScoreSubTitle, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42438
+ [exports.NotationElement.ScoreArtist, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42439
+ [exports.NotationElement.ScoreAlbum, new Font(RenderingResources._serifFont, 20, FontStyle.Plain)],
42440
+ [exports.NotationElement.ScoreWords, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42441
+ [exports.NotationElement.ScoreMusic, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42442
+ [exports.NotationElement.ScoreWordsAndMusic, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)],
42443
+ [exports.NotationElement.ScoreCopyright, new Font(RenderingResources._sansFont, 12, FontStyle.Plain, FontWeight.Bold)],
42444
+ [exports.NotationElement.EffectBeatTimer, new Font(RenderingResources._serifFont, 12, FontStyle.Plain)],
42445
+ [exports.NotationElement.EffectDirections, new Font(RenderingResources._serifFont, 14, FontStyle.Plain)],
42446
+ [exports.NotationElement.ChordDiagramFretboardNumbers, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42447
+ [exports.NotationElement.EffectFingering, new Font(RenderingResources._serifFont, 14, FontStyle.Plain)],
42448
+ [exports.NotationElement.EffectMarker, new Font(RenderingResources._serifFont, 14, FontStyle.Plain, FontWeight.Bold)],
42449
+ [exports.NotationElement.EffectCapo, RenderingResources._effectFont],
42450
+ [exports.NotationElement.EffectFreeTime, RenderingResources._effectFont],
42451
+ [exports.NotationElement.EffectLyrics, RenderingResources._effectFont],
42452
+ [exports.NotationElement.EffectTap, RenderingResources._effectFont],
42453
+ [exports.NotationElement.ChordDiagrams, RenderingResources._effectFont],
42454
+ [exports.NotationElement.EffectChordNames, RenderingResources._effectFont],
42455
+ [exports.NotationElement.EffectText, RenderingResources._effectFont],
42456
+ [exports.NotationElement.EffectPalmMute, RenderingResources._effectFont],
42457
+ [exports.NotationElement.EffectLetRing, RenderingResources._effectFont],
42458
+ [exports.NotationElement.EffectBeatBarre, RenderingResources._effectFont],
42459
+ [exports.NotationElement.EffectTripletFeel, RenderingResources._effectFont],
42460
+ [exports.NotationElement.EffectHarmonics, RenderingResources._effectFont],
42461
+ [exports.NotationElement.EffectPickSlide, RenderingResources._effectFont],
42462
+ [exports.NotationElement.GuitarTuning, RenderingResources._effectFont],
42463
+ [exports.NotationElement.EffectRasgueado, RenderingResources._effectFont],
42464
+ [exports.NotationElement.EffectWhammyBar, RenderingResources._effectFont],
42465
+ [exports.NotationElement.TrackNames, RenderingResources._effectFont],
42466
+ [exports.NotationElement.RepeatCount, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42467
+ [exports.NotationElement.BarNumber, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42468
+ [exports.NotationElement.ScoreBendSlur, new Font(RenderingResources._sansFont, 11, FontStyle.Plain)],
42469
+ [exports.NotationElement.EffectAlternateEndings, new Font(RenderingResources._serifFont, 15, FontStyle.Plain)]
42470
+ ]);
41980
42471
  /**
41981
42472
  * The name of the SMuFL Font to use for rendering music symbols.
41982
42473
  *
@@ -42003,50 +42494,168 @@
42003
42494
  * The font to use for displaying the songs copyright information in the header of the music sheet.
42004
42495
  * @defaultValue `bold 12px Arial, sans-serif`
42005
42496
  * @since 0.9.6
42497
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreCopyright}
42498
+ */
42499
+ get copyrightFont() {
42500
+ return this.elementFonts.get(exports.NotationElement.ScoreCopyright);
42501
+ }
42502
+ /**
42503
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreCopyright}
42006
42504
  */
42007
- copyrightFont = new Font(RenderingResources._sansFont, 12, FontStyle.Plain, FontWeight.Bold);
42505
+ set copyrightFont(value) {
42506
+ this.elementFonts.set(exports.NotationElement.ScoreCopyright, value);
42507
+ }
42008
42508
  /**
42009
42509
  * The font to use for displaying the songs title in the header of the music sheet.
42010
42510
  * @defaultValue `32px Georgia, serif`
42011
42511
  * @since 0.9.6
42512
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreTitle}
42513
+ */
42514
+ get titleFont() {
42515
+ return this.elementFonts.get(exports.NotationElement.ScoreTitle);
42516
+ }
42517
+ /**
42518
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreTitle}
42012
42519
  */
42013
- titleFont = new Font(RenderingResources._serifFont, 32, FontStyle.Plain);
42520
+ set titleFont(value) {
42521
+ this.elementFonts.set(exports.NotationElement.ScoreTitle, value);
42522
+ }
42014
42523
  /**
42015
42524
  * The font to use for displaying the songs subtitle in the header of the music sheet.
42016
42525
  * @defaultValue `20px Georgia, serif`
42017
42526
  * @since 0.9.6
42527
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreSubTitle}
42018
42528
  */
42019
- subTitleFont = new Font(RenderingResources._serifFont, 20, FontStyle.Plain);
42529
+ get subTitleFont() {
42530
+ return this.elementFonts.get(exports.NotationElement.ScoreSubTitle);
42531
+ }
42532
+ /**
42533
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreSubTitle}
42534
+ */
42535
+ set subTitleFont(value) {
42536
+ this.elementFonts.set(exports.NotationElement.ScoreSubTitle, value);
42537
+ }
42020
42538
  /**
42021
42539
  * The font to use for displaying the lyrics information in the header of the music sheet.
42022
42540
  * @defaultValue `15px Arial, sans-serif`
42023
42541
  * @since 0.9.6
42542
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreWords}
42024
42543
  */
42025
- wordsFont = new Font(RenderingResources._serifFont, 15, FontStyle.Plain);
42544
+ get wordsFont() {
42545
+ return this.elementFonts.get(exports.NotationElement.ScoreWords);
42546
+ }
42026
42547
  /**
42027
- * The font to use for displaying certain effect related elements in the music sheet.
42028
- * @defaultValue `italic 12px Georgia, serif`
42029
- * @since 0.9.6
42548
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ScoreWords}
42030
42549
  */
42031
- effectFont = new Font(RenderingResources._serifFont, 12, FontStyle.Italic);
42550
+ set wordsFont(value) {
42551
+ this.elementFonts.set(exports.NotationElement.ScoreWords, value);
42552
+ }
42032
42553
  /**
42033
42554
  * The font to use for displaying beat time information in the music sheet.
42034
42555
  * @defaultValue `12px Georgia, serif`
42035
42556
  * @since 1.4.0
42557
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectBeatTimer}
42036
42558
  */
42037
- timerFont = new Font(RenderingResources._serifFont, 12, FontStyle.Plain);
42559
+ get timerFont() {
42560
+ return this.elementFonts.get(exports.NotationElement.EffectBeatTimer);
42561
+ }
42562
+ /**
42563
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectBeatTimer}
42564
+ */
42565
+ set timerFont(value) {
42566
+ this.elementFonts.set(exports.NotationElement.EffectBeatTimer, value);
42567
+ }
42038
42568
  /**
42039
42569
  * The font to use for displaying the directions texts.
42040
42570
  * @defaultValue `14px Georgia, serif`
42041
42571
  * @since 1.4.0
42572
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectDirections}
42042
42573
  */
42043
- directionsFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain);
42574
+ get directionsFont() {
42575
+ return this.elementFonts.get(exports.NotationElement.EffectDirections);
42576
+ }
42577
+ /**
42578
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectDirections}
42579
+ */
42580
+ set directionsFont(value) {
42581
+ this.elementFonts.set(exports.NotationElement.EffectDirections, value);
42582
+ }
42044
42583
  /**
42045
42584
  * The font to use for displaying the fretboard numbers in chord diagrams.
42046
42585
  * @defaultValue `11px Arial, sans-serif`
42047
42586
  * @since 0.9.6
42587
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ChordDiagramFretboardNumbers}
42048
42588
  */
42049
- fretboardNumberFont = new Font(RenderingResources._sansFont, 11, FontStyle.Plain);
42589
+ get fretboardNumberFont() {
42590
+ return this.elementFonts.get(exports.NotationElement.ChordDiagramFretboardNumbers);
42591
+ }
42592
+ /**
42593
+ * @deprecated use {@link elementFonts} with {@link NotationElement.ChordDiagramFretboardNumbers}
42594
+ */
42595
+ set fretboardNumberFont(value) {
42596
+ this.elementFonts.set(exports.NotationElement.ChordDiagramFretboardNumbers, value);
42597
+ }
42598
+ /**
42599
+ * Unused, see deprecation note.
42600
+ * @defaultValue `14px Georgia, serif`
42601
+ * @since 0.9.6
42602
+ * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42603
+ * @json_ignore
42604
+ */
42605
+ fingeringFont = RenderingResources._effectFont;
42606
+ /**
42607
+ * Unused, see deprecation note.
42608
+ * @defaultValue `12px Georgia, serif`
42609
+ * @since 1.4.0
42610
+ * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42611
+ * @json_ignore
42612
+ */
42613
+ inlineFingeringFont = RenderingResources._effectFont;
42614
+ /**
42615
+ * The font to use for section marker labels shown above the music sheet.
42616
+ * @defaultValue `bold 14px Georgia, serif`
42617
+ * @since 0.9.6
42618
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectMarker}
42619
+ */
42620
+ get markerFont() {
42621
+ return this.elementFonts.get(exports.NotationElement.EffectMarker);
42622
+ }
42623
+ /**
42624
+ * @deprecated use {@link elementFonts} with {@link NotationElement.EffectMarker}
42625
+ */
42626
+ set markerFont(value) {
42627
+ this.elementFonts.set(exports.NotationElement.EffectMarker, value);
42628
+ }
42629
+ /**
42630
+ * Ununsed, see deprecation note.
42631
+ * @defaultValue `italic 12px Georgia, serif`
42632
+ * @since 0.9.6
42633
+ * @deprecated use {@link elementFonts} with the respective
42634
+ * @json_ignore
42635
+ */
42636
+ effectFont = RenderingResources._effectFont;
42637
+ /**
42638
+ * The font to use for displaying the bar numbers above the music sheet.
42639
+ * @defaultValue `11px Arial, sans-serif`
42640
+ * @since 0.9.6
42641
+ * @deprecated use {@link elementFonts} with {@link NotationElement.BarNumber}
42642
+ */
42643
+ get barNumberFont() {
42644
+ return this.elementFonts.get(exports.NotationElement.BarNumber);
42645
+ }
42646
+ /**
42647
+ * @deprecated use {@link elementFonts} with {@link NotationElement.BarNumber}
42648
+ */
42649
+ set barNumberFont(value) {
42650
+ this.elementFonts.set(exports.NotationElement.BarNumber, value);
42651
+ }
42652
+ // NOTE: the main staff fonts are still own properties.
42653
+ /**
42654
+ * The fonts used by individual elements. Check {@link defaultFonts} for the elements which have custom fonts.
42655
+ * Removing fonts from this map can lead to unexpected side effects and errors. Only update it with new values.
42656
+ * @json_immutable
42657
+ */
42658
+ elementFonts = new Map();
42050
42659
  /**
42051
42660
  * The font to use for displaying the numbered music notation in the music sheet.
42052
42661
  * @defaultValue `14px Arial, sans-serif`
@@ -42083,38 +42692,12 @@
42083
42692
  * @since 0.9.6
42084
42693
  */
42085
42694
  barSeparatorColor = new Color(34, 34, 17, 0xff);
42086
- /**
42087
- * The font to use for displaying the bar numbers above the music sheet.
42088
- * @defaultValue `11px Arial, sans-serif`
42089
- * @since 0.9.6
42090
- */
42091
- barNumberFont = new Font(RenderingResources._sansFont, 11, FontStyle.Plain);
42092
42695
  /**
42093
42696
  * The color to use for displaying the bar numbers above the music sheet.
42094
42697
  * @defaultValue `rgb(200, 0, 0)`
42095
42698
  * @since 0.9.6
42096
42699
  */
42097
42700
  barNumberColor = new Color(200, 0, 0, 0xff);
42098
- /**
42099
- * The font to use for displaying finger information in the music sheet.
42100
- * @defaultValue `14px Georgia, serif`
42101
- * @since 0.9.6
42102
- * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42103
- */
42104
- fingeringFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain);
42105
- /**
42106
- * The font to use for displaying finger information when inline into the music sheet.
42107
- * @defaultValue `12px Georgia, serif`
42108
- * @since 1.4.0
42109
- * @deprecated Since 1.7.0 alphaTab uses the glyphs contained in the SMuFL font
42110
- */
42111
- inlineFingeringFont = new Font(RenderingResources._serifFont, 12, FontStyle.Plain);
42112
- /**
42113
- * The font to use for section marker labels shown above the music sheet.
42114
- * @defaultValue `bold 14px Georgia, serif`
42115
- * @since 0.9.6
42116
- */
42117
- markerFont = new Font(RenderingResources._serifFont, 14, FontStyle.Plain, FontWeight.Bold);
42118
42701
  /**
42119
42702
  * The color to use for music notation elements of the primary voice.
42120
42703
  * @defaultValue `rgb(0, 0, 0)`
@@ -42133,28 +42716,50 @@
42133
42716
  * @since 0.9.6
42134
42717
  */
42135
42718
  scoreInfoColor = new Color(0, 0, 0, 0xff);
42719
+ constructor() {
42720
+ for (const [k, v] of RenderingResources.defaultFonts) {
42721
+ this.elementFonts.set(k, v.withSize(v.size));
42722
+ }
42723
+ }
42136
42724
  /**
42137
42725
  * @internal
42138
42726
  * @param element
42139
42727
  */
42140
42728
  getFontForElement(element) {
42729
+ let notationElement = exports.NotationElement.ScoreWords;
42141
42730
  switch (element) {
42142
42731
  case ScoreSubElement.Title:
42143
- return this.titleFont;
42732
+ notationElement = exports.NotationElement.ScoreTitle;
42733
+ break;
42144
42734
  case ScoreSubElement.SubTitle:
42735
+ notationElement = exports.NotationElement.ScoreSubTitle;
42736
+ break;
42145
42737
  case ScoreSubElement.Artist:
42738
+ notationElement = exports.NotationElement.ScoreArtist;
42739
+ break;
42146
42740
  case ScoreSubElement.Album:
42147
- return this.subTitleFont;
42741
+ notationElement = exports.NotationElement.ScoreAlbum;
42742
+ break;
42148
42743
  case ScoreSubElement.Words:
42744
+ notationElement = exports.NotationElement.ScoreWords;
42745
+ break;
42149
42746
  case ScoreSubElement.Music:
42747
+ notationElement = exports.NotationElement.ScoreMusic;
42748
+ break;
42150
42749
  case ScoreSubElement.WordsAndMusic:
42151
- case ScoreSubElement.Transcriber:
42152
- return this.wordsFont;
42750
+ notationElement = exports.NotationElement.ScoreWordsAndMusic;
42751
+ break;
42153
42752
  case ScoreSubElement.Copyright:
42154
42753
  case ScoreSubElement.CopyrightSecondLine:
42155
- return this.copyrightFont;
42754
+ notationElement = exports.NotationElement.ScoreCopyright;
42755
+ break;
42756
+ default:
42757
+ notationElement = exports.NotationElement.ScoreWords;
42758
+ break;
42156
42759
  }
42157
- return this.wordsFont;
42760
+ return this.elementFonts.has(notationElement)
42761
+ ? this.elementFonts.get(notationElement)
42762
+ : RenderingResources.defaultFonts.get(exports.NotationElement.ScoreWords);
42158
42763
  }
42159
42764
  }
42160
42765
 
@@ -42459,6 +43064,13 @@
42459
43064
  * @defaultValue `5`
42460
43065
  */
42461
43066
  trackStaffPaddingBetween = 5;
43067
+ /**
43068
+ * The additional padding to apply between multiple lyric lines.
43069
+ * @since 1.8.0
43070
+ * @category Display
43071
+ * @defaultValue `5`
43072
+ */
43073
+ lyricLinesPaddingBetween = 5;
42462
43074
  /**
42463
43075
  * The mode used to arrange staves and systems.
42464
43076
  * @since 1.3.0
@@ -42569,6 +43181,7 @@
42569
43181
  o.set("staffpaddingleft", obj.staffPaddingLeft);
42570
43182
  o.set("effectbandpaddingbottom", obj.effectBandPaddingBottom);
42571
43183
  o.set("trackstaffpaddingbetween", obj.trackStaffPaddingBetween);
43184
+ o.set("lyriclinespaddingbetween", obj.lyricLinesPaddingBetween);
42572
43185
  o.set("systemslayoutmode", obj.systemsLayoutMode);
42573
43186
  return o;
42574
43187
  }
@@ -42655,6 +43268,9 @@
42655
43268
  case "trackstaffpaddingbetween":
42656
43269
  obj.trackStaffPaddingBetween = v;
42657
43270
  return true;
43271
+ case "lyriclinespaddingbetween":
43272
+ obj.lyricLinesPaddingBetween = v;
43273
+ return true;
42658
43274
  case "systemslayoutmode":
42659
43275
  obj.systemsLayoutMode = JsonHelper.parseEnum(v, exports.SystemsLayoutMode);
42660
43276
  return true;
@@ -55363,23 +55979,20 @@
55363
55979
  this._contents = '';
55364
55980
  const element = api.container;
55365
55981
  if (settings.core.tex) {
55366
- this._contents = element.element.innerText;
55982
+ this._contents = element.element.textContent;
55367
55983
  element.element.innerText = '';
55368
55984
  }
55369
55985
  this._createStyleElements(settings);
55370
55986
  this._file = settings.core.file;
55371
55987
  }
55372
55988
  _setupFontCheckers(settings) {
55373
- this._registerFontChecker(settings.display.resources.copyrightFont);
55374
- this._registerFontChecker(settings.display.resources.effectFont);
55989
+ for (const font of settings.display.resources.elementFonts.values()) {
55990
+ this._registerFontChecker(font);
55991
+ }
55375
55992
  this._registerFontChecker(settings.display.resources.graceFont);
55376
- this._registerFontChecker(settings.display.resources.markerFont);
55377
55993
  this._registerFontChecker(settings.display.resources.tablatureFont);
55378
- this._registerFontChecker(settings.display.resources.titleFont);
55379
- this._registerFontChecker(settings.display.resources.wordsFont);
55380
- this._registerFontChecker(settings.display.resources.barNumberFont);
55381
- this._registerFontChecker(settings.display.resources.fretboardNumberFont);
55382
- this._registerFontChecker(settings.display.resources.subTitleFont);
55994
+ this._registerFontChecker(settings.display.resources.numberedNotationFont);
55995
+ this._registerFontChecker(settings.display.resources.numberedNotationGraceFont);
55383
55996
  }
55384
55997
  _registerFontChecker(font) {
55385
55998
  if (!this._fontCheckers.has(font.families.join(', '))) {
@@ -57216,7 +57829,9 @@
57216
57829
  }
57217
57830
  doLayout() {
57218
57831
  super.doLayout();
57219
- this.height = this.renderer.resources.wordsFont.size + this.renderer.smuflMetrics.alternateEndingsPadding * 2;
57832
+ this.height =
57833
+ this.renderer.resources.elementFonts.get(exports.NotationElement.EffectAlternateEndings).size +
57834
+ this.renderer.smuflMetrics.alternateEndingsPadding * 2;
57220
57835
  let endingsStrings = '';
57221
57836
  for (let i = 0, j = this._endings.length; i < j; i++) {
57222
57837
  endingsStrings += this._endings[i] + 1;
@@ -57252,7 +57867,7 @@
57252
57867
  const baseline = canvas.textBaseline;
57253
57868
  canvas.textBaseline = TextBaseline.Top;
57254
57869
  const res = this.renderer.resources;
57255
- canvas.font = res.wordsFont;
57870
+ canvas.font = res.elementFonts.get(exports.NotationElement.EffectAlternateEndings);
57256
57871
  canvas.fillText(this._endingsString, cx + this.x + this.renderer.smuflMetrics.alternateEndingsPadding, cy + this.y + this.renderer.smuflMetrics.alternateEndingsPadding);
57257
57872
  canvas.textBaseline = baseline;
57258
57873
  }
@@ -57394,10 +58009,12 @@
57394
58009
  _label;
57395
58010
  _dashed;
57396
58011
  _labelWidth = 0;
57397
- constructor(label, dashed = true) {
58012
+ _fontElement;
58013
+ constructor(label, fontElement, dashed = true) {
57398
58014
  super(BeatXPosition.OnNotes);
57399
58015
  this._label = label;
57400
58016
  this._dashed = dashed;
58017
+ this._fontElement = fontElement;
57401
58018
  }
57402
58019
  doLayout() {
57403
58020
  if (this.renderer.settings.notation.extendLineEffectsToBeatEnd) {
@@ -57405,14 +58022,14 @@
57405
58022
  this.forceGroupedRendering = true;
57406
58023
  }
57407
58024
  super.doLayout();
57408
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.effectFont;
58025
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(this._fontElement);
57409
58026
  const size = this.renderer.scoreRenderer.canvas.measureText(this._label);
57410
58027
  this.height = size.height;
57411
58028
  this._labelWidth = size.width;
57412
58029
  }
57413
58030
  paintNonGrouped(cx, cy, canvas) {
57414
58031
  const res = this.renderer.resources;
57415
- canvas.font = res.effectFont;
58032
+ canvas.font = res.elementFonts.get(this._fontElement);
57416
58033
  const b = canvas.textBaseline;
57417
58034
  canvas.textBaseline = TextBaseline.Middle;
57418
58035
  canvas.fillText(this._label, cx + this.x - this._labelWidth / 2, cy + this.y + this.height / 2);
@@ -57473,7 +58090,7 @@
57473
58090
  break;
57474
58091
  }
57475
58092
  barre += `B ${BeatBarreEffectInfo.toRoman(beat.barreFret)}`;
57476
- return new LineRangedGlyph(barre, false);
58093
+ return new LineRangedGlyph(barre, exports.NotationElement.EffectBeatBarre, false);
57477
58094
  }
57478
58095
  static _romanLetters = new Map([
57479
58096
  // ['M', 1000],
@@ -57523,7 +58140,7 @@
57523
58140
  const seconds = ((this._timer - minutes * 60000) / 1000) | 0;
57524
58141
  this._text = `${minutes}:${seconds.toString().padStart(2, '0')}`;
57525
58142
  const c = this.renderer.scoreRenderer.canvas;
57526
- c.font = this.renderer.resources.timerFont;
58143
+ c.font = this.renderer.resources.elementFonts.get(exports.NotationElement.EffectBeatTimer);
57527
58144
  const size = c.measureText(this._text);
57528
58145
  this._textHeight = c.font.size + this.renderer.smuflMetrics.beatTimerPadding * 2;
57529
58146
  this._textWidth = size.width + this.renderer.smuflMetrics.beatTimerPadding * 2;
@@ -57535,7 +58152,7 @@
57535
58152
  const f = canvas.font;
57536
58153
  const b = canvas.textBaseline;
57537
58154
  const a = canvas.textAlign;
57538
- canvas.font = this.renderer.resources.timerFont;
58155
+ canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.EffectBeatTimer);
57539
58156
  canvas.textBaseline = TextBaseline.Middle;
57540
58157
  canvas.textAlign = TextAlign.Center;
57541
58158
  canvas.fillText(this._text, cx + this.x, cy + this.y + this.height / 2);
@@ -57644,7 +58261,7 @@
57644
58261
  return beat.index === 0 && beat.voice.bar.index === 0 && beat.voice.bar.staff.capo !== 0;
57645
58262
  }
57646
58263
  createNewGlyph(renderer, beat) {
57647
- return new TextGlyph(0, 0, `Capo. fret ${beat.voice.bar.staff.capo}`, renderer.resources.effectFont, TextAlign.Left);
58264
+ return new TextGlyph(0, 0, `Capo. fret ${beat.voice.bar.staff.capo}`, renderer.resources.elementFonts.get(exports.NotationElement.EffectCapo), TextAlign.Left);
57648
58265
  }
57649
58266
  canExpand(_from, _to) {
57650
58267
  return false;
@@ -57661,31 +58278,41 @@
57661
58278
  _fretRow = 0;
57662
58279
  _firstFretSpacing = 0;
57663
58280
  _center;
57664
- constructor(x, y, chord, center = false) {
58281
+ _fontElement;
58282
+ constructor(x, y, chord, fontElement, center = false) {
57665
58283
  super(x, y);
57666
58284
  this._chord = chord;
57667
58285
  this._center = center;
58286
+ this._fontElement = fontElement;
57668
58287
  }
57669
58288
  doLayout() {
57670
58289
  super.doLayout();
57671
58290
  const res = this.renderer.resources;
57672
- this._textRow = res.effectFont.size * 1.5;
57673
- this._fretRow = res.effectFont.size * 1.5;
57674
- if (this._chord.firstFret > 1) {
57675
- this._firstFretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
57676
- }
57677
- else {
57678
- this._firstFretSpacing = 0;
57679
- }
57680
- this.height =
57681
- this._textRow +
58291
+ const font = res.elementFonts.get(this._fontElement);
58292
+ this._textRow = font.size * 1.5;
58293
+ this._fretRow = font.size * 1.5;
58294
+ this.height = this._textRow;
58295
+ this.width = 2 * this.renderer.smuflMetrics.chordDiagramPaddingX;
58296
+ if (this.renderer.settings.notation.isNotationElementVisible(exports.NotationElement.ChordDiagramFretboardNumbers)) {
58297
+ if (this._chord.firstFret > 1) {
58298
+ this._firstFretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
58299
+ }
58300
+ else {
58301
+ this._firstFretSpacing = 0;
58302
+ }
58303
+ this.height +=
57682
58304
  this._fretRow +
57683
- ChordDiagramGlyph._frets * this.renderer.smuflMetrics.chordDiagramFretSpacing +
57684
- 2 * this.renderer.smuflMetrics.chordDiagramPaddingY;
57685
- this.width =
57686
- this._firstFretSpacing +
57687
- (this._chord.strings.length - 1) * this.renderer.smuflMetrics.chordDiagramStringSpacing +
57688
- 2 * this.renderer.smuflMetrics.chordDiagramPaddingX;
58305
+ ChordDiagramGlyph._frets * this.renderer.smuflMetrics.chordDiagramFretSpacing +
58306
+ 2 * this.renderer.smuflMetrics.chordDiagramPaddingY;
58307
+ this.width +=
58308
+ this._firstFretSpacing +
58309
+ (this._chord.strings.length - 1) * this.renderer.smuflMetrics.chordDiagramStringSpacing;
58310
+ }
58311
+ else if (this._chord.showName) {
58312
+ const canvas = this.renderer.scoreRenderer.canvas;
58313
+ canvas.font = font;
58314
+ this.width += canvas.measureText(this._chord.name).width;
58315
+ }
57689
58316
  }
57690
58317
  paint(cx, cy, canvas) {
57691
58318
  cx += this.x + this.renderer.smuflMetrics.chordDiagramPaddingX + this._firstFretSpacing;
@@ -57693,25 +58320,35 @@
57693
58320
  if (this._center) {
57694
58321
  cx -= this.width / 2;
57695
58322
  }
57696
- const stringSpacing = this.renderer.smuflMetrics.chordDiagramStringSpacing;
57697
- const fretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
57698
58323
  const res = this.renderer.resources;
57699
58324
  const lineWidth = res.engravingSettings.chordDiagramLineWidth;
57700
58325
  const w = this.width - 2 * this.renderer.smuflMetrics.chordDiagramPaddingX - this._firstFretSpacing + lineWidth;
57701
- const circleHeight = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardFilledCircle);
57702
- const circleTopOffset = res.engravingSettings.glyphTop.get(MusicFontSymbol.FretboardFilledCircle);
57703
- const xTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardX) / 2;
57704
- const oTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardO) / 2;
57705
58326
  const align = canvas.textAlign;
57706
58327
  const baseline = canvas.textBaseline;
57707
- canvas.font = res.effectFont;
58328
+ const font = res.elementFonts.get(this._fontElement);
58329
+ canvas.font = font;
57708
58330
  canvas.textAlign = TextAlign.Center;
57709
58331
  canvas.textBaseline = TextBaseline.Top;
57710
58332
  if (this._chord.showName) {
57711
- canvas.fillText(this._chord.name, cx + w / 2, cy + res.effectFont.size / 2);
58333
+ canvas.fillText(this._chord.name, cx + w / 2, cy + font.size / 2);
57712
58334
  }
58335
+ if (this.renderer.settings.notation.isNotationElementVisible(exports.NotationElement.ChordDiagramFretboardNumbers)) {
58336
+ this._paintFretboard(cx, cy, canvas, w);
58337
+ }
58338
+ canvas.textAlign = align;
58339
+ canvas.textBaseline = baseline;
58340
+ }
58341
+ _paintFretboard(cx, cy, canvas, w) {
57713
58342
  cy += this._textRow;
57714
- canvas.font = res.fretboardNumberFont;
58343
+ const res = this.renderer.resources;
58344
+ const stringSpacing = this.renderer.smuflMetrics.chordDiagramStringSpacing;
58345
+ const fretSpacing = this.renderer.smuflMetrics.chordDiagramFretSpacing;
58346
+ const circleHeight = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardFilledCircle);
58347
+ const circleTopOffset = res.engravingSettings.glyphTop.get(MusicFontSymbol.FretboardFilledCircle);
58348
+ const xTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardX) / 2;
58349
+ const oTopOffset = res.engravingSettings.glyphHeights.get(MusicFontSymbol.FretboardO) / 2;
58350
+ const lineWidth = res.engravingSettings.chordDiagramLineWidth;
58351
+ canvas.font = res.elementFonts.get(exports.NotationElement.ChordDiagramFretboardNumbers);
57715
58352
  canvas.textBaseline = TextBaseline.Middle;
57716
58353
  for (let i = 0; i < this._chord.strings.length; i++) {
57717
58354
  const x = cx + i * stringSpacing;
@@ -57774,8 +58411,6 @@
57774
58411
  const xRight = cx + (this._chord.strings.length - strings[0] - 1) * stringSpacing;
57775
58412
  canvas.fillRect(xLeft, y - circleHeight / 2, xRight - xLeft, circleHeight);
57776
58413
  }
57777
- canvas.textAlign = align;
57778
- canvas.textBaseline = baseline;
57779
58414
  }
57780
58415
  }
57781
58416
 
@@ -57801,8 +58436,8 @@
57801
58436
  createNewGlyph(renderer, beat) {
57802
58437
  const showDiagram = beat.voice.bar.staff.track.score.stylesheet.globalDisplayChordDiagramsInScore;
57803
58438
  return showDiagram
57804
- ? new ChordDiagramGlyph(0, 0, beat.chord, true)
57805
- : new TextGlyph(0, 0, beat.chord.name, renderer.resources.effectFont, TextAlign.Center);
58439
+ ? new ChordDiagramGlyph(0, 0, beat.chord, exports.NotationElement.EffectChordNames, true)
58440
+ : new TextGlyph(0, 0, beat.chord.name, renderer.resources.elementFonts.get(exports.NotationElement.EffectChordNames), TextAlign.Center);
57806
58441
  }
57807
58442
  canExpand(_from, _to) {
57808
58443
  return false;
@@ -57913,14 +58548,14 @@
57913
58548
  }
57914
58549
  doLayout() {
57915
58550
  const c = this.renderer.scoreRenderer.canvas;
57916
- c.font = this.renderer.resources.directionsFont;
58551
+ c.font = this.renderer.resources.elementFonts.get(exports.NotationElement.EffectDirections);
57917
58552
  this.height = c.measureText(this._text).height;
57918
58553
  }
57919
58554
  paint(cx, cy, canvas) {
57920
58555
  const font = canvas.font;
57921
58556
  const baseline = canvas.textBaseline;
57922
58557
  const align = canvas.textAlign;
57923
- canvas.font = this.renderer.resources.directionsFont;
58558
+ canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.EffectDirections);
57924
58559
  canvas.textBaseline = TextBaseline.Middle;
57925
58560
  canvas.textAlign = TextAlign.Right;
57926
58561
  canvas.fillText(this._text, cx + this.x, cy + this.y + this.height / 2);
@@ -58816,7 +59451,7 @@
58816
59451
  (masterBar.index === 0 || masterBar.isFreeTime !== masterBar.previousMasterBar.isFreeTime));
58817
59452
  }
58818
59453
  createNewGlyph(renderer, _beat) {
58819
- return new TextGlyph(0, 0, 'Free time', renderer.resources.effectFont, TextAlign.Left);
59454
+ return new TextGlyph(0, 0, 'Free time', renderer.resources.elementFonts.get(exports.NotationElement.EffectFreeTime), TextAlign.Left);
58820
59455
  }
58821
59456
  canExpand(_from, _to) {
58822
59457
  return true;
@@ -58954,7 +59589,7 @@
58954
59589
  return EffectBarGlyphSizing.GroupedOnBeat;
58955
59590
  }
58956
59591
  createNewGlyph(_renderer, _beat) {
58957
- return new LineRangedGlyph(HarmonicsEffectInfo.harmonicToString(this._harmonicType));
59592
+ return new LineRangedGlyph(HarmonicsEffectInfo.harmonicToString(this._harmonicType), exports.NotationElement.EffectHarmonics);
58958
59593
  }
58959
59594
  static harmonicToString(type) {
58960
59595
  switch (type) {
@@ -59027,7 +59662,7 @@
59027
59662
  return EffectBarGlyphSizing.GroupedOnBeat;
59028
59663
  }
59029
59664
  createNewGlyph(_renderer, _beat) {
59030
- return new LineRangedGlyph('LetRing');
59665
+ return new LineRangedGlyph('LetRing', exports.NotationElement.EffectLetRing);
59031
59666
  }
59032
59667
  canExpand(_from, _to) {
59033
59668
  return true;
@@ -59039,6 +59674,7 @@
59039
59674
  */
59040
59675
  class LyricsGlyph extends EffectGlyph {
59041
59676
  _lines;
59677
+ _linePositions = [];
59042
59678
  font;
59043
59679
  textAlign;
59044
59680
  constructor(x, y, lines, font, textAlign = TextAlign.Center) {
@@ -59049,7 +59685,17 @@
59049
59685
  }
59050
59686
  doLayout() {
59051
59687
  super.doLayout();
59052
- this.height = this.font.size * this._lines.length;
59688
+ const lineSpacing = this.renderer.settings.display.lyricLinesPaddingBetween;
59689
+ const canvas = this.renderer.scoreRenderer.canvas;
59690
+ canvas.font = this.font;
59691
+ let y = 0;
59692
+ for (const line of this._lines) {
59693
+ this._linePositions.push(y);
59694
+ const size = canvas.measureText(line.length > 0 ? line : ' ');
59695
+ y += size.height + lineSpacing;
59696
+ }
59697
+ y -= lineSpacing;
59698
+ this.height = y;
59053
59699
  }
59054
59700
  paint(cx, cy, canvas) {
59055
59701
  canvas.font = this.font;
@@ -59057,7 +59703,7 @@
59057
59703
  canvas.textAlign = this.textAlign;
59058
59704
  for (let i = 0; i < this._lines.length; i++) {
59059
59705
  if (this._lines[i]) {
59060
- canvas.fillText(this._lines[i], cx + this.x, cy + this.y + i * this.font.size);
59706
+ canvas.fillText(this._lines[i], cx + this.x, cy + this.y + this._linePositions[i]);
59061
59707
  }
59062
59708
  }
59063
59709
  canvas.textAlign = old;
@@ -59084,7 +59730,7 @@
59084
59730
  return !!beat.lyrics;
59085
59731
  }
59086
59732
  createNewGlyph(renderer, beat) {
59087
- return new LyricsGlyph(0, 0, beat.lyrics, renderer.resources.effectFont, TextAlign.Center);
59733
+ return new LyricsGlyph(0, 0, beat.lyrics, renderer.resources.elementFonts.get(exports.NotationElement.EffectLyrics), TextAlign.Center);
59088
59734
  }
59089
59735
  canExpand(_from, _to) {
59090
59736
  return true;
@@ -59116,7 +59762,7 @@
59116
59762
  createNewGlyph(renderer, beat) {
59117
59763
  return new TextGlyph(0, 0, !beat.voice.bar.masterBar.section.marker
59118
59764
  ? beat.voice.bar.masterBar.section.text
59119
- : `[${beat.voice.bar.masterBar.section.marker}] ${beat.voice.bar.masterBar.section.text}`, renderer.resources.markerFont, TextAlign.Left);
59765
+ : `[${beat.voice.bar.masterBar.section.marker}] ${beat.voice.bar.masterBar.section.text}`, renderer.resources.elementFonts.get(exports.NotationElement.EffectMarker), TextAlign.Left);
59120
59766
  }
59121
59767
  canExpand(_from, _to) {
59122
59768
  return true;
@@ -59555,7 +60201,7 @@
59555
60201
  return EffectBarGlyphSizing.GroupedOnBeat;
59556
60202
  }
59557
60203
  createNewGlyph(_renderer, _beat) {
59558
- return new LineRangedGlyph('P.M.');
60204
+ return new LineRangedGlyph('P.M.', exports.NotationElement.EffectPalmMute);
59559
60205
  }
59560
60206
  }
59561
60207
 
@@ -59573,7 +60219,7 @@
59573
60219
  return EffectBarGlyphSizing.GroupedOnBeat;
59574
60220
  }
59575
60221
  createNewGlyph(_renderer, _beat) {
59576
- return new LineRangedGlyph('P.S.');
60222
+ return new LineRangedGlyph('P.S.', exports.NotationElement.EffectPickSlide);
59577
60223
  }
59578
60224
  }
59579
60225
 
@@ -59648,7 +60294,7 @@
59648
60294
  return EffectBarGlyphSizing.GroupedOnBeat;
59649
60295
  }
59650
60296
  createNewGlyph(_renderer, _beat) {
59651
- return new LineRangedGlyph('rasg.');
60297
+ return new LineRangedGlyph('rasg.', exports.NotationElement.EffectRasgueado);
59652
60298
  }
59653
60299
  canExpand(_from, _to) {
59654
60300
  return true;
@@ -61154,6 +61800,7 @@
61154
61800
  class BeamingHelper {
61155
61801
  _staff;
61156
61802
  _renderer;
61803
+ _beamingRuleLookup;
61157
61804
  voice = null;
61158
61805
  beats = [];
61159
61806
  shortestDuration = Duration.QuadrupleWhole;
@@ -61187,10 +61834,11 @@
61187
61834
  static beatHasFlag(beat) {
61188
61835
  return (!beat.deadSlapped && !beat.isRest && (beat.duration > Duration.Quarter || beat.graceType !== GraceType.None));
61189
61836
  }
61190
- constructor(staff, renderer) {
61837
+ constructor(staff, renderer, beamingRuleLookup) {
61191
61838
  this._staff = staff;
61192
61839
  this._renderer = renderer;
61193
61840
  this.beats = [];
61841
+ this._beamingRuleLookup = beamingRuleLookup;
61194
61842
  }
61195
61843
  alignWithBeats() {
61196
61844
  for (const v of this.drawingInfos.values()) {
@@ -61245,7 +61893,7 @@
61245
61893
  switch (this.beats[this.beats.length - 1].beamingMode) {
61246
61894
  case BeatBeamingMode.Auto:
61247
61895
  case BeatBeamingMode.ForceSplitOnSecondaryToNext:
61248
- add = BeamingHelper._canJoin(this.beats[this.beats.length - 1], beat);
61896
+ add = this._canJoin(this.beats[this.beats.length - 1], beat);
61249
61897
  break;
61250
61898
  case BeatBeamingMode.ForceSplitToNext:
61251
61899
  add = false;
@@ -61317,8 +61965,7 @@
61317
61965
  this._highestNoteCompareValueInHelper = highestValueForNote;
61318
61966
  }
61319
61967
  }
61320
- // TODO: Check if this beaming is really correct, I'm not sure if we are connecting beats correctly
61321
- static _canJoin(b1, b2) {
61968
+ _canJoin(b1, b2) {
61322
61969
  // is this a voice we can join with?
61323
61970
  if (!b1 ||
61324
61971
  !b2 ||
@@ -61356,19 +62003,10 @@
61356
62003
  return true;
61357
62004
  }
61358
62005
  }
61359
- // TODO: create more rules for automatic beaming
61360
- let divisionLength = MidiUtils.QuarterTime;
61361
- switch (m1.masterBar.timeSignatureDenominator) {
61362
- case 8:
61363
- if (m1.masterBar.timeSignatureNumerator % 3 === 0) {
61364
- divisionLength += (MidiUtils.QuarterTime / 2) | 0;
61365
- }
61366
- break;
61367
- }
61368
- // check if they are on the same division
61369
- const division1 = ((divisionLength + start1) / divisionLength) | 0 | 0;
61370
- const division2 = ((divisionLength + start2) / divisionLength) | 0 | 0;
61371
- return division1 === division2;
62006
+ // check if they are on the same group as per rule definitions
62007
+ const groupId1 = this._beamingRuleLookup.calculateGroupIndex(start1);
62008
+ const groupId2 = this._beamingRuleLookup.calculateGroupIndex(start2);
62009
+ return groupId1 === groupId2;
61372
62010
  }
61373
62011
  static _canJoinDuration(d) {
61374
62012
  switch (d) {
@@ -61535,6 +62173,61 @@
61535
62173
  }
61536
62174
  }
61537
62175
 
62176
+ /**
62177
+ * @internal
62178
+ */
62179
+ class BeamingRuleLookup {
62180
+ _division = 0;
62181
+ _slots = [];
62182
+ _barDuration;
62183
+ constructor(barDuration, division, slots) {
62184
+ this._division = division;
62185
+ this._slots = slots;
62186
+ this._barDuration = barDuration;
62187
+ }
62188
+ calculateGroupIndex(beatStartTime) {
62189
+ // no slots -> all have their own group based (use the start time as index)
62190
+ if (this._slots.length === 0) {
62191
+ return beatStartTime;
62192
+ }
62193
+ // rollover within the bar.
62194
+ beatStartTime = beatStartTime % this._barDuration;
62195
+ const slotIndex = Math.floor(beatStartTime / this._division);
62196
+ return this._slots[slotIndex];
62197
+ }
62198
+ static build(masterBar, ruleDuration, ruleGroups) {
62199
+ const totalDuration = masterBar.calculateDuration(false);
62200
+ const division = MidiUtils.toTicks(ruleDuration);
62201
+ const slotCount = totalDuration / division;
62202
+ // should only happen in case of improper data.
62203
+ if (slotCount < 0 || ruleGroups.length === 0) {
62204
+ return new BeamingRuleLookup(0, 0, []);
62205
+ }
62206
+ let groupIndex = 0;
62207
+ let remainingSlots = ruleGroups[groupIndex];
62208
+ const slots = [];
62209
+ for (let i = 0; i < slotCount; i++) {
62210
+ if (groupIndex < ruleGroups.length) {
62211
+ slots.push(groupIndex);
62212
+ remainingSlots--;
62213
+ if (remainingSlots <= 0) {
62214
+ groupIndex++;
62215
+ if (groupIndex < ruleGroups.length) {
62216
+ remainingSlots = ruleGroups[groupIndex];
62217
+ }
62218
+ }
62219
+ }
62220
+ else {
62221
+ // no groups defined for the remaining slots: all slots are treated
62222
+ // as unjoined
62223
+ slots.push(groupIndex);
62224
+ groupIndex++;
62225
+ }
62226
+ }
62227
+ return new BeamingRuleLookup(totalDuration, division, slots);
62228
+ }
62229
+ }
62230
+
61538
62231
  /**
61539
62232
  * @internal
61540
62233
  */
@@ -61551,6 +62244,19 @@
61551
62244
  initialize() {
61552
62245
  const barRenderer = this._renderer;
61553
62246
  const bar = this._renderer.bar;
62247
+ const masterBar = bar.masterBar;
62248
+ const beamingRules = masterBar.actualBeamingRules ?? BarHelpers._findOrBuildDefaultBeamingRules(masterBar);
62249
+ const rule = beamingRules.findRule(bar.shortestDuration);
62250
+ // NOTE: moste rules have only one group definition, so its better to reuse the unique id
62251
+ // than compute a potentially shorter id here.
62252
+ const key = `beaming_${beamingRules.uniqueId}_${rule[0]}`;
62253
+ let beamingRuleLookup = this._renderer.scoreRenderer.layout.beamingRuleLookups.has(key)
62254
+ ? this._renderer.scoreRenderer.layout.beamingRuleLookups.get(key)
62255
+ : undefined;
62256
+ if (!beamingRuleLookup) {
62257
+ beamingRuleLookup = BeamingRuleLookup.build(masterBar, rule[0], rule[1]);
62258
+ this._renderer.scoreRenderer.layout.beamingRuleLookups.set(key, beamingRuleLookup);
62259
+ }
61554
62260
  let currentBeamHelper = null;
61555
62261
  let currentGraceBeamHelper = null;
61556
62262
  for (let i = 0, j = bar.voices.length; i < j; i++) {
@@ -61576,7 +62282,7 @@
61576
62282
  helperForBeat.finish();
61577
62283
  }
61578
62284
  // if not possible, create the next beaming helper
61579
- helperForBeat = new BeamingHelper(bar.staff, barRenderer);
62285
+ helperForBeat = new BeamingHelper(bar.staff, barRenderer, beamingRuleLookup);
61580
62286
  helperForBeat.preferredBeamDirection = this.preferredBeamDirection;
61581
62287
  helperForBeat.checkBeat(b);
61582
62288
  if (b.graceType !== GraceType.None) {
@@ -61599,6 +62305,69 @@
61599
62305
  currentGraceBeamHelper = null;
61600
62306
  }
61601
62307
  }
62308
+ static _defaultBeamingRules;
62309
+ static _findOrBuildDefaultBeamingRules(masterBar) {
62310
+ let defaultBeamingRules = BarHelpers._defaultBeamingRules;
62311
+ if (!defaultBeamingRules) {
62312
+ defaultBeamingRules = new Map([
62313
+ BeamingRules.createSimple(2, 16, Duration.Sixteenth, [1, 1]),
62314
+ BeamingRules.createSimple(1, 8, Duration.Eighth, [1]),
62315
+ BeamingRules.createSimple(1, 4, Duration.Quarter, [1]),
62316
+ BeamingRules.createSimple(3, 16, Duration.Sixteenth, [3]),
62317
+ BeamingRules.createSimple(4, 16, Duration.Sixteenth, [2, 2]),
62318
+ BeamingRules.createSimple(2, 8, Duration.Eighth, [1, 1]),
62319
+ BeamingRules.createSimple(5, 16, Duration.Sixteenth, [3, 2]),
62320
+ BeamingRules.createSimple(6, 16, Duration.Sixteenth, [3, 3]),
62321
+ BeamingRules.createSimple(3, 8, Duration.Eighth, [3]),
62322
+ BeamingRules.createSimple(4, 8, Duration.Eighth, [2, 2]),
62323
+ BeamingRules.createSimple(2, 4, Duration.Quarter, [1, 1]),
62324
+ BeamingRules.createSimple(9, 16, Duration.Sixteenth, [3, 3, 3]),
62325
+ BeamingRules.createSimple(5, 8, Duration.Eighth, [3, 2]),
62326
+ BeamingRules.createSimple(12, 16, Duration.Sixteenth, [3, 3, 3, 3]),
62327
+ BeamingRules.createSimple(6, 8, Duration.Eighth, [3, 3, 3]),
62328
+ BeamingRules.createSimple(3, 4, Duration.Quarter, [1, 1, 1]),
62329
+ BeamingRules.createSimple(7, 8, Duration.Eighth, [4, 3]),
62330
+ BeamingRules.createSimple(8, 8, Duration.Eighth, [3, 3, 2]),
62331
+ BeamingRules.createSimple(4, 4, Duration.Quarter, [1, 1, 1, 1]),
62332
+ BeamingRules.createSimple(9, 8, Duration.Eighth, [3, 3, 3]),
62333
+ BeamingRules.createSimple(10, 8, Duration.Eighth, [4, 3, 3]),
62334
+ BeamingRules.createSimple(5, 4, Duration.Quarter, [1, 1, 1, 1, 1]),
62335
+ BeamingRules.createSimple(12, 8, Duration.Eighth, [3, 3, 3, 3]),
62336
+ BeamingRules.createSimple(6, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1]),
62337
+ BeamingRules.createSimple(15, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3]),
62338
+ BeamingRules.createSimple(8, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1, 1, 1]),
62339
+ BeamingRules.createSimple(18, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3])
62340
+ ].map(r => [`${r.timeSignatureNumerator}_${r.timeSignatureDenominator}`, r]));
62341
+ BarHelpers._defaultBeamingRules = defaultBeamingRules;
62342
+ }
62343
+ const key = `${masterBar.timeSignatureNumerator}_${masterBar.timeSignatureDenominator}`;
62344
+ if (defaultBeamingRules.has(key)) {
62345
+ return defaultBeamingRules.get(key);
62346
+ }
62347
+ // NOTE: this is the old alphaTab logic how we used to beamed bars.
62348
+ // we either group in quarters, or in 3x8ths depending on the key signature
62349
+ let divisionLength = MidiUtils.QuarterTime;
62350
+ switch (masterBar.timeSignatureDenominator) {
62351
+ case 8:
62352
+ if (masterBar.timeSignatureNumerator % 3 === 0) {
62353
+ divisionLength += (MidiUtils.QuarterTime / 2) | 0;
62354
+ }
62355
+ break;
62356
+ }
62357
+ const numberOfDivisions = Math.ceil(masterBar.calculateDuration(false) / divisionLength);
62358
+ const notesPerDivision = (divisionLength / MidiUtils.QuarterTime) * 2;
62359
+ const fallback = new BeamingRules();
62360
+ const groups = [];
62361
+ for (let i = 0; i < numberOfDivisions; i++) {
62362
+ groups.push(notesPerDivision);
62363
+ }
62364
+ fallback.groups.set(Duration.Eighth, groups);
62365
+ fallback.timeSignatureNumerator = masterBar.timeSignatureNumerator;
62366
+ fallback.timeSignatureDenominator = masterBar.timeSignatureDenominator;
62367
+ fallback.finish();
62368
+ defaultBeamingRules.set(key, fallback);
62369
+ return fallback;
62370
+ }
61602
62371
  getBeamingHelperForBeat(beat) {
61603
62372
  return this._beamHelperLookup.has(beat.id) ? this._beamHelperLookup.get(beat.id) : undefined;
61604
62373
  }
@@ -63256,12 +64025,12 @@
63256
64025
  createNewGlyph(renderer, beat) {
63257
64026
  const res = renderer.resources;
63258
64027
  if (beat.slap) {
63259
- return new TextGlyph(0, 0, 'S', res.effectFont, TextAlign.Center);
64028
+ return new TextGlyph(0, 0, 'S', res.elementFonts.get(exports.NotationElement.EffectTap), TextAlign.Center);
63260
64029
  }
63261
64030
  if (beat.pop) {
63262
- return new TextGlyph(0, 0, 'P', res.effectFont, TextAlign.Center);
64031
+ return new TextGlyph(0, 0, 'P', res.elementFonts.get(exports.NotationElement.EffectTap), TextAlign.Center);
63263
64032
  }
63264
- return new TextGlyph(0, 0, 'T', res.effectFont, TextAlign.Center);
64033
+ return new TextGlyph(0, 0, 'T', res.elementFonts.get(exports.NotationElement.EffectTap), TextAlign.Center);
63265
64034
  }
63266
64035
  canExpand(_from, _to) {
63267
64036
  return true;
@@ -63290,7 +64059,7 @@
63290
64059
  for (const automation of this._tempoAutomations) {
63291
64060
  let x = cx + this.renderer.getRatioPositionX(automation.ratioPosition);
63292
64061
  const res = this.renderer.resources;
63293
- canvas.font = res.markerFont;
64062
+ canvas.font = res.elementFonts.get(exports.NotationElement.EffectMarker);
63294
64063
  const notePosY = cy +
63295
64064
  this.y +
63296
64065
  this.height +
@@ -63368,7 +64137,7 @@
63368
64137
  return !!beat.text;
63369
64138
  }
63370
64139
  createNewGlyph(renderer, beat) {
63371
- return new TextGlyph(0, 0, beat.text, renderer.resources.effectFont, TextAlign.Left);
64140
+ return new TextGlyph(0, 0, beat.text, renderer.resources.elementFonts.get(exports.NotationElement.EffectText), TextAlign.Left);
63372
64141
  }
63373
64142
  canExpand(_from, _to) {
63374
64143
  return true;
@@ -63493,7 +64262,7 @@
63493
64262
  const textY = cy + this.height;
63494
64263
  const b = canvas.textBaseline;
63495
64264
  canvas.textBaseline = TextBaseline.Bottom;
63496
- canvas.font = this.renderer.resources.effectFont;
64265
+ canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.EffectTripletFeel);
63497
64266
  canvas.fillText('(', cx, textY);
63498
64267
  cx += canvas.measureText('( ').width;
63499
64268
  cx = this._drawGroup(cx, noteY + this._tupletHeight, canvas, leftNotes);
@@ -63735,7 +64504,7 @@
63735
64504
  return beat.hasWhammyBar;
63736
64505
  }
63737
64506
  createNewGlyph(_renderer, _beat) {
63738
- return new LineRangedGlyph('w/bar');
64507
+ return new LineRangedGlyph('w/bar', exports.NotationElement.EffectWhammyBar);
63739
64508
  }
63740
64509
  canExpand(_from, _to) {
63741
64510
  return true;
@@ -63887,7 +64656,7 @@
63887
64656
  class ChordDiagramContainerGlyph extends RowContainerGlyph {
63888
64657
  addChord(chord) {
63889
64658
  if (chord.strings.length > 0) {
63890
- const chordDiagram = new ChordDiagramGlyph(0, 0, chord);
64659
+ const chordDiagram = new ChordDiagramGlyph(0, 0, chord, exports.NotationElement.ChordDiagrams);
63891
64660
  chordDiagram.renderer = this.renderer;
63892
64661
  chordDiagram.doLayout();
63893
64662
  this.glyphs.push(chordDiagram);
@@ -63951,7 +64720,7 @@
63951
64720
  this.height = 0;
63952
64721
  // Track name
63953
64722
  if (this._trackLabel.length > 0) {
63954
- const trackName = new TextGlyph(0, this.height, this._trackLabel, res.effectFont, TextAlign.Left);
64723
+ const trackName = new TextGlyph(0, this.height, this._trackLabel, res.elementFonts.get(exports.NotationElement.GuitarTuning), TextAlign.Left);
63955
64724
  trackName.renderer = this.renderer;
63956
64725
  trackName.doLayout();
63957
64726
  this.height += trackName.height;
@@ -63959,7 +64728,7 @@
63959
64728
  }
63960
64729
  // Name
63961
64730
  if (tuning.name.length > 0) {
63962
- const tuningName = new TextGlyph(0, this.height, tuning.name, res.effectFont, TextAlign.Left);
64731
+ const tuningName = new TextGlyph(0, this.height, tuning.name, res.elementFonts.get(exports.NotationElement.GuitarTuning), TextAlign.Left);
63963
64732
  tuningName.renderer = this.renderer;
63964
64733
  tuningName.doLayout();
63965
64734
  this.height += tuningName.height;
@@ -63967,7 +64736,7 @@
63967
64736
  }
63968
64737
  const circleScale = this.renderer.smuflMetrics.tuningGlyphCircleNumberScale;
63969
64738
  const circleHeight = this.renderer.smuflMetrics.glyphHeights.get(MusicFontSymbol.GuitarString0) * circleScale;
63970
- this.renderer.scoreRenderer.canvas.font = res.effectFont;
64739
+ this.renderer.scoreRenderer.canvas.font = res.elementFonts.get(exports.NotationElement.GuitarTuning);
63971
64740
  const stringColumnWidth = (circleHeight + this.renderer.scoreRenderer.canvas.measureText(' = Gb').width) *
63972
64741
  res.engravingSettings.tuningGlyphStringColumnScale;
63973
64742
  this.width = Math.max(this.renderer.scoreRenderer.canvas.measureText(this._trackLabel).width, Math.max(this.renderer.scoreRenderer.canvas.measureText(tuning.name).width, 2 * stringColumnWidth));
@@ -63981,7 +64750,7 @@
63981
64750
  const symbol = (MusicFontSymbol.GuitarString0 + (i + 1));
63982
64751
  this.addGlyph(new MusicFontGlyph(currentX, currentY + circleHeight, circleScale, symbol));
63983
64752
  const str = ` = ${Tuning.getTextForTuning(tuning.tunings[i], false)}`;
63984
- this.addGlyph(new TextGlyph(currentX + circleHeight, currentY + circleHeight / 2, str, res.effectFont, TextAlign.Left, TextBaseline.Middle));
64753
+ this.addGlyph(new TextGlyph(currentX + circleHeight, currentY + circleHeight / 2, str, res.elementFonts.get(exports.NotationElement.GuitarTuning), TextAlign.Left, TextBaseline.Middle));
63985
64754
  currentY += circleHeight + this.renderer.smuflMetrics.tuningGlyphStringRowPadding;
63986
64755
  const bottomY = currentY;
63987
64756
  if (this.height < bottomY) {
@@ -65033,7 +65802,7 @@
65033
65802
  let hasAnyTrackName = false;
65034
65803
  if (shouldRender) {
65035
65804
  const canvas = this.layout.renderer.canvas;
65036
- const res = settings.display.resources.effectFont;
65805
+ const res = settings.display.resources.elementFonts.get(exports.NotationElement.TrackNames);
65037
65806
  canvas.font = res;
65038
65807
  for (const t of tracks) {
65039
65808
  let trackNameText = '';
@@ -65187,7 +65956,7 @@
65187
65956
  // Draw track names
65188
65957
  const settings = this.layout.renderer.settings;
65189
65958
  const hasTrackName = this.layout.renderer.settings.notation.isNotationElementVisible(exports.NotationElement.TrackNames);
65190
- canvas.font = res.effectFont;
65959
+ canvas.font = res.elementFonts.get(exports.NotationElement.TrackNames);
65191
65960
  if (hasTrackName) {
65192
65961
  const stylesheet = this.layout.renderer.score.stylesheet;
65193
65962
  const trackNamePolicy = this.layout.renderer.tracks.length === 1
@@ -65531,6 +66300,7 @@
65531
66300
  this.renderer = renderer;
65532
66301
  }
65533
66302
  slurRegistry = new SlurRegistry();
66303
+ beamingRuleLookups = new Map();
65534
66304
  resize() {
65535
66305
  this._lazyPartials.clear();
65536
66306
  this.slurRegistry.clear();
@@ -65539,6 +66309,7 @@
65539
66309
  layoutAndRender() {
65540
66310
  this._lazyPartials.clear();
65541
66311
  this.slurRegistry.clear();
66312
+ this.beamingRuleLookups.clear();
65542
66313
  this._barRendererLookup.clear();
65543
66314
  this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
65544
66315
  const score = this.renderer.score;
@@ -65877,7 +66648,10 @@
65877
66648
  const msg = 'rendered by alphaTab';
65878
66649
  const resources = this.renderer.settings.display.resources;
65879
66650
  const size = 12;
65880
- const font = Font.withFamilyList(resources.copyrightFont.families, size, FontStyle.Plain, FontWeight.Bold);
66651
+ const fontFamilies = resources.elementFonts.has(exports.NotationElement.ScoreCopyright)
66652
+ ? resources.elementFonts.get(exports.NotationElement.ScoreCopyright).families
66653
+ : resources.tablatureFont.families;
66654
+ const font = Font.withFamilyList(fontFamilies, size, FontStyle.Plain, FontWeight.Bold);
65881
66655
  const fakeBarRenderer = new BarRendererBase(this.renderer, this.renderer.tracks[0].staves[0].bars[0]);
65882
66656
  const glyph = new TextGlyph(0, 0, msg, font, TextAlign.Center, undefined, resources.mainGlyphColor);
65883
66657
  glyph.renderer = fakeBarRenderer;
@@ -66790,13 +67564,7 @@
66790
67564
  this._number = `${num} `;
66791
67565
  }
66792
67566
  doLayout() {
66793
- // TODO: activate this and update paddings accordingly.
66794
- // if (!this.renderer.staff!.isFirstInSystem) {
66795
- // this.width = 0;
66796
- // this.height = 0;
66797
- // return;
66798
- // }
66799
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.barNumberFont;
67567
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.BarNumber);
66800
67568
  const size = this.renderer.scoreRenderer.canvas.measureText(this._number);
66801
67569
  this.width = size.width;
66802
67570
  this.height = size.height;
@@ -66810,7 +67578,7 @@
66810
67578
  try {
66811
67579
  const res = this.renderer.resources;
66812
67580
  const baseline = canvas.textBaseline;
66813
- canvas.font = res.barNumberFont;
67581
+ canvas.font = res.elementFonts.get(exports.NotationElement.BarNumber);
66814
67582
  canvas.textBaseline = TextBaseline.Top;
66815
67583
  canvas.fillText(this._number, cx + this.x, cy + this.y);
66816
67584
  canvas.textBaseline = baseline;
@@ -67909,7 +68677,7 @@
67909
68677
  this._count = count;
67910
68678
  }
67911
68679
  doLayout() {
67912
- this.renderer.scoreRenderer.canvas.font = this.renderer.resources.barNumberFont;
68680
+ this.renderer.scoreRenderer.canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.RepeatCount);
67913
68681
  const size = this.renderer.scoreRenderer.canvas.measureText(`x${this._count}`);
67914
68682
  this.width = 0; // do not account width
67915
68683
  this.height = size.height;
@@ -67920,7 +68688,7 @@
67920
68688
  try {
67921
68689
  const res = this.renderer.resources;
67922
68690
  const oldAlign = canvas.textAlign;
67923
- canvas.font = res.barNumberFont;
68691
+ canvas.font = res.elementFonts.get(exports.NotationElement.RepeatCount);
67924
68692
  canvas.textAlign = TextAlign.Right;
67925
68693
  const s = `x${this._count}`;
67926
68694
  const w = canvas.measureText(s).width / 1.5;
@@ -68045,11 +68813,12 @@
68045
68813
  }
68046
68814
  createStartSpacing() {
68047
68815
  if (this._startSpacing) {
68048
- return;
68816
+ return false;
68049
68817
  }
68050
68818
  const padding = this.index === 0 ? this.settings.display.firstStaffPaddingLeft : this.settings.display.staffPaddingLeft;
68051
68819
  this.addPreBeatGlyph(new SpacingGlyph(0, 0, padding));
68052
68820
  this._startSpacing = true;
68821
+ return true;
68053
68822
  }
68054
68823
  paintTuplets(cx, cy, canvas, beatElement, bracketsAsArcs = false) {
68055
68824
  for (const v of this.voiceContainer.voiceDrawOrder) {
@@ -68391,16 +69160,45 @@
68391
69160
  super.createPreBeatGlyphs();
68392
69161
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
68393
69162
  this.createLinePreBeatGlyphs();
69163
+ let hasSpaceAfterStartGlyphs = false;
68394
69164
  if (this.index === 0) {
68395
- this.createStartSpacing();
69165
+ hasSpaceAfterStartGlyphs = this.createStartSpacing();
69166
+ }
69167
+ if (this.shouldCreateBarNumber()) {
69168
+ this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69169
+ }
69170
+ else if (!hasSpaceAfterStartGlyphs) {
69171
+ this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
69172
+ }
69173
+ }
69174
+ shouldCreateBarNumber() {
69175
+ let display = BarNumberDisplay.AllBars;
69176
+ if (!this.settings.notation.isNotationElementVisible(exports.NotationElement.BarNumber)) {
69177
+ display = BarNumberDisplay.Hide;
69178
+ }
69179
+ else if (this.bar.barNumberDisplay !== undefined) {
69180
+ display = this.bar.barNumberDisplay;
69181
+ }
69182
+ else {
69183
+ display = this.bar.staff.track.score.stylesheet.barNumberDisplay;
69184
+ }
69185
+ switch (display) {
69186
+ case BarNumberDisplay.AllBars:
69187
+ return true;
69188
+ case BarNumberDisplay.FirstOfSystem:
69189
+ return this.isFirstOfStaff;
69190
+ case BarNumberDisplay.Hide:
69191
+ return false;
68396
69192
  }
68397
- this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69193
+ return true;
68398
69194
  }
68399
69195
  createPostBeatGlyphs() {
68400
69196
  super.createPostBeatGlyphs();
68401
69197
  const lastBar = this.lastBar;
68402
69198
  this.addPostBeatGlyph(new BarLineGlyph(true, this.bar.staff.track.score.stylesheet.extendBarLines));
68403
- if (lastBar.masterBar.isRepeatEnd && lastBar.masterBar.repeatCount > 2) {
69199
+ if (lastBar.masterBar.isRepeatEnd &&
69200
+ lastBar.masterBar.repeatCount > 2 &&
69201
+ this.settings.notation.isNotationElementVisible(exports.NotationElement.RepeatCount)) {
68404
69202
  this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getLineHeight(-0.5), this.bar.masterBar.repeatCount));
68405
69203
  }
68406
69204
  }
@@ -68964,8 +69762,13 @@
68964
69762
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
68965
69763
  }
68966
69764
  this.createLinePreBeatGlyphs();
68967
- this.createStartSpacing();
68968
- this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69765
+ const hasSpaceAfterStartGlyphs = this.createStartSpacing();
69766
+ if (this.shouldCreateBarNumber()) {
69767
+ this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-0.5), this.bar.index + 1));
69768
+ }
69769
+ else if (!hasSpaceAfterStartGlyphs) {
69770
+ this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
69771
+ }
68969
69772
  }
68970
69773
  createLinePreBeatGlyphs() {
68971
69774
  if (this._isOnlyNumbered &&
@@ -71513,7 +72316,7 @@
71513
72316
  if (note.bendStyle === BendStyle.Gradual) {
71514
72317
  const res = this.renderer.resources;
71515
72318
  const c = this.renderer.scoreRenderer.canvas;
71516
- c.font = res.barNumberFont; // see note in paint()
72319
+ c.font = res.elementFonts.get(exports.NotationElement.ScoreBendSlur);
71517
72320
  slurHeight += c.measureText('grad.').height;
71518
72321
  }
71519
72322
  if (slurHeight > maxSlurHeight) {
@@ -71613,11 +72416,16 @@
71613
72416
  this._notes.sort((a, b) => {
71614
72417
  return b.displayValue - a.displayValue;
71615
72418
  });
72419
+ // draw slurs
72420
+ if (this.renderer.settings.notation.isNotationElementVisible(exports.NotationElement.ScoreBendSlur)) {
72421
+ this._paintSlurs(cx, cy, canvas, startNoteRenderer, startX, middleX, endBeatX);
72422
+ }
72423
+ }
72424
+ _paintSlurs(cx, cy, canvas, startNoteRenderer, startX, middleX, endBeatX) {
71616
72425
  const directionBeat = this._beat.graceType === GraceType.BendGrace ? this._beat.nextBeat : this._beat;
71617
72426
  let direction = this._notes.length === 1 ? this.getTieDirection(directionBeat, startNoteRenderer) : BeamDirection.Up;
71618
72427
  const noteHeadHeight = this.renderer.smuflMetrics.glyphHeights.get(MusicFontSymbol.NoteheadBlack);
71619
- // draw slurs
71620
- canvas.font = this.renderer.resources.barNumberFont; // we have never have set it explicitly, that's the current state
72428
+ canvas.font = this.renderer.resources.elementFonts.get(exports.NotationElement.ScoreBendSlur);
71621
72429
  for (let i = 0; i < this._notes.length; i++) {
71622
72430
  const note = this._notes[i];
71623
72431
  const _ = ElementStyleHelper.note(canvas, NoteSubElement.StandardNotationEffects, note);
@@ -77689,6 +78497,9 @@
77689
78497
  key.addElement('Sharps').innerText = 'Sharps';
77690
78498
  masterBarNode.addElement('Time').innerText =
77691
78499
  `${masterBar.timeSignatureNumerator}/${masterBar.timeSignatureDenominator}`;
78500
+ if (masterBar.actualBeamingRules) {
78501
+ this._writeBarXProperties(masterBarNode, masterBar);
78502
+ }
77692
78503
  if (masterBar.isFreeTime) {
77693
78504
  masterBarNode.addElement('FreeTime');
77694
78505
  }
@@ -77798,6 +78609,29 @@
77798
78609
  }
77799
78610
  this._writeFermatas(masterBarNode, masterBar);
77800
78611
  }
78612
+ _writeBarXProperties(masterBarNode, masterBar) {
78613
+ const properties = masterBarNode.addElement('XProperties');
78614
+ const beamingRules = masterBar.actualBeamingRules;
78615
+ if (beamingRules) {
78616
+ // prefer 8th note rule (that's what GP mostly has)
78617
+ const rule = beamingRules.findRule(Duration.Eighth);
78618
+ // NOTE: it's not clear if guitar pro supports quarter rules
78619
+ // for that case we better convert this to an "8th" note rule.
78620
+ let durationProp = rule[0];
78621
+ let groupSizeFactor = 1;
78622
+ if (rule[0] === Duration.Quarter) {
78623
+ durationProp = 8;
78624
+ groupSizeFactor = 2;
78625
+ }
78626
+ this._writeSimpleXPropertyNode(properties, '1124139010', 'Int', durationProp.toString());
78627
+ const startGroupid = 1124139264;
78628
+ let i = 0;
78629
+ while (i < rule[1].length) {
78630
+ this._writeSimpleXPropertyNode(properties, (startGroupid + i).toString(), 'Int', (rule[1][i] * groupSizeFactor).toString());
78631
+ i++;
78632
+ }
78633
+ }
78634
+ }
77801
78635
  _writeFermatas(parent, masterBar) {
77802
78636
  const fermataCount = masterBar.fermata?.size ?? 0;
77803
78637
  if (fermataCount === 0) {
@@ -79746,6 +80580,7 @@
79746
80580
  BackingTrack,
79747
80581
  Bar,
79748
80582
  get BarLineStyle () { return BarLineStyle; },
80583
+ get BarNumberDisplay () { return BarNumberDisplay; },
79749
80584
  BarStyle,
79750
80585
  get BarSubElement () { return BarSubElement; },
79751
80586
  get BarreShape () { return BarreShape; },