@coderline/alphatab 1.8.0-alpha.1670 → 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.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +508 -30
- package/dist/alphaTab.d.ts +40 -0
- package/dist/alphaTab.js +508 -30
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +1 -1
package/dist/alphaTab.core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.8.0-alpha.
|
|
2
|
+
* alphaTab v1.8.0-alpha.1671 (develop, build 1671)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -203,9 +203,9 @@ class AlphaTabError extends Error {
|
|
|
203
203
|
* @internal
|
|
204
204
|
*/
|
|
205
205
|
class VersionInfo {
|
|
206
|
-
static version = '1.8.0-alpha.
|
|
207
|
-
static date = '2026-01-
|
|
208
|
-
static commit = '
|
|
206
|
+
static version = '1.8.0-alpha.1671';
|
|
207
|
+
static date = '2026-01-10T02:20:48.787Z';
|
|
208
|
+
static commit = '1edf83d58fe31a924adb1980bc4a529aed07dad4';
|
|
209
209
|
static print(print) {
|
|
210
210
|
print(`alphaTab ${VersionInfo.version}`);
|
|
211
211
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -2196,6 +2196,12 @@ class Bar {
|
|
|
2196
2196
|
* If specified, overrides the value from the stylesheet on score level.
|
|
2197
2197
|
*/
|
|
2198
2198
|
barNumberDisplay;
|
|
2199
|
+
/**
|
|
2200
|
+
* The shortest duration contained across beats in this bar.
|
|
2201
|
+
* @internal
|
|
2202
|
+
* @json_ignore
|
|
2203
|
+
*/
|
|
2204
|
+
shortestDuration = Duration.DoubleWhole;
|
|
2199
2205
|
/**
|
|
2200
2206
|
* The bar line to draw on the left side of the bar with an "automatic" type resolved to the actual one.
|
|
2201
2207
|
* @param isFirstOfSystem Whether the bar is the first one in the system.
|
|
@@ -2267,12 +2273,16 @@ class Bar {
|
|
|
2267
2273
|
this._filledVoices.add(0);
|
|
2268
2274
|
this._isEmpty = true;
|
|
2269
2275
|
this._isRestOnly = true;
|
|
2276
|
+
this.shortestDuration = Duration.DoubleWhole;
|
|
2270
2277
|
for (let i = 0, j = this.voices.length; i < j; i++) {
|
|
2271
2278
|
const voice = this.voices[i];
|
|
2272
2279
|
voice.finish(settings, sharedDataBag);
|
|
2273
2280
|
if (!voice.isEmpty) {
|
|
2274
2281
|
this._isEmpty = false;
|
|
2275
2282
|
this._filledVoices.add(i);
|
|
2283
|
+
if (voice.shortestDuration > this.shortestDuration) {
|
|
2284
|
+
this.shortestDuration = voice.shortestDuration;
|
|
2285
|
+
}
|
|
2276
2286
|
}
|
|
2277
2287
|
if (!voice.isRestOnly) {
|
|
2278
2288
|
this._isRestOnly = false;
|
|
@@ -2369,6 +2379,116 @@ var TripletFeel;
|
|
|
2369
2379
|
TripletFeel[TripletFeel["Scottish8th"] = 6] = "Scottish8th";
|
|
2370
2380
|
})(TripletFeel || (TripletFeel = {}));
|
|
2371
2381
|
|
|
2382
|
+
/**
|
|
2383
|
+
* Defines the custom beaming rules which define how beats are beamed together or split apart
|
|
2384
|
+
* during the automatic beaming when displayed.
|
|
2385
|
+
* @json
|
|
2386
|
+
* @json_strict
|
|
2387
|
+
* @public
|
|
2388
|
+
*
|
|
2389
|
+
* @remarks
|
|
2390
|
+
* The beaming logic works like this:
|
|
2391
|
+
*
|
|
2392
|
+
* The time axis of the bar is sliced into even chunks. The chunk-size is defined by the respective group definition.
|
|
2393
|
+
* Within these chunks groups can then be placed spanning 1 or more chunks.
|
|
2394
|
+
*
|
|
2395
|
+
* If beats start within the same "group" they are beamed together.
|
|
2396
|
+
*/
|
|
2397
|
+
class BeamingRules {
|
|
2398
|
+
_singleGroupKey;
|
|
2399
|
+
/**
|
|
2400
|
+
* The the group for a given "longest duration" within the bar.
|
|
2401
|
+
* @remarks
|
|
2402
|
+
* The map key is the duration to which the bar will be sliced into.
|
|
2403
|
+
* The map value defines the "groups" placed within the sliced.
|
|
2404
|
+
*/
|
|
2405
|
+
groups = new Map();
|
|
2406
|
+
/**
|
|
2407
|
+
* @internal
|
|
2408
|
+
* @json_ignore
|
|
2409
|
+
*/
|
|
2410
|
+
uniqueId = '';
|
|
2411
|
+
/**
|
|
2412
|
+
* @internal
|
|
2413
|
+
* @json_ignore
|
|
2414
|
+
*/
|
|
2415
|
+
timeSignatureNumerator = 0;
|
|
2416
|
+
/**
|
|
2417
|
+
* @internal
|
|
2418
|
+
* @json_ignore
|
|
2419
|
+
*/
|
|
2420
|
+
timeSignatureDenominator = 0;
|
|
2421
|
+
/**
|
|
2422
|
+
* @internal
|
|
2423
|
+
*/
|
|
2424
|
+
static createSimple(timeSignatureNumerator, timeSignatureDenominator, duration, groups) {
|
|
2425
|
+
const r = new BeamingRules();
|
|
2426
|
+
r.timeSignatureNumerator = timeSignatureNumerator;
|
|
2427
|
+
r.timeSignatureDenominator = timeSignatureDenominator;
|
|
2428
|
+
r.groups.set(duration, groups);
|
|
2429
|
+
r.finish();
|
|
2430
|
+
return r;
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* @internal
|
|
2434
|
+
*/
|
|
2435
|
+
findRule(shortestDuration) {
|
|
2436
|
+
// fast path: one rule -> take it
|
|
2437
|
+
const singleGroupKey = this._singleGroupKey;
|
|
2438
|
+
if (singleGroupKey) {
|
|
2439
|
+
return [singleGroupKey, this.groups.get(singleGroupKey)];
|
|
2440
|
+
}
|
|
2441
|
+
if (shortestDuration < Duration.Quarter) {
|
|
2442
|
+
return [shortestDuration, []];
|
|
2443
|
+
}
|
|
2444
|
+
// first search shorter
|
|
2445
|
+
let durationValue = shortestDuration;
|
|
2446
|
+
do {
|
|
2447
|
+
const duration = durationValue;
|
|
2448
|
+
if (this.groups.has(duration)) {
|
|
2449
|
+
return [duration, this.groups.get(duration)];
|
|
2450
|
+
}
|
|
2451
|
+
durationValue = durationValue * 2;
|
|
2452
|
+
} while (durationValue <= Duration.TwoHundredFiftySixth);
|
|
2453
|
+
// then longer
|
|
2454
|
+
durationValue = shortestDuration / 2;
|
|
2455
|
+
do {
|
|
2456
|
+
const duration = durationValue;
|
|
2457
|
+
if (this.groups.has(duration)) {
|
|
2458
|
+
return [duration, this.groups.get(duration)];
|
|
2459
|
+
}
|
|
2460
|
+
durationValue = durationValue / 2;
|
|
2461
|
+
} while (durationValue > Duration.Half);
|
|
2462
|
+
return [shortestDuration, []];
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* @internal
|
|
2466
|
+
*/
|
|
2467
|
+
finish() {
|
|
2468
|
+
let uniqueId = `${this.timeSignatureNumerator}_${this.timeSignatureDenominator}`;
|
|
2469
|
+
for (const [k, v] of this.groups) {
|
|
2470
|
+
uniqueId += `__${k}`;
|
|
2471
|
+
// trim of 0s at the end of the group
|
|
2472
|
+
let lastZero = v.length;
|
|
2473
|
+
for (let i = v.length - 1; i >= 0; i--) {
|
|
2474
|
+
if (v[i] === 0) {
|
|
2475
|
+
lastZero = i;
|
|
2476
|
+
}
|
|
2477
|
+
else {
|
|
2478
|
+
break;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (lastZero < v.length) {
|
|
2482
|
+
v.splice(lastZero, v.length - lastZero);
|
|
2483
|
+
}
|
|
2484
|
+
uniqueId += `_${v.join('_')}`;
|
|
2485
|
+
if (this.groups.size === 1) {
|
|
2486
|
+
this._singleGroupKey = k;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
this.uniqueId = uniqueId;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2372
2492
|
/**
|
|
2373
2493
|
* The MasterBar stores information about a bar which affects
|
|
2374
2494
|
* all tracks.
|
|
@@ -2486,6 +2606,16 @@ class MasterBar {
|
|
|
2486
2606
|
* Gets or sets whether this is bar has a common time signature.
|
|
2487
2607
|
*/
|
|
2488
2608
|
timeSignatureCommon = false;
|
|
2609
|
+
/**
|
|
2610
|
+
* Defines the custom beaming rules which should be applied to this bar and all bars following.
|
|
2611
|
+
*/
|
|
2612
|
+
beamingRules;
|
|
2613
|
+
/**
|
|
2614
|
+
* The actual (custom) beaming rules to use for this bar if any were specified.
|
|
2615
|
+
* @json_ignore
|
|
2616
|
+
* @internal
|
|
2617
|
+
*/
|
|
2618
|
+
actualBeamingRules;
|
|
2489
2619
|
/**
|
|
2490
2620
|
* Gets or sets whether the bar indicates a free time playing.
|
|
2491
2621
|
*/
|
|
@@ -2618,6 +2748,32 @@ class MasterBar {
|
|
|
2618
2748
|
}
|
|
2619
2749
|
this.syncPoints.push(syncPoint);
|
|
2620
2750
|
}
|
|
2751
|
+
finish(sharedDataBag) {
|
|
2752
|
+
let beamingRules = this.beamingRules;
|
|
2753
|
+
if (beamingRules) {
|
|
2754
|
+
beamingRules.timeSignatureNumerator = this.timeSignatureNumerator;
|
|
2755
|
+
beamingRules.timeSignatureDenominator = this.timeSignatureDenominator;
|
|
2756
|
+
beamingRules.finish();
|
|
2757
|
+
}
|
|
2758
|
+
if (this.index > 0) {
|
|
2759
|
+
this.start = this.previousMasterBar.start + this.previousMasterBar.calculateDuration();
|
|
2760
|
+
// clear out equal rules to reduce memory consumption.
|
|
2761
|
+
const previousRules = sharedDataBag.has('beamingRules')
|
|
2762
|
+
? sharedDataBag.get('beamingRules')
|
|
2763
|
+
: undefined;
|
|
2764
|
+
if (previousRules && previousRules.uniqueId === beamingRules?.uniqueId) {
|
|
2765
|
+
this.beamingRules = undefined;
|
|
2766
|
+
beamingRules = previousRules;
|
|
2767
|
+
}
|
|
2768
|
+
else if (!beamingRules) {
|
|
2769
|
+
beamingRules = previousRules;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
this.actualBeamingRules = beamingRules;
|
|
2773
|
+
if (this.beamingRules) {
|
|
2774
|
+
sharedDataBag.set('beamingRules', beamingRules);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2621
2777
|
}
|
|
2622
2778
|
|
|
2623
2779
|
/**
|
|
@@ -2968,6 +3124,12 @@ let Voice$1 = class Voice {
|
|
|
2968
3124
|
get isRestOnly() {
|
|
2969
3125
|
return this._isRestOnly;
|
|
2970
3126
|
}
|
|
3127
|
+
/**
|
|
3128
|
+
* The shortest duration contained across beats in this bar.
|
|
3129
|
+
* @internal
|
|
3130
|
+
* @json_ignore
|
|
3131
|
+
*/
|
|
3132
|
+
shortestDuration = Duration.DoubleWhole;
|
|
2971
3133
|
insertBeat(after, newBeat) {
|
|
2972
3134
|
newBeat.nextBeat = after.nextBeat;
|
|
2973
3135
|
if (newBeat.nextBeat) {
|
|
@@ -3061,6 +3223,7 @@ let Voice$1 = class Voice {
|
|
|
3061
3223
|
}
|
|
3062
3224
|
let currentDisplayTick = 0;
|
|
3063
3225
|
let currentPlaybackTick = 0;
|
|
3226
|
+
this.shortestDuration = Duration.DoubleWhole;
|
|
3064
3227
|
for (let i = 0; i < this.beats.length; i++) {
|
|
3065
3228
|
const beat = this.beats[i];
|
|
3066
3229
|
beat.index = i;
|
|
@@ -3069,6 +3232,9 @@ let Voice$1 = class Voice {
|
|
|
3069
3232
|
// we need to first steal the duration from the right beat
|
|
3070
3233
|
// and place the grace beats correctly
|
|
3071
3234
|
if (beat.graceType === GraceType.None) {
|
|
3235
|
+
if (beat.duration > this.shortestDuration) {
|
|
3236
|
+
this.shortestDuration = beat.duration;
|
|
3237
|
+
}
|
|
3072
3238
|
if (beat.graceGroup) {
|
|
3073
3239
|
const firstGraceBeat = beat.graceGroup.beats[0];
|
|
3074
3240
|
const lastGraceBeat = beat.graceGroup.beats[beat.graceGroup.beats.length - 1];
|
|
@@ -3529,7 +3695,7 @@ class Score {
|
|
|
3529
3695
|
bar.previousMasterBar.nextMasterBar = bar;
|
|
3530
3696
|
// NOTE: this will not work on anacrusis. Correct anacrusis durations are only working
|
|
3531
3697
|
// when there are beats with playback positions already computed which requires full finish
|
|
3532
|
-
// chicken-egg problem here. temporarily forcing anacrusis length here to 0,
|
|
3698
|
+
// chicken-egg problem here. temporarily forcing anacrusis length here to 0,
|
|
3533
3699
|
// .finish() will correct these times
|
|
3534
3700
|
bar.start =
|
|
3535
3701
|
bar.previousMasterBar.start +
|
|
@@ -3593,9 +3759,7 @@ class Score {
|
|
|
3593
3759
|
}
|
|
3594
3760
|
// fixup masterbar starts to handle anacrusis lengths
|
|
3595
3761
|
for (const mb of this.masterBars) {
|
|
3596
|
-
|
|
3597
|
-
mb.start = mb.previousMasterBar.start + mb.previousMasterBar.calculateDuration();
|
|
3598
|
-
}
|
|
3762
|
+
mb.finish(sharedDataBag);
|
|
3599
3763
|
}
|
|
3600
3764
|
}
|
|
3601
3765
|
/**
|
|
@@ -8964,7 +9128,16 @@ class AlphaTex1LanguageDefinitions {
|
|
|
8964
9128
|
['spu', [[[[16], 2]]]],
|
|
8965
9129
|
['db', null],
|
|
8966
9130
|
['voicemode', [[[[10, 17], 0, ['staffwise', 'barwise']]]]],
|
|
8967
|
-
['barnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]]
|
|
9131
|
+
['barnumberdisplay', [[[[10, 17], 0, ['allbars', 'firstofsystem', 'hide']]]]],
|
|
9132
|
+
[
|
|
9133
|
+
'beaming',
|
|
9134
|
+
[
|
|
9135
|
+
[
|
|
9136
|
+
[[16], 0],
|
|
9137
|
+
[[16], 5]
|
|
9138
|
+
]
|
|
9139
|
+
]
|
|
9140
|
+
]
|
|
8968
9141
|
]);
|
|
8969
9142
|
static metaDataProperties = AlphaTex1LanguageDefinitions._metaProps([
|
|
8970
9143
|
[
|
|
@@ -9080,7 +9253,8 @@ class AlphaTex1LanguageDefinitions {
|
|
|
9080
9253
|
['spu', null],
|
|
9081
9254
|
['db', null],
|
|
9082
9255
|
['voicemode', null],
|
|
9083
|
-
['barnumberdisplay', null]
|
|
9256
|
+
['barnumberdisplay', null],
|
|
9257
|
+
['beaming', null]
|
|
9084
9258
|
]);
|
|
9085
9259
|
static metaDataSignatures = [
|
|
9086
9260
|
AlphaTex1LanguageDefinitions.scoreMetaDataSignatures,
|
|
@@ -12795,6 +12969,7 @@ var BeamDirection;
|
|
|
12795
12969
|
*/
|
|
12796
12970
|
class AlphaTex1LanguageHandler {
|
|
12797
12971
|
static instance = new AlphaTex1LanguageHandler();
|
|
12972
|
+
static _timeSignatureDenominators = new Set([1, 2, 4, 8, 16, 32, 64, 128]);
|
|
12798
12973
|
applyScoreMetaData(importer, score, metaData) {
|
|
12799
12974
|
const result = this._checkArgumentTypes(importer, [AlphaTex1LanguageDefinitions.scoreMetaDataSignatures], metaData, metaData.tag.tag.text.toLowerCase(), metaData.arguments);
|
|
12800
12975
|
if (result !== undefined) {
|
|
@@ -13216,8 +13391,29 @@ class AlphaTex1LanguageHandler {
|
|
|
13216
13391
|
case 'ts':
|
|
13217
13392
|
switch (metaData.arguments.arguments[0].nodeType) {
|
|
13218
13393
|
case AlphaTexNodeType.Number:
|
|
13219
|
-
bar.masterBar.timeSignatureNumerator =
|
|
13220
|
-
|
|
13394
|
+
bar.masterBar.timeSignatureNumerator =
|
|
13395
|
+
metaData.arguments.arguments[0].value | 0;
|
|
13396
|
+
if (bar.masterBar.timeSignatureNumerator < 1 || bar.masterBar.timeSignatureNumerator > 32) {
|
|
13397
|
+
importer.addSemanticDiagnostic({
|
|
13398
|
+
code: AlphaTexDiagnosticCode.AT211,
|
|
13399
|
+
message: `Value is out of valid range. Allowed range: 1-32, Actual Value: ${bar.masterBar.timeSignatureNumerator}`,
|
|
13400
|
+
start: metaData.arguments.arguments[0].start,
|
|
13401
|
+
end: metaData.arguments.arguments[0].end,
|
|
13402
|
+
severity: AlphaTexDiagnosticsSeverity.Error
|
|
13403
|
+
});
|
|
13404
|
+
}
|
|
13405
|
+
bar.masterBar.timeSignatureDenominator =
|
|
13406
|
+
metaData.arguments.arguments[1].value | 0;
|
|
13407
|
+
if (!AlphaTex1LanguageHandler._timeSignatureDenominators.has(bar.masterBar.timeSignatureDenominator)) {
|
|
13408
|
+
const valueList = Array.from(AlphaTex1LanguageHandler._timeSignatureDenominators).join(', ');
|
|
13409
|
+
importer.addSemanticDiagnostic({
|
|
13410
|
+
code: AlphaTexDiagnosticCode.AT211,
|
|
13411
|
+
message: `Value is out of valid range. Allowed range: ${valueList}, Actual Value: ${bar.masterBar.timeSignatureDenominator}`,
|
|
13412
|
+
start: metaData.arguments.arguments[0].start,
|
|
13413
|
+
end: metaData.arguments.arguments[0].end,
|
|
13414
|
+
severity: AlphaTexDiagnosticsSeverity.Error
|
|
13415
|
+
});
|
|
13416
|
+
}
|
|
13221
13417
|
break;
|
|
13222
13418
|
case AlphaTexNodeType.Ident:
|
|
13223
13419
|
case AlphaTexNodeType.String:
|
|
@@ -13240,6 +13436,8 @@ class AlphaTex1LanguageHandler {
|
|
|
13240
13436
|
break;
|
|
13241
13437
|
}
|
|
13242
13438
|
return ApplyNodeResult.Applied;
|
|
13439
|
+
case 'beaming':
|
|
13440
|
+
return this._parseBeamingRule(importer, metaData, bar.masterBar);
|
|
13243
13441
|
case 'ks':
|
|
13244
13442
|
const keySignature = AlphaTex1LanguageHandler._parseEnumValue(importer, metaData.arguments, 'key signature', AlphaTex1EnumMappings.keySignature);
|
|
13245
13443
|
if (keySignature === undefined) {
|
|
@@ -13440,6 +13638,53 @@ class AlphaTex1LanguageHandler {
|
|
|
13440
13638
|
return ApplyNodeResult.NotAppliedUnrecognizedMarker;
|
|
13441
13639
|
}
|
|
13442
13640
|
}
|
|
13641
|
+
_parseBeamingRule(importer, metaData, masterBar) {
|
|
13642
|
+
let duration = Duration.Eighth;
|
|
13643
|
+
const groupSizes = [];
|
|
13644
|
+
const durationValue = metaData.arguments.arguments[0].value;
|
|
13645
|
+
switch (durationValue) {
|
|
13646
|
+
case 4:
|
|
13647
|
+
duration = Duration.QuadrupleWhole;
|
|
13648
|
+
break;
|
|
13649
|
+
case 8:
|
|
13650
|
+
duration = Duration.Eighth;
|
|
13651
|
+
break;
|
|
13652
|
+
case 16:
|
|
13653
|
+
duration = Duration.Sixteenth;
|
|
13654
|
+
break;
|
|
13655
|
+
case 32:
|
|
13656
|
+
duration = Duration.ThirtySecond;
|
|
13657
|
+
break;
|
|
13658
|
+
default:
|
|
13659
|
+
importer.addSemanticDiagnostic({
|
|
13660
|
+
code: AlphaTexDiagnosticCode.AT209,
|
|
13661
|
+
message: `Value is out of valid range. Allowed range: 4,8,16 or 32, Actual Value: ${durationValue}`,
|
|
13662
|
+
severity: AlphaTexDiagnosticsSeverity.Error,
|
|
13663
|
+
start: metaData.arguments.arguments[0].start,
|
|
13664
|
+
end: metaData.arguments.arguments[0].end
|
|
13665
|
+
});
|
|
13666
|
+
return ApplyNodeResult.NotAppliedSemanticError;
|
|
13667
|
+
}
|
|
13668
|
+
for (let i = 1; i < metaData.arguments.arguments.length; i++) {
|
|
13669
|
+
const groupSize = metaData.arguments.arguments[i].value;
|
|
13670
|
+
if (groupSize < 1) {
|
|
13671
|
+
importer.addSemanticDiagnostic({
|
|
13672
|
+
code: AlphaTexDiagnosticCode.AT209,
|
|
13673
|
+
message: `Value is out of valid range. Allowed range: >0, Actual Value: ${durationValue}`,
|
|
13674
|
+
severity: AlphaTexDiagnosticsSeverity.Error,
|
|
13675
|
+
start: metaData.arguments.arguments[i].start,
|
|
13676
|
+
end: metaData.arguments.arguments[i].end
|
|
13677
|
+
});
|
|
13678
|
+
return ApplyNodeResult.NotAppliedSemanticError;
|
|
13679
|
+
}
|
|
13680
|
+
groupSizes.push(metaData.arguments.arguments[i].value);
|
|
13681
|
+
}
|
|
13682
|
+
if (!masterBar.beamingRules) {
|
|
13683
|
+
masterBar.beamingRules = new BeamingRules();
|
|
13684
|
+
}
|
|
13685
|
+
masterBar.beamingRules.groups.set(duration, groupSizes);
|
|
13686
|
+
return ApplyNodeResult.Applied;
|
|
13687
|
+
}
|
|
13443
13688
|
static _handleAccidentalMode(importer, args) {
|
|
13444
13689
|
const accidentalMode = AlphaTex1LanguageHandler._parseEnumValue(importer, args, 'accidental mode', AlphaTex1EnumMappings.alphaTexAccidentalMode);
|
|
13445
13690
|
if (accidentalMode === undefined) {
|
|
@@ -15032,6 +15277,15 @@ class AlphaTex1LanguageHandler {
|
|
|
15032
15277
|
])));
|
|
15033
15278
|
}
|
|
15034
15279
|
}
|
|
15280
|
+
if (masterBar.beamingRules) {
|
|
15281
|
+
for (const [k, v] of masterBar.beamingRules.groups) {
|
|
15282
|
+
const args = Atnf.args([Atnf.number(k)], true);
|
|
15283
|
+
for (const i of v) {
|
|
15284
|
+
args.arguments.push(Atnf.number(i));
|
|
15285
|
+
}
|
|
15286
|
+
nodes.push(Atnf.meta('beaming', args));
|
|
15287
|
+
}
|
|
15288
|
+
}
|
|
15035
15289
|
if ((masterBar.index > 0 && masterBar.tripletFeel !== masterBar.previousMasterBar?.tripletFeel) ||
|
|
15036
15290
|
(masterBar.index === 0 && masterBar.tripletFeel !== TripletFeel.NoTripletFeel)) {
|
|
15037
15291
|
nodes.push(Atnf.identMeta('tf', AlphaTex1EnumMappings.tripletFeelReversed.get(masterBar.tripletFeel)));
|
|
@@ -23032,6 +23286,8 @@ class GpifParser {
|
|
|
23032
23286
|
}
|
|
23033
23287
|
}
|
|
23034
23288
|
_parseMasterBarXProperties(masterBar, node) {
|
|
23289
|
+
let beamingRuleDuration = Number.NaN;
|
|
23290
|
+
let beamingRuleGroups = undefined;
|
|
23035
23291
|
for (const c of node.childElements()) {
|
|
23036
23292
|
switch (c.localName) {
|
|
23037
23293
|
case 'XProperty':
|
|
@@ -23040,10 +23296,32 @@ class GpifParser {
|
|
|
23040
23296
|
case '1124073984':
|
|
23041
23297
|
masterBar.displayScale = GpifParser._parseFloatSafe(c.findChildElement('Double')?.innerText, 1);
|
|
23042
23298
|
break;
|
|
23299
|
+
case '1124139010':
|
|
23300
|
+
beamingRuleDuration = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
|
|
23301
|
+
break;
|
|
23302
|
+
default:
|
|
23303
|
+
const idNumeric = GpifParser._parseIntSafe(id, 0);
|
|
23304
|
+
if (idNumeric >= 1124139264 && idNumeric <= 1124139295) {
|
|
23305
|
+
const groupIndex = idNumeric - 1124139264;
|
|
23306
|
+
const groupSize = GpifParser._parseIntSafe(c.findChildElement('Int')?.innerText, Number.NaN);
|
|
23307
|
+
if (beamingRuleGroups === undefined) {
|
|
23308
|
+
beamingRuleGroups = [];
|
|
23309
|
+
}
|
|
23310
|
+
while (beamingRuleGroups.length < groupIndex + 1) {
|
|
23311
|
+
beamingRuleGroups.push(0);
|
|
23312
|
+
}
|
|
23313
|
+
beamingRuleGroups[groupIndex] = groupSize;
|
|
23314
|
+
}
|
|
23315
|
+
break;
|
|
23043
23316
|
}
|
|
23044
23317
|
break;
|
|
23045
23318
|
}
|
|
23046
23319
|
}
|
|
23320
|
+
if (!Number.isNaN(beamingRuleDuration) && beamingRuleGroups) {
|
|
23321
|
+
const rules = new BeamingRules();
|
|
23322
|
+
rules.groups.set(beamingRuleDuration, beamingRuleGroups);
|
|
23323
|
+
masterBar.beamingRules = rules;
|
|
23324
|
+
}
|
|
23047
23325
|
}
|
|
23048
23326
|
_parseBeatProperties(node, beat) {
|
|
23049
23327
|
let isWhammy = false;
|
|
@@ -36579,6 +36857,43 @@ class JsonHelper {
|
|
|
36579
36857
|
}
|
|
36580
36858
|
}
|
|
36581
36859
|
|
|
36860
|
+
/**
|
|
36861
|
+
* @internal
|
|
36862
|
+
*/
|
|
36863
|
+
class BeamingRulesSerializer {
|
|
36864
|
+
static fromJson(obj, m) {
|
|
36865
|
+
if (!m) {
|
|
36866
|
+
return;
|
|
36867
|
+
}
|
|
36868
|
+
JsonHelper.forEach(m, (v, k) => BeamingRulesSerializer.setProperty(obj, k, v));
|
|
36869
|
+
}
|
|
36870
|
+
static toJson(obj) {
|
|
36871
|
+
if (!obj) {
|
|
36872
|
+
return null;
|
|
36873
|
+
}
|
|
36874
|
+
const o = new Map();
|
|
36875
|
+
{
|
|
36876
|
+
const m = new Map();
|
|
36877
|
+
o.set("groups", m);
|
|
36878
|
+
for (const [k, v] of obj.groups) {
|
|
36879
|
+
m.set(k.toString(), v);
|
|
36880
|
+
}
|
|
36881
|
+
}
|
|
36882
|
+
return o;
|
|
36883
|
+
}
|
|
36884
|
+
static setProperty(obj, property, v) {
|
|
36885
|
+
switch (property) {
|
|
36886
|
+
case "groups":
|
|
36887
|
+
obj.groups = new Map();
|
|
36888
|
+
JsonHelper.forEach(v, (v, k) => {
|
|
36889
|
+
obj.groups.set(JsonHelper.parseEnum(k, Duration), v);
|
|
36890
|
+
});
|
|
36891
|
+
return true;
|
|
36892
|
+
}
|
|
36893
|
+
return false;
|
|
36894
|
+
}
|
|
36895
|
+
}
|
|
36896
|
+
|
|
36582
36897
|
/**
|
|
36583
36898
|
* @internal
|
|
36584
36899
|
*/
|
|
@@ -36757,6 +37072,9 @@ class MasterBarSerializer {
|
|
|
36757
37072
|
o.set("timesignaturenumerator", obj.timeSignatureNumerator);
|
|
36758
37073
|
o.set("timesignaturedenominator", obj.timeSignatureDenominator);
|
|
36759
37074
|
o.set("timesignaturecommon", obj.timeSignatureCommon);
|
|
37075
|
+
if (obj.beamingRules) {
|
|
37076
|
+
o.set("beamingrules", BeamingRulesSerializer.toJson(obj.beamingRules));
|
|
37077
|
+
}
|
|
36760
37078
|
o.set("isfreetime", obj.isFreeTime);
|
|
36761
37079
|
o.set("tripletfeel", obj.tripletFeel);
|
|
36762
37080
|
if (obj.section) {
|
|
@@ -36809,6 +37127,15 @@ class MasterBarSerializer {
|
|
|
36809
37127
|
case "timesignaturecommon":
|
|
36810
37128
|
obj.timeSignatureCommon = v;
|
|
36811
37129
|
return true;
|
|
37130
|
+
case "beamingrules":
|
|
37131
|
+
if (v) {
|
|
37132
|
+
obj.beamingRules = new BeamingRules();
|
|
37133
|
+
BeamingRulesSerializer.fromJson(obj.beamingRules, v);
|
|
37134
|
+
}
|
|
37135
|
+
else {
|
|
37136
|
+
obj.beamingRules = undefined;
|
|
37137
|
+
}
|
|
37138
|
+
return true;
|
|
36812
37139
|
case "isfreetime":
|
|
36813
37140
|
obj.isFreeTime = v;
|
|
36814
37141
|
return true;
|
|
@@ -61467,6 +61794,7 @@ class BeamingHelperDrawInfo {
|
|
|
61467
61794
|
class BeamingHelper {
|
|
61468
61795
|
_staff;
|
|
61469
61796
|
_renderer;
|
|
61797
|
+
_beamingRuleLookup;
|
|
61470
61798
|
voice = null;
|
|
61471
61799
|
beats = [];
|
|
61472
61800
|
shortestDuration = Duration.QuadrupleWhole;
|
|
@@ -61500,10 +61828,11 @@ class BeamingHelper {
|
|
|
61500
61828
|
static beatHasFlag(beat) {
|
|
61501
61829
|
return (!beat.deadSlapped && !beat.isRest && (beat.duration > Duration.Quarter || beat.graceType !== GraceType.None));
|
|
61502
61830
|
}
|
|
61503
|
-
constructor(staff, renderer) {
|
|
61831
|
+
constructor(staff, renderer, beamingRuleLookup) {
|
|
61504
61832
|
this._staff = staff;
|
|
61505
61833
|
this._renderer = renderer;
|
|
61506
61834
|
this.beats = [];
|
|
61835
|
+
this._beamingRuleLookup = beamingRuleLookup;
|
|
61507
61836
|
}
|
|
61508
61837
|
alignWithBeats() {
|
|
61509
61838
|
for (const v of this.drawingInfos.values()) {
|
|
@@ -61558,7 +61887,7 @@ class BeamingHelper {
|
|
|
61558
61887
|
switch (this.beats[this.beats.length - 1].beamingMode) {
|
|
61559
61888
|
case BeatBeamingMode.Auto:
|
|
61560
61889
|
case BeatBeamingMode.ForceSplitOnSecondaryToNext:
|
|
61561
|
-
add =
|
|
61890
|
+
add = this._canJoin(this.beats[this.beats.length - 1], beat);
|
|
61562
61891
|
break;
|
|
61563
61892
|
case BeatBeamingMode.ForceSplitToNext:
|
|
61564
61893
|
add = false;
|
|
@@ -61630,8 +61959,7 @@ class BeamingHelper {
|
|
|
61630
61959
|
this._highestNoteCompareValueInHelper = highestValueForNote;
|
|
61631
61960
|
}
|
|
61632
61961
|
}
|
|
61633
|
-
|
|
61634
|
-
static _canJoin(b1, b2) {
|
|
61962
|
+
_canJoin(b1, b2) {
|
|
61635
61963
|
// is this a voice we can join with?
|
|
61636
61964
|
if (!b1 ||
|
|
61637
61965
|
!b2 ||
|
|
@@ -61669,19 +61997,10 @@ class BeamingHelper {
|
|
|
61669
61997
|
return true;
|
|
61670
61998
|
}
|
|
61671
61999
|
}
|
|
61672
|
-
//
|
|
61673
|
-
|
|
61674
|
-
|
|
61675
|
-
|
|
61676
|
-
if (m1.masterBar.timeSignatureNumerator % 3 === 0) {
|
|
61677
|
-
divisionLength += (MidiUtils.QuarterTime / 2) | 0;
|
|
61678
|
-
}
|
|
61679
|
-
break;
|
|
61680
|
-
}
|
|
61681
|
-
// check if they are on the same division
|
|
61682
|
-
const division1 = ((divisionLength + start1) / divisionLength) | 0 | 0;
|
|
61683
|
-
const division2 = ((divisionLength + start2) / divisionLength) | 0 | 0;
|
|
61684
|
-
return division1 === division2;
|
|
62000
|
+
// check if they are on the same group as per rule definitions
|
|
62001
|
+
const groupId1 = this._beamingRuleLookup.calculateGroupIndex(start1);
|
|
62002
|
+
const groupId2 = this._beamingRuleLookup.calculateGroupIndex(start2);
|
|
62003
|
+
return groupId1 === groupId2;
|
|
61685
62004
|
}
|
|
61686
62005
|
static _canJoinDuration(d) {
|
|
61687
62006
|
switch (d) {
|
|
@@ -61848,6 +62167,61 @@ class BarCollisionHelper {
|
|
|
61848
62167
|
}
|
|
61849
62168
|
}
|
|
61850
62169
|
|
|
62170
|
+
/**
|
|
62171
|
+
* @internal
|
|
62172
|
+
*/
|
|
62173
|
+
class BeamingRuleLookup {
|
|
62174
|
+
_division = 0;
|
|
62175
|
+
_slots = [];
|
|
62176
|
+
_barDuration;
|
|
62177
|
+
constructor(barDuration, division, slots) {
|
|
62178
|
+
this._division = division;
|
|
62179
|
+
this._slots = slots;
|
|
62180
|
+
this._barDuration = barDuration;
|
|
62181
|
+
}
|
|
62182
|
+
calculateGroupIndex(beatStartTime) {
|
|
62183
|
+
// no slots -> all have their own group based (use the start time as index)
|
|
62184
|
+
if (this._slots.length === 0) {
|
|
62185
|
+
return beatStartTime;
|
|
62186
|
+
}
|
|
62187
|
+
// rollover within the bar.
|
|
62188
|
+
beatStartTime = beatStartTime % this._barDuration;
|
|
62189
|
+
const slotIndex = Math.floor(beatStartTime / this._division);
|
|
62190
|
+
return this._slots[slotIndex];
|
|
62191
|
+
}
|
|
62192
|
+
static build(masterBar, ruleDuration, ruleGroups) {
|
|
62193
|
+
const totalDuration = masterBar.calculateDuration(false);
|
|
62194
|
+
const division = MidiUtils.toTicks(ruleDuration);
|
|
62195
|
+
const slotCount = totalDuration / division;
|
|
62196
|
+
// should only happen in case of improper data.
|
|
62197
|
+
if (slotCount < 0 || ruleGroups.length === 0) {
|
|
62198
|
+
return new BeamingRuleLookup(0, 0, []);
|
|
62199
|
+
}
|
|
62200
|
+
let groupIndex = 0;
|
|
62201
|
+
let remainingSlots = ruleGroups[groupIndex];
|
|
62202
|
+
const slots = [];
|
|
62203
|
+
for (let i = 0; i < slotCount; i++) {
|
|
62204
|
+
if (groupIndex < ruleGroups.length) {
|
|
62205
|
+
slots.push(groupIndex);
|
|
62206
|
+
remainingSlots--;
|
|
62207
|
+
if (remainingSlots <= 0) {
|
|
62208
|
+
groupIndex++;
|
|
62209
|
+
if (groupIndex < ruleGroups.length) {
|
|
62210
|
+
remainingSlots = ruleGroups[groupIndex];
|
|
62211
|
+
}
|
|
62212
|
+
}
|
|
62213
|
+
}
|
|
62214
|
+
else {
|
|
62215
|
+
// no groups defined for the remaining slots: all slots are treated
|
|
62216
|
+
// as unjoined
|
|
62217
|
+
slots.push(groupIndex);
|
|
62218
|
+
groupIndex++;
|
|
62219
|
+
}
|
|
62220
|
+
}
|
|
62221
|
+
return new BeamingRuleLookup(totalDuration, division, slots);
|
|
62222
|
+
}
|
|
62223
|
+
}
|
|
62224
|
+
|
|
61851
62225
|
/**
|
|
61852
62226
|
* @internal
|
|
61853
62227
|
*/
|
|
@@ -61864,6 +62238,19 @@ class BarHelpers {
|
|
|
61864
62238
|
initialize() {
|
|
61865
62239
|
const barRenderer = this._renderer;
|
|
61866
62240
|
const bar = this._renderer.bar;
|
|
62241
|
+
const masterBar = bar.masterBar;
|
|
62242
|
+
const beamingRules = masterBar.actualBeamingRules ?? BarHelpers._findOrBuildDefaultBeamingRules(masterBar);
|
|
62243
|
+
const rule = beamingRules.findRule(bar.shortestDuration);
|
|
62244
|
+
// NOTE: moste rules have only one group definition, so its better to reuse the unique id
|
|
62245
|
+
// than compute a potentially shorter id here.
|
|
62246
|
+
const key = `beaming_${beamingRules.uniqueId}_${rule[0]}`;
|
|
62247
|
+
let beamingRuleLookup = this._renderer.scoreRenderer.layout.beamingRuleLookups.has(key)
|
|
62248
|
+
? this._renderer.scoreRenderer.layout.beamingRuleLookups.get(key)
|
|
62249
|
+
: undefined;
|
|
62250
|
+
if (!beamingRuleLookup) {
|
|
62251
|
+
beamingRuleLookup = BeamingRuleLookup.build(masterBar, rule[0], rule[1]);
|
|
62252
|
+
this._renderer.scoreRenderer.layout.beamingRuleLookups.set(key, beamingRuleLookup);
|
|
62253
|
+
}
|
|
61867
62254
|
let currentBeamHelper = null;
|
|
61868
62255
|
let currentGraceBeamHelper = null;
|
|
61869
62256
|
for (let i = 0, j = bar.voices.length; i < j; i++) {
|
|
@@ -61889,7 +62276,7 @@ class BarHelpers {
|
|
|
61889
62276
|
helperForBeat.finish();
|
|
61890
62277
|
}
|
|
61891
62278
|
// if not possible, create the next beaming helper
|
|
61892
|
-
helperForBeat = new BeamingHelper(bar.staff, barRenderer);
|
|
62279
|
+
helperForBeat = new BeamingHelper(bar.staff, barRenderer, beamingRuleLookup);
|
|
61893
62280
|
helperForBeat.preferredBeamDirection = this.preferredBeamDirection;
|
|
61894
62281
|
helperForBeat.checkBeat(b);
|
|
61895
62282
|
if (b.graceType !== GraceType.None) {
|
|
@@ -61912,6 +62299,69 @@ class BarHelpers {
|
|
|
61912
62299
|
currentGraceBeamHelper = null;
|
|
61913
62300
|
}
|
|
61914
62301
|
}
|
|
62302
|
+
static _defaultBeamingRules;
|
|
62303
|
+
static _findOrBuildDefaultBeamingRules(masterBar) {
|
|
62304
|
+
let defaultBeamingRules = BarHelpers._defaultBeamingRules;
|
|
62305
|
+
if (!defaultBeamingRules) {
|
|
62306
|
+
defaultBeamingRules = new Map([
|
|
62307
|
+
BeamingRules.createSimple(2, 16, Duration.Sixteenth, [1, 1]),
|
|
62308
|
+
BeamingRules.createSimple(1, 8, Duration.Eighth, [1]),
|
|
62309
|
+
BeamingRules.createSimple(1, 4, Duration.Quarter, [1]),
|
|
62310
|
+
BeamingRules.createSimple(3, 16, Duration.Sixteenth, [3]),
|
|
62311
|
+
BeamingRules.createSimple(4, 16, Duration.Sixteenth, [2, 2]),
|
|
62312
|
+
BeamingRules.createSimple(2, 8, Duration.Eighth, [1, 1]),
|
|
62313
|
+
BeamingRules.createSimple(5, 16, Duration.Sixteenth, [3, 2]),
|
|
62314
|
+
BeamingRules.createSimple(6, 16, Duration.Sixteenth, [3, 3]),
|
|
62315
|
+
BeamingRules.createSimple(3, 8, Duration.Eighth, [3]),
|
|
62316
|
+
BeamingRules.createSimple(4, 8, Duration.Eighth, [2, 2]),
|
|
62317
|
+
BeamingRules.createSimple(2, 4, Duration.Quarter, [1, 1]),
|
|
62318
|
+
BeamingRules.createSimple(9, 16, Duration.Sixteenth, [3, 3, 3]),
|
|
62319
|
+
BeamingRules.createSimple(5, 8, Duration.Eighth, [3, 2]),
|
|
62320
|
+
BeamingRules.createSimple(12, 16, Duration.Sixteenth, [3, 3, 3, 3]),
|
|
62321
|
+
BeamingRules.createSimple(6, 8, Duration.Eighth, [3, 3, 3]),
|
|
62322
|
+
BeamingRules.createSimple(3, 4, Duration.Quarter, [1, 1, 1]),
|
|
62323
|
+
BeamingRules.createSimple(7, 8, Duration.Eighth, [4, 3]),
|
|
62324
|
+
BeamingRules.createSimple(8, 8, Duration.Eighth, [3, 3, 2]),
|
|
62325
|
+
BeamingRules.createSimple(4, 4, Duration.Quarter, [1, 1, 1, 1]),
|
|
62326
|
+
BeamingRules.createSimple(9, 8, Duration.Eighth, [3, 3, 3]),
|
|
62327
|
+
BeamingRules.createSimple(10, 8, Duration.Eighth, [4, 3, 3]),
|
|
62328
|
+
BeamingRules.createSimple(5, 4, Duration.Quarter, [1, 1, 1, 1, 1]),
|
|
62329
|
+
BeamingRules.createSimple(12, 8, Duration.Eighth, [3, 3, 3, 3]),
|
|
62330
|
+
BeamingRules.createSimple(6, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1]),
|
|
62331
|
+
BeamingRules.createSimple(15, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3]),
|
|
62332
|
+
BeamingRules.createSimple(8, 4, Duration.Quarter, [1, 1, 1, 1, 1, 1, 1, 1]),
|
|
62333
|
+
BeamingRules.createSimple(18, 8, Duration.Eighth, [3, 3, 3, 3, 3, 3])
|
|
62334
|
+
].map(r => [`${r.timeSignatureNumerator}_${r.timeSignatureDenominator}`, r]));
|
|
62335
|
+
BarHelpers._defaultBeamingRules = defaultBeamingRules;
|
|
62336
|
+
}
|
|
62337
|
+
const key = `${masterBar.timeSignatureNumerator}_${masterBar.timeSignatureDenominator}`;
|
|
62338
|
+
if (defaultBeamingRules.has(key)) {
|
|
62339
|
+
return defaultBeamingRules.get(key);
|
|
62340
|
+
}
|
|
62341
|
+
// NOTE: this is the old alphaTab logic how we used to beamed bars.
|
|
62342
|
+
// we either group in quarters, or in 3x8ths depending on the key signature
|
|
62343
|
+
let divisionLength = MidiUtils.QuarterTime;
|
|
62344
|
+
switch (masterBar.timeSignatureDenominator) {
|
|
62345
|
+
case 8:
|
|
62346
|
+
if (masterBar.timeSignatureNumerator % 3 === 0) {
|
|
62347
|
+
divisionLength += (MidiUtils.QuarterTime / 2) | 0;
|
|
62348
|
+
}
|
|
62349
|
+
break;
|
|
62350
|
+
}
|
|
62351
|
+
const numberOfDivisions = Math.ceil(masterBar.calculateDuration(false) / divisionLength);
|
|
62352
|
+
const notesPerDivision = (divisionLength / MidiUtils.QuarterTime) * 2;
|
|
62353
|
+
const fallback = new BeamingRules();
|
|
62354
|
+
const groups = [];
|
|
62355
|
+
for (let i = 0; i < numberOfDivisions; i++) {
|
|
62356
|
+
groups.push(notesPerDivision);
|
|
62357
|
+
}
|
|
62358
|
+
fallback.groups.set(Duration.Eighth, groups);
|
|
62359
|
+
fallback.timeSignatureNumerator = masterBar.timeSignatureNumerator;
|
|
62360
|
+
fallback.timeSignatureDenominator = masterBar.timeSignatureDenominator;
|
|
62361
|
+
fallback.finish();
|
|
62362
|
+
defaultBeamingRules.set(key, fallback);
|
|
62363
|
+
return fallback;
|
|
62364
|
+
}
|
|
61915
62365
|
getBeamingHelperForBeat(beat) {
|
|
61916
62366
|
return this._beamHelperLookup.has(beat.id) ? this._beamHelperLookup.get(beat.id) : undefined;
|
|
61917
62367
|
}
|
|
@@ -65844,6 +66294,7 @@ class ScoreLayout {
|
|
|
65844
66294
|
this.renderer = renderer;
|
|
65845
66295
|
}
|
|
65846
66296
|
slurRegistry = new SlurRegistry();
|
|
66297
|
+
beamingRuleLookups = new Map();
|
|
65847
66298
|
resize() {
|
|
65848
66299
|
this._lazyPartials.clear();
|
|
65849
66300
|
this.slurRegistry.clear();
|
|
@@ -65852,6 +66303,7 @@ class ScoreLayout {
|
|
|
65852
66303
|
layoutAndRender() {
|
|
65853
66304
|
this._lazyPartials.clear();
|
|
65854
66305
|
this.slurRegistry.clear();
|
|
66306
|
+
this.beamingRuleLookups.clear();
|
|
65855
66307
|
this._barRendererLookup.clear();
|
|
65856
66308
|
this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
|
|
65857
66309
|
const score = this.renderer.score;
|
|
@@ -78039,6 +78491,9 @@ class GpifWriter {
|
|
|
78039
78491
|
key.addElement('Sharps').innerText = 'Sharps';
|
|
78040
78492
|
masterBarNode.addElement('Time').innerText =
|
|
78041
78493
|
`${masterBar.timeSignatureNumerator}/${masterBar.timeSignatureDenominator}`;
|
|
78494
|
+
if (masterBar.actualBeamingRules) {
|
|
78495
|
+
this._writeBarXProperties(masterBarNode, masterBar);
|
|
78496
|
+
}
|
|
78042
78497
|
if (masterBar.isFreeTime) {
|
|
78043
78498
|
masterBarNode.addElement('FreeTime');
|
|
78044
78499
|
}
|
|
@@ -78148,6 +78603,29 @@ class GpifWriter {
|
|
|
78148
78603
|
}
|
|
78149
78604
|
this._writeFermatas(masterBarNode, masterBar);
|
|
78150
78605
|
}
|
|
78606
|
+
_writeBarXProperties(masterBarNode, masterBar) {
|
|
78607
|
+
const properties = masterBarNode.addElement('XProperties');
|
|
78608
|
+
const beamingRules = masterBar.actualBeamingRules;
|
|
78609
|
+
if (beamingRules) {
|
|
78610
|
+
// prefer 8th note rule (that's what GP mostly has)
|
|
78611
|
+
const rule = beamingRules.findRule(Duration.Eighth);
|
|
78612
|
+
// NOTE: it's not clear if guitar pro supports quarter rules
|
|
78613
|
+
// for that case we better convert this to an "8th" note rule.
|
|
78614
|
+
let durationProp = rule[0];
|
|
78615
|
+
let groupSizeFactor = 1;
|
|
78616
|
+
if (rule[0] === Duration.Quarter) {
|
|
78617
|
+
durationProp = 8;
|
|
78618
|
+
groupSizeFactor = 2;
|
|
78619
|
+
}
|
|
78620
|
+
this._writeSimpleXPropertyNode(properties, '1124139010', 'Int', durationProp.toString());
|
|
78621
|
+
const startGroupid = 1124139264;
|
|
78622
|
+
let i = 0;
|
|
78623
|
+
while (i < rule[1].length) {
|
|
78624
|
+
this._writeSimpleXPropertyNode(properties, (startGroupid + i).toString(), 'Int', (rule[1][i] * groupSizeFactor).toString());
|
|
78625
|
+
i++;
|
|
78626
|
+
}
|
|
78627
|
+
}
|
|
78628
|
+
}
|
|
78151
78629
|
_writeFermatas(parent, masterBar) {
|
|
78152
78630
|
const fermataCount = masterBar.fermata?.size ?? 0;
|
|
78153
78631
|
if (fermataCount === 0) {
|