@coderline/alphatab 1.8.0-alpha.1647 → 1.8.0-alpha.1651
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 +940 -270
- package/dist/alphaTab.d.ts +147 -4
- package/dist/alphaTab.js +940 -270
- 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.1651 (develop, build 1651)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, 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 = '2025-12-
|
|
208
|
-
static commit = '
|
|
206
|
+
static version = '1.8.0-alpha.1651';
|
|
207
|
+
static date = '2025-12-21T02:27:24.035Z';
|
|
208
|
+
static commit = '758ae890dfa8cdb21a9dd469ade012f834ac0433';
|
|
209
209
|
static print(print) {
|
|
210
210
|
print(`alphaTab ${VersionInfo.version}`);
|
|
211
211
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -2744,6 +2744,23 @@ class RenderStylesheet {
|
|
|
2744
2744
|
* Whether barlines should be drawn across staves within the same system.
|
|
2745
2745
|
*/
|
|
2746
2746
|
extendBarLines = false;
|
|
2747
|
+
/**
|
|
2748
|
+
* Whether to hide empty staves.
|
|
2749
|
+
*/
|
|
2750
|
+
hideEmptyStaves = false;
|
|
2751
|
+
/**
|
|
2752
|
+
* Whether to also hide empty staves in the first system.
|
|
2753
|
+
* @remarks
|
|
2754
|
+
* Only has an effect when activating {@link hideEmptyStaves}.
|
|
2755
|
+
*/
|
|
2756
|
+
hideEmptyStavesInFirstSystem = false;
|
|
2757
|
+
/**
|
|
2758
|
+
* Whether to show brackets and braces across single staves.
|
|
2759
|
+
* @remarks
|
|
2760
|
+
* This allows a more consistent view for identifying staves when using
|
|
2761
|
+
* {@link hideEmptyStaves}
|
|
2762
|
+
*/
|
|
2763
|
+
showSingleStaffBrackets = false;
|
|
2747
2764
|
}
|
|
2748
2765
|
|
|
2749
2766
|
/**
|
|
@@ -8673,7 +8690,10 @@ class AlphaTex1LanguageDefinitions {
|
|
|
8673
8690
|
['firstsystemtracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
|
|
8674
8691
|
['othersystemstracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]],
|
|
8675
8692
|
['extendbarlines', null],
|
|
8676
|
-
['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]]
|
|
8693
|
+
['chorddiagramsinscore', [[[[10], 1, ['true', 'false']]]]],
|
|
8694
|
+
['hideemptystaves', null],
|
|
8695
|
+
['hideemptystavesinfirstsystem', null],
|
|
8696
|
+
['showsinglestaffbrackets', null]
|
|
8677
8697
|
]);
|
|
8678
8698
|
static staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([
|
|
8679
8699
|
['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]],
|
|
@@ -9013,6 +9033,9 @@ class AlphaTex1LanguageDefinitions {
|
|
|
9013
9033
|
['othersystemstracknameorientation', null],
|
|
9014
9034
|
['extendbarlines', null],
|
|
9015
9035
|
['chorddiagramsinscore', null],
|
|
9036
|
+
['hideemptystaves', null],
|
|
9037
|
+
['hideemptystavesinfirstsystem', null],
|
|
9038
|
+
['showsinglestaffbrackets', null],
|
|
9016
9039
|
[
|
|
9017
9040
|
'tuning',
|
|
9018
9041
|
[
|
|
@@ -13066,7 +13089,7 @@ class Bounds {
|
|
|
13066
13089
|
*/
|
|
13067
13090
|
class MasterBarBounds {
|
|
13068
13091
|
/**
|
|
13069
|
-
*
|
|
13092
|
+
* The MasterBar index within the data model represented by these bounds.
|
|
13070
13093
|
*/
|
|
13071
13094
|
index = 0;
|
|
13072
13095
|
/**
|
|
@@ -13274,6 +13297,7 @@ class BoundsLookup {
|
|
|
13274
13297
|
mb.visualBounds = this._boundsToJson(masterBar.visualBounds);
|
|
13275
13298
|
mb.realBounds = this._boundsToJson(masterBar.realBounds);
|
|
13276
13299
|
mb.index = masterBar.index;
|
|
13300
|
+
mb.isFirstOfLine = masterBar.isFirstOfLine;
|
|
13277
13301
|
mb.bars = [];
|
|
13278
13302
|
for (const bar of masterBar.bars) {
|
|
13279
13303
|
const b = {};
|
|
@@ -13330,7 +13354,7 @@ class BoundsLookup {
|
|
|
13330
13354
|
mb.lineAlignedBounds = BoundsLookup._boundsFromJson(masterBar.lineAlignedBounds);
|
|
13331
13355
|
mb.visualBounds = BoundsLookup._boundsFromJson(masterBar.visualBounds);
|
|
13332
13356
|
mb.realBounds = BoundsLookup._boundsFromJson(masterBar.realBounds);
|
|
13333
|
-
|
|
13357
|
+
lookup.addMasterBar(mb);
|
|
13334
13358
|
for (const bar of masterBar.bars) {
|
|
13335
13359
|
const b = new BarBounds();
|
|
13336
13360
|
b.visualBounds = BoundsLookup._boundsFromJson(bar.visualBounds);
|
|
@@ -13887,6 +13911,15 @@ class AlphaTex1LanguageHandler {
|
|
|
13887
13911
|
? AlphaTex1LanguageHandler._booleanLikeValue(metaData.arguments.arguments, 0)
|
|
13888
13912
|
: true;
|
|
13889
13913
|
return ApplyNodeResult.Applied;
|
|
13914
|
+
case 'hideemptystaves':
|
|
13915
|
+
score.stylesheet.hideEmptyStaves = true;
|
|
13916
|
+
return ApplyNodeResult.Applied;
|
|
13917
|
+
case 'hideemptystavesinfirstsystem':
|
|
13918
|
+
score.stylesheet.hideEmptyStavesInFirstSystem = true;
|
|
13919
|
+
return ApplyNodeResult.Applied;
|
|
13920
|
+
case 'showsinglestaffbrackets':
|
|
13921
|
+
score.stylesheet.showSingleStaffBrackets = true;
|
|
13922
|
+
return ApplyNodeResult.Applied;
|
|
13890
13923
|
default:
|
|
13891
13924
|
return ApplyNodeResult.NotAppliedUnrecognizedMarker;
|
|
13892
13925
|
}
|
|
@@ -15718,6 +15751,15 @@ class AlphaTex1LanguageHandler {
|
|
|
15718
15751
|
if (stylesheet.globalDisplayChordDiagramsInScore) {
|
|
15719
15752
|
nodes.push(Atnf.meta('chordDiagramsInScore'));
|
|
15720
15753
|
}
|
|
15754
|
+
if (stylesheet.hideEmptyStaves) {
|
|
15755
|
+
nodes.push(Atnf.meta('hideEmptyStaves'));
|
|
15756
|
+
}
|
|
15757
|
+
if (stylesheet.hideEmptyStavesInFirstSystem) {
|
|
15758
|
+
nodes.push(Atnf.meta('hideEmptyStavesInFirstSystem'));
|
|
15759
|
+
}
|
|
15760
|
+
if (stylesheet.showSingleStaffBrackets) {
|
|
15761
|
+
nodes.push(Atnf.meta('showSingleStaffBrackets'));
|
|
15762
|
+
}
|
|
15721
15763
|
// Unsupported:
|
|
15722
15764
|
// 'globaldisplaychorddiagramsontop',
|
|
15723
15765
|
// 'pertrackchorddiagramsontop',
|
|
@@ -38804,6 +38846,9 @@ class RenderStylesheetSerializer {
|
|
|
38804
38846
|
}
|
|
38805
38847
|
}
|
|
38806
38848
|
o.set("extendbarlines", obj.extendBarLines);
|
|
38849
|
+
o.set("hideemptystaves", obj.hideEmptyStaves);
|
|
38850
|
+
o.set("hideemptystavesinfirstsystem", obj.hideEmptyStavesInFirstSystem);
|
|
38851
|
+
o.set("showsinglestaffbrackets", obj.showSingleStaffBrackets);
|
|
38807
38852
|
return o;
|
|
38808
38853
|
}
|
|
38809
38854
|
static setProperty(obj, property, v) {
|
|
@@ -38865,6 +38910,15 @@ class RenderStylesheetSerializer {
|
|
|
38865
38910
|
case "extendbarlines":
|
|
38866
38911
|
obj.extendBarLines = v;
|
|
38867
38912
|
return true;
|
|
38913
|
+
case "hideemptystaves":
|
|
38914
|
+
obj.hideEmptyStaves = v;
|
|
38915
|
+
return true;
|
|
38916
|
+
case "hideemptystavesinfirstsystem":
|
|
38917
|
+
obj.hideEmptyStavesInFirstSystem = v;
|
|
38918
|
+
return true;
|
|
38919
|
+
case "showsinglestaffbrackets":
|
|
38920
|
+
obj.showSingleStaffBrackets = v;
|
|
38921
|
+
return true;
|
|
38868
38922
|
}
|
|
38869
38923
|
return false;
|
|
38870
38924
|
}
|
|
@@ -43604,6 +43658,12 @@ var ScrollMode;
|
|
|
43604
43658
|
* Scrolling happens as soon the cursors exceed the displayed range.
|
|
43605
43659
|
*/
|
|
43606
43660
|
ScrollMode[ScrollMode["OffScreen"] = 2] = "OffScreen";
|
|
43661
|
+
/**
|
|
43662
|
+
* Scrolling happens constantly in a smooth fashion.
|
|
43663
|
+
* This will disable the use of any native scroll optimizations but
|
|
43664
|
+
* manually scroll the scroll container in the required speed.
|
|
43665
|
+
*/
|
|
43666
|
+
ScrollMode[ScrollMode["Smooth"] = 3] = "Smooth";
|
|
43607
43667
|
})(ScrollMode || (ScrollMode = {}));
|
|
43608
43668
|
/**
|
|
43609
43669
|
* This object defines the details on how to generate the vibrato effects.
|
|
@@ -45892,6 +45952,19 @@ class BeatTickLookup {
|
|
|
45892
45952
|
}
|
|
45893
45953
|
return null;
|
|
45894
45954
|
}
|
|
45955
|
+
/**
|
|
45956
|
+
* Looks for the first visible beat which starts at this lookup so it can be used for cursor placement.
|
|
45957
|
+
* @param checker The custom checker to see if a beat is visible.
|
|
45958
|
+
* @returns The first beat which is visible according to the given tracks or null.
|
|
45959
|
+
*/
|
|
45960
|
+
getVisibleBeatAtStartWithChecker(checker) {
|
|
45961
|
+
for (const b of this.highlightedBeats) {
|
|
45962
|
+
if (b.playbackStart === this.start && checker.isVisible(b.beat)) {
|
|
45963
|
+
return b.beat;
|
|
45964
|
+
}
|
|
45965
|
+
}
|
|
45966
|
+
return null;
|
|
45967
|
+
}
|
|
45895
45968
|
}
|
|
45896
45969
|
|
|
45897
45970
|
/**
|
|
@@ -46331,6 +46404,18 @@ class MidiTickLookupFindBeatResult {
|
|
|
46331
46404
|
}
|
|
46332
46405
|
}
|
|
46333
46406
|
}
|
|
46407
|
+
/**
|
|
46408
|
+
* @internal
|
|
46409
|
+
*/
|
|
46410
|
+
class TrackLookupBeatVisibilityChecker {
|
|
46411
|
+
_lookup;
|
|
46412
|
+
constructor(lookup) {
|
|
46413
|
+
this._lookup = lookup;
|
|
46414
|
+
}
|
|
46415
|
+
isVisible(beat) {
|
|
46416
|
+
return this._lookup.has(beat.voice.bar.staff.track.index);
|
|
46417
|
+
}
|
|
46418
|
+
}
|
|
46334
46419
|
/**
|
|
46335
46420
|
* This class holds all information about when {@link MasterBar}s and {@link Beat}s are played.
|
|
46336
46421
|
*
|
|
@@ -46391,31 +46476,44 @@ class MidiTickLookup {
|
|
|
46391
46476
|
* @returns The information about the current beat or null if no beat could be found.
|
|
46392
46477
|
*/
|
|
46393
46478
|
findBeat(trackLookup, tick, currentBeatHint = null) {
|
|
46479
|
+
return this.findBeatWithChecker(new TrackLookupBeatVisibilityChecker(trackLookup), tick, currentBeatHint);
|
|
46480
|
+
}
|
|
46481
|
+
/**
|
|
46482
|
+
* Finds the currently played beat given a list of tracks and the current time.
|
|
46483
|
+
* @param checker The checker to ask whether a beat is visible and should be considered for result.
|
|
46484
|
+
* @param tick The current time in midi ticks.
|
|
46485
|
+
* @param currentBeatHint Used for optimized lookup during playback. By passing in a previous result lookup of the next one can be optimized using heuristics. (optional).
|
|
46486
|
+
* @returns The information about the current beat or null if no beat could be found.
|
|
46487
|
+
*/
|
|
46488
|
+
findBeatWithChecker(checker, tick, currentBeatHint = null) {
|
|
46394
46489
|
let result = null;
|
|
46395
46490
|
if (currentBeatHint) {
|
|
46396
|
-
result = this._findBeatFast(
|
|
46491
|
+
result = this._findBeatFast(checker, currentBeatHint, tick);
|
|
46397
46492
|
}
|
|
46398
46493
|
if (!result) {
|
|
46399
|
-
result = this._findBeatSlow(
|
|
46494
|
+
result = this._findBeatSlow(checker, currentBeatHint, tick, false);
|
|
46400
46495
|
}
|
|
46401
46496
|
return result;
|
|
46402
46497
|
}
|
|
46403
|
-
_findBeatFast(
|
|
46498
|
+
_findBeatFast(checker, currentBeatHint, tick) {
|
|
46404
46499
|
// still within current lookup.
|
|
46405
46500
|
if (tick >= currentBeatHint.start && tick < currentBeatHint.end) {
|
|
46406
46501
|
return currentBeatHint;
|
|
46407
46502
|
}
|
|
46408
46503
|
// already on the next beat?
|
|
46409
|
-
if (currentBeatHint.nextBeat &&
|
|
46504
|
+
if (currentBeatHint.nextBeat &&
|
|
46505
|
+
tick >= currentBeatHint.nextBeat.start &&
|
|
46506
|
+
tick < currentBeatHint.nextBeat.end &&
|
|
46507
|
+
(checker === undefined || checker.isVisible(currentBeatHint.nextBeat.beat))) {
|
|
46410
46508
|
const next = currentBeatHint.nextBeat;
|
|
46411
46509
|
// fill next in chain
|
|
46412
|
-
this._fillNextBeat(next,
|
|
46510
|
+
this._fillNextBeat(next, checker);
|
|
46413
46511
|
return next;
|
|
46414
46512
|
}
|
|
46415
46513
|
// likely a loop or manual seek, need to fallback to slow path
|
|
46416
46514
|
return null;
|
|
46417
46515
|
}
|
|
46418
|
-
_fillNextBeatMultiBarRest(current,
|
|
46516
|
+
_fillNextBeatMultiBarRest(current, checker) {
|
|
46419
46517
|
const group = this.multiBarRestInfo.get(current.masterBar.masterBar.index);
|
|
46420
46518
|
// this is a bit sensitive. we assume that the sequence of multi-rest bars and the
|
|
46421
46519
|
// chained nextMasterBar equal. so we just jump over X bars
|
|
@@ -46429,7 +46527,7 @@ class MidiTickLookup {
|
|
|
46429
46527
|
if (endMasterBar) {
|
|
46430
46528
|
// one more following -> use start of next
|
|
46431
46529
|
if (endMasterBar.nextMasterBar) {
|
|
46432
|
-
current.nextBeat = this._firstBeatInMasterBar(
|
|
46530
|
+
current.nextBeat = this._firstBeatInMasterBar(checker, endMasterBar.nextMasterBar, endMasterBar.nextMasterBar.start, true);
|
|
46433
46531
|
// if we have the next beat take the difference between the times as duration
|
|
46434
46532
|
if (current.nextBeat) {
|
|
46435
46533
|
current.tickDuration = current.nextBeat.start - current.start;
|
|
@@ -46459,19 +46557,19 @@ class MidiTickLookup {
|
|
|
46459
46557
|
}
|
|
46460
46558
|
current.calculateDuration();
|
|
46461
46559
|
}
|
|
46462
|
-
_fillNextBeat(current,
|
|
46560
|
+
_fillNextBeat(current, checker) {
|
|
46463
46561
|
// on multibar rests take the duration until the end.
|
|
46464
46562
|
if (this._isMultiBarRestResult(current)) {
|
|
46465
|
-
this._fillNextBeatMultiBarRest(current,
|
|
46563
|
+
this._fillNextBeatMultiBarRest(current, checker);
|
|
46466
46564
|
}
|
|
46467
46565
|
else {
|
|
46468
|
-
this._fillNextBeatDefault(current,
|
|
46566
|
+
this._fillNextBeatDefault(current, checker);
|
|
46469
46567
|
}
|
|
46470
46568
|
}
|
|
46471
|
-
_fillNextBeatDefault(current,
|
|
46472
|
-
current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end,
|
|
46569
|
+
_fillNextBeatDefault(current, checker) {
|
|
46570
|
+
current.nextBeat = this._findBeatInMasterBar(current.masterBar, current.beatLookup.nextBeat, current.end, checker, true);
|
|
46473
46571
|
if (current.nextBeat == null) {
|
|
46474
|
-
current.nextBeat = this._findBeatSlow(
|
|
46572
|
+
current.nextBeat = this._findBeatSlow(checker, current, current.end, true);
|
|
46475
46573
|
}
|
|
46476
46574
|
// if we have the next beat take the difference between the times as duration
|
|
46477
46575
|
if (current.nextBeat) {
|
|
@@ -46502,7 +46600,7 @@ class MidiTickLookup {
|
|
|
46502
46600
|
beat.isRest &&
|
|
46503
46601
|
beat.voice.bar.isRestOnly);
|
|
46504
46602
|
}
|
|
46505
|
-
_findBeatSlow(
|
|
46603
|
+
_findBeatSlow(checker, currentBeatHint, tick, isNextSearch) {
|
|
46506
46604
|
// get all beats within the masterbar
|
|
46507
46605
|
let masterBar = null;
|
|
46508
46606
|
if (currentBeatHint != null) {
|
|
@@ -46524,14 +46622,14 @@ class MidiTickLookup {
|
|
|
46524
46622
|
if (!masterBar) {
|
|
46525
46623
|
return null;
|
|
46526
46624
|
}
|
|
46527
|
-
return this._firstBeatInMasterBar(
|
|
46625
|
+
return this._firstBeatInMasterBar(checker, masterBar, tick, isNextSearch);
|
|
46528
46626
|
}
|
|
46529
|
-
_firstBeatInMasterBar(
|
|
46627
|
+
_firstBeatInMasterBar(checker, startMasterBar, tick, isNextSearch) {
|
|
46530
46628
|
let masterBar = startMasterBar;
|
|
46531
46629
|
// scan through beats and find first one which has a beat visible
|
|
46532
46630
|
while (masterBar) {
|
|
46533
46631
|
if (masterBar.firstBeat) {
|
|
46534
|
-
const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick,
|
|
46632
|
+
const beat = this._findBeatInMasterBar(masterBar, masterBar.firstBeat, tick, checker, isNextSearch);
|
|
46535
46633
|
if (beat) {
|
|
46536
46634
|
return beat;
|
|
46537
46635
|
}
|
|
@@ -46549,7 +46647,7 @@ class MidiTickLookup {
|
|
|
46549
46647
|
* @param isNextSearch
|
|
46550
46648
|
* @returns
|
|
46551
46649
|
*/
|
|
46552
|
-
_findBeatInMasterBar(masterBar, currentStartLookup, tick,
|
|
46650
|
+
_findBeatInMasterBar(masterBar, currentStartLookup, tick, checker, isNextSearch) {
|
|
46553
46651
|
if (!currentStartLookup) {
|
|
46554
46652
|
return null;
|
|
46555
46653
|
}
|
|
@@ -46563,7 +46661,7 @@ class MidiTickLookup {
|
|
|
46563
46661
|
(currentStartLookup.start <= relativeTick || (isNextSearch && relativeTick < 0)) &&
|
|
46564
46662
|
relativeTick < currentStartLookup.end) {
|
|
46565
46663
|
startBeatLookup = currentStartLookup;
|
|
46566
|
-
startBeat = currentStartLookup.
|
|
46664
|
+
startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
|
|
46567
46665
|
// found the matching beat lookup but none of the beats are visible
|
|
46568
46666
|
// in this case scan further to the next lookup which has any visible beat
|
|
46569
46667
|
if (!startBeat) {
|
|
@@ -46571,7 +46669,7 @@ class MidiTickLookup {
|
|
|
46571
46669
|
let currentMasterBar = masterBar;
|
|
46572
46670
|
while (currentMasterBar != null && startBeat == null) {
|
|
46573
46671
|
while (currentStartLookup != null) {
|
|
46574
|
-
startBeat = currentStartLookup.
|
|
46672
|
+
startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
|
|
46575
46673
|
if (startBeat) {
|
|
46576
46674
|
startBeatLookup = currentStartLookup;
|
|
46577
46675
|
masterBar = currentMasterBar;
|
|
@@ -46589,7 +46687,7 @@ class MidiTickLookup {
|
|
|
46589
46687
|
let currentMasterBar = masterBar;
|
|
46590
46688
|
while (currentMasterBar != null && startBeat == null) {
|
|
46591
46689
|
while (currentStartLookup != null) {
|
|
46592
|
-
startBeat = currentStartLookup.
|
|
46690
|
+
startBeat = currentStartLookup.getVisibleBeatAtStartWithChecker(checker);
|
|
46593
46691
|
if (startBeat) {
|
|
46594
46692
|
startBeatLookup = currentStartLookup;
|
|
46595
46693
|
masterBar = currentMasterBar;
|
|
@@ -46613,17 +46711,17 @@ class MidiTickLookup {
|
|
|
46613
46711
|
if (startBeat == null) {
|
|
46614
46712
|
return null;
|
|
46615
46713
|
}
|
|
46616
|
-
const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch,
|
|
46714
|
+
const result = this._createResult(masterBar, startBeatLookup, startBeat, isNextSearch, checker);
|
|
46617
46715
|
return result;
|
|
46618
46716
|
}
|
|
46619
|
-
_createResult(masterBar, beatLookup, beat, isNextSearch,
|
|
46717
|
+
_createResult(masterBar, beatLookup, beat, isNextSearch, checker) {
|
|
46620
46718
|
const result = new MidiTickLookupFindBeatResult(masterBar);
|
|
46621
46719
|
result.beat = beat;
|
|
46622
46720
|
result.beatLookup = beatLookup;
|
|
46623
46721
|
result.tickDuration = beatLookup.end - beatLookup.start;
|
|
46624
46722
|
if (!isNextSearch) {
|
|
46625
46723
|
// the next beat filling will adjust this result with the respective durations
|
|
46626
|
-
this._fillNextBeat(result,
|
|
46724
|
+
this._fillNextBeat(result, checker);
|
|
46627
46725
|
}
|
|
46628
46726
|
else if (this._isMultiBarRestResult(result)) {
|
|
46629
46727
|
const multiRest = this.multiBarRestInfo.get(masterBar.masterBar.index);
|
|
@@ -48496,6 +48594,25 @@ class MidiFileGenerator {
|
|
|
48496
48594
|
}
|
|
48497
48595
|
}
|
|
48498
48596
|
|
|
48597
|
+
/**
|
|
48598
|
+
* Represents the information related to a resize event.
|
|
48599
|
+
* @public
|
|
48600
|
+
*/
|
|
48601
|
+
class ResizeEventArgs {
|
|
48602
|
+
/**
|
|
48603
|
+
* Gets the size before the resizing happened.
|
|
48604
|
+
*/
|
|
48605
|
+
oldWidth = 0;
|
|
48606
|
+
/**
|
|
48607
|
+
* Gets the size after the resize was complete.
|
|
48608
|
+
*/
|
|
48609
|
+
newWidth = 0;
|
|
48610
|
+
/**
|
|
48611
|
+
* Gets the settings currently used for rendering.
|
|
48612
|
+
*/
|
|
48613
|
+
settings = null;
|
|
48614
|
+
}
|
|
48615
|
+
|
|
48499
48616
|
/**
|
|
48500
48617
|
* Lists the different position modes for {@link BarRendererBase.getBeatX}
|
|
48501
48618
|
* @internal
|
|
@@ -48922,22 +49039,262 @@ class ScoreRendererWrapper {
|
|
|
48922
49039
|
}
|
|
48923
49040
|
|
|
48924
49041
|
/**
|
|
48925
|
-
*
|
|
48926
|
-
* @
|
|
49042
|
+
* Some basic scroll handler checking for changed offsets and scroll if changed.
|
|
49043
|
+
* @internal
|
|
48927
49044
|
*/
|
|
48928
|
-
class
|
|
48929
|
-
|
|
48930
|
-
|
|
48931
|
-
|
|
48932
|
-
|
|
48933
|
-
|
|
48934
|
-
|
|
48935
|
-
|
|
48936
|
-
|
|
48937
|
-
|
|
48938
|
-
|
|
48939
|
-
|
|
48940
|
-
|
|
49045
|
+
class BasicScrollHandler {
|
|
49046
|
+
api;
|
|
49047
|
+
lastScroll = -1;
|
|
49048
|
+
constructor(api) {
|
|
49049
|
+
this.api = api;
|
|
49050
|
+
}
|
|
49051
|
+
[Symbol.dispose]() {
|
|
49052
|
+
}
|
|
49053
|
+
forceScrollTo(currentBeatBounds) {
|
|
49054
|
+
this._scrollToBeat(currentBeatBounds, true);
|
|
49055
|
+
this.lastScroll = -1; // force new scroll on next update
|
|
49056
|
+
}
|
|
49057
|
+
_scrollToBeat(currentBeatBounds, force) {
|
|
49058
|
+
const newLastScroll = this.calculateLastScroll(currentBeatBounds);
|
|
49059
|
+
// no change, and no instant/force scroll
|
|
49060
|
+
if (newLastScroll === this.lastScroll && !force) {
|
|
49061
|
+
return;
|
|
49062
|
+
}
|
|
49063
|
+
this.lastScroll = newLastScroll;
|
|
49064
|
+
this.doScroll(currentBeatBounds);
|
|
49065
|
+
}
|
|
49066
|
+
onBeatCursorUpdating(startBeat, _endBeat, _cursorMode, _actualBeatCursorStartX, _actualBeatCursorEndX, _actualBeatCursorTransitionDuration) {
|
|
49067
|
+
this._scrollToBeat(startBeat, false);
|
|
49068
|
+
}
|
|
49069
|
+
}
|
|
49070
|
+
/**
|
|
49071
|
+
* This is the default scroll handler for vertical layouts using {@link ScrollMode.Continuous}.
|
|
49072
|
+
* Whenever the system changes, we scroll to the new system position vertically.
|
|
49073
|
+
* @internal
|
|
49074
|
+
*/
|
|
49075
|
+
class VerticalContinuousScrollHandler extends BasicScrollHandler {
|
|
49076
|
+
calculateLastScroll(currentBeatBounds) {
|
|
49077
|
+
return currentBeatBounds.barBounds.masterBarBounds.realBounds.y;
|
|
49078
|
+
}
|
|
49079
|
+
doScroll(currentBeatBounds) {
|
|
49080
|
+
const ui = this.api.uiFacade;
|
|
49081
|
+
const settings = this.api.settings;
|
|
49082
|
+
const scroll = ui.getScrollContainer();
|
|
49083
|
+
const elementOffset = ui.getOffset(scroll, this.api.container);
|
|
49084
|
+
const y = currentBeatBounds.barBounds.masterBarBounds.realBounds.y + settings.player.scrollOffsetY;
|
|
49085
|
+
ui.scrollToY(scroll, elementOffset.y + y, this.api.settings.player.scrollSpeed);
|
|
49086
|
+
}
|
|
49087
|
+
}
|
|
49088
|
+
/**
|
|
49089
|
+
* This is the default scroll handler for vertical layouts using {@link ScrollMode.OffScreen}.
|
|
49090
|
+
* Whenever the system changes, we check if the new system bounds are out-of-screen and if yes, we scroll.
|
|
49091
|
+
* @internal
|
|
49092
|
+
*/
|
|
49093
|
+
class VerticalOffScreenScrollHandler extends BasicScrollHandler {
|
|
49094
|
+
calculateLastScroll(currentBeatBounds) {
|
|
49095
|
+
// check for system change
|
|
49096
|
+
return currentBeatBounds.barBounds.masterBarBounds.realBounds.y;
|
|
49097
|
+
}
|
|
49098
|
+
doScroll(currentBeatBounds) {
|
|
49099
|
+
const ui = this.api.uiFacade;
|
|
49100
|
+
const settings = this.api.settings;
|
|
49101
|
+
const scroll = ui.getScrollContainer();
|
|
49102
|
+
const elementBottom = scroll.scrollTop + ui.getOffset(null, scroll).h;
|
|
49103
|
+
const barBoundings = currentBeatBounds.barBounds.masterBarBounds;
|
|
49104
|
+
if (barBoundings.visualBounds.y + barBoundings.visualBounds.h >= elementBottom ||
|
|
49105
|
+
barBoundings.visualBounds.y < scroll.scrollTop) {
|
|
49106
|
+
const scrollTop = barBoundings.realBounds.y + settings.player.scrollOffsetY;
|
|
49107
|
+
ui.scrollToY(scroll, scrollTop, settings.player.scrollSpeed);
|
|
49108
|
+
}
|
|
49109
|
+
}
|
|
49110
|
+
}
|
|
49111
|
+
/**
|
|
49112
|
+
* This is the default scroll handler for vertical layouts using {@link ScrollMode.Smooth}.
|
|
49113
|
+
* vertical smooth scrolling aims to place the on-time position
|
|
49114
|
+
* at scrollOffsetY **at the time when a system starts**
|
|
49115
|
+
* this means when a system starts, it is at scrollOffsetY,
|
|
49116
|
+
* then gradually scrolls down the system height reaching the bottom
|
|
49117
|
+
* when the system completes.
|
|
49118
|
+
* @internal
|
|
49119
|
+
*/
|
|
49120
|
+
class VerticalSmoothScrollHandler {
|
|
49121
|
+
_api;
|
|
49122
|
+
_lastScroll = -1;
|
|
49123
|
+
_scrollContainerResizeUnregister;
|
|
49124
|
+
constructor(api) {
|
|
49125
|
+
this._api = api;
|
|
49126
|
+
// we need a resize listener for the overflow calculation
|
|
49127
|
+
this._scrollContainerResizeUnregister = api.uiFacade.getScrollContainer().resize.on(() => {
|
|
49128
|
+
const scrollContainer = api.uiFacade.getScrollContainer();
|
|
49129
|
+
const overflowNeeded = api.settings.player.scrollOffsetX;
|
|
49130
|
+
const viewPortSize = scrollContainer.width;
|
|
49131
|
+
// the content needs to shift out of screen (and back into screen with the offset)
|
|
49132
|
+
// that's why we need the whole width as additional overflow
|
|
49133
|
+
const overflowNeededAbsolute = viewPortSize + overflowNeeded;
|
|
49134
|
+
api.uiFacade.setCanvasOverflow(api.canvasElement, overflowNeededAbsolute, true);
|
|
49135
|
+
});
|
|
49136
|
+
}
|
|
49137
|
+
[Symbol.dispose]() {
|
|
49138
|
+
this._api.uiFacade.setCanvasOverflow(this._api.canvasElement, 0, true);
|
|
49139
|
+
this._scrollContainerResizeUnregister();
|
|
49140
|
+
}
|
|
49141
|
+
forceScrollTo(currentBeatBounds) {
|
|
49142
|
+
const ui = this._api.uiFacade;
|
|
49143
|
+
const settings = this._api.settings;
|
|
49144
|
+
const scroll = ui.getScrollContainer();
|
|
49145
|
+
const systemTop = currentBeatBounds.barBounds.masterBarBounds.realBounds.y + settings.player.scrollOffsetY;
|
|
49146
|
+
ui.scrollToY(scroll, systemTop, 0);
|
|
49147
|
+
this._lastScroll = -1;
|
|
49148
|
+
}
|
|
49149
|
+
onBeatCursorUpdating(startBeat, _endBeat, _cursorMode, _actualBeatCursorStartX, _actualBeatCursorEndX, actualBeatCursorTransitionDuration) {
|
|
49150
|
+
const ui = this._api.uiFacade;
|
|
49151
|
+
const settings = this._api.settings;
|
|
49152
|
+
const barBoundings = startBeat.barBounds.masterBarBounds;
|
|
49153
|
+
const systemTop = barBoundings.realBounds.y + settings.player.scrollOffsetY;
|
|
49154
|
+
if (systemTop === this._lastScroll && actualBeatCursorTransitionDuration > 0) {
|
|
49155
|
+
return;
|
|
49156
|
+
}
|
|
49157
|
+
// jump to start of new system
|
|
49158
|
+
const scroll = ui.getScrollContainer();
|
|
49159
|
+
ui.scrollToY(scroll, systemTop, 0);
|
|
49160
|
+
// instant scroll
|
|
49161
|
+
if (actualBeatCursorTransitionDuration === 0) {
|
|
49162
|
+
this._lastScroll = -1;
|
|
49163
|
+
return;
|
|
49164
|
+
}
|
|
49165
|
+
// dynamic scrolling
|
|
49166
|
+
this._lastScroll = systemTop;
|
|
49167
|
+
// scroll to bottom over time
|
|
49168
|
+
const systemBottom = systemTop + barBoundings.realBounds.h;
|
|
49169
|
+
// NOTE: this calculation is a bit more expensive, but we only do it once per system
|
|
49170
|
+
// so we should be good:
|
|
49171
|
+
// * the more bars we have, the longer the system will play, hence the duration can take a bit longer
|
|
49172
|
+
// * if we have less bars, we calculate more often, but the calculation will be faster because we sum up less bars.
|
|
49173
|
+
const systemDuration = this._calculateSystemDuration(barBoundings);
|
|
49174
|
+
ui.scrollToY(scroll, systemBottom, systemDuration);
|
|
49175
|
+
}
|
|
49176
|
+
_calculateSystemDuration(barBoundings) {
|
|
49177
|
+
const systemBars = barBoundings.staffSystemBounds.bars;
|
|
49178
|
+
const tickCache = this._api.tickCache;
|
|
49179
|
+
let duration = 0;
|
|
49180
|
+
const masterBars = this._api.score.masterBars;
|
|
49181
|
+
for (const bar of systemBars) {
|
|
49182
|
+
const mb = masterBars[bar.index];
|
|
49183
|
+
const mbInfo = tickCache.getMasterBar(mb);
|
|
49184
|
+
const tempoChanges = tickCache.getMasterBar(mb).tempoChanges;
|
|
49185
|
+
let tempo = tempoChanges[0].tempo;
|
|
49186
|
+
let tick = tempoChanges[0].tick;
|
|
49187
|
+
for (let i = 1; i < tempoChanges.length; i++) {
|
|
49188
|
+
const diff = tempoChanges[i].tick - tick;
|
|
49189
|
+
duration += MidiUtils.ticksToMillis(diff, tempo);
|
|
49190
|
+
tempo = tempoChanges[i].tempo;
|
|
49191
|
+
tick = tempoChanges[i].tick;
|
|
49192
|
+
}
|
|
49193
|
+
const toEnd = mbInfo.end - tick;
|
|
49194
|
+
duration += MidiUtils.ticksToMillis(toEnd, tempo);
|
|
49195
|
+
}
|
|
49196
|
+
return duration;
|
|
49197
|
+
}
|
|
49198
|
+
}
|
|
49199
|
+
/**
|
|
49200
|
+
* This is the default scroll handler for horizontal layouts using {@link ScrollMode.Continuous}.
|
|
49201
|
+
* Whenever the master bar changes, we scroll to the position horizontally.
|
|
49202
|
+
* @internal
|
|
49203
|
+
*/
|
|
49204
|
+
class HorizontalContinuousScrollHandler extends BasicScrollHandler {
|
|
49205
|
+
calculateLastScroll(currentBeatBounds) {
|
|
49206
|
+
return currentBeatBounds.barBounds.masterBarBounds.visualBounds.x;
|
|
49207
|
+
}
|
|
49208
|
+
doScroll(currentBeatBounds) {
|
|
49209
|
+
const ui = this.api.uiFacade;
|
|
49210
|
+
const settings = this.api.settings;
|
|
49211
|
+
const scroll = ui.getScrollContainer();
|
|
49212
|
+
const barBoundings = currentBeatBounds.barBounds.masterBarBounds;
|
|
49213
|
+
const scrollLeftContinuous = barBoundings.realBounds.x + settings.player.scrollOffsetX;
|
|
49214
|
+
ui.scrollToX(scroll, scrollLeftContinuous, settings.player.scrollSpeed);
|
|
49215
|
+
}
|
|
49216
|
+
}
|
|
49217
|
+
/**
|
|
49218
|
+
* This is the default scroll handler for horizontal layouts using {@link ScrollMode.OffScreen}.
|
|
49219
|
+
* Whenever the system changes, we check if the new system bounds are out-of-screen and if yes, we scroll.
|
|
49220
|
+
* @internal
|
|
49221
|
+
*/
|
|
49222
|
+
class HorizontalOffScreenScrollHandler extends BasicScrollHandler {
|
|
49223
|
+
calculateLastScroll(currentBeatBounds) {
|
|
49224
|
+
return currentBeatBounds.barBounds.masterBarBounds.visualBounds.x;
|
|
49225
|
+
}
|
|
49226
|
+
doScroll(currentBeatBounds) {
|
|
49227
|
+
const ui = this.api.uiFacade;
|
|
49228
|
+
const settings = this.api.settings;
|
|
49229
|
+
const scroll = ui.getScrollContainer();
|
|
49230
|
+
const elementRight = scroll.scrollLeft + ui.getOffset(null, scroll).w;
|
|
49231
|
+
const barBoundings = currentBeatBounds.barBounds.masterBarBounds;
|
|
49232
|
+
if (barBoundings.visualBounds.x + barBoundings.visualBounds.w >= elementRight ||
|
|
49233
|
+
barBoundings.visualBounds.x < scroll.scrollLeft) {
|
|
49234
|
+
const scrollLeftOffScreen = barBoundings.realBounds.x + settings.player.scrollOffsetX;
|
|
49235
|
+
ui.scrollToX(scroll, scrollLeftOffScreen, settings.player.scrollSpeed);
|
|
49236
|
+
}
|
|
49237
|
+
}
|
|
49238
|
+
}
|
|
49239
|
+
/**
|
|
49240
|
+
* This is the default scroll handler for horizontal layouts using {@link ScrollMode.Smooth}.
|
|
49241
|
+
* horiontal smooth scrolling aims to place the on-time position
|
|
49242
|
+
* at scrollOffsetX from a beat-to-beat perspective.
|
|
49243
|
+
* This achieves an steady cursor at the same position with rather the music sheet scrolling past it.
|
|
49244
|
+
* Due to some animation inconsistencies (e.g. CSS animation vs scrolling) there might be a slight
|
|
49245
|
+
* flickering of the cursor.
|
|
49246
|
+
*
|
|
49247
|
+
* To get a fully steady cursor the beat cursor can simply be visually hidden and a cursor can be placed at
|
|
49248
|
+
* `scrollOffsetX` by the integrator.
|
|
49249
|
+
* @internal
|
|
49250
|
+
*/
|
|
49251
|
+
class HorizontalSmoothScrollHandler {
|
|
49252
|
+
_api;
|
|
49253
|
+
_lastScroll = -1;
|
|
49254
|
+
_scrollContainerResizeUnregister;
|
|
49255
|
+
constructor(api) {
|
|
49256
|
+
this._api = api;
|
|
49257
|
+
// we need a resize listener for the overflow calculation
|
|
49258
|
+
this._scrollContainerResizeUnregister = api.uiFacade.getScrollContainer().resize.on(() => {
|
|
49259
|
+
const scrollContainer = api.uiFacade.getScrollContainer();
|
|
49260
|
+
const overflowNeeded = api.settings.player.scrollOffsetX;
|
|
49261
|
+
const viewPortSize = scrollContainer.width;
|
|
49262
|
+
// the content needs to shift out of screen (and back into screen with the offset)
|
|
49263
|
+
// that's why we need the whole width as additional overflow
|
|
49264
|
+
const overflowNeededAbsolute = viewPortSize + overflowNeeded;
|
|
49265
|
+
api.uiFacade.setCanvasOverflow(api.canvasElement, overflowNeededAbsolute, false);
|
|
49266
|
+
});
|
|
49267
|
+
}
|
|
49268
|
+
[Symbol.dispose]() {
|
|
49269
|
+
this._scrollContainerResizeUnregister();
|
|
49270
|
+
this._api.uiFacade.setCanvasOverflow(this._api.canvasElement, 0, false);
|
|
49271
|
+
}
|
|
49272
|
+
forceScrollTo(currentBeatBounds) {
|
|
49273
|
+
const ui = this._api.uiFacade;
|
|
49274
|
+
const settings = this._api.settings;
|
|
49275
|
+
const scroll = ui.getScrollContainer();
|
|
49276
|
+
const barStartX = currentBeatBounds.onNotesX + settings.player.scrollOffsetY;
|
|
49277
|
+
ui.scrollToY(scroll, barStartX, 0);
|
|
49278
|
+
this._lastScroll = -1;
|
|
49279
|
+
}
|
|
49280
|
+
onBeatCursorUpdating(_startBeat, _endBeat, _cursorMode, actualBeatCursorStartX, actualBeatCursorEndX, actualBeatCursorTransitionDuration) {
|
|
49281
|
+
const ui = this._api.uiFacade;
|
|
49282
|
+
if (actualBeatCursorEndX === this._lastScroll && actualBeatCursorTransitionDuration > 0) {
|
|
49283
|
+
return;
|
|
49284
|
+
}
|
|
49285
|
+
// jump to start of new system
|
|
49286
|
+
const settings = this._api.settings;
|
|
49287
|
+
const scroll = ui.getScrollContainer();
|
|
49288
|
+
ui.scrollToX(scroll, actualBeatCursorStartX + settings.player.scrollOffsetX, 0);
|
|
49289
|
+
// instant scroll
|
|
49290
|
+
if (actualBeatCursorTransitionDuration === 0) {
|
|
49291
|
+
this._lastScroll = -1;
|
|
49292
|
+
return;
|
|
49293
|
+
}
|
|
49294
|
+
this._lastScroll = actualBeatCursorEndX;
|
|
49295
|
+
const scrollX = actualBeatCursorEndX + settings.player.scrollOffsetX;
|
|
49296
|
+
ui.scrollToX(scroll, scrollX, actualBeatCursorTransitionDuration);
|
|
49297
|
+
}
|
|
48941
49298
|
}
|
|
48942
49299
|
|
|
48943
49300
|
/**
|
|
@@ -49550,6 +49907,19 @@ class ExternalMediaPlayer extends BackingTrackPlayer {
|
|
|
49550
49907
|
}
|
|
49551
49908
|
}
|
|
49552
49909
|
|
|
49910
|
+
/**
|
|
49911
|
+
* @internal
|
|
49912
|
+
*/
|
|
49913
|
+
class BoundsLookupVisibilityChecker {
|
|
49914
|
+
bounds = null;
|
|
49915
|
+
isVisible(beat) {
|
|
49916
|
+
const bounds = this.bounds;
|
|
49917
|
+
if (!bounds) {
|
|
49918
|
+
return false;
|
|
49919
|
+
}
|
|
49920
|
+
return bounds.findBeat(beat) !== null;
|
|
49921
|
+
}
|
|
49922
|
+
}
|
|
49553
49923
|
/**
|
|
49554
49924
|
* This class represents the public API of alphaTab and provides all logic to display
|
|
49555
49925
|
* a music sheet in any UI using the given {@link IUiFacade}
|
|
@@ -49560,12 +49930,14 @@ class AlphaTabApiBase {
|
|
|
49560
49930
|
_startTime = 0;
|
|
49561
49931
|
_trackIndexes = null;
|
|
49562
49932
|
_trackIndexLookup = null;
|
|
49933
|
+
_beatVisibilityChecker = new BoundsLookupVisibilityChecker();
|
|
49563
49934
|
_isDestroyed = false;
|
|
49564
49935
|
_score = null;
|
|
49565
49936
|
_tracks = [];
|
|
49566
49937
|
_actualPlayerMode = PlayerMode.Disabled;
|
|
49567
49938
|
_player;
|
|
49568
49939
|
_renderer;
|
|
49940
|
+
_defaultScrollHandler;
|
|
49569
49941
|
/**
|
|
49570
49942
|
* An indicator by how many midi-ticks the song contents are shifted.
|
|
49571
49943
|
* Grace beats at start might require a shift for the first beat to start at 0.
|
|
@@ -50335,6 +50707,42 @@ class AlphaTabApiBase {
|
|
|
50335
50707
|
}
|
|
50336
50708
|
}
|
|
50337
50709
|
_tickCache = null;
|
|
50710
|
+
/**
|
|
50711
|
+
* A custom scroll handler which will be used to handle scrolling operations during playback.
|
|
50712
|
+
*
|
|
50713
|
+
* @category Properties - Player
|
|
50714
|
+
* @since 1.8.0
|
|
50715
|
+
* @example
|
|
50716
|
+
* JavaScript
|
|
50717
|
+
* ```js
|
|
50718
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
50719
|
+
* api.customScrollHandler = {
|
|
50720
|
+
* forceScrollTo(currentBeatBounds) {
|
|
50721
|
+
* const scroll = api.uiFacade.getScrollElement();
|
|
50722
|
+
* api.uiFacade.scrollToY(scroll, currentBeatBounds.barBounds.masterBarBounds.realBounds.y, 0);
|
|
50723
|
+
* },
|
|
50724
|
+
* onBeatCursorUpdating(startBeat, endBeat, cursorMode, relativePosition, actualBeatCursorStartX, actualBeatCursorEndX, actualBeatCursorTransitionDuration) {
|
|
50725
|
+
* const scroll = api.uiFacade.getScrollElement();
|
|
50726
|
+
* api.uiFacade.scrollToY(scroll, startBeat.barBounds.masterBarBounds.realBounds.y, 0);
|
|
50727
|
+
* }
|
|
50728
|
+
* }
|
|
50729
|
+
* ```
|
|
50730
|
+
*
|
|
50731
|
+
* @example
|
|
50732
|
+
* C#
|
|
50733
|
+
* ```cs
|
|
50734
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
50735
|
+
* api.CustomScrollHandler = new CustomScrollHandler();
|
|
50736
|
+
* ```
|
|
50737
|
+
*
|
|
50738
|
+
* @example
|
|
50739
|
+
* Android
|
|
50740
|
+
* ```kotlin
|
|
50741
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
50742
|
+
* api.customScrollHandler = CustomScrollHandler();
|
|
50743
|
+
* ```
|
|
50744
|
+
*/
|
|
50745
|
+
customScrollHandler;
|
|
50338
50746
|
/**
|
|
50339
50747
|
* The tick cache allowing lookup of midi ticks to beats.
|
|
50340
50748
|
* @remarks
|
|
@@ -51313,7 +51721,6 @@ class AlphaTabApiBase {
|
|
|
51313
51721
|
_isInitialBeatCursorUpdate = true;
|
|
51314
51722
|
_previousStateForCursor = PlayerState.Paused;
|
|
51315
51723
|
_previousCursorCache = null;
|
|
51316
|
-
_lastScroll = 0;
|
|
51317
51724
|
_destroyCursors() {
|
|
51318
51725
|
if (!this._cursorWrapper) {
|
|
51319
51726
|
return;
|
|
@@ -51324,28 +51731,79 @@ class AlphaTabApiBase {
|
|
|
51324
51731
|
this._beatCursor = null;
|
|
51325
51732
|
this._selectionWrapper = null;
|
|
51326
51733
|
}
|
|
51734
|
+
_createCursors() {
|
|
51735
|
+
if (this._cursorWrapper) {
|
|
51736
|
+
return;
|
|
51737
|
+
}
|
|
51738
|
+
const cursors = this.uiFacade.createCursors();
|
|
51739
|
+
if (cursors) {
|
|
51740
|
+
// store options and created elements for fast access
|
|
51741
|
+
this._cursorWrapper = cursors.cursorWrapper;
|
|
51742
|
+
this._barCursor = cursors.barCursor;
|
|
51743
|
+
this._beatCursor = cursors.beatCursor;
|
|
51744
|
+
this._selectionWrapper = cursors.selectionWrapper;
|
|
51745
|
+
this._isInitialBeatCursorUpdate = true;
|
|
51746
|
+
}
|
|
51747
|
+
if (this._currentBeat !== null) {
|
|
51748
|
+
this._cursorUpdateBeat(this._currentBeat, false, this._previousTick > 10, 1, true);
|
|
51749
|
+
}
|
|
51750
|
+
}
|
|
51327
51751
|
_updateCursors() {
|
|
51752
|
+
this._updateScrollHandler();
|
|
51328
51753
|
const enable = this._hasCursor;
|
|
51329
|
-
if (enable
|
|
51330
|
-
|
|
51331
|
-
// Create cursors
|
|
51332
|
-
const cursors = this.uiFacade.createCursors();
|
|
51333
|
-
if (cursors) {
|
|
51334
|
-
// store options and created elements for fast access
|
|
51335
|
-
this._cursorWrapper = cursors.cursorWrapper;
|
|
51336
|
-
this._barCursor = cursors.barCursor;
|
|
51337
|
-
this._beatCursor = cursors.beatCursor;
|
|
51338
|
-
this._selectionWrapper = cursors.selectionWrapper;
|
|
51339
|
-
this._isInitialBeatCursorUpdate = true;
|
|
51340
|
-
}
|
|
51341
|
-
if (this._currentBeat !== null) {
|
|
51342
|
-
this._cursorUpdateBeat(this._currentBeat, false, this._previousTick > 10, 1, true);
|
|
51343
|
-
}
|
|
51754
|
+
if (enable) {
|
|
51755
|
+
this._createCursors();
|
|
51344
51756
|
}
|
|
51345
51757
|
else if (!enable && this._cursorWrapper) {
|
|
51346
51758
|
this._destroyCursors();
|
|
51347
51759
|
}
|
|
51348
51760
|
}
|
|
51761
|
+
_scrollHandlerMode = ScrollMode.Off;
|
|
51762
|
+
_scrollHandlerVertical = true;
|
|
51763
|
+
_updateScrollHandler() {
|
|
51764
|
+
const currentHandler = this._defaultScrollHandler;
|
|
51765
|
+
const scrollMode = this.settings.player.scrollMode;
|
|
51766
|
+
const isVertical = Environment.getLayoutEngineFactory(this.settings.display.layoutMode).vertical;
|
|
51767
|
+
// no change
|
|
51768
|
+
if (this._scrollHandlerMode === scrollMode && this._scrollHandlerVertical === isVertical) {
|
|
51769
|
+
return;
|
|
51770
|
+
}
|
|
51771
|
+
// destroy current handler in favor of new one
|
|
51772
|
+
if (currentHandler) {
|
|
51773
|
+
currentHandler[Symbol.dispose]();
|
|
51774
|
+
const scroll = this.uiFacade.getScrollContainer();
|
|
51775
|
+
this.uiFacade.stopScrolling(scroll);
|
|
51776
|
+
}
|
|
51777
|
+
switch (scrollMode) {
|
|
51778
|
+
case ScrollMode.Off:
|
|
51779
|
+
this._defaultScrollHandler = undefined;
|
|
51780
|
+
break;
|
|
51781
|
+
case ScrollMode.Continuous:
|
|
51782
|
+
if (isVertical) {
|
|
51783
|
+
this._defaultScrollHandler = new VerticalContinuousScrollHandler(this);
|
|
51784
|
+
}
|
|
51785
|
+
else {
|
|
51786
|
+
this._defaultScrollHandler = new HorizontalContinuousScrollHandler(this);
|
|
51787
|
+
}
|
|
51788
|
+
break;
|
|
51789
|
+
case ScrollMode.OffScreen:
|
|
51790
|
+
if (isVertical) {
|
|
51791
|
+
this._defaultScrollHandler = new VerticalOffScreenScrollHandler(this);
|
|
51792
|
+
}
|
|
51793
|
+
else {
|
|
51794
|
+
this._defaultScrollHandler = new HorizontalOffScreenScrollHandler(this);
|
|
51795
|
+
}
|
|
51796
|
+
break;
|
|
51797
|
+
case ScrollMode.Smooth:
|
|
51798
|
+
if (isVertical) {
|
|
51799
|
+
this._defaultScrollHandler = new VerticalSmoothScrollHandler(this);
|
|
51800
|
+
}
|
|
51801
|
+
else {
|
|
51802
|
+
this._defaultScrollHandler = new HorizontalSmoothScrollHandler(this);
|
|
51803
|
+
}
|
|
51804
|
+
break;
|
|
51805
|
+
}
|
|
51806
|
+
}
|
|
51349
51807
|
/**
|
|
51350
51808
|
* updates the cursors to highlight the beat at the specified tick position
|
|
51351
51809
|
* @param tick
|
|
@@ -51356,12 +51814,9 @@ class AlphaTabApiBase {
|
|
|
51356
51814
|
this._previousTick = tick;
|
|
51357
51815
|
const cache = this._tickCache;
|
|
51358
51816
|
if (cache) {
|
|
51359
|
-
const
|
|
51360
|
-
if (
|
|
51361
|
-
|
|
51362
|
-
if (beat) {
|
|
51363
|
-
this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
|
|
51364
|
-
}
|
|
51817
|
+
const beat = cache.findBeatWithChecker(this._beatVisibilityChecker, tick, this._currentBeat);
|
|
51818
|
+
if (beat) {
|
|
51819
|
+
this._cursorUpdateBeat(beat, stop, shouldScroll, cursorSpeed, forceUpdate || this.playerState === PlayerState.Paused);
|
|
51365
51820
|
}
|
|
51366
51821
|
}
|
|
51367
51822
|
}
|
|
@@ -51411,57 +51866,9 @@ class AlphaTabApiBase {
|
|
|
51411
51866
|
scrollToCursor() {
|
|
51412
51867
|
const beatBounds = this._currentBeatBounds;
|
|
51413
51868
|
if (beatBounds) {
|
|
51414
|
-
this.
|
|
51415
|
-
|
|
51416
|
-
|
|
51417
|
-
_internalScrollToCursor(barBoundings) {
|
|
51418
|
-
const scrollElement = this.uiFacade.getScrollContainer();
|
|
51419
|
-
const isVertical = Environment.getLayoutEngineFactory(this.settings.display.layoutMode).vertical;
|
|
51420
|
-
const mode = this.settings.player.scrollMode;
|
|
51421
|
-
if (isVertical) {
|
|
51422
|
-
// when scrolling on the y-axis, we preliminary check if the new beat/bar have
|
|
51423
|
-
// moved on the y-axis
|
|
51424
|
-
const y = barBoundings.realBounds.y + this.settings.player.scrollOffsetY;
|
|
51425
|
-
if (y !== this._lastScroll) {
|
|
51426
|
-
this._lastScroll = y;
|
|
51427
|
-
switch (mode) {
|
|
51428
|
-
case ScrollMode.Continuous:
|
|
51429
|
-
const elementOffset = this.uiFacade.getOffset(scrollElement, this.container);
|
|
51430
|
-
this.uiFacade.scrollToY(scrollElement, elementOffset.y + y, this.settings.player.scrollSpeed);
|
|
51431
|
-
break;
|
|
51432
|
-
case ScrollMode.OffScreen:
|
|
51433
|
-
const elementBottom = scrollElement.scrollTop + this.uiFacade.getOffset(null, scrollElement).h;
|
|
51434
|
-
if (barBoundings.visualBounds.y + barBoundings.visualBounds.h >= elementBottom ||
|
|
51435
|
-
barBoundings.visualBounds.y < scrollElement.scrollTop) {
|
|
51436
|
-
const scrollTop = barBoundings.realBounds.y + this.settings.player.scrollOffsetY;
|
|
51437
|
-
this.uiFacade.scrollToY(scrollElement, scrollTop, this.settings.player.scrollSpeed);
|
|
51438
|
-
}
|
|
51439
|
-
break;
|
|
51440
|
-
}
|
|
51441
|
-
}
|
|
51442
|
-
}
|
|
51443
|
-
else {
|
|
51444
|
-
// when scrolling on the x-axis, we preliminary check if the new bar has
|
|
51445
|
-
// moved on the x-axis
|
|
51446
|
-
const x = barBoundings.visualBounds.x;
|
|
51447
|
-
if (x !== this._lastScroll) {
|
|
51448
|
-
this._lastScroll = x;
|
|
51449
|
-
switch (mode) {
|
|
51450
|
-
case ScrollMode.Continuous:
|
|
51451
|
-
const scrollLeftContinuous = barBoundings.realBounds.x + this.settings.player.scrollOffsetX;
|
|
51452
|
-
this._lastScroll = barBoundings.visualBounds.x;
|
|
51453
|
-
this.uiFacade.scrollToX(scrollElement, scrollLeftContinuous, this.settings.player.scrollSpeed);
|
|
51454
|
-
break;
|
|
51455
|
-
case ScrollMode.OffScreen:
|
|
51456
|
-
const elementRight = scrollElement.scrollLeft + this.uiFacade.getOffset(null, scrollElement).w;
|
|
51457
|
-
if (barBoundings.visualBounds.x + barBoundings.visualBounds.w >= elementRight ||
|
|
51458
|
-
barBoundings.visualBounds.x < scrollElement.scrollLeft) {
|
|
51459
|
-
const scrollLeftOffScreen = barBoundings.realBounds.x + this.settings.player.scrollOffsetX;
|
|
51460
|
-
this._lastScroll = barBoundings.visualBounds.x;
|
|
51461
|
-
this.uiFacade.scrollToX(scrollElement, scrollLeftOffScreen, this.settings.player.scrollSpeed);
|
|
51462
|
-
}
|
|
51463
|
-
break;
|
|
51464
|
-
}
|
|
51869
|
+
const handler = this.customScrollHandler ?? this._defaultScrollHandler;
|
|
51870
|
+
if (handler) {
|
|
51871
|
+
handler.forceScrollTo(beatBounds);
|
|
51465
51872
|
}
|
|
51466
51873
|
}
|
|
51467
51874
|
}
|
|
@@ -51477,11 +51884,12 @@ class AlphaTabApiBase {
|
|
|
51477
51884
|
}
|
|
51478
51885
|
const isPlayingUpdate = this._player.state === PlayerState.Playing && !stop;
|
|
51479
51886
|
let nextBeatX = barBoundings.visualBounds.x + barBoundings.visualBounds.w;
|
|
51887
|
+
let nextBeatBoundings = null;
|
|
51480
51888
|
// get position of next beat on same system
|
|
51481
51889
|
if (nextBeat && cursorMode === MidiTickLookupFindBeatResultCursorMode.ToNextBext) {
|
|
51482
51890
|
// if we are moving within the same bar or to the next bar
|
|
51483
51891
|
// transition to the next beat, otherwise transition to the end of the bar.
|
|
51484
|
-
|
|
51892
|
+
nextBeatBoundings = cache.findBeat(nextBeat);
|
|
51485
51893
|
if (nextBeatBoundings &&
|
|
51486
51894
|
nextBeatBoundings.barBounds.masterBarBounds.staffSystemBounds === barBoundings.staffSystemBounds) {
|
|
51487
51895
|
nextBeatX = nextBeatBoundings.onNotesX;
|
|
@@ -51507,25 +51915,29 @@ class AlphaTabApiBase {
|
|
|
51507
51915
|
beatCursor.transitionToX(0, startBeatX);
|
|
51508
51916
|
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
51509
51917
|
}
|
|
51918
|
+
// it can happen that the cursor reaches the target position slightly too early (especially on backing tracks)
|
|
51919
|
+
// to avoid the cursor stopping, causing a wierd look, we animate the cursor to the double position in double time.
|
|
51920
|
+
// beatCursor!.transitionToX((duration / cursorSpeed), nextBeatX);
|
|
51921
|
+
const factor = cursorMode === MidiTickLookupFindBeatResultCursorMode.ToNextBext ? 2 : 1;
|
|
51922
|
+
nextBeatX = startBeatX + (nextBeatX - startBeatX) * factor;
|
|
51923
|
+
duration = (duration / cursorSpeed) * factor;
|
|
51510
51924
|
// we need to put the transition to an own animation frame
|
|
51511
51925
|
// otherwise the stop animation above is not applied.
|
|
51512
51926
|
this.uiFacade.beginInvoke(() => {
|
|
51513
|
-
|
|
51514
|
-
// to avoid the cursor stopping, causing a wierd look, we animate the cursor to the double position in double time.
|
|
51515
|
-
// beatCursor!.transitionToX((duration / cursorSpeed), nextBeatX);
|
|
51516
|
-
const factor = cursorMode === MidiTickLookupFindBeatResultCursorMode.ToNextBext ? 2 : 1;
|
|
51517
|
-
const doubleEndBeatX = startBeatX + (nextBeatX - startBeatX) * factor;
|
|
51518
|
-
beatCursor.transitionToX((duration / cursorSpeed) * factor, doubleEndBeatX);
|
|
51927
|
+
beatCursor.transitionToX(duration, nextBeatX);
|
|
51519
51928
|
});
|
|
51520
51929
|
}
|
|
51521
51930
|
else {
|
|
51522
|
-
|
|
51931
|
+
duration = 0;
|
|
51932
|
+
beatCursor.transitionToX(duration, nextBeatX);
|
|
51523
51933
|
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
51524
51934
|
}
|
|
51525
51935
|
}
|
|
51526
51936
|
else {
|
|
51527
51937
|
// ticking cursor
|
|
51528
|
-
|
|
51938
|
+
duration = 0;
|
|
51939
|
+
nextBeatX = startBeatX;
|
|
51940
|
+
beatCursor.transitionToX(duration, nextBeatX);
|
|
51529
51941
|
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
51530
51942
|
}
|
|
51531
51943
|
this._isInitialBeatCursorUpdate = false;
|
|
@@ -51548,7 +51960,10 @@ class AlphaTabApiBase {
|
|
|
51548
51960
|
shouldNotifyBeatChange = true;
|
|
51549
51961
|
}
|
|
51550
51962
|
if (shouldScroll && !this._isBeatMouseDown && this.settings.player.scrollMode !== ScrollMode.Off) {
|
|
51551
|
-
this.
|
|
51963
|
+
const handler = this.customScrollHandler ?? this._defaultScrollHandler;
|
|
51964
|
+
if (handler) {
|
|
51965
|
+
handler.onBeatCursorUpdating(beatBoundings, nextBeatBoundings === null ? undefined : nextBeatBoundings, cursorMode, startBeatX, nextBeatX, duration);
|
|
51966
|
+
}
|
|
51552
51967
|
}
|
|
51553
51968
|
// trigger an event for others to indicate which beat/bar is played
|
|
51554
51969
|
if (shouldNotifyBeatChange) {
|
|
@@ -52563,6 +52978,7 @@ class AlphaTabApiBase {
|
|
|
52563
52978
|
if (this._isDestroyed) {
|
|
52564
52979
|
return;
|
|
52565
52980
|
}
|
|
52981
|
+
this._beatVisibilityChecker.bounds = this.boundsLookup;
|
|
52566
52982
|
this._currentBeat = null;
|
|
52567
52983
|
this._cursorUpdateTick(this._previousTick, false, 1, true, true);
|
|
52568
52984
|
this.postRenderFinished.trigger();
|
|
@@ -52921,6 +53337,7 @@ class AlphaTabApiBase {
|
|
|
52921
53337
|
const tickCache = this._tickCache;
|
|
52922
53338
|
if (currentBeat && tickCache) {
|
|
52923
53339
|
this._player.tickPosition = tickCache.getBeatStart(currentBeat.beat);
|
|
53340
|
+
this.scrollToCursor();
|
|
52924
53341
|
}
|
|
52925
53342
|
}
|
|
52926
53343
|
this.uiFacade.triggerEvent(this.container, 'playerStateChanged', e);
|
|
@@ -54881,6 +55298,22 @@ class BrowserUiFacade {
|
|
|
54881
55298
|
canvasElement.style.position = 'relative';
|
|
54882
55299
|
return new HtmlElementContainer(canvasElement);
|
|
54883
55300
|
}
|
|
55301
|
+
setCanvasOverflow(canvasElement, overflow, isVertical) {
|
|
55302
|
+
const html = canvasElement.element;
|
|
55303
|
+
if (overflow === 0) {
|
|
55304
|
+
html.style.boxSizing = '';
|
|
55305
|
+
html.style.paddingRight = '';
|
|
55306
|
+
html.style.paddingBottom = '';
|
|
55307
|
+
}
|
|
55308
|
+
else if (isVertical) {
|
|
55309
|
+
html.style.boxSizing = 'content-box';
|
|
55310
|
+
html.style.paddingBottom = `${overflow}px`;
|
|
55311
|
+
}
|
|
55312
|
+
else {
|
|
55313
|
+
html.style.boxSizing = 'content-box';
|
|
55314
|
+
html.style.paddingRight = `${overflow}px`;
|
|
55315
|
+
}
|
|
55316
|
+
}
|
|
54884
55317
|
triggerEvent(container, name, details = null, originalEvent) {
|
|
54885
55318
|
const element = container.element;
|
|
54886
55319
|
name = `alphaTab.${name}`;
|
|
@@ -55438,54 +55871,79 @@ class BrowserUiFacade {
|
|
|
55438
55871
|
scrollToX(element, scrollTargetY, speed) {
|
|
55439
55872
|
this._internalScrollToX(element.element, scrollTargetY, speed);
|
|
55440
55873
|
}
|
|
55874
|
+
stopScrolling(scrollElement) {
|
|
55875
|
+
// stop any current animation
|
|
55876
|
+
const currentAnimation = this._scrollAnimationLookup.get(scrollElement.element);
|
|
55877
|
+
if (currentAnimation !== undefined) {
|
|
55878
|
+
this._activeScrollAnimations.delete(currentAnimation);
|
|
55879
|
+
}
|
|
55880
|
+
}
|
|
55881
|
+
get _nativeBrowserSmoothScroll() {
|
|
55882
|
+
const settings = this._api.settings.player;
|
|
55883
|
+
return settings.nativeBrowserSmoothScroll && settings.scrollMode !== ScrollMode.Smooth;
|
|
55884
|
+
}
|
|
55885
|
+
_scrollAnimationId = 0;
|
|
55886
|
+
_activeScrollAnimations = new Set();
|
|
55887
|
+
_scrollAnimationLookup = new Map();
|
|
55441
55888
|
_internalScrollToY(element, scrollTargetY, speed) {
|
|
55442
|
-
if (this.
|
|
55889
|
+
if (this._nativeBrowserSmoothScroll) {
|
|
55443
55890
|
element.scrollTo({
|
|
55444
55891
|
top: scrollTargetY,
|
|
55445
55892
|
behavior: 'smooth'
|
|
55446
55893
|
});
|
|
55447
55894
|
}
|
|
55448
55895
|
else {
|
|
55449
|
-
|
|
55450
|
-
|
|
55451
|
-
|
|
55452
|
-
const step = (x) => {
|
|
55453
|
-
if (start === 0) {
|
|
55454
|
-
start = x;
|
|
55455
|
-
}
|
|
55456
|
-
const time = x - start;
|
|
55457
|
-
const percent = Math.min(time / speed, 1);
|
|
55458
|
-
element.scrollTop = (startY + diff * percent) | 0;
|
|
55459
|
-
if (time < speed) {
|
|
55460
|
-
window.requestAnimationFrame(step);
|
|
55461
|
-
}
|
|
55462
|
-
};
|
|
55463
|
-
window.requestAnimationFrame(step);
|
|
55896
|
+
this._internalScrollTo(element, element.scrollTop, scrollTargetY, speed, scroll => {
|
|
55897
|
+
element.scrollTop = scroll;
|
|
55898
|
+
});
|
|
55464
55899
|
}
|
|
55465
55900
|
}
|
|
55901
|
+
_internalScrollTo(element, startScroll, endScroll, scrollDuration, setValue) {
|
|
55902
|
+
// stop any current animation
|
|
55903
|
+
const currentAnimation = this._scrollAnimationLookup.get(element);
|
|
55904
|
+
if (currentAnimation !== undefined) {
|
|
55905
|
+
this._activeScrollAnimations.delete(currentAnimation);
|
|
55906
|
+
}
|
|
55907
|
+
if (scrollDuration === 0) {
|
|
55908
|
+
setValue(endScroll);
|
|
55909
|
+
return;
|
|
55910
|
+
}
|
|
55911
|
+
// start new animation
|
|
55912
|
+
const animationId = this._scrollAnimationId++;
|
|
55913
|
+
this._scrollAnimationLookup.set(element, animationId);
|
|
55914
|
+
this._activeScrollAnimations.add(animationId);
|
|
55915
|
+
const diff = endScroll - startScroll;
|
|
55916
|
+
let start = 0;
|
|
55917
|
+
const step = (x) => {
|
|
55918
|
+
if (!this._activeScrollAnimations.has(animationId)) {
|
|
55919
|
+
return;
|
|
55920
|
+
}
|
|
55921
|
+
if (start === 0) {
|
|
55922
|
+
start = x;
|
|
55923
|
+
}
|
|
55924
|
+
const time = x - start;
|
|
55925
|
+
const percent = Math.min(time / scrollDuration, 1);
|
|
55926
|
+
setValue((startScroll + diff * percent) | 0);
|
|
55927
|
+
if (time < scrollDuration) {
|
|
55928
|
+
window.requestAnimationFrame(step);
|
|
55929
|
+
}
|
|
55930
|
+
else {
|
|
55931
|
+
this._activeScrollAnimations.delete(animationId);
|
|
55932
|
+
}
|
|
55933
|
+
};
|
|
55934
|
+
window.requestAnimationFrame(step);
|
|
55935
|
+
}
|
|
55466
55936
|
_internalScrollToX(element, scrollTargetX, speed) {
|
|
55467
|
-
if (this.
|
|
55937
|
+
if (this._nativeBrowserSmoothScroll) {
|
|
55468
55938
|
element.scrollTo({
|
|
55469
55939
|
left: scrollTargetX,
|
|
55470
55940
|
behavior: 'smooth'
|
|
55471
55941
|
});
|
|
55472
55942
|
}
|
|
55473
55943
|
else {
|
|
55474
|
-
|
|
55475
|
-
|
|
55476
|
-
|
|
55477
|
-
const step = (t) => {
|
|
55478
|
-
if (start === 0) {
|
|
55479
|
-
start = t;
|
|
55480
|
-
}
|
|
55481
|
-
const time = t - start;
|
|
55482
|
-
const percent = Math.min(time / speed, 1);
|
|
55483
|
-
element.scrollLeft = (startX + diff * percent) | 0;
|
|
55484
|
-
if (time < speed) {
|
|
55485
|
-
window.requestAnimationFrame(step);
|
|
55486
|
-
}
|
|
55487
|
-
};
|
|
55488
|
-
window.requestAnimationFrame(step);
|
|
55944
|
+
this._internalScrollTo(element, element.scrollLeft, scrollTargetX, speed, scroll => {
|
|
55945
|
+
element.scrollLeft = scroll;
|
|
55946
|
+
});
|
|
55489
55947
|
}
|
|
55490
55948
|
}
|
|
55491
55949
|
createBackingTrackPlayer() {
|
|
@@ -60570,8 +61028,10 @@ class RenderStaff {
|
|
|
60570
61028
|
height = 0;
|
|
60571
61029
|
index = 0;
|
|
60572
61030
|
staffIndex = 0;
|
|
61031
|
+
isVisible = false;
|
|
61032
|
+
_emptyBarCount = 0;
|
|
60573
61033
|
get isFirstInSystem() {
|
|
60574
|
-
return this.
|
|
61034
|
+
return this.system.firstVisibleStaff === this;
|
|
60575
61035
|
}
|
|
60576
61036
|
topEffectInfos = [];
|
|
60577
61037
|
bottomEffectInfos = [];
|
|
@@ -60606,10 +61066,11 @@ class RenderStaff {
|
|
|
60606
61066
|
get contentBottom() {
|
|
60607
61067
|
return this.y + this.topPadding + this.topOverflow + this.staffBottom;
|
|
60608
61068
|
}
|
|
60609
|
-
constructor(trackIndex, staff, factory) {
|
|
61069
|
+
constructor(system, trackIndex, staff, factory) {
|
|
60610
61070
|
this._factory = factory;
|
|
60611
61071
|
this.trackIndex = trackIndex;
|
|
60612
61072
|
this.modelStaff = staff;
|
|
61073
|
+
this.system = system;
|
|
60613
61074
|
for (const b of factory.effectBands) {
|
|
60614
61075
|
if (b.shouldCreate && !b.shouldCreate(staff)) {
|
|
60615
61076
|
continue;
|
|
@@ -60625,6 +61086,7 @@ class RenderStaff {
|
|
|
60625
61086
|
break;
|
|
60626
61087
|
}
|
|
60627
61088
|
}
|
|
61089
|
+
this._updateVisibility();
|
|
60628
61090
|
}
|
|
60629
61091
|
getSharedLayoutData(key, def) {
|
|
60630
61092
|
if (this._sharedLayoutData.has(key)) {
|
|
@@ -60651,6 +61113,20 @@ class RenderStaff {
|
|
|
60651
61113
|
renderer.reLayout();
|
|
60652
61114
|
this.barRenderers.push(renderer);
|
|
60653
61115
|
this.system.layout.registerBarRenderer(this.staffId, renderer);
|
|
61116
|
+
if (renderer.bar.isEmpty || renderer.bar.isRestOnly) {
|
|
61117
|
+
this._emptyBarCount++;
|
|
61118
|
+
}
|
|
61119
|
+
this._updateVisibility();
|
|
61120
|
+
}
|
|
61121
|
+
_updateVisibility() {
|
|
61122
|
+
const stylesheet = this.modelStaff.track.score.stylesheet;
|
|
61123
|
+
const canHideEmptyStaves = stylesheet.hideEmptyStaves && (stylesheet.hideEmptyStavesInFirstSystem || this.system.index > 0);
|
|
61124
|
+
if (canHideEmptyStaves) {
|
|
61125
|
+
this.isVisible = this._emptyBarCount < this.barRenderers.length;
|
|
61126
|
+
}
|
|
61127
|
+
else {
|
|
61128
|
+
this.isVisible = true;
|
|
61129
|
+
}
|
|
60654
61130
|
}
|
|
60655
61131
|
addBar(bar, layoutingInfo, additionalMultiBarsRestBars) {
|
|
60656
61132
|
const renderer = this._factory.create(this.system.layout.renderer, bar);
|
|
@@ -60670,6 +61146,10 @@ class RenderStaff {
|
|
|
60670
61146
|
}
|
|
60671
61147
|
this.barRenderers.push(renderer);
|
|
60672
61148
|
this.system.layout.registerBarRenderer(this.staffId, renderer);
|
|
61149
|
+
if (bar.isEmpty || bar.isRestOnly) {
|
|
61150
|
+
this._emptyBarCount++;
|
|
61151
|
+
}
|
|
61152
|
+
this._updateVisibility();
|
|
60673
61153
|
}
|
|
60674
61154
|
revertLastBar() {
|
|
60675
61155
|
this._sharedLayoutData = new Map();
|
|
@@ -60680,6 +61160,10 @@ class RenderStaff {
|
|
|
60680
61160
|
for (const r of this.barRenderers) {
|
|
60681
61161
|
r.afterStaffBarReverted();
|
|
60682
61162
|
}
|
|
61163
|
+
if (lastBar.bar.isEmpty || lastBar.bar.isRestOnly) {
|
|
61164
|
+
this._emptyBarCount--;
|
|
61165
|
+
}
|
|
61166
|
+
this._updateVisibility();
|
|
60683
61167
|
return lastBar;
|
|
60684
61168
|
}
|
|
60685
61169
|
scaleToWidth(width) {
|
|
@@ -60790,6 +61274,7 @@ class RenderStaff {
|
|
|
60790
61274
|
this.height += this.topPadding + topOverflow + this.bottomOverflow + this.bottomPadding;
|
|
60791
61275
|
}
|
|
60792
61276
|
this.height = Math.ceil(this.height);
|
|
61277
|
+
this._updateVisibility();
|
|
60793
61278
|
}
|
|
60794
61279
|
paint(cx, cy, canvas, startIndex, count) {
|
|
60795
61280
|
if (this.height === 0 || count === 0) {
|
|
@@ -61190,6 +61675,8 @@ class StaffTrackGroup {
|
|
|
61190
61675
|
track;
|
|
61191
61676
|
staffSystem;
|
|
61192
61677
|
staves = [];
|
|
61678
|
+
firstVisibleStaff;
|
|
61679
|
+
lastVisibleStaff;
|
|
61193
61680
|
bracket = null;
|
|
61194
61681
|
constructor(staffSystem, track) {
|
|
61195
61682
|
this.staffSystem = staffSystem;
|
|
@@ -61204,18 +61691,47 @@ class StaffTrackGroup {
|
|
|
61204
61691
|
* @internal
|
|
61205
61692
|
*/
|
|
61206
61693
|
class SystemBracket {
|
|
61207
|
-
|
|
61208
|
-
|
|
61694
|
+
_system;
|
|
61695
|
+
firstStaffInBracket;
|
|
61696
|
+
lastStaffInBracket;
|
|
61697
|
+
firstVisibleStaffInBracket;
|
|
61698
|
+
lastVisibleStaffInBracket;
|
|
61209
61699
|
drawAsBrace = false;
|
|
61210
61700
|
braceScale = 1;
|
|
61211
61701
|
width = 0;
|
|
61212
61702
|
index = 0;
|
|
61213
|
-
|
|
61214
|
-
|
|
61703
|
+
canPaint = false;
|
|
61704
|
+
constructor(system) {
|
|
61705
|
+
this._system = system;
|
|
61706
|
+
}
|
|
61707
|
+
updateCanPaint() {
|
|
61708
|
+
let firstVisibleStaff = undefined;
|
|
61709
|
+
let lastVisibleStaff = undefined;
|
|
61710
|
+
for (let i = this.firstStaffInBracket.index; i <= this.lastStaffInBracket.index; i++) {
|
|
61711
|
+
const staff = this._system.allStaves[i];
|
|
61712
|
+
if (staff.isVisible) {
|
|
61713
|
+
if (!firstVisibleStaff) {
|
|
61714
|
+
firstVisibleStaff = staff;
|
|
61715
|
+
}
|
|
61716
|
+
lastVisibleStaff = staff;
|
|
61717
|
+
}
|
|
61718
|
+
}
|
|
61719
|
+
this.firstVisibleStaffInBracket = firstVisibleStaff;
|
|
61720
|
+
this.lastVisibleStaffInBracket = lastVisibleStaff;
|
|
61721
|
+
if (!firstVisibleStaff || !lastVisibleStaff) {
|
|
61722
|
+
this.canPaint = false;
|
|
61723
|
+
return;
|
|
61724
|
+
}
|
|
61725
|
+
// single staff brackets?
|
|
61726
|
+
const singleStaffBrackets = this._system.layout.renderer.score.stylesheet.showSingleStaffBrackets;
|
|
61727
|
+
if (!singleStaffBrackets && firstVisibleStaff === lastVisibleStaff) {
|
|
61728
|
+
this.canPaint = false;
|
|
61729
|
+
return;
|
|
61730
|
+
}
|
|
61731
|
+
this.canPaint = true;
|
|
61215
61732
|
}
|
|
61216
61733
|
finalizeBracket(smuflMetrics) {
|
|
61217
|
-
|
|
61218
|
-
if (this.firstStaffInBracket === this.lastStaffInBracket) {
|
|
61734
|
+
if (!this.canPaint) {
|
|
61219
61735
|
this.width = 0;
|
|
61220
61736
|
return;
|
|
61221
61737
|
}
|
|
@@ -61229,11 +61745,11 @@ class SystemBracket {
|
|
|
61229
61745
|
else {
|
|
61230
61746
|
this.width = smuflMetrics.bracketThickness;
|
|
61231
61747
|
}
|
|
61232
|
-
if (!this.drawAsBrace
|
|
61748
|
+
if (!this.drawAsBrace) {
|
|
61233
61749
|
return;
|
|
61234
61750
|
}
|
|
61235
|
-
const firstStart = this.
|
|
61236
|
-
const lastEnd = this.
|
|
61751
|
+
const firstStart = this.firstVisibleStaffInBracket.contentTop;
|
|
61752
|
+
const lastEnd = this.lastVisibleStaffInBracket.contentBottom;
|
|
61237
61753
|
const requiredHeight = lastEnd - firstStart;
|
|
61238
61754
|
const requiredScaleForBracket = requiredHeight / bravuraBraceHeightAtMusicFontSize;
|
|
61239
61755
|
this.braceScale = requiredScaleForBracket;
|
|
@@ -61245,8 +61761,8 @@ class SystemBracket {
|
|
|
61245
61761
|
*/
|
|
61246
61762
|
class SingleTrackSystemBracket extends SystemBracket {
|
|
61247
61763
|
track;
|
|
61248
|
-
constructor(track) {
|
|
61249
|
-
super();
|
|
61764
|
+
constructor(system, track) {
|
|
61765
|
+
super(system);
|
|
61250
61766
|
this.track = track;
|
|
61251
61767
|
this.drawAsBrace = SingleTrackSystemBracket.isTrackDrawAsBrace(track);
|
|
61252
61768
|
}
|
|
@@ -61311,6 +61827,7 @@ class StaffSystem {
|
|
|
61311
61827
|
topPadding;
|
|
61312
61828
|
bottomPadding;
|
|
61313
61829
|
allStaves = [];
|
|
61830
|
+
firstVisibleStaff;
|
|
61314
61831
|
constructor(layout) {
|
|
61315
61832
|
this.layout = layout;
|
|
61316
61833
|
this.topPadding = layout.renderer.settings.display.systemPaddingTop;
|
|
@@ -61329,14 +61846,40 @@ class StaffSystem {
|
|
|
61329
61846
|
this.masterBarsRenderers.push(renderers);
|
|
61330
61847
|
renderers.layoutingInfo.preBeatSize = 0;
|
|
61331
61848
|
let src = 0;
|
|
61332
|
-
|
|
61333
|
-
|
|
61334
|
-
|
|
61335
|
-
|
|
61849
|
+
let firstVisibleStaff = undefined;
|
|
61850
|
+
let anyStaffVisible = false;
|
|
61851
|
+
for (const g of this.staves) {
|
|
61852
|
+
let firstVisibleStaffInGroup = undefined;
|
|
61853
|
+
let lastVisibleStaffInGroup = undefined;
|
|
61854
|
+
for (const s of g.staves) {
|
|
61336
61855
|
const renderer = renderers.renderers[src++];
|
|
61337
61856
|
s.addBarRenderer(renderer);
|
|
61857
|
+
if (s.isVisible) {
|
|
61858
|
+
anyStaffVisible = true;
|
|
61859
|
+
if (!firstVisibleStaffInGroup) {
|
|
61860
|
+
firstVisibleStaffInGroup = s;
|
|
61861
|
+
}
|
|
61862
|
+
if (!firstVisibleStaff) {
|
|
61863
|
+
firstVisibleStaff = s;
|
|
61864
|
+
}
|
|
61865
|
+
lastVisibleStaffInGroup = s;
|
|
61866
|
+
}
|
|
61867
|
+
}
|
|
61868
|
+
g.firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61869
|
+
g.lastVisibleStaff = lastVisibleStaffInGroup;
|
|
61870
|
+
if (!firstVisibleStaff) {
|
|
61871
|
+
firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61338
61872
|
}
|
|
61339
61873
|
}
|
|
61874
|
+
if (!anyStaffVisible) {
|
|
61875
|
+
const group = this.staves[0];
|
|
61876
|
+
const firstStaff = group.staves[0];
|
|
61877
|
+
firstStaff.isVisible = true;
|
|
61878
|
+
group.firstVisibleStaff = firstStaff;
|
|
61879
|
+
group.lastVisibleStaff = firstStaff;
|
|
61880
|
+
firstVisibleStaff = firstStaff;
|
|
61881
|
+
}
|
|
61882
|
+
this.firstVisibleStaff = firstVisibleStaff;
|
|
61340
61883
|
this._calculateAccoladeSpacing(tracks);
|
|
61341
61884
|
this._updateWidthFromLastBar();
|
|
61342
61885
|
return renderers;
|
|
@@ -61347,15 +61890,26 @@ class StaffSystem {
|
|
|
61347
61890
|
result.layoutingInfo = new BarLayoutingInfo();
|
|
61348
61891
|
result.masterBar = tracks[0].score.masterBars[barIndex];
|
|
61349
61892
|
this.masterBarsRenderers.push(result);
|
|
61893
|
+
let firstVisibleStaff = undefined;
|
|
61894
|
+
let anyStaffVisible = false;
|
|
61350
61895
|
// add renderers
|
|
61351
61896
|
const barLayoutingInfo = result.layoutingInfo;
|
|
61352
61897
|
for (const g of this.staves) {
|
|
61898
|
+
let firstVisibleStaffInGroup = undefined;
|
|
61899
|
+
let lastVisibleStaffInGroup = undefined;
|
|
61353
61900
|
for (const s of g.staves) {
|
|
61354
61901
|
const bar = g.track.staves[s.modelStaff.index].bars[barIndex];
|
|
61355
61902
|
const additionalMultiBarsRestBars = additionalMultiBarRestIndexes == null
|
|
61356
61903
|
? null
|
|
61357
61904
|
: additionalMultiBarRestIndexes.map(b => g.track.staves[s.modelStaff.index].bars[b]);
|
|
61358
61905
|
s.addBar(bar, barLayoutingInfo, additionalMultiBarsRestBars);
|
|
61906
|
+
if (s.isVisible) {
|
|
61907
|
+
anyStaffVisible = true;
|
|
61908
|
+
if (!firstVisibleStaffInGroup) {
|
|
61909
|
+
firstVisibleStaffInGroup = s;
|
|
61910
|
+
}
|
|
61911
|
+
lastVisibleStaffInGroup = s;
|
|
61912
|
+
}
|
|
61359
61913
|
const renderer = s.barRenderers[s.barRenderers.length - 1];
|
|
61360
61914
|
result.renderers.push(renderer);
|
|
61361
61915
|
if (renderer.isLinkedToPrevious) {
|
|
@@ -61365,7 +61919,21 @@ class StaffSystem {
|
|
|
61365
61919
|
result.canWrap = false;
|
|
61366
61920
|
}
|
|
61367
61921
|
}
|
|
61922
|
+
g.firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61923
|
+
g.lastVisibleStaff = lastVisibleStaffInGroup;
|
|
61924
|
+
if (!firstVisibleStaff) {
|
|
61925
|
+
firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61926
|
+
}
|
|
61927
|
+
}
|
|
61928
|
+
if (!anyStaffVisible) {
|
|
61929
|
+
const group = this.staves[0];
|
|
61930
|
+
const firstStaff = group.staves[0];
|
|
61931
|
+
firstStaff.isVisible = true;
|
|
61932
|
+
group.firstVisibleStaff = firstStaff;
|
|
61933
|
+
group.lastVisibleStaff = firstStaff;
|
|
61934
|
+
firstVisibleStaff = firstStaff;
|
|
61368
61935
|
}
|
|
61936
|
+
this.firstVisibleStaff = firstVisibleStaff;
|
|
61369
61937
|
this._calculateAccoladeSpacing(tracks);
|
|
61370
61938
|
barLayoutingInfo.finish();
|
|
61371
61939
|
// ensure same widths of new renderer
|
|
@@ -61378,19 +61946,35 @@ class StaffSystem {
|
|
|
61378
61946
|
this.masterBarsRenderers.splice(this.masterBarsRenderers.length - 1, 1);
|
|
61379
61947
|
let width = 0;
|
|
61380
61948
|
let barDisplayScale = 0;
|
|
61381
|
-
|
|
61382
|
-
|
|
61383
|
-
|
|
61384
|
-
|
|
61385
|
-
|
|
61386
|
-
|
|
61949
|
+
let firstVisibleStaff = undefined;
|
|
61950
|
+
for (const g of this.staves) {
|
|
61951
|
+
let firstVisibleStaffInGroup = undefined;
|
|
61952
|
+
let lastVisibleStaffInGroup = undefined;
|
|
61953
|
+
for (const s of g.staves) {
|
|
61954
|
+
const lastBar = s.revertLastBar();
|
|
61955
|
+
const computedWidth = lastBar.computedWidth;
|
|
61956
|
+
if (computedWidth > width) {
|
|
61957
|
+
width = computedWidth;
|
|
61958
|
+
}
|
|
61959
|
+
const newBarDisplayScale = lastBar.barDisplayScale;
|
|
61960
|
+
if (newBarDisplayScale > barDisplayScale) {
|
|
61961
|
+
barDisplayScale = newBarDisplayScale;
|
|
61962
|
+
}
|
|
61963
|
+
lastBar.afterReverted();
|
|
61964
|
+
if (s.isVisible) {
|
|
61965
|
+
if (!firstVisibleStaffInGroup) {
|
|
61966
|
+
firstVisibleStaffInGroup = s;
|
|
61967
|
+
}
|
|
61968
|
+
lastVisibleStaffInGroup = s;
|
|
61969
|
+
}
|
|
61387
61970
|
}
|
|
61388
|
-
|
|
61389
|
-
|
|
61390
|
-
|
|
61971
|
+
g.firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61972
|
+
g.lastVisibleStaff = lastVisibleStaffInGroup;
|
|
61973
|
+
if (!firstVisibleStaff) {
|
|
61974
|
+
firstVisibleStaff = firstVisibleStaffInGroup;
|
|
61391
61975
|
}
|
|
61392
|
-
lastBar.afterReverted();
|
|
61393
61976
|
}
|
|
61977
|
+
this.firstVisibleStaff = firstVisibleStaff;
|
|
61394
61978
|
this.width -= width;
|
|
61395
61979
|
this.computedWidth -= width;
|
|
61396
61980
|
this.totalBarDisplayScale -= barDisplayScale;
|
|
@@ -61472,8 +62056,8 @@ class StaffSystem {
|
|
|
61472
62056
|
}
|
|
61473
62057
|
}
|
|
61474
62058
|
}
|
|
62059
|
+
this.accoladeWidth += settings.display.systemLabelPaddingLeft;
|
|
61475
62060
|
if (hasAnyTrackName) {
|
|
61476
|
-
this.accoladeWidth += settings.display.systemLabelPaddingLeft;
|
|
61477
62061
|
this.accoladeWidth += settings.display.systemLabelPaddingRight;
|
|
61478
62062
|
}
|
|
61479
62063
|
}
|
|
@@ -61497,6 +62081,7 @@ class StaffSystem {
|
|
|
61497
62081
|
}
|
|
61498
62082
|
let braceWidth = 0;
|
|
61499
62083
|
for (const b of this._brackets) {
|
|
62084
|
+
b.updateCanPaint();
|
|
61500
62085
|
b.finalizeBracket(settings.display.resources.engravingSettings);
|
|
61501
62086
|
braceWidth = Math.max(braceWidth, b.width);
|
|
61502
62087
|
}
|
|
@@ -61504,6 +62089,12 @@ class StaffSystem {
|
|
|
61504
62089
|
this.width += this.accoladeWidth;
|
|
61505
62090
|
this.computedWidth += this.accoladeWidth;
|
|
61506
62091
|
}
|
|
62092
|
+
else {
|
|
62093
|
+
for (const b of this._brackets) {
|
|
62094
|
+
b.updateCanPaint();
|
|
62095
|
+
b.finalizeBracket(settings.display.resources.engravingSettings);
|
|
62096
|
+
}
|
|
62097
|
+
}
|
|
61507
62098
|
}
|
|
61508
62099
|
_getStaffTrackGroup(track) {
|
|
61509
62100
|
for (let i = 0, j = this.staves.length; i < j; i++) {
|
|
@@ -61533,12 +62124,12 @@ class StaffSystem {
|
|
|
61533
62124
|
break;
|
|
61534
62125
|
case BracketExtendMode.GroupStaves:
|
|
61535
62126
|
// when grouping staves, we create one bracket for the whole track across all staves
|
|
61536
|
-
bracket = new SingleTrackSystemBracket(track);
|
|
62127
|
+
bracket = new SingleTrackSystemBracket(this, track);
|
|
61537
62128
|
bracket.index = this._brackets.length;
|
|
61538
62129
|
this._brackets.push(bracket);
|
|
61539
62130
|
break;
|
|
61540
62131
|
case BracketExtendMode.GroupSimilarInstruments:
|
|
61541
|
-
bracket = new SimilarInstrumentSystemBracket(track);
|
|
62132
|
+
bracket = new SimilarInstrumentSystemBracket(this, track);
|
|
61542
62133
|
bracket.index = this._brackets.length;
|
|
61543
62134
|
this._brackets.push(bracket);
|
|
61544
62135
|
break;
|
|
@@ -61585,8 +62176,10 @@ class StaffSystem {
|
|
|
61585
62176
|
}
|
|
61586
62177
|
}
|
|
61587
62178
|
paintPartial(cx, cy, canvas, startIndex, count) {
|
|
61588
|
-
for (
|
|
61589
|
-
|
|
62179
|
+
for (const s of this.allStaves) {
|
|
62180
|
+
if (s.isVisible) {
|
|
62181
|
+
s.paint(cx, cy, canvas, startIndex, count);
|
|
62182
|
+
}
|
|
61590
62183
|
}
|
|
61591
62184
|
const res = this.layout.renderer.settings.display.resources;
|
|
61592
62185
|
if (this.staves.length > 0 && startIndex === 0) {
|
|
@@ -61623,9 +62216,9 @@ class StaffSystem {
|
|
|
61623
62216
|
const oldBaseLine = canvas.textBaseline;
|
|
61624
62217
|
const oldTextAlign = canvas.textAlign;
|
|
61625
62218
|
for (const g of this.staves) {
|
|
61626
|
-
if (g.
|
|
61627
|
-
const firstStart = cy + g.
|
|
61628
|
-
const lastEnd = cy + g.
|
|
62219
|
+
if (g.firstVisibleStaff) {
|
|
62220
|
+
const firstStart = cy + g.firstVisibleStaff.contentTop;
|
|
62221
|
+
const lastEnd = cy + g.lastVisibleStaff.contentBottom;
|
|
61629
62222
|
let trackNameText = '';
|
|
61630
62223
|
switch (trackNameMode) {
|
|
61631
62224
|
case TrackNameMode.FullName:
|
|
@@ -61679,6 +62272,9 @@ class StaffSystem {
|
|
|
61679
62272
|
if (this.allStaves.length > 0 && needsSystemBarLine) {
|
|
61680
62273
|
let previousStaffInBracket = null;
|
|
61681
62274
|
for (const s of this.allStaves) {
|
|
62275
|
+
if (!s.isVisible) {
|
|
62276
|
+
continue;
|
|
62277
|
+
}
|
|
61682
62278
|
if (previousStaffInBracket !== null) {
|
|
61683
62279
|
const previousBottom = previousStaffInBracket.contentBottom;
|
|
61684
62280
|
const thisTop = s.contentTop;
|
|
@@ -61705,17 +62301,17 @@ class StaffSystem {
|
|
|
61705
62301
|
const settings = this.layout.renderer.settings;
|
|
61706
62302
|
for (const bracket of this._brackets) {
|
|
61707
62303
|
if (bracket.canPaint) {
|
|
61708
|
-
const barStartX = cx + bracket.
|
|
62304
|
+
const barStartX = cx + bracket.firstVisibleStaffInBracket.x;
|
|
61709
62305
|
const barSize = bracket.width;
|
|
61710
62306
|
const barOffset = settings.display.accoladeBarPaddingRight;
|
|
61711
|
-
const firstStart = cy + bracket.
|
|
61712
|
-
const lastEnd = cy + bracket.
|
|
62307
|
+
const firstStart = cy + bracket.firstVisibleStaffInBracket.contentTop;
|
|
62308
|
+
const lastEnd = cy + bracket.lastVisibleStaffInBracket.contentBottom;
|
|
61713
62309
|
let accoladeStart = firstStart;
|
|
61714
62310
|
let accoladeEnd = lastEnd;
|
|
61715
62311
|
if (bracket.drawAsBrace) {
|
|
61716
62312
|
CanvasHelper.fillMusicFontSymbolSafe(canvas, barStartX - barOffset - barSize, accoladeEnd, bracket.braceScale, MusicFontSymbol.Brace);
|
|
61717
62313
|
}
|
|
61718
|
-
else if (bracket.
|
|
62314
|
+
else if (bracket.firstVisibleStaffInBracket !== bracket.lastVisibleStaffInBracket) {
|
|
61719
62315
|
// brackets typically overflow by 1/4 staff-space
|
|
61720
62316
|
const smuflMetrics = settings.display.resources.engravingSettings;
|
|
61721
62317
|
const bracketOverflow = smuflMetrics.oneStaffSpace * 0.25;
|
|
@@ -61747,40 +62343,86 @@ class StaffSystem {
|
|
|
61747
62343
|
this.bottomPadding = Math.max(this.bottomPadding, neededHeight);
|
|
61748
62344
|
this._hasSystemSeparator = true;
|
|
61749
62345
|
}
|
|
62346
|
+
const anyStaffVisible = this._finalizeTrackGroups();
|
|
62347
|
+
// for now we always force one staff to be visible.
|
|
62348
|
+
// making also whole systems invisible needs separate attention (also on player cursor handling)
|
|
62349
|
+
if (!anyStaffVisible) {
|
|
62350
|
+
const group = this.staves[0];
|
|
62351
|
+
const firstStaff = group.staves[0];
|
|
62352
|
+
firstStaff.isVisible = true;
|
|
62353
|
+
this._finalizeTrackGroups(true);
|
|
62354
|
+
}
|
|
62355
|
+
for (const b of this._brackets) {
|
|
62356
|
+
b.finalizeBracket(settings.display.resources.engravingSettings);
|
|
62357
|
+
}
|
|
62358
|
+
}
|
|
62359
|
+
_finalizeTrackGroups(onlyFirstGroup = false) {
|
|
61750
62360
|
let currentY = 0;
|
|
62361
|
+
const settings = this.layout.renderer.settings;
|
|
61751
62362
|
const smufl = settings.display.resources.engravingSettings;
|
|
61752
62363
|
const topBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketTop);
|
|
61753
62364
|
const bottomBracketSpikeHeight = smufl.glyphHeights.get(MusicFontSymbol.BracketBottom);
|
|
61754
62365
|
let previousStaff = undefined;
|
|
61755
|
-
|
|
61756
|
-
|
|
61757
|
-
|
|
61758
|
-
|
|
61759
|
-
|
|
61760
|
-
const
|
|
61761
|
-
|
|
61762
|
-
|
|
61763
|
-
|
|
61764
|
-
|
|
61765
|
-
|
|
62366
|
+
let endSpikeOverflow = 0;
|
|
62367
|
+
let anyStaffVisible = false;
|
|
62368
|
+
for (const group of this.staves) {
|
|
62369
|
+
let firstVisibleStaffInGroup = undefined;
|
|
62370
|
+
let lastVisibleStaffInGroup = undefined;
|
|
62371
|
+
for (const staff of group.staves) {
|
|
62372
|
+
// check if we need "in-between padding"
|
|
62373
|
+
if (previousStaff !== undefined && previousStaff.trackIndex !== staff.trackIndex) {
|
|
62374
|
+
currentY += settings.display.trackStaffPaddingBetween;
|
|
62375
|
+
}
|
|
62376
|
+
const bracket = this._staffToBracket.has(staff) ? this._staffToBracket.get(staff) : undefined;
|
|
62377
|
+
const hasBracket = bracket && !bracket.drawAsBrace && bracket.canPaint;
|
|
62378
|
+
if (hasBracket && bracket.firstStaffInBracket === staff) {
|
|
62379
|
+
const spikeOverflow = topBracketSpikeHeight - staff.topOverflow;
|
|
62380
|
+
if (spikeOverflow > 0) {
|
|
62381
|
+
currentY += spikeOverflow;
|
|
62382
|
+
}
|
|
62383
|
+
}
|
|
62384
|
+
staff.x = this.accoladeWidth;
|
|
62385
|
+
staff.y = currentY;
|
|
62386
|
+
if (!onlyFirstGroup) {
|
|
62387
|
+
staff.finalizeStaff();
|
|
62388
|
+
}
|
|
62389
|
+
if (staff.isVisible) {
|
|
62390
|
+
currentY += staff.height;
|
|
62391
|
+
anyStaffVisible = true;
|
|
62392
|
+
previousStaff = staff;
|
|
62393
|
+
if (!firstVisibleStaffInGroup) {
|
|
62394
|
+
firstVisibleStaffInGroup = staff;
|
|
62395
|
+
}
|
|
62396
|
+
lastVisibleStaffInGroup = staff;
|
|
62397
|
+
}
|
|
62398
|
+
endSpikeOverflow = 0;
|
|
62399
|
+
if (hasBracket && bracket.lastStaffInBracket === staff) {
|
|
62400
|
+
const spikeOverflow = bottomBracketSpikeHeight - staff.bottomOverflow;
|
|
62401
|
+
if (spikeOverflow > 0) {
|
|
62402
|
+
if (staff.isVisible) {
|
|
62403
|
+
currentY += spikeOverflow;
|
|
62404
|
+
}
|
|
62405
|
+
else {
|
|
62406
|
+
endSpikeOverflow = spikeOverflow;
|
|
62407
|
+
}
|
|
62408
|
+
}
|
|
61766
62409
|
}
|
|
61767
62410
|
}
|
|
61768
|
-
|
|
61769
|
-
|
|
61770
|
-
|
|
61771
|
-
|
|
61772
|
-
|
|
61773
|
-
|
|
61774
|
-
|
|
61775
|
-
currentY += spikeOverflow;
|
|
61776
|
-
}
|
|
62411
|
+
group.firstVisibleStaff = firstVisibleStaffInGroup;
|
|
62412
|
+
group.lastVisibleStaff = lastVisibleStaffInGroup;
|
|
62413
|
+
if (!this.firstVisibleStaff) {
|
|
62414
|
+
this.firstVisibleStaff = firstVisibleStaffInGroup;
|
|
62415
|
+
}
|
|
62416
|
+
if (onlyFirstGroup) {
|
|
62417
|
+
break;
|
|
61777
62418
|
}
|
|
61778
|
-
previousStaff = staff;
|
|
61779
62419
|
}
|
|
61780
|
-
|
|
61781
|
-
|
|
61782
|
-
|
|
62420
|
+
// ensure we add overflow if last bracket is hidden
|
|
62421
|
+
if (endSpikeOverflow) {
|
|
62422
|
+
currentY += endSpikeOverflow;
|
|
61783
62423
|
}
|
|
62424
|
+
this._contentHeight = currentY;
|
|
62425
|
+
return anyStaffVisible;
|
|
61784
62426
|
}
|
|
61785
62427
|
buildBoundingsLookup(cx, cy) {
|
|
61786
62428
|
if (this.layout.renderer.boundsLookup.isFinished) {
|
|
@@ -61814,6 +62456,9 @@ class StaffSystem {
|
|
|
61814
62456
|
const masterBarBoundsLookup = new Map();
|
|
61815
62457
|
for (let i = 0; i < this.staves.length; i++) {
|
|
61816
62458
|
for (const staff of this.staves[i].staves) {
|
|
62459
|
+
if (!staff.isVisible) {
|
|
62460
|
+
continue;
|
|
62461
|
+
}
|
|
61817
62462
|
for (const renderer of staff.barRenderers) {
|
|
61818
62463
|
let masterBarBounds;
|
|
61819
62464
|
if (!masterBarBoundsLookup.has(renderer.bar.masterBar.index)) {
|
|
@@ -62124,8 +62769,9 @@ class ScoreLayout {
|
|
|
62124
62769
|
}
|
|
62125
62770
|
firstBarIndex = 0;
|
|
62126
62771
|
lastBarIndex = 0;
|
|
62127
|
-
createEmptyStaffSystem() {
|
|
62772
|
+
createEmptyStaffSystem(index) {
|
|
62128
62773
|
const system = new StaffSystem(this);
|
|
62774
|
+
system.index = index;
|
|
62129
62775
|
const allFactories = Environment.defaultRenderers;
|
|
62130
62776
|
const renderStaves = [];
|
|
62131
62777
|
for (let trackIndex = 0; trackIndex < this.renderer.tracks.length; trackIndex++) {
|
|
@@ -62137,7 +62783,7 @@ class ScoreLayout {
|
|
|
62137
62783
|
let previousStaff = undefined;
|
|
62138
62784
|
for (const factory of allFactories) {
|
|
62139
62785
|
if (this.profile.has(factory.staffId) && factory.canCreate(track, staff)) {
|
|
62140
|
-
const renderStaff = new RenderStaff(trackIndex, staff, factory);
|
|
62786
|
+
const renderStaff = new RenderStaff(system, trackIndex, staff, factory);
|
|
62141
62787
|
// insert shared effect bands at front
|
|
62142
62788
|
renderStaff.topEffectInfos.splice(0, 0, ...sharedTopEffects);
|
|
62143
62789
|
renderStaff.bottomEffectInfos.push(...sharedBottomEffects);
|
|
@@ -65295,7 +65941,7 @@ class HorizontalScreenLayout extends ScoreLayout {
|
|
|
65295
65941
|
}
|
|
65296
65942
|
endBarIndex = startIndex + endBarIndex - 1; // map count to array index
|
|
65297
65943
|
endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex));
|
|
65298
|
-
this._system = this.createEmptyStaffSystem();
|
|
65944
|
+
this._system = this.createEmptyStaffSystem(0);
|
|
65299
65945
|
this._system.isLast = true;
|
|
65300
65946
|
this._system.x = this.pagePadding[0];
|
|
65301
65947
|
this._system.y = this.pagePadding[1];
|
|
@@ -65568,8 +66214,7 @@ class PageViewLayout extends ScoreLayout {
|
|
|
65568
66214
|
this._systems = [];
|
|
65569
66215
|
let currentIndex = 0;
|
|
65570
66216
|
const maxWidth = this._maxWidth;
|
|
65571
|
-
let system = this.createEmptyStaffSystem();
|
|
65572
|
-
system.index = this._systems.length;
|
|
66217
|
+
let system = this.createEmptyStaffSystem(this._systems.length);
|
|
65573
66218
|
system.x = this.pagePadding[0];
|
|
65574
66219
|
system.y = y;
|
|
65575
66220
|
while (currentIndex < this._allMasterBarRenderers.length) {
|
|
@@ -65600,8 +66245,7 @@ class PageViewLayout extends ScoreLayout {
|
|
|
65600
66245
|
this._fitSystem(system);
|
|
65601
66246
|
y += this._paintSystem(system, oldHeight);
|
|
65602
66247
|
// note: we do not increase currentIndex here to have it added to the next system
|
|
65603
|
-
system = this.createEmptyStaffSystem();
|
|
65604
|
-
system.index = this._systems.length;
|
|
66248
|
+
system = this.createEmptyStaffSystem(this._systems.length);
|
|
65605
66249
|
system.x = this.pagePadding[0];
|
|
65606
66250
|
system.y = y;
|
|
65607
66251
|
}
|
|
@@ -65686,8 +66330,7 @@ class PageViewLayout extends ScoreLayout {
|
|
|
65686
66330
|
return barsPerRow;
|
|
65687
66331
|
}
|
|
65688
66332
|
_createStaffSystem(currentBarIndex, endIndex) {
|
|
65689
|
-
const system = this.createEmptyStaffSystem();
|
|
65690
|
-
system.index = this._systems.length;
|
|
66333
|
+
const system = this.createEmptyStaffSystem(this._systems.length);
|
|
65691
66334
|
const barsPerRow = this._getBarsPerSystem(system.index);
|
|
65692
66335
|
const maxWidth = this._maxWidth;
|
|
65693
66336
|
const end = endIndex + 1;
|
|
@@ -68367,16 +69010,77 @@ class NumberedBarRendererFactory extends BarRendererFactory {
|
|
|
68367
69010
|
class ClefGlyph extends MusicFontGlyph {
|
|
68368
69011
|
_clef;
|
|
68369
69012
|
_clefOttava;
|
|
69013
|
+
_ottavaGlyph;
|
|
68370
69014
|
constructor(x, y, clef, clefOttava) {
|
|
68371
69015
|
super(x, y, 1, ClefGlyph._getSymbol(clef, clefOttava));
|
|
68372
69016
|
this._clef = clef;
|
|
68373
69017
|
this._clefOttava = clefOttava;
|
|
68374
69018
|
}
|
|
69019
|
+
getBoundingBoxTop() {
|
|
69020
|
+
let top = super.getBoundingBoxTop();
|
|
69021
|
+
const ottava = this._ottavaGlyph;
|
|
69022
|
+
if (ottava) {
|
|
69023
|
+
const ottavaTop = this.y + ottava.getBoundingBoxTop();
|
|
69024
|
+
top = ModelUtils.minBoundingBox(top, ottavaTop);
|
|
69025
|
+
}
|
|
69026
|
+
return top;
|
|
69027
|
+
}
|
|
69028
|
+
getBoundingBoxBottom() {
|
|
69029
|
+
let bottom = super.getBoundingBoxBottom();
|
|
69030
|
+
const ottava = this._ottavaGlyph;
|
|
69031
|
+
if (ottava) {
|
|
69032
|
+
const ottavaBottom = this.y + ottava.getBoundingBoxBottom();
|
|
69033
|
+
bottom = ModelUtils.maxBoundingBox(bottom, ottavaBottom);
|
|
69034
|
+
}
|
|
69035
|
+
return bottom;
|
|
69036
|
+
}
|
|
68375
69037
|
doLayout() {
|
|
68376
69038
|
this.center = true;
|
|
68377
69039
|
super.doLayout();
|
|
68378
69040
|
this.width = this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.GClef);
|
|
68379
69041
|
this.offsetX = this.width / 2;
|
|
69042
|
+
this._ottavaGlyph = undefined;
|
|
69043
|
+
switch (this._clef) {
|
|
69044
|
+
case Clef.C3:
|
|
69045
|
+
case Clef.C4:
|
|
69046
|
+
switch (this._clefOttava) {
|
|
69047
|
+
case Ottavia._8vb:
|
|
69048
|
+
return;
|
|
69049
|
+
}
|
|
69050
|
+
break;
|
|
69051
|
+
case Clef.F4:
|
|
69052
|
+
case Clef.G2:
|
|
69053
|
+
return;
|
|
69054
|
+
}
|
|
69055
|
+
let ottavaSymbol;
|
|
69056
|
+
let top = false;
|
|
69057
|
+
switch (this._clefOttava) {
|
|
69058
|
+
case Ottavia._15ma:
|
|
69059
|
+
ottavaSymbol = MusicFontSymbol.Clef15;
|
|
69060
|
+
top = true;
|
|
69061
|
+
break;
|
|
69062
|
+
case Ottavia._8va:
|
|
69063
|
+
ottavaSymbol = MusicFontSymbol.Clef8;
|
|
69064
|
+
top = true;
|
|
69065
|
+
break;
|
|
69066
|
+
case Ottavia._8vb:
|
|
69067
|
+
ottavaSymbol = MusicFontSymbol.Clef8;
|
|
69068
|
+
break;
|
|
69069
|
+
case Ottavia._15mb:
|
|
69070
|
+
ottavaSymbol = MusicFontSymbol.Clef15;
|
|
69071
|
+
break;
|
|
69072
|
+
default:
|
|
69073
|
+
return;
|
|
69074
|
+
}
|
|
69075
|
+
const ottavaX = this.width / 2;
|
|
69076
|
+
const ottavaY = top
|
|
69077
|
+
? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
|
|
69078
|
+
: this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
|
|
69079
|
+
this.renderer.smuflMetrics.glyphHeights.get(ottavaSymbol);
|
|
69080
|
+
this._ottavaGlyph = new MusicFontGlyph(ottavaX, -ottavaY, 1, ottavaSymbol);
|
|
69081
|
+
this._ottavaGlyph.center = true;
|
|
69082
|
+
this._ottavaGlyph.renderer = this.renderer;
|
|
69083
|
+
this._ottavaGlyph.doLayout();
|
|
68380
69084
|
}
|
|
68381
69085
|
static _getSymbol(clef, clefOttava) {
|
|
68382
69086
|
switch (clef) {
|
|
@@ -68424,44 +69128,10 @@ class ClefGlyph extends MusicFontGlyph {
|
|
|
68424
69128
|
const _ = ElementStyleHelper.bar(canvas, BarSubElement.StandardNotationClef, this.renderer.bar);
|
|
68425
69129
|
try {
|
|
68426
69130
|
super.paint(cx, cy, canvas);
|
|
68427
|
-
|
|
68428
|
-
|
|
68429
|
-
|
|
68430
|
-
switch (this._clefOttava) {
|
|
68431
|
-
case Ottavia._8vb:
|
|
68432
|
-
return;
|
|
68433
|
-
}
|
|
68434
|
-
break;
|
|
68435
|
-
case Clef.F4:
|
|
68436
|
-
case Clef.G2:
|
|
68437
|
-
return;
|
|
68438
|
-
}
|
|
68439
|
-
let ottavaGlyph;
|
|
68440
|
-
let top = false;
|
|
68441
|
-
switch (this._clefOttava) {
|
|
68442
|
-
case Ottavia._15ma:
|
|
68443
|
-
ottavaGlyph = MusicFontSymbol.Clef15;
|
|
68444
|
-
top = true;
|
|
68445
|
-
break;
|
|
68446
|
-
case Ottavia._8va:
|
|
68447
|
-
ottavaGlyph = MusicFontSymbol.Clef8;
|
|
68448
|
-
top = true;
|
|
68449
|
-
break;
|
|
68450
|
-
case Ottavia._8vb:
|
|
68451
|
-
ottavaGlyph = MusicFontSymbol.Clef8;
|
|
68452
|
-
break;
|
|
68453
|
-
case Ottavia._15mb:
|
|
68454
|
-
ottavaGlyph = MusicFontSymbol.Clef15;
|
|
68455
|
-
break;
|
|
68456
|
-
default:
|
|
68457
|
-
return;
|
|
69131
|
+
const ottava = this._ottavaGlyph;
|
|
69132
|
+
if (ottava) {
|
|
69133
|
+
ottava.paint(cx + this.x, cy + this.y, canvas);
|
|
68458
69134
|
}
|
|
68459
|
-
const ottavaX = this.width / 2;
|
|
68460
|
-
const ottavaY = top
|
|
68461
|
-
? this.renderer.smuflMetrics.glyphTop.get(this.symbol)
|
|
68462
|
-
: this.renderer.smuflMetrics.glyphBottom.get(this.symbol) -
|
|
68463
|
-
this.renderer.smuflMetrics.glyphHeights.get(ottavaGlyph);
|
|
68464
|
-
CanvasHelper.fillMusicFontSymbolSafe(canvas, cx + this.x + ottavaX, cy + this.y - ottavaY, 1, ottavaGlyph, true);
|
|
68465
69135
|
}
|
|
68466
69136
|
finally {
|
|
68467
69137
|
_?.[Symbol.dispose]?.();
|