@coderline/alphatab 1.6.0-alpha.1416 → 1.6.0-alpha.1420
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 +441 -214
- package/dist/alphaTab.d.ts +114 -30
- package/dist/alphaTab.js +440 -213
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.vite.js +1 -1
- package/dist/alphaTab.vite.mjs +1 -1
- package/dist/alphaTab.webpack.js +1 -1
- package/dist/alphaTab.webpack.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.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.6.0-alpha.
|
|
2
|
+
* alphaTab v1.6.0-alpha.1420 (develop, build 1420)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -14318,13 +14318,6 @@
|
|
|
14318
14318
|
* @json_strict
|
|
14319
14319
|
*/
|
|
14320
14320
|
class BackingTrack {
|
|
14321
|
-
constructor() {
|
|
14322
|
-
/**
|
|
14323
|
-
* The number of milliseconds the audio should be shifted to align with the song.
|
|
14324
|
-
* (e.g. negative values allow skipping potential silent parts at the start of the file and directly start with the first note).
|
|
14325
|
-
*/
|
|
14326
|
-
this.padding = 0;
|
|
14327
|
-
}
|
|
14328
14321
|
}
|
|
14329
14322
|
|
|
14330
14323
|
/**
|
|
@@ -14357,6 +14350,7 @@
|
|
|
14357
14350
|
constructor() {
|
|
14358
14351
|
this._hasAnacrusis = false;
|
|
14359
14352
|
this._skipApplyLyrics = false;
|
|
14353
|
+
this._backingTrackPadding = 0;
|
|
14360
14354
|
this._doubleBars = new Set();
|
|
14361
14355
|
this._keySignatures = new Map();
|
|
14362
14356
|
}
|
|
@@ -14589,7 +14583,7 @@
|
|
|
14589
14583
|
assetId = c.innerText;
|
|
14590
14584
|
break;
|
|
14591
14585
|
case 'FramePadding':
|
|
14592
|
-
|
|
14586
|
+
this._backingTrackPadding = GpifParser.parseIntSafe(c.innerText, 0) / GpifParser.SampleRate * 1000;
|
|
14593
14587
|
break;
|
|
14594
14588
|
}
|
|
14595
14589
|
}
|
|
@@ -16803,6 +16797,7 @@
|
|
|
16803
16797
|
masterBar.tempoAutomations.push(automation);
|
|
16804
16798
|
break;
|
|
16805
16799
|
case AutomationType.SyncPoint:
|
|
16800
|
+
automation.syncPointValue.millisecondOffset -= this._backingTrackPadding;
|
|
16806
16801
|
masterBar.addSyncPoint(automation);
|
|
16807
16802
|
break;
|
|
16808
16803
|
}
|
|
@@ -22291,17 +22286,6 @@
|
|
|
22291
22286
|
}
|
|
22292
22287
|
}
|
|
22293
22288
|
|
|
22294
|
-
/**
|
|
22295
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22296
|
-
*/
|
|
22297
|
-
class BackingTrackSyncPoint {
|
|
22298
|
-
constructor(tick, data) {
|
|
22299
|
-
this.tick = 0;
|
|
22300
|
-
this.tick = tick;
|
|
22301
|
-
this.data = data;
|
|
22302
|
-
}
|
|
22303
|
-
}
|
|
22304
|
-
|
|
22305
22289
|
class MidiFileSequencerTempoChange {
|
|
22306
22290
|
constructor(bpm, ticks, time) {
|
|
22307
22291
|
this.bpm = bpm;
|
|
@@ -22309,10 +22293,12 @@
|
|
|
22309
22293
|
this.time = time;
|
|
22310
22294
|
}
|
|
22311
22295
|
}
|
|
22312
|
-
class BackingTrackSyncPointWithTime
|
|
22313
|
-
constructor(tick,
|
|
22314
|
-
|
|
22315
|
-
this.
|
|
22296
|
+
class BackingTrackSyncPointWithTime {
|
|
22297
|
+
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22298
|
+
this.alphaTabTick = tick;
|
|
22299
|
+
this.alphaTabTime = time;
|
|
22300
|
+
this.modifiedTempo = modifiedTempo;
|
|
22301
|
+
this.millisecondOffset = millisecondOffset;
|
|
22316
22302
|
}
|
|
22317
22303
|
}
|
|
22318
22304
|
class MidiSequencerState {
|
|
@@ -22418,7 +22404,7 @@
|
|
|
22418
22404
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22419
22405
|
this._mainState.modifiedTempo =
|
|
22420
22406
|
this._mainState.syncPoints.length > 0
|
|
22421
|
-
? this._mainState.syncPoints[0].
|
|
22407
|
+
? this._mainState.syncPoints[0].modifiedTempo
|
|
22422
22408
|
: this._mainState.currentTempo;
|
|
22423
22409
|
if (this.isPlayingMain) {
|
|
22424
22410
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22590,25 +22576,52 @@
|
|
|
22590
22576
|
mainUpdateSyncPoints(syncPoints) {
|
|
22591
22577
|
const state = this._mainState;
|
|
22592
22578
|
syncPoints.sort((a, b) => a.tick - b.tick); // just in case
|
|
22593
|
-
state.syncPoints =
|
|
22579
|
+
state.syncPoints = [];
|
|
22594
22580
|
if (syncPoints.length >= 0) {
|
|
22595
22581
|
let bpm = 120;
|
|
22596
22582
|
let absTick = 0;
|
|
22597
22583
|
let absTime = 0.0;
|
|
22598
|
-
let previousTick = 0;
|
|
22599
22584
|
let tempoChangeIndex = 0;
|
|
22600
22585
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22601
22586
|
const p = syncPoints[i];
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
previousTick
|
|
22587
|
+
let deltaTick = 0;
|
|
22588
|
+
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22589
|
+
let previousModifiedTempo;
|
|
22590
|
+
let previousMillisecondOffset;
|
|
22591
|
+
let previousTick;
|
|
22592
|
+
if (i === 0) {
|
|
22593
|
+
previousModifiedTempo = bpm;
|
|
22594
|
+
previousMillisecondOffset = 0;
|
|
22595
|
+
previousTick = 0;
|
|
22596
|
+
}
|
|
22597
|
+
else {
|
|
22598
|
+
const previousSyncPoint = syncPoints[i - 1];
|
|
22599
|
+
previousModifiedTempo = previousSyncPoint.data.modifiedTempo;
|
|
22600
|
+
previousMillisecondOffset = previousSyncPoint.data.millisecondOffset;
|
|
22601
|
+
previousTick = previousSyncPoint.tick;
|
|
22602
|
+
}
|
|
22603
|
+
// process time until sync point
|
|
22604
|
+
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
22605
|
+
// we have to calculate a interpolated sync point on the alphaTab time axis.
|
|
22606
|
+
// otherwise the linear interpolation later in the lookup will fail.
|
|
22607
|
+
// goal is to have always a linear increase between two points, no matter if the time axis is sliced by tempo changes or sync points
|
|
22607
22608
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22608
|
-
state.tempoChanges[tempoChangeIndex].ticks <=
|
|
22609
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.tick) {
|
|
22610
|
+
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22611
|
+
if (deltaTick > 0) {
|
|
22612
|
+
absTick += deltaTick;
|
|
22613
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22614
|
+
const millisPerTick = (p.data.millisecondOffset - previousMillisecondOffset) / (p.tick - previousTick);
|
|
22615
|
+
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22616
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(absTick, absTime, previousModifiedTempo, interpolatedMillisecondOffset));
|
|
22617
|
+
}
|
|
22609
22618
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22610
22619
|
tempoChangeIndex++;
|
|
22611
22620
|
}
|
|
22621
|
+
deltaTick = p.tick - absTick;
|
|
22622
|
+
absTick += deltaTick;
|
|
22623
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22624
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(p.tick, absTime, p.data.modifiedTempo, p.data.millisecondOffset));
|
|
22612
22625
|
}
|
|
22613
22626
|
}
|
|
22614
22627
|
state.syncPointIndex = 0;
|
|
@@ -22622,7 +22635,7 @@
|
|
|
22622
22635
|
this.updateCurrentTempo(state, timePosition);
|
|
22623
22636
|
const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
|
|
22624
22637
|
const timeDiff = timePosition - lastTempoChange.time;
|
|
22625
|
-
const ticks = (
|
|
22638
|
+
const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
|
|
22626
22639
|
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22627
22640
|
return lastTempoChange.ticks + ticks + 1;
|
|
22628
22641
|
}
|
|
@@ -22642,16 +22655,16 @@
|
|
|
22642
22655
|
const syncPoints = state.syncPoints;
|
|
22643
22656
|
if (syncPoints.length > 0) {
|
|
22644
22657
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22645
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22658
|
+
if (timePosition < syncPoints[syncPointIndex].millisecondOffset) {
|
|
22646
22659
|
syncPointIndex = 0;
|
|
22647
22660
|
}
|
|
22648
22661
|
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22649
|
-
syncPoints[syncPointIndex + 1].
|
|
22662
|
+
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22650
22663
|
syncPointIndex++;
|
|
22651
22664
|
}
|
|
22652
22665
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22653
22666
|
state.syncPointIndex = syncPointIndex;
|
|
22654
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22667
|
+
state.modifiedTempo = syncPoints[syncPointIndex].modifiedTempo;
|
|
22655
22668
|
}
|
|
22656
22669
|
}
|
|
22657
22670
|
else {
|
|
@@ -22667,18 +22680,18 @@
|
|
|
22667
22680
|
this.updateCurrentTempo(this._mainState, timePosition);
|
|
22668
22681
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22669
22682
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22670
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22683
|
+
const timeDiff = timePosition - currentSyncPoint.millisecondOffset;
|
|
22671
22684
|
let alphaTabTimeDiff;
|
|
22672
22685
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22673
22686
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22674
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22675
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22687
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset);
|
|
22688
|
+
alphaTabTimeDiff = (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22676
22689
|
}
|
|
22677
22690
|
else {
|
|
22678
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22679
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22691
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.millisecondOffset);
|
|
22692
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22680
22693
|
}
|
|
22681
|
-
return (currentSyncPoint.
|
|
22694
|
+
return (currentSyncPoint.alphaTabTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22682
22695
|
}
|
|
22683
22696
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22684
22697
|
const mainState = this._mainState;
|
|
@@ -22688,25 +22701,27 @@
|
|
|
22688
22701
|
}
|
|
22689
22702
|
timePosition *= this.playbackSpeed;
|
|
22690
22703
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22691
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22704
|
+
if (timePosition < syncPoints[syncPointIndex].alphaTabTime) {
|
|
22692
22705
|
syncPointIndex = 0;
|
|
22693
22706
|
}
|
|
22694
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22707
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].alphaTabTime <= timePosition) {
|
|
22695
22708
|
syncPointIndex++;
|
|
22696
22709
|
}
|
|
22710
|
+
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22711
|
+
// we ensure that we have a linear increase between two points
|
|
22697
22712
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22698
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22713
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.alphaTabTime;
|
|
22699
22714
|
let backingTrackPos;
|
|
22700
22715
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22701
22716
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22702
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22703
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22704
|
-
backingTrackPos = currentSyncPoint.
|
|
22717
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime);
|
|
22718
|
+
const backingTrackDiff = nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset;
|
|
22719
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22705
22720
|
}
|
|
22706
22721
|
else {
|
|
22707
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22708
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22709
|
-
backingTrackPos = currentSyncPoint.
|
|
22722
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.alphaTabTime);
|
|
22723
|
+
const frameDiff = backingTrackLength - currentSyncPoint.millisecondOffset;
|
|
22724
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + frameDiff * relativeAlphaTabTimeDiff;
|
|
22710
22725
|
}
|
|
22711
22726
|
return backingTrackPos;
|
|
22712
22727
|
}
|
|
@@ -28588,7 +28603,9 @@
|
|
|
28588
28603
|
hasSamplesForPercussion(key) {
|
|
28589
28604
|
return this.synthesizer.hasSamplesForPercussion(key);
|
|
28590
28605
|
}
|
|
28591
|
-
loadBackingTrack(_score
|
|
28606
|
+
loadBackingTrack(_score) {
|
|
28607
|
+
}
|
|
28608
|
+
updateSyncPoints(_syncPoints) {
|
|
28592
28609
|
}
|
|
28593
28610
|
}
|
|
28594
28611
|
/**
|
|
@@ -29076,7 +29093,7 @@
|
|
|
29076
29093
|
}
|
|
29077
29094
|
switch (typeof v) {
|
|
29078
29095
|
case 'undefined':
|
|
29079
|
-
return
|
|
29096
|
+
return undefined;
|
|
29080
29097
|
case 'object': {
|
|
29081
29098
|
const m = v;
|
|
29082
29099
|
const families = m.get('families');
|
|
@@ -29156,10 +29173,13 @@
|
|
|
29156
29173
|
return Font.withFamilyList(families, fontSize, fontStyle, fontWeight);
|
|
29157
29174
|
}
|
|
29158
29175
|
default:
|
|
29159
|
-
return
|
|
29176
|
+
return undefined;
|
|
29160
29177
|
}
|
|
29161
29178
|
}
|
|
29162
29179
|
static toJson(font) {
|
|
29180
|
+
if (!font) {
|
|
29181
|
+
return undefined;
|
|
29182
|
+
}
|
|
29163
29183
|
const o = new Map();
|
|
29164
29184
|
o.set('families', font.families);
|
|
29165
29185
|
o.set('size', font.size);
|
|
@@ -30166,6 +30186,14 @@
|
|
|
30166
30186
|
/*@target web*/
|
|
30167
30187
|
o.set("fontdirectory", obj.fontDirectory);
|
|
30168
30188
|
/*@target web*/
|
|
30189
|
+
if (obj.smuflFontSources !== null) {
|
|
30190
|
+
const m = new Map();
|
|
30191
|
+
o.set("smuflfontsources", m);
|
|
30192
|
+
for (const [k, v] of obj.smuflFontSources) {
|
|
30193
|
+
m.set(k.toString(), v);
|
|
30194
|
+
}
|
|
30195
|
+
}
|
|
30196
|
+
/*@target web*/
|
|
30169
30197
|
o.set("file", obj.file);
|
|
30170
30198
|
/*@target web*/
|
|
30171
30199
|
o.set("tex", obj.tex);
|
|
@@ -30189,6 +30217,13 @@
|
|
|
30189
30217
|
obj.fontDirectory = v;
|
|
30190
30218
|
return true;
|
|
30191
30219
|
/*@target web*/
|
|
30220
|
+
case "smuflfontsources":
|
|
30221
|
+
obj.smuflFontSources = new Map();
|
|
30222
|
+
JsonHelper.forEach(v, (v, k) => {
|
|
30223
|
+
obj.smuflFontSources.set(JsonHelper.parseEnum(k, exports.FontFileFormat), v);
|
|
30224
|
+
});
|
|
30225
|
+
return true;
|
|
30226
|
+
/*@target web*/
|
|
30192
30227
|
case "file":
|
|
30193
30228
|
obj.file = v;
|
|
30194
30229
|
return true;
|
|
@@ -30232,6 +30267,7 @@
|
|
|
30232
30267
|
return null;
|
|
30233
30268
|
}
|
|
30234
30269
|
const o = new Map();
|
|
30270
|
+
o.set("smuflfont", Font.toJson(obj.smuflFont));
|
|
30235
30271
|
o.set("copyrightfont", Font.toJson(obj.copyrightFont));
|
|
30236
30272
|
o.set("titlefont", Font.toJson(obj.titleFont));
|
|
30237
30273
|
o.set("subtitlefont", Font.toJson(obj.subTitleFont));
|
|
@@ -30258,6 +30294,9 @@
|
|
|
30258
30294
|
}
|
|
30259
30295
|
static setProperty(obj, property, v) {
|
|
30260
30296
|
switch (property) {
|
|
30297
|
+
case "smuflfont":
|
|
30298
|
+
obj.smuflFont = Font.fromJson(v);
|
|
30299
|
+
return true;
|
|
30261
30300
|
case "copyrightfont":
|
|
30262
30301
|
obj.copyrightFont = Font.fromJson(v);
|
|
30263
30302
|
return true;
|
|
@@ -32463,15 +32502,9 @@
|
|
|
32463
32502
|
return null;
|
|
32464
32503
|
}
|
|
32465
32504
|
const o = new Map();
|
|
32466
|
-
o.set("padding", obj.padding);
|
|
32467
32505
|
return o;
|
|
32468
32506
|
}
|
|
32469
32507
|
static setProperty(obj, property, v) {
|
|
32470
|
-
switch (property) {
|
|
32471
|
-
case "padding":
|
|
32472
|
-
obj.padding = v;
|
|
32473
|
-
return true;
|
|
32474
|
-
}
|
|
32475
32508
|
return false;
|
|
32476
32509
|
}
|
|
32477
32510
|
}
|
|
@@ -34235,15 +34268,6 @@
|
|
|
34235
34268
|
this._color = new Color(0, 0, 0, 0xff);
|
|
34236
34269
|
this._font = new Font('Arial', 10, FontStyle.Plain);
|
|
34237
34270
|
this._lineWidth = 0;
|
|
34238
|
-
const fontElement = document.createElement('span');
|
|
34239
|
-
fontElement.classList.add('at');
|
|
34240
|
-
document.body.appendChild(fontElement);
|
|
34241
|
-
const style = window.getComputedStyle(fontElement);
|
|
34242
|
-
let family = style.fontFamily;
|
|
34243
|
-
if (family.startsWith('"') || family.startsWith("'")) {
|
|
34244
|
-
family = family.substr(1, family.length - 2);
|
|
34245
|
-
}
|
|
34246
|
-
this._musicFont = new Font(family, Number.parseFloat(style.fontSize), FontStyle.Plain);
|
|
34247
34271
|
this._measureCanvas = document.createElement('canvas');
|
|
34248
34272
|
this._measureCanvas.width = 10;
|
|
34249
34273
|
this._measureCanvas.height = 10;
|
|
@@ -34258,6 +34282,7 @@
|
|
|
34258
34282
|
return null;
|
|
34259
34283
|
}
|
|
34260
34284
|
beginRender(width, height) {
|
|
34285
|
+
this._musicFont = this.settings.display.resources.smuflFont;
|
|
34261
34286
|
const scale = this.settings.display.scale;
|
|
34262
34287
|
this._canvas = document.createElement('canvas');
|
|
34263
34288
|
this._canvas.width = (width * Environment.HighDpiFactor) | 0;
|
|
@@ -34942,7 +34967,13 @@
|
|
|
34942
34967
|
* Gets the list of tempo changes within the tick lookup.
|
|
34943
34968
|
*/
|
|
34944
34969
|
this.tempoChanges = [];
|
|
34970
|
+
/**
|
|
34971
|
+
* The first beat in the bar.
|
|
34972
|
+
*/
|
|
34945
34973
|
this.firstBeat = null;
|
|
34974
|
+
/**
|
|
34975
|
+
* The last beat in the bar.
|
|
34976
|
+
*/
|
|
34946
34977
|
this.lastBeat = null;
|
|
34947
34978
|
/**
|
|
34948
34979
|
* Gets or sets the {@link MasterBarTickLookup} of the next masterbar in the {@link Score}
|
|
@@ -35356,14 +35387,13 @@
|
|
|
35356
35387
|
constructor() {
|
|
35357
35388
|
this._currentMasterBar = null;
|
|
35358
35389
|
/**
|
|
35359
|
-
*
|
|
35390
|
+
* A dictionary of all master bars played. The index is the index equals to {@link MasterBar.index}.
|
|
35360
35391
|
* This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to {@link MasterBars}.
|
|
35361
35392
|
* @internal
|
|
35362
35393
|
*/
|
|
35363
35394
|
this.masterBarLookup = new Map();
|
|
35364
35395
|
/**
|
|
35365
|
-
*
|
|
35366
|
-
* @internal
|
|
35396
|
+
* A list of all {@link MasterBarTickLookup} sorted by time.
|
|
35367
35397
|
*/
|
|
35368
35398
|
this.masterBars = [];
|
|
35369
35399
|
/**
|
|
@@ -35737,6 +35767,17 @@
|
|
|
35737
35767
|
}
|
|
35738
35768
|
}
|
|
35739
35769
|
|
|
35770
|
+
/**
|
|
35771
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35772
|
+
*/
|
|
35773
|
+
class BackingTrackSyncPoint {
|
|
35774
|
+
constructor(tick, data) {
|
|
35775
|
+
this.tick = 0;
|
|
35776
|
+
this.tick = tick;
|
|
35777
|
+
this.data = data;
|
|
35778
|
+
}
|
|
35779
|
+
}
|
|
35780
|
+
|
|
35740
35781
|
class MidiNoteDuration {
|
|
35741
35782
|
constructor() {
|
|
35742
35783
|
this.noteOnly = 0;
|
|
@@ -35806,62 +35847,22 @@
|
|
|
35806
35847
|
this.generateTrack(track);
|
|
35807
35848
|
}
|
|
35808
35849
|
Logger.debug('Midi', 'Begin midi generation');
|
|
35809
|
-
|
|
35810
|
-
|
|
35811
|
-
|
|
35812
|
-
|
|
35813
|
-
|
|
35814
|
-
|
|
35815
|
-
|
|
35816
|
-
|
|
35817
|
-
const currentTick = controller.currentTick;
|
|
35818
|
-
controller.processCurrent();
|
|
35819
|
-
if (controller.shouldPlay) {
|
|
35820
|
-
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35821
|
-
occurence++;
|
|
35822
|
-
barOccurence.set(index, occurence);
|
|
35823
|
-
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35824
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35825
|
-
currentTempo = bar.tempoAutomations[0].value;
|
|
35826
|
-
}
|
|
35827
|
-
for (const track of this._score.tracks) {
|
|
35828
|
-
for (const staff of track.staves) {
|
|
35829
|
-
if (index < staff.bars.length) {
|
|
35830
|
-
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35831
|
-
}
|
|
35850
|
+
this.syncPoints = [];
|
|
35851
|
+
MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
|
|
35852
|
+
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35853
|
+
}, (index, currentTick, currentTempo) => {
|
|
35854
|
+
for (const track of this._score.tracks) {
|
|
35855
|
+
for (const staff of track.staves) {
|
|
35856
|
+
if (index < staff.bars.length) {
|
|
35857
|
+
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35832
35858
|
}
|
|
35833
35859
|
}
|
|
35834
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35835
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35836
|
-
}
|
|
35837
35860
|
}
|
|
35838
|
-
|
|
35839
|
-
|
|
35840
|
-
|
|
35841
|
-
// here we interpolate the sync point which marks the end of the sync.
|
|
35842
|
-
// Sync points define new tempos at certain positions.
|
|
35843
|
-
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35844
|
-
// but where it ends according to the BPM and the remaining ticks.
|
|
35845
|
-
if (this.syncPoints.length > 0) {
|
|
35846
|
-
const lastSyncPoint = this.syncPoints[this.syncPoints.length - 1];
|
|
35847
|
-
const endTick = controller.currentTick;
|
|
35848
|
-
const remainingTicks = endTick - lastSyncPoint.tick;
|
|
35849
|
-
if (remainingTicks > 0) {
|
|
35850
|
-
const syncPointData = new SyncPointData();
|
|
35851
|
-
// last occurence of the last bar
|
|
35852
|
-
syncPointData.barOccurence = barOccurence.get(this._score.masterBars.length - 1);
|
|
35853
|
-
// same tempo as last point
|
|
35854
|
-
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35855
|
-
// interpolated end from last syncPoint
|
|
35856
|
-
syncPointData.millisecondOffset =
|
|
35857
|
-
lastSyncPoint.data.millisecondOffset +
|
|
35858
|
-
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35859
|
-
this.syncPoints.push(new BackingTrackSyncPoint(endTick, syncPointData));
|
|
35861
|
+
}, endTick => {
|
|
35862
|
+
for (const track of this._score.tracks) {
|
|
35863
|
+
this._handler.finishTrack(track.index, endTick);
|
|
35860
35864
|
}
|
|
35861
|
-
}
|
|
35862
|
-
for (const track of this._score.tracks) {
|
|
35863
|
-
this._handler.finishTrack(track.index, controller.currentTick);
|
|
35864
|
-
}
|
|
35865
|
+
});
|
|
35865
35866
|
Logger.debug('Midi', 'Midi generation done');
|
|
35866
35867
|
}
|
|
35867
35868
|
generateTrack(track) {
|
|
@@ -35906,6 +35907,81 @@
|
|
|
35906
35907
|
this._handler.addControlChange(track.index, 0, channel, ControllerType.DataEntryCoarse, MidiFileGenerator.PitchBendRangeInSemitones);
|
|
35907
35908
|
this.addProgramChange(track, 0, channel, playbackInfo.program);
|
|
35908
35909
|
}
|
|
35910
|
+
/**
|
|
35911
|
+
* Generates the sync points for the given score without re-generating the midi itself.
|
|
35912
|
+
* @remarks
|
|
35913
|
+
* Use this method if a re-generation of the sync points after modification is required.
|
|
35914
|
+
* It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
|
|
35915
|
+
* need to be considered for synchronization.
|
|
35916
|
+
* @param score The song for which to regenerate the sync points.
|
|
35917
|
+
* @returns The generated sync points for usage in the backing track playback.
|
|
35918
|
+
*/
|
|
35919
|
+
static generateSyncPoints(score) {
|
|
35920
|
+
const syncPoints = [];
|
|
35921
|
+
MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
35922
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
35923
|
+
}, _endTick => {
|
|
35924
|
+
});
|
|
35925
|
+
return syncPoints;
|
|
35926
|
+
}
|
|
35927
|
+
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35928
|
+
const controller = new MidiPlaybackController(score);
|
|
35929
|
+
let currentTempo = score.tempo;
|
|
35930
|
+
let previousMasterBar = null;
|
|
35931
|
+
// store the previous played bar for repeats
|
|
35932
|
+
const barOccurence = new Map();
|
|
35933
|
+
while (!controller.finished) {
|
|
35934
|
+
const index = controller.index;
|
|
35935
|
+
const bar = score.masterBars[index];
|
|
35936
|
+
const currentTick = controller.currentTick;
|
|
35937
|
+
controller.processCurrent();
|
|
35938
|
+
if (controller.shouldPlay) {
|
|
35939
|
+
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35940
|
+
occurence++;
|
|
35941
|
+
barOccurence.set(index, occurence);
|
|
35942
|
+
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35943
|
+
const barSyncPoints = bar.syncPoints;
|
|
35944
|
+
if (barSyncPoints) {
|
|
35945
|
+
for (const syncPoint of barSyncPoints) {
|
|
35946
|
+
if (syncPoint.syncPointValue.barOccurence === occurence) {
|
|
35947
|
+
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
35948
|
+
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
35949
|
+
}
|
|
35950
|
+
}
|
|
35951
|
+
}
|
|
35952
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35953
|
+
currentTempo = bar.tempoAutomations[0].value;
|
|
35954
|
+
}
|
|
35955
|
+
generateTracks(index, currentTick, currentTempo);
|
|
35956
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35957
|
+
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35958
|
+
}
|
|
35959
|
+
}
|
|
35960
|
+
controller.moveNext();
|
|
35961
|
+
previousMasterBar = bar;
|
|
35962
|
+
}
|
|
35963
|
+
// here we interpolate the sync point which marks the end of the sync.
|
|
35964
|
+
// Sync points define new tempos at certain positions.
|
|
35965
|
+
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35966
|
+
// but where it ends according to the BPM and the remaining ticks.
|
|
35967
|
+
if (syncPoints.length > 0) {
|
|
35968
|
+
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
35969
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.tick;
|
|
35970
|
+
if (remainingTicks > 0) {
|
|
35971
|
+
const syncPointData = new SyncPointData();
|
|
35972
|
+
// last occurence of the last bar
|
|
35973
|
+
syncPointData.barOccurence = barOccurence.get(score.masterBars.length - 1);
|
|
35974
|
+
// same tempo as last point
|
|
35975
|
+
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35976
|
+
// interpolated end from last syncPoint
|
|
35977
|
+
syncPointData.millisecondOffset =
|
|
35978
|
+
lastSyncPoint.data.millisecondOffset +
|
|
35979
|
+
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35980
|
+
syncPoints.push(new BackingTrackSyncPoint(controller.currentTick, syncPointData));
|
|
35981
|
+
}
|
|
35982
|
+
}
|
|
35983
|
+
finish(controller.currentTick);
|
|
35984
|
+
}
|
|
35909
35985
|
static toChannelShort(data) {
|
|
35910
35986
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
35911
35987
|
return Math.max(value, -1) + 1;
|
|
@@ -38242,21 +38318,23 @@
|
|
|
38242
38318
|
this._backingTrackOutput.seekTo(this.sequencer.mainTimePositionToBackingTrack(timePosition, this._backingTrackOutput.backingTrackDuration));
|
|
38243
38319
|
}
|
|
38244
38320
|
}
|
|
38245
|
-
loadBackingTrack(score
|
|
38321
|
+
loadBackingTrack(score) {
|
|
38246
38322
|
const backingTrackInfo = score.backingTrack;
|
|
38247
38323
|
if (backingTrackInfo) {
|
|
38248
38324
|
this._backingTrackOutput.loadBackingTrack(backingTrackInfo);
|
|
38249
|
-
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38250
38325
|
this.timePosition = 0;
|
|
38251
38326
|
}
|
|
38252
38327
|
}
|
|
38328
|
+
updateSyncPoints(syncPoints) {
|
|
38329
|
+
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38330
|
+
this.tickPosition = this.tickPosition;
|
|
38331
|
+
}
|
|
38253
38332
|
}
|
|
38254
38333
|
|
|
38255
38334
|
class ExternalMediaSynthOutput {
|
|
38256
38335
|
constructor() {
|
|
38257
38336
|
// fake rate
|
|
38258
38337
|
this.sampleRate = 44100;
|
|
38259
|
-
this._padding = 0;
|
|
38260
38338
|
this._seekPosition = 0;
|
|
38261
38339
|
this.ready = new EventEmitter();
|
|
38262
38340
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -38299,20 +38377,19 @@
|
|
|
38299
38377
|
seekTo(time) {
|
|
38300
38378
|
const handler = this.handler;
|
|
38301
38379
|
if (handler) {
|
|
38302
|
-
handler.seekTo(time
|
|
38380
|
+
handler.seekTo(time);
|
|
38303
38381
|
}
|
|
38304
38382
|
else {
|
|
38305
|
-
this._seekPosition = time
|
|
38383
|
+
this._seekPosition = time;
|
|
38306
38384
|
}
|
|
38307
38385
|
}
|
|
38308
|
-
loadBackingTrack(
|
|
38309
|
-
this._padding = backingTrack.padding;
|
|
38386
|
+
loadBackingTrack(_backingTrack) {
|
|
38310
38387
|
}
|
|
38311
38388
|
open(_bufferTimeInMilliseconds) {
|
|
38312
38389
|
this.ready.trigger();
|
|
38313
38390
|
}
|
|
38314
38391
|
updatePosition(currentTime) {
|
|
38315
|
-
this.timeUpdate.trigger(currentTime
|
|
38392
|
+
this.timeUpdate.trigger(currentTime);
|
|
38316
38393
|
}
|
|
38317
38394
|
play() {
|
|
38318
38395
|
this.handler?.play();
|
|
@@ -38577,9 +38654,14 @@
|
|
|
38577
38654
|
this._instance.loadMidiFile(midi);
|
|
38578
38655
|
}
|
|
38579
38656
|
}
|
|
38580
|
-
loadBackingTrack(score
|
|
38657
|
+
loadBackingTrack(score) {
|
|
38581
38658
|
if (this._instance) {
|
|
38582
|
-
this._instance.loadBackingTrack(score
|
|
38659
|
+
this._instance.loadBackingTrack(score);
|
|
38660
|
+
}
|
|
38661
|
+
}
|
|
38662
|
+
updateSyncPoints(syncPoints) {
|
|
38663
|
+
if (this._instance) {
|
|
38664
|
+
this._instance.updateSyncPoints(syncPoints);
|
|
38583
38665
|
}
|
|
38584
38666
|
}
|
|
38585
38667
|
applyTranspositionPitches(transpositionPitches) {
|
|
@@ -39908,7 +39990,7 @@
|
|
|
39908
39990
|
}
|
|
39909
39991
|
appendRenderResult(result, isLast) {
|
|
39910
39992
|
// resizing the canvas and wrapper elements at the end is enough
|
|
39911
|
-
// it avoids flickering on resizes and re-renders.
|
|
39993
|
+
// it avoids flickering on resizes and re-renders.
|
|
39912
39994
|
// the individual partials are anyhow sized correctly
|
|
39913
39995
|
if (isLast) {
|
|
39914
39996
|
this.canvasElement.width = result.totalWidth;
|
|
@@ -40670,9 +40752,23 @@
|
|
|
40670
40752
|
this.onMidiLoad(midiFile);
|
|
40671
40753
|
const player = this._player;
|
|
40672
40754
|
player.loadMidiFile(midiFile);
|
|
40673
|
-
player.loadBackingTrack(score
|
|
40755
|
+
player.loadBackingTrack(score);
|
|
40756
|
+
player.updateSyncPoints(generator.syncPoints);
|
|
40674
40757
|
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40675
40758
|
}
|
|
40759
|
+
/**
|
|
40760
|
+
* Triggers an update of the sync points for the current score after modification within the data model
|
|
40761
|
+
* @category Methods - Player
|
|
40762
|
+
* @since 1.6.0
|
|
40763
|
+
*/
|
|
40764
|
+
updateSyncPoints() {
|
|
40765
|
+
if (!this.score) {
|
|
40766
|
+
return;
|
|
40767
|
+
}
|
|
40768
|
+
const score = this.score;
|
|
40769
|
+
const player = this._player;
|
|
40770
|
+
player.updateSyncPoints(MidiFileGenerator.generateSyncPoints(score));
|
|
40771
|
+
}
|
|
40676
40772
|
/**
|
|
40677
40773
|
* Changes the volume of the given tracks.
|
|
40678
40774
|
* @param tracks The tracks for which the volume should be changed.
|
|
@@ -43340,6 +43436,8 @@
|
|
|
43340
43436
|
}
|
|
43341
43437
|
loadBackingTrack(_score) {
|
|
43342
43438
|
}
|
|
43439
|
+
updateSyncPoints(_syncPoints) {
|
|
43440
|
+
}
|
|
43343
43441
|
}
|
|
43344
43442
|
|
|
43345
43443
|
/**
|
|
@@ -43714,7 +43812,6 @@
|
|
|
43714
43812
|
constructor() {
|
|
43715
43813
|
// fake rate
|
|
43716
43814
|
this.sampleRate = 44100;
|
|
43717
|
-
this._padding = 0;
|
|
43718
43815
|
this._updateInterval = 0;
|
|
43719
43816
|
this.ready = new EventEmitter();
|
|
43720
43817
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -43738,13 +43835,12 @@
|
|
|
43738
43835
|
this.audioElement.volume = value;
|
|
43739
43836
|
}
|
|
43740
43837
|
seekTo(time) {
|
|
43741
|
-
this.audioElement.currentTime = time / 1000
|
|
43838
|
+
this.audioElement.currentTime = time / 1000;
|
|
43742
43839
|
}
|
|
43743
43840
|
loadBackingTrack(backingTrack) {
|
|
43744
43841
|
if (this.audioElement?.src) {
|
|
43745
43842
|
URL.revokeObjectURL(this.audioElement.src);
|
|
43746
43843
|
}
|
|
43747
|
-
this._padding = backingTrack.padding / 1000;
|
|
43748
43844
|
const blob = new Blob([backingTrack.rawAudioFile]);
|
|
43749
43845
|
// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
|
|
43750
43846
|
// Step 8. resets the playbackRate, we need to remember and restore it.
|
|
@@ -43763,7 +43859,7 @@
|
|
|
43763
43859
|
this.ready.trigger();
|
|
43764
43860
|
}
|
|
43765
43861
|
updatePosition() {
|
|
43766
|
-
const timePos =
|
|
43862
|
+
const timePos = this.audioElement.currentTime * 1000;
|
|
43767
43863
|
this.timeUpdate.trigger(timePos);
|
|
43768
43864
|
}
|
|
43769
43865
|
play() {
|
|
@@ -43849,10 +43945,6 @@
|
|
|
43849
43945
|
return this.areAllFontsLoaded();
|
|
43850
43946
|
}
|
|
43851
43947
|
areAllFontsLoaded() {
|
|
43852
|
-
Environment.bravuraFontChecker.checkForFontAvailability();
|
|
43853
|
-
if (!Environment.bravuraFontChecker.isFontLoaded) {
|
|
43854
|
-
return false;
|
|
43855
|
-
}
|
|
43856
43948
|
let isAnyNotLoaded = false;
|
|
43857
43949
|
for (const checker of this._fontCheckers.values()) {
|
|
43858
43950
|
if (!checker.isFontLoaded) {
|
|
@@ -43889,7 +43981,6 @@
|
|
|
43889
43981
|
rootElement.classList.add('alphaTab');
|
|
43890
43982
|
this.rootContainer = new HtmlElementContainer(rootElement);
|
|
43891
43983
|
this.areWorkersSupported = 'Worker' in window;
|
|
43892
|
-
Environment.bravuraFontChecker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
43893
43984
|
this._intersectionObserver = new IntersectionObserver(this.onElementVisibilityChanged.bind(this), {
|
|
43894
43985
|
threshold: [0, 0.01, 1]
|
|
43895
43986
|
});
|
|
@@ -43957,7 +44048,7 @@
|
|
|
43957
44048
|
this._contents = element.element.innerHTML;
|
|
43958
44049
|
element.element.innerHTML = '';
|
|
43959
44050
|
}
|
|
43960
|
-
this.
|
|
44051
|
+
this.createStyleElements(settings);
|
|
43961
44052
|
this._file = settings.core.file;
|
|
43962
44053
|
}
|
|
43963
44054
|
setupFontCheckers(settings) {
|
|
@@ -43983,10 +44074,16 @@
|
|
|
43983
44074
|
}
|
|
43984
44075
|
destroy() {
|
|
43985
44076
|
this.rootContainer.element.innerHTML = '';
|
|
44077
|
+
const webFont = this._webFont;
|
|
44078
|
+
webFont.usages--;
|
|
44079
|
+
if (webFont.usages <= 0) {
|
|
44080
|
+
webFont.element.remove();
|
|
44081
|
+
BrowserUiFacade._registeredWebFonts.delete(webFont.hash);
|
|
44082
|
+
}
|
|
43986
44083
|
}
|
|
43987
44084
|
createCanvasElement() {
|
|
43988
44085
|
const canvasElement = document.createElement('div');
|
|
43989
|
-
canvasElement.
|
|
44086
|
+
canvasElement.classList.add('at-surface', `at${this._webFont.fontSuffix}`);
|
|
43990
44087
|
canvasElement.style.fontSize = '0';
|
|
43991
44088
|
canvasElement.style.overflow = 'hidden';
|
|
43992
44089
|
canvasElement.style.lineHeight = '0';
|
|
@@ -44084,9 +44181,133 @@
|
|
|
44084
44181
|
initialRender();
|
|
44085
44182
|
}
|
|
44086
44183
|
}
|
|
44087
|
-
|
|
44088
|
-
const
|
|
44089
|
-
|
|
44184
|
+
createStyleElements(settings) {
|
|
44185
|
+
const root = this._api.container.element.ownerDocument;
|
|
44186
|
+
BrowserUiFacade.createSharedStyleElement(root);
|
|
44187
|
+
// SmuFl Font Specific style
|
|
44188
|
+
const smuflFontSources = settings.core.smuflFontSources ?? CoreSettings.buildDefaultSmuflFontSources(settings.core.fontDirectory);
|
|
44189
|
+
// create a simple unique hash for the font source definition
|
|
44190
|
+
// as data urls might be used we don't want to just use the plain strings.
|
|
44191
|
+
const hash = BrowserUiFacade.cyrb53(smuflFontSources.values());
|
|
44192
|
+
// reuse existing style if available
|
|
44193
|
+
const registeredWebFonts = BrowserUiFacade._registeredWebFonts;
|
|
44194
|
+
if (registeredWebFonts.has(hash)) {
|
|
44195
|
+
const webFont = registeredWebFonts.get(hash);
|
|
44196
|
+
webFont.usages++;
|
|
44197
|
+
webFont.checker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
44198
|
+
this._webFont = webFont;
|
|
44199
|
+
return;
|
|
44200
|
+
}
|
|
44201
|
+
const fontSuffix = registeredWebFonts.size === 0 ? '' : String(registeredWebFonts.size);
|
|
44202
|
+
const familyName = `alphaTab${fontSuffix}`;
|
|
44203
|
+
const src = Array.from(smuflFontSources.entries())
|
|
44204
|
+
.map(e => `url(${JSON.stringify(e[1])}) format('${BrowserUiFacade.cssFormat(e[0])}')`)
|
|
44205
|
+
.join(',');
|
|
44206
|
+
const css = `
|
|
44207
|
+
@font-face {
|
|
44208
|
+
font-display: block;
|
|
44209
|
+
font-family: '${familyName}';
|
|
44210
|
+
src: ${src};
|
|
44211
|
+
font-weight: normal;
|
|
44212
|
+
font-style: normal;
|
|
44213
|
+
}
|
|
44214
|
+
.at-surface.at${fontSuffix} .at {
|
|
44215
|
+
font-family: '${familyName}';
|
|
44216
|
+
speak: none;
|
|
44217
|
+
font-style: normal;
|
|
44218
|
+
font-weight: normal;
|
|
44219
|
+
font-variant: normal;
|
|
44220
|
+
text-transform: none;
|
|
44221
|
+
line-height: 1;
|
|
44222
|
+
line-height: 1;
|
|
44223
|
+
-webkit-font-smoothing: antialiased;
|
|
44224
|
+
-moz-osx-font-smoothing: grayscale;
|
|
44225
|
+
font-size: ${Environment.MusicFontSize}px;
|
|
44226
|
+
overflow: visible !important;
|
|
44227
|
+
}`;
|
|
44228
|
+
const styleElement = root.createElement('style');
|
|
44229
|
+
styleElement.id = `alphaTabStyle${fontSuffix}`;
|
|
44230
|
+
styleElement.innerHTML = css;
|
|
44231
|
+
root.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
44232
|
+
const checker = new FontLoadingChecker([familyName]);
|
|
44233
|
+
checker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
44234
|
+
this._fontCheckers.set(familyName, checker);
|
|
44235
|
+
checker.checkForFontAvailability();
|
|
44236
|
+
settings.display.resources.smuflFont = new Font(familyName, Environment.MusicFontSize, FontStyle.Plain, FontWeight.Regular);
|
|
44237
|
+
const webFont = {
|
|
44238
|
+
hash,
|
|
44239
|
+
element: styleElement,
|
|
44240
|
+
fontSuffix,
|
|
44241
|
+
usages: 1,
|
|
44242
|
+
checker
|
|
44243
|
+
};
|
|
44244
|
+
registeredWebFonts.set(hash, webFont);
|
|
44245
|
+
this._webFont = webFont;
|
|
44246
|
+
}
|
|
44247
|
+
static cssFormat(format) {
|
|
44248
|
+
switch (format) {
|
|
44249
|
+
case exports.FontFileFormat.EmbeddedOpenType:
|
|
44250
|
+
return 'embedded-opentype';
|
|
44251
|
+
case exports.FontFileFormat.Woff:
|
|
44252
|
+
return 'woff';
|
|
44253
|
+
case exports.FontFileFormat.Woff2:
|
|
44254
|
+
return 'woff2';
|
|
44255
|
+
case exports.FontFileFormat.OpenType:
|
|
44256
|
+
return 'opentype';
|
|
44257
|
+
case exports.FontFileFormat.TrueType:
|
|
44258
|
+
return 'truetype';
|
|
44259
|
+
case exports.FontFileFormat.Svg:
|
|
44260
|
+
return 'svg';
|
|
44261
|
+
}
|
|
44262
|
+
}
|
|
44263
|
+
/**
|
|
44264
|
+
* cyrb53 (c) 2018 bryc (github.com/bryc)
|
|
44265
|
+
* License: Public domain (or MIT if needed). Attribution appreciated.
|
|
44266
|
+
* A fast and simple 53-bit string hash function with decent collision resistance.
|
|
44267
|
+
* Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity
|
|
44268
|
+
* @param str
|
|
44269
|
+
* @param seed
|
|
44270
|
+
* @returns
|
|
44271
|
+
*/
|
|
44272
|
+
static cyrb53(strings, seed = 0) {
|
|
44273
|
+
let h1 = 0xdeadbeef ^ seed;
|
|
44274
|
+
let h2 = 0x41c6ce57 ^ seed;
|
|
44275
|
+
for (const str of strings) {
|
|
44276
|
+
for (let i = 0; i < str.length; i++) {
|
|
44277
|
+
const ch = str.charCodeAt(i);
|
|
44278
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
44279
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
44280
|
+
}
|
|
44281
|
+
}
|
|
44282
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
44283
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
44284
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
44285
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
44286
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
44287
|
+
}
|
|
44288
|
+
/**
|
|
44289
|
+
* Creates the default CSS styles used across all alphaTab instances.
|
|
44290
|
+
* @target web
|
|
44291
|
+
* @internal
|
|
44292
|
+
*/
|
|
44293
|
+
static createSharedStyleElement(root) {
|
|
44294
|
+
let styleElement = root.getElementById('alphaTabStyle');
|
|
44295
|
+
if (!styleElement) {
|
|
44296
|
+
styleElement = document.createElement('style');
|
|
44297
|
+
styleElement.id = 'alphaTabStyleShared';
|
|
44298
|
+
const css = `
|
|
44299
|
+
.at-surface * {
|
|
44300
|
+
cursor: default;
|
|
44301
|
+
vertical-align: top;
|
|
44302
|
+
overflow: visible;
|
|
44303
|
+
}
|
|
44304
|
+
.at-surface-svg text {
|
|
44305
|
+
dominant-baseline: central;
|
|
44306
|
+
white-space:pre;
|
|
44307
|
+
}`;
|
|
44308
|
+
styleElement.innerHTML = css;
|
|
44309
|
+
document.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
44310
|
+
}
|
|
44090
44311
|
}
|
|
44091
44312
|
parseTracks(tracksData) {
|
|
44092
44313
|
if (!tracksData) {
|
|
@@ -44468,6 +44689,7 @@
|
|
|
44468
44689
|
return new BackingTrackPlayer(new AudioElementBackingTrackSynthOutput(), this._api.settings.player.bufferTimeInMilliseconds);
|
|
44469
44690
|
}
|
|
44470
44691
|
}
|
|
44692
|
+
BrowserUiFacade._registeredWebFonts = new Map();
|
|
44471
44693
|
|
|
44472
44694
|
/**
|
|
44473
44695
|
* Represents the progress of any data being loaded.
|
|
@@ -60725,9 +60947,9 @@
|
|
|
60725
60947
|
print(`build date: ${VersionInfo.date}`);
|
|
60726
60948
|
}
|
|
60727
60949
|
}
|
|
60728
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
60729
|
-
VersionInfo.date = '2025-05-
|
|
60730
|
-
VersionInfo.commit = '
|
|
60950
|
+
VersionInfo.version = '1.6.0-alpha.1420';
|
|
60951
|
+
VersionInfo.date = '2025-05-22T03:03:04.057Z';
|
|
60952
|
+
VersionInfo.commit = '230bdd455a1906e6f334b63bad3b8cf773f890e6';
|
|
60731
60953
|
|
|
60732
60954
|
/**
|
|
60733
60955
|
* A factory for custom layout engines.
|
|
@@ -60759,59 +60981,6 @@
|
|
|
60759
60981
|
* @partial
|
|
60760
60982
|
*/
|
|
60761
60983
|
class Environment {
|
|
60762
|
-
/**
|
|
60763
|
-
* @target web
|
|
60764
|
-
* @internal
|
|
60765
|
-
*/
|
|
60766
|
-
static createStyleElement(elementDocument, fontDirectory) {
|
|
60767
|
-
let styleElement = elementDocument.getElementById('alphaTabStyle');
|
|
60768
|
-
if (!styleElement) {
|
|
60769
|
-
if (!fontDirectory) {
|
|
60770
|
-
Logger.error('AlphaTab', 'Font directory could not be detected, cannot create style element');
|
|
60771
|
-
return;
|
|
60772
|
-
}
|
|
60773
|
-
styleElement = elementDocument.createElement('style');
|
|
60774
|
-
styleElement.id = 'alphaTabStyle';
|
|
60775
|
-
const css = `
|
|
60776
|
-
@font-face {
|
|
60777
|
-
font-display: block;
|
|
60778
|
-
font-family: 'alphaTab';
|
|
60779
|
-
src: url('${fontDirectory}Bravura.eot');
|
|
60780
|
-
src: url('${fontDirectory}Bravura.eot?#iefix') format('embedded-opentype')
|
|
60781
|
-
, url('${fontDirectory}Bravura.woff') format('woff')
|
|
60782
|
-
, url('${fontDirectory}Bravura.otf') format('opentype')
|
|
60783
|
-
, url('${fontDirectory}Bravura.svg#Bravura') format('svg');
|
|
60784
|
-
font-weight: normal;
|
|
60785
|
-
font-style: normal;
|
|
60786
|
-
}
|
|
60787
|
-
.at-surface * {
|
|
60788
|
-
cursor: default;
|
|
60789
|
-
vertical-align: top;
|
|
60790
|
-
overflow: visible;
|
|
60791
|
-
}
|
|
60792
|
-
.at-surface-svg text {
|
|
60793
|
-
dominant-baseline: central;
|
|
60794
|
-
white-space:pre;
|
|
60795
|
-
}
|
|
60796
|
-
.at {
|
|
60797
|
-
font-family: 'alphaTab';
|
|
60798
|
-
speak: none;
|
|
60799
|
-
font-style: normal;
|
|
60800
|
-
font-weight: normal;
|
|
60801
|
-
font-variant: normal;
|
|
60802
|
-
text-transform: none;
|
|
60803
|
-
line-height: 1;
|
|
60804
|
-
line-height: 1;
|
|
60805
|
-
-webkit-font-smoothing: antialiased;
|
|
60806
|
-
-moz-osx-font-smoothing: grayscale;
|
|
60807
|
-
font-size: ${Environment.MusicFontSize}px;
|
|
60808
|
-
overflow: visible !important;
|
|
60809
|
-
}`;
|
|
60810
|
-
styleElement.innerHTML = css;
|
|
60811
|
-
elementDocument.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
60812
|
-
Environment.bravuraFontChecker.checkForFontAvailability();
|
|
60813
|
-
}
|
|
60814
|
-
}
|
|
60815
60984
|
/**
|
|
60816
60985
|
* @target web
|
|
60817
60986
|
* @internal
|
|
@@ -60887,7 +61056,9 @@
|
|
|
60887
61056
|
catch (e) {
|
|
60888
61057
|
}
|
|
60889
61058
|
// normal browser include as <script>
|
|
60890
|
-
if ('document' in Environment.globalThis &&
|
|
61059
|
+
if ('document' in Environment.globalThis &&
|
|
61060
|
+
document.currentScript &&
|
|
61061
|
+
document.currentScript instanceof HTMLScriptElement) {
|
|
60891
61062
|
return document.currentScript.src;
|
|
60892
61063
|
}
|
|
60893
61064
|
return null;
|
|
@@ -61365,11 +61536,6 @@
|
|
|
61365
61536
|
* @target web
|
|
61366
61537
|
*/
|
|
61367
61538
|
Environment.fontDirectory = Environment.detectFontDirectory();
|
|
61368
|
-
/**
|
|
61369
|
-
* @target web
|
|
61370
|
-
* @internal
|
|
61371
|
-
*/
|
|
61372
|
-
Environment.bravuraFontChecker = new FontLoadingChecker(['alphaTab']);
|
|
61373
61539
|
Environment.renderEngines = Environment.createDefaultRenderEngines();
|
|
61374
61540
|
/**
|
|
61375
61541
|
* @internal
|
|
@@ -61380,12 +61546,58 @@
|
|
|
61380
61546
|
*/
|
|
61381
61547
|
Environment.staveProfiles = Environment.createDefaultStaveProfiles();
|
|
61382
61548
|
|
|
61549
|
+
/**
|
|
61550
|
+
* Lists the known file formats for font files.
|
|
61551
|
+
* @target web
|
|
61552
|
+
*/
|
|
61553
|
+
exports.FontFileFormat = void 0;
|
|
61554
|
+
(function (FontFileFormat) {
|
|
61555
|
+
/**
|
|
61556
|
+
* .eot
|
|
61557
|
+
*/
|
|
61558
|
+
FontFileFormat[FontFileFormat["EmbeddedOpenType"] = 0] = "EmbeddedOpenType";
|
|
61559
|
+
/**
|
|
61560
|
+
* .woff
|
|
61561
|
+
*/
|
|
61562
|
+
FontFileFormat[FontFileFormat["Woff"] = 1] = "Woff";
|
|
61563
|
+
/**
|
|
61564
|
+
* .woff2
|
|
61565
|
+
*/
|
|
61566
|
+
FontFileFormat[FontFileFormat["Woff2"] = 2] = "Woff2";
|
|
61567
|
+
/**
|
|
61568
|
+
* .otf
|
|
61569
|
+
*/
|
|
61570
|
+
FontFileFormat[FontFileFormat["OpenType"] = 3] = "OpenType";
|
|
61571
|
+
/**
|
|
61572
|
+
* .ttf
|
|
61573
|
+
*/
|
|
61574
|
+
FontFileFormat[FontFileFormat["TrueType"] = 4] = "TrueType";
|
|
61575
|
+
/**
|
|
61576
|
+
* .svg
|
|
61577
|
+
*/
|
|
61578
|
+
FontFileFormat[FontFileFormat["Svg"] = 5] = "Svg";
|
|
61579
|
+
})(exports.FontFileFormat || (exports.FontFileFormat = {}));
|
|
61383
61580
|
/**
|
|
61384
61581
|
* All main settings of alphaTab controlling rather general aspects of its behavior.
|
|
61385
61582
|
* @json
|
|
61386
61583
|
* @json_declaration
|
|
61387
61584
|
*/
|
|
61388
61585
|
class CoreSettings {
|
|
61586
|
+
/**
|
|
61587
|
+
* Builds the default SMuFL font sources for the usage with alphaTab in cases
|
|
61588
|
+
* where no custom {@link smuflFontSources} are provided.
|
|
61589
|
+
* @param fontDirectory The {@link fontDirectory} configured.
|
|
61590
|
+
* @target web
|
|
61591
|
+
*/
|
|
61592
|
+
static buildDefaultSmuflFontSources(fontDirectory) {
|
|
61593
|
+
const map = new Map();
|
|
61594
|
+
// WOFF, WOFF2 and OTF should cover all our platform needs
|
|
61595
|
+
const prefix = fontDirectory ?? '';
|
|
61596
|
+
map.set(exports.FontFileFormat.Woff2, `${prefix}Bravura.woff2`);
|
|
61597
|
+
map.set(exports.FontFileFormat.Woff, `${prefix}Bravura.woff`);
|
|
61598
|
+
map.set(exports.FontFileFormat.OpenType, `${prefix}Bravura.otf`);
|
|
61599
|
+
return map;
|
|
61600
|
+
}
|
|
61389
61601
|
/**
|
|
61390
61602
|
* @target web
|
|
61391
61603
|
*/
|
|
@@ -61411,12 +61623,27 @@
|
|
|
61411
61623
|
* where the Web Font files of [Bravura](https://github.com/steinbergmedia/bravura) are. Normally alphaTab expects
|
|
61412
61624
|
* them to be in a `font` subfolder beside the script file. If this is not the case, this setting must be used to configure the path.
|
|
61413
61625
|
* Alternatively also a global variable `ALPHATAB_FONT` can be set on the page before initializing alphaTab.
|
|
61626
|
+
*
|
|
61627
|
+
* Use {@link smuflFontSources} for more flexible font configuration.
|
|
61414
61628
|
* @defaultValue `"${AlphaTabScriptFolder}/font/"`
|
|
61415
61629
|
* @category Core - JavaScript Specific
|
|
61416
61630
|
* @target web
|
|
61417
61631
|
* @since 0.9.6
|
|
61418
61632
|
*/
|
|
61419
61633
|
this.fontDirectory = null;
|
|
61634
|
+
/**
|
|
61635
|
+
* Defines the URLs from which to load the SMuFL compliant font files.
|
|
61636
|
+
* @remarks
|
|
61637
|
+
* These sources will be used to load and register the webfonts on the page so
|
|
61638
|
+
* they are available for rendering the music sheet. The sources can be set to any
|
|
61639
|
+
* CSS compatible URL which can be passed into `url()`.
|
|
61640
|
+
* See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src#url
|
|
61641
|
+
* @defaultValue Bravura files located at {@link fontDirectory} .
|
|
61642
|
+
* @category Core - JavaScript Specific
|
|
61643
|
+
* @target web
|
|
61644
|
+
* @since 1.6.0
|
|
61645
|
+
*/
|
|
61646
|
+
this.smuflFontSources = null;
|
|
61420
61647
|
/**
|
|
61421
61648
|
* The full URL to the input file to be loaded.
|
|
61422
61649
|
* @remarks
|