@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.core.mjs
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
|
*
|
|
@@ -14312,13 +14312,6 @@ class XmlDocument extends XmlNode {
|
|
|
14312
14312
|
* @json_strict
|
|
14313
14313
|
*/
|
|
14314
14314
|
class BackingTrack {
|
|
14315
|
-
constructor() {
|
|
14316
|
-
/**
|
|
14317
|
-
* The number of milliseconds the audio should be shifted to align with the song.
|
|
14318
|
-
* (e.g. negative values allow skipping potential silent parts at the start of the file and directly start with the first note).
|
|
14319
|
-
*/
|
|
14320
|
-
this.padding = 0;
|
|
14321
|
-
}
|
|
14322
14315
|
}
|
|
14323
14316
|
|
|
14324
14317
|
/**
|
|
@@ -14351,6 +14344,7 @@ class GpifParser {
|
|
|
14351
14344
|
constructor() {
|
|
14352
14345
|
this._hasAnacrusis = false;
|
|
14353
14346
|
this._skipApplyLyrics = false;
|
|
14347
|
+
this._backingTrackPadding = 0;
|
|
14354
14348
|
this._doubleBars = new Set();
|
|
14355
14349
|
this._keySignatures = new Map();
|
|
14356
14350
|
}
|
|
@@ -14583,7 +14577,7 @@ class GpifParser {
|
|
|
14583
14577
|
assetId = c.innerText;
|
|
14584
14578
|
break;
|
|
14585
14579
|
case 'FramePadding':
|
|
14586
|
-
|
|
14580
|
+
this._backingTrackPadding = GpifParser.parseIntSafe(c.innerText, 0) / GpifParser.SampleRate * 1000;
|
|
14587
14581
|
break;
|
|
14588
14582
|
}
|
|
14589
14583
|
}
|
|
@@ -16797,6 +16791,7 @@ class GpifParser {
|
|
|
16797
16791
|
masterBar.tempoAutomations.push(automation);
|
|
16798
16792
|
break;
|
|
16799
16793
|
case AutomationType.SyncPoint:
|
|
16794
|
+
automation.syncPointValue.millisecondOffset -= this._backingTrackPadding;
|
|
16800
16795
|
masterBar.addSyncPoint(automation);
|
|
16801
16796
|
break;
|
|
16802
16797
|
}
|
|
@@ -22285,17 +22280,6 @@ class SynthEvent {
|
|
|
22285
22280
|
}
|
|
22286
22281
|
}
|
|
22287
22282
|
|
|
22288
|
-
/**
|
|
22289
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22290
|
-
*/
|
|
22291
|
-
class BackingTrackSyncPoint {
|
|
22292
|
-
constructor(tick, data) {
|
|
22293
|
-
this.tick = 0;
|
|
22294
|
-
this.tick = tick;
|
|
22295
|
-
this.data = data;
|
|
22296
|
-
}
|
|
22297
|
-
}
|
|
22298
|
-
|
|
22299
22283
|
class MidiFileSequencerTempoChange {
|
|
22300
22284
|
constructor(bpm, ticks, time) {
|
|
22301
22285
|
this.bpm = bpm;
|
|
@@ -22303,10 +22287,12 @@ class MidiFileSequencerTempoChange {
|
|
|
22303
22287
|
this.time = time;
|
|
22304
22288
|
}
|
|
22305
22289
|
}
|
|
22306
|
-
class BackingTrackSyncPointWithTime
|
|
22307
|
-
constructor(tick,
|
|
22308
|
-
|
|
22309
|
-
this.
|
|
22290
|
+
class BackingTrackSyncPointWithTime {
|
|
22291
|
+
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22292
|
+
this.alphaTabTick = tick;
|
|
22293
|
+
this.alphaTabTime = time;
|
|
22294
|
+
this.modifiedTempo = modifiedTempo;
|
|
22295
|
+
this.millisecondOffset = millisecondOffset;
|
|
22310
22296
|
}
|
|
22311
22297
|
}
|
|
22312
22298
|
class MidiSequencerState {
|
|
@@ -22412,7 +22398,7 @@ class MidiFileSequencer {
|
|
|
22412
22398
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22413
22399
|
this._mainState.modifiedTempo =
|
|
22414
22400
|
this._mainState.syncPoints.length > 0
|
|
22415
|
-
? this._mainState.syncPoints[0].
|
|
22401
|
+
? this._mainState.syncPoints[0].modifiedTempo
|
|
22416
22402
|
: this._mainState.currentTempo;
|
|
22417
22403
|
if (this.isPlayingMain) {
|
|
22418
22404
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22584,25 +22570,52 @@ class MidiFileSequencer {
|
|
|
22584
22570
|
mainUpdateSyncPoints(syncPoints) {
|
|
22585
22571
|
const state = this._mainState;
|
|
22586
22572
|
syncPoints.sort((a, b) => a.tick - b.tick); // just in case
|
|
22587
|
-
state.syncPoints =
|
|
22573
|
+
state.syncPoints = [];
|
|
22588
22574
|
if (syncPoints.length >= 0) {
|
|
22589
22575
|
let bpm = 120;
|
|
22590
22576
|
let absTick = 0;
|
|
22591
22577
|
let absTime = 0.0;
|
|
22592
|
-
let previousTick = 0;
|
|
22593
22578
|
let tempoChangeIndex = 0;
|
|
22594
22579
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22595
22580
|
const p = syncPoints[i];
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
previousTick
|
|
22581
|
+
let deltaTick = 0;
|
|
22582
|
+
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22583
|
+
let previousModifiedTempo;
|
|
22584
|
+
let previousMillisecondOffset;
|
|
22585
|
+
let previousTick;
|
|
22586
|
+
if (i === 0) {
|
|
22587
|
+
previousModifiedTempo = bpm;
|
|
22588
|
+
previousMillisecondOffset = 0;
|
|
22589
|
+
previousTick = 0;
|
|
22590
|
+
}
|
|
22591
|
+
else {
|
|
22592
|
+
const previousSyncPoint = syncPoints[i - 1];
|
|
22593
|
+
previousModifiedTempo = previousSyncPoint.data.modifiedTempo;
|
|
22594
|
+
previousMillisecondOffset = previousSyncPoint.data.millisecondOffset;
|
|
22595
|
+
previousTick = previousSyncPoint.tick;
|
|
22596
|
+
}
|
|
22597
|
+
// process time until sync point
|
|
22598
|
+
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
22599
|
+
// we have to calculate a interpolated sync point on the alphaTab time axis.
|
|
22600
|
+
// otherwise the linear interpolation later in the lookup will fail.
|
|
22601
|
+
// 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
|
|
22601
22602
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22602
|
-
state.tempoChanges[tempoChangeIndex].ticks <=
|
|
22603
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.tick) {
|
|
22604
|
+
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22605
|
+
if (deltaTick > 0) {
|
|
22606
|
+
absTick += deltaTick;
|
|
22607
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22608
|
+
const millisPerTick = (p.data.millisecondOffset - previousMillisecondOffset) / (p.tick - previousTick);
|
|
22609
|
+
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22610
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(absTick, absTime, previousModifiedTempo, interpolatedMillisecondOffset));
|
|
22611
|
+
}
|
|
22603
22612
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22604
22613
|
tempoChangeIndex++;
|
|
22605
22614
|
}
|
|
22615
|
+
deltaTick = p.tick - absTick;
|
|
22616
|
+
absTick += deltaTick;
|
|
22617
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22618
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(p.tick, absTime, p.data.modifiedTempo, p.data.millisecondOffset));
|
|
22606
22619
|
}
|
|
22607
22620
|
}
|
|
22608
22621
|
state.syncPointIndex = 0;
|
|
@@ -22616,7 +22629,7 @@ class MidiFileSequencer {
|
|
|
22616
22629
|
this.updateCurrentTempo(state, timePosition);
|
|
22617
22630
|
const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
|
|
22618
22631
|
const timeDiff = timePosition - lastTempoChange.time;
|
|
22619
|
-
const ticks = (
|
|
22632
|
+
const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
|
|
22620
22633
|
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22621
22634
|
return lastTempoChange.ticks + ticks + 1;
|
|
22622
22635
|
}
|
|
@@ -22636,16 +22649,16 @@ class MidiFileSequencer {
|
|
|
22636
22649
|
const syncPoints = state.syncPoints;
|
|
22637
22650
|
if (syncPoints.length > 0) {
|
|
22638
22651
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22639
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22652
|
+
if (timePosition < syncPoints[syncPointIndex].millisecondOffset) {
|
|
22640
22653
|
syncPointIndex = 0;
|
|
22641
22654
|
}
|
|
22642
22655
|
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22643
|
-
syncPoints[syncPointIndex + 1].
|
|
22656
|
+
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22644
22657
|
syncPointIndex++;
|
|
22645
22658
|
}
|
|
22646
22659
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22647
22660
|
state.syncPointIndex = syncPointIndex;
|
|
22648
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22661
|
+
state.modifiedTempo = syncPoints[syncPointIndex].modifiedTempo;
|
|
22649
22662
|
}
|
|
22650
22663
|
}
|
|
22651
22664
|
else {
|
|
@@ -22661,18 +22674,18 @@ class MidiFileSequencer {
|
|
|
22661
22674
|
this.updateCurrentTempo(this._mainState, timePosition);
|
|
22662
22675
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22663
22676
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22664
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22677
|
+
const timeDiff = timePosition - currentSyncPoint.millisecondOffset;
|
|
22665
22678
|
let alphaTabTimeDiff;
|
|
22666
22679
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22667
22680
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22668
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22669
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22681
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset);
|
|
22682
|
+
alphaTabTimeDiff = (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22670
22683
|
}
|
|
22671
22684
|
else {
|
|
22672
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22673
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22685
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.millisecondOffset);
|
|
22686
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22674
22687
|
}
|
|
22675
|
-
return (currentSyncPoint.
|
|
22688
|
+
return (currentSyncPoint.alphaTabTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22676
22689
|
}
|
|
22677
22690
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22678
22691
|
const mainState = this._mainState;
|
|
@@ -22682,25 +22695,27 @@ class MidiFileSequencer {
|
|
|
22682
22695
|
}
|
|
22683
22696
|
timePosition *= this.playbackSpeed;
|
|
22684
22697
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22685
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22698
|
+
if (timePosition < syncPoints[syncPointIndex].alphaTabTime) {
|
|
22686
22699
|
syncPointIndex = 0;
|
|
22687
22700
|
}
|
|
22688
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22701
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].alphaTabTime <= timePosition) {
|
|
22689
22702
|
syncPointIndex++;
|
|
22690
22703
|
}
|
|
22704
|
+
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22705
|
+
// we ensure that we have a linear increase between two points
|
|
22691
22706
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22692
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22707
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.alphaTabTime;
|
|
22693
22708
|
let backingTrackPos;
|
|
22694
22709
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22695
22710
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22696
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22697
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22698
|
-
backingTrackPos = currentSyncPoint.
|
|
22711
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime);
|
|
22712
|
+
const backingTrackDiff = nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset;
|
|
22713
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22699
22714
|
}
|
|
22700
22715
|
else {
|
|
22701
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22702
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22703
|
-
backingTrackPos = currentSyncPoint.
|
|
22716
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.alphaTabTime);
|
|
22717
|
+
const frameDiff = backingTrackLength - currentSyncPoint.millisecondOffset;
|
|
22718
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + frameDiff * relativeAlphaTabTimeDiff;
|
|
22704
22719
|
}
|
|
22705
22720
|
return backingTrackPos;
|
|
22706
22721
|
}
|
|
@@ -28582,7 +28597,9 @@ class AlphaSynthBase {
|
|
|
28582
28597
|
hasSamplesForPercussion(key) {
|
|
28583
28598
|
return this.synthesizer.hasSamplesForPercussion(key);
|
|
28584
28599
|
}
|
|
28585
|
-
loadBackingTrack(_score
|
|
28600
|
+
loadBackingTrack(_score) {
|
|
28601
|
+
}
|
|
28602
|
+
updateSyncPoints(_syncPoints) {
|
|
28586
28603
|
}
|
|
28587
28604
|
}
|
|
28588
28605
|
/**
|
|
@@ -29070,7 +29087,7 @@ class Font {
|
|
|
29070
29087
|
}
|
|
29071
29088
|
switch (typeof v) {
|
|
29072
29089
|
case 'undefined':
|
|
29073
|
-
return
|
|
29090
|
+
return undefined;
|
|
29074
29091
|
case 'object': {
|
|
29075
29092
|
const m = v;
|
|
29076
29093
|
const families = m.get('families');
|
|
@@ -29150,10 +29167,13 @@ class Font {
|
|
|
29150
29167
|
return Font.withFamilyList(families, fontSize, fontStyle, fontWeight);
|
|
29151
29168
|
}
|
|
29152
29169
|
default:
|
|
29153
|
-
return
|
|
29170
|
+
return undefined;
|
|
29154
29171
|
}
|
|
29155
29172
|
}
|
|
29156
29173
|
static toJson(font) {
|
|
29174
|
+
if (!font) {
|
|
29175
|
+
return undefined;
|
|
29176
|
+
}
|
|
29157
29177
|
const o = new Map();
|
|
29158
29178
|
o.set('families', font.families);
|
|
29159
29179
|
o.set('size', font.size);
|
|
@@ -30160,6 +30180,14 @@ class CoreSettingsSerializer {
|
|
|
30160
30180
|
/*@target web*/
|
|
30161
30181
|
o.set("fontdirectory", obj.fontDirectory);
|
|
30162
30182
|
/*@target web*/
|
|
30183
|
+
if (obj.smuflFontSources !== null) {
|
|
30184
|
+
const m = new Map();
|
|
30185
|
+
o.set("smuflfontsources", m);
|
|
30186
|
+
for (const [k, v] of obj.smuflFontSources) {
|
|
30187
|
+
m.set(k.toString(), v);
|
|
30188
|
+
}
|
|
30189
|
+
}
|
|
30190
|
+
/*@target web*/
|
|
30163
30191
|
o.set("file", obj.file);
|
|
30164
30192
|
/*@target web*/
|
|
30165
30193
|
o.set("tex", obj.tex);
|
|
@@ -30183,6 +30211,13 @@ class CoreSettingsSerializer {
|
|
|
30183
30211
|
obj.fontDirectory = v;
|
|
30184
30212
|
return true;
|
|
30185
30213
|
/*@target web*/
|
|
30214
|
+
case "smuflfontsources":
|
|
30215
|
+
obj.smuflFontSources = new Map();
|
|
30216
|
+
JsonHelper.forEach(v, (v, k) => {
|
|
30217
|
+
obj.smuflFontSources.set(JsonHelper.parseEnum(k, FontFileFormat), v);
|
|
30218
|
+
});
|
|
30219
|
+
return true;
|
|
30220
|
+
/*@target web*/
|
|
30186
30221
|
case "file":
|
|
30187
30222
|
obj.file = v;
|
|
30188
30223
|
return true;
|
|
@@ -30226,6 +30261,7 @@ class RenderingResourcesSerializer {
|
|
|
30226
30261
|
return null;
|
|
30227
30262
|
}
|
|
30228
30263
|
const o = new Map();
|
|
30264
|
+
o.set("smuflfont", Font.toJson(obj.smuflFont));
|
|
30229
30265
|
o.set("copyrightfont", Font.toJson(obj.copyrightFont));
|
|
30230
30266
|
o.set("titlefont", Font.toJson(obj.titleFont));
|
|
30231
30267
|
o.set("subtitlefont", Font.toJson(obj.subTitleFont));
|
|
@@ -30252,6 +30288,9 @@ class RenderingResourcesSerializer {
|
|
|
30252
30288
|
}
|
|
30253
30289
|
static setProperty(obj, property, v) {
|
|
30254
30290
|
switch (property) {
|
|
30291
|
+
case "smuflfont":
|
|
30292
|
+
obj.smuflFont = Font.fromJson(v);
|
|
30293
|
+
return true;
|
|
30255
30294
|
case "copyrightfont":
|
|
30256
30295
|
obj.copyrightFont = Font.fromJson(v);
|
|
30257
30296
|
return true;
|
|
@@ -32457,15 +32496,9 @@ class BackingTrackSerializer {
|
|
|
32457
32496
|
return null;
|
|
32458
32497
|
}
|
|
32459
32498
|
const o = new Map();
|
|
32460
|
-
o.set("padding", obj.padding);
|
|
32461
32499
|
return o;
|
|
32462
32500
|
}
|
|
32463
32501
|
static setProperty(obj, property, v) {
|
|
32464
|
-
switch (property) {
|
|
32465
|
-
case "padding":
|
|
32466
|
-
obj.padding = v;
|
|
32467
|
-
return true;
|
|
32468
|
-
}
|
|
32469
32502
|
return false;
|
|
32470
32503
|
}
|
|
32471
32504
|
}
|
|
@@ -34229,15 +34262,6 @@ class Html5Canvas {
|
|
|
34229
34262
|
this._color = new Color(0, 0, 0, 0xff);
|
|
34230
34263
|
this._font = new Font('Arial', 10, FontStyle.Plain);
|
|
34231
34264
|
this._lineWidth = 0;
|
|
34232
|
-
const fontElement = document.createElement('span');
|
|
34233
|
-
fontElement.classList.add('at');
|
|
34234
|
-
document.body.appendChild(fontElement);
|
|
34235
|
-
const style = window.getComputedStyle(fontElement);
|
|
34236
|
-
let family = style.fontFamily;
|
|
34237
|
-
if (family.startsWith('"') || family.startsWith("'")) {
|
|
34238
|
-
family = family.substr(1, family.length - 2);
|
|
34239
|
-
}
|
|
34240
|
-
this._musicFont = new Font(family, Number.parseFloat(style.fontSize), FontStyle.Plain);
|
|
34241
34265
|
this._measureCanvas = document.createElement('canvas');
|
|
34242
34266
|
this._measureCanvas.width = 10;
|
|
34243
34267
|
this._measureCanvas.height = 10;
|
|
@@ -34252,6 +34276,7 @@ class Html5Canvas {
|
|
|
34252
34276
|
return null;
|
|
34253
34277
|
}
|
|
34254
34278
|
beginRender(width, height) {
|
|
34279
|
+
this._musicFont = this.settings.display.resources.smuflFont;
|
|
34255
34280
|
const scale = this.settings.display.scale;
|
|
34256
34281
|
this._canvas = document.createElement('canvas');
|
|
34257
34282
|
this._canvas.width = (width * Environment.HighDpiFactor) | 0;
|
|
@@ -34936,7 +34961,13 @@ class MasterBarTickLookup {
|
|
|
34936
34961
|
* Gets the list of tempo changes within the tick lookup.
|
|
34937
34962
|
*/
|
|
34938
34963
|
this.tempoChanges = [];
|
|
34964
|
+
/**
|
|
34965
|
+
* The first beat in the bar.
|
|
34966
|
+
*/
|
|
34939
34967
|
this.firstBeat = null;
|
|
34968
|
+
/**
|
|
34969
|
+
* The last beat in the bar.
|
|
34970
|
+
*/
|
|
34940
34971
|
this.lastBeat = null;
|
|
34941
34972
|
/**
|
|
34942
34973
|
* Gets or sets the {@link MasterBarTickLookup} of the next masterbar in the {@link Score}
|
|
@@ -35350,14 +35381,13 @@ class MidiTickLookup {
|
|
|
35350
35381
|
constructor() {
|
|
35351
35382
|
this._currentMasterBar = null;
|
|
35352
35383
|
/**
|
|
35353
|
-
*
|
|
35384
|
+
* A dictionary of all master bars played. The index is the index equals to {@link MasterBar.index}.
|
|
35354
35385
|
* This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to {@link MasterBars}.
|
|
35355
35386
|
* @internal
|
|
35356
35387
|
*/
|
|
35357
35388
|
this.masterBarLookup = new Map();
|
|
35358
35389
|
/**
|
|
35359
|
-
*
|
|
35360
|
-
* @internal
|
|
35390
|
+
* A list of all {@link MasterBarTickLookup} sorted by time.
|
|
35361
35391
|
*/
|
|
35362
35392
|
this.masterBars = [];
|
|
35363
35393
|
/**
|
|
@@ -35731,6 +35761,17 @@ class MidiTickLookup {
|
|
|
35731
35761
|
}
|
|
35732
35762
|
}
|
|
35733
35763
|
|
|
35764
|
+
/**
|
|
35765
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35766
|
+
*/
|
|
35767
|
+
class BackingTrackSyncPoint {
|
|
35768
|
+
constructor(tick, data) {
|
|
35769
|
+
this.tick = 0;
|
|
35770
|
+
this.tick = tick;
|
|
35771
|
+
this.data = data;
|
|
35772
|
+
}
|
|
35773
|
+
}
|
|
35774
|
+
|
|
35734
35775
|
class MidiNoteDuration {
|
|
35735
35776
|
constructor() {
|
|
35736
35777
|
this.noteOnly = 0;
|
|
@@ -35800,62 +35841,22 @@ class MidiFileGenerator {
|
|
|
35800
35841
|
this.generateTrack(track);
|
|
35801
35842
|
}
|
|
35802
35843
|
Logger.debug('Midi', 'Begin midi generation');
|
|
35803
|
-
|
|
35804
|
-
|
|
35805
|
-
|
|
35806
|
-
|
|
35807
|
-
|
|
35808
|
-
|
|
35809
|
-
|
|
35810
|
-
|
|
35811
|
-
const currentTick = controller.currentTick;
|
|
35812
|
-
controller.processCurrent();
|
|
35813
|
-
if (controller.shouldPlay) {
|
|
35814
|
-
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35815
|
-
occurence++;
|
|
35816
|
-
barOccurence.set(index, occurence);
|
|
35817
|
-
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35818
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35819
|
-
currentTempo = bar.tempoAutomations[0].value;
|
|
35820
|
-
}
|
|
35821
|
-
for (const track of this._score.tracks) {
|
|
35822
|
-
for (const staff of track.staves) {
|
|
35823
|
-
if (index < staff.bars.length) {
|
|
35824
|
-
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35825
|
-
}
|
|
35844
|
+
this.syncPoints = [];
|
|
35845
|
+
MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
|
|
35846
|
+
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35847
|
+
}, (index, currentTick, currentTempo) => {
|
|
35848
|
+
for (const track of this._score.tracks) {
|
|
35849
|
+
for (const staff of track.staves) {
|
|
35850
|
+
if (index < staff.bars.length) {
|
|
35851
|
+
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35826
35852
|
}
|
|
35827
35853
|
}
|
|
35828
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35829
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35830
|
-
}
|
|
35831
35854
|
}
|
|
35832
|
-
|
|
35833
|
-
|
|
35834
|
-
|
|
35835
|
-
// here we interpolate the sync point which marks the end of the sync.
|
|
35836
|
-
// Sync points define new tempos at certain positions.
|
|
35837
|
-
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35838
|
-
// but where it ends according to the BPM and the remaining ticks.
|
|
35839
|
-
if (this.syncPoints.length > 0) {
|
|
35840
|
-
const lastSyncPoint = this.syncPoints[this.syncPoints.length - 1];
|
|
35841
|
-
const endTick = controller.currentTick;
|
|
35842
|
-
const remainingTicks = endTick - lastSyncPoint.tick;
|
|
35843
|
-
if (remainingTicks > 0) {
|
|
35844
|
-
const syncPointData = new SyncPointData();
|
|
35845
|
-
// last occurence of the last bar
|
|
35846
|
-
syncPointData.barOccurence = barOccurence.get(this._score.masterBars.length - 1);
|
|
35847
|
-
// same tempo as last point
|
|
35848
|
-
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35849
|
-
// interpolated end from last syncPoint
|
|
35850
|
-
syncPointData.millisecondOffset =
|
|
35851
|
-
lastSyncPoint.data.millisecondOffset +
|
|
35852
|
-
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35853
|
-
this.syncPoints.push(new BackingTrackSyncPoint(endTick, syncPointData));
|
|
35855
|
+
}, endTick => {
|
|
35856
|
+
for (const track of this._score.tracks) {
|
|
35857
|
+
this._handler.finishTrack(track.index, endTick);
|
|
35854
35858
|
}
|
|
35855
|
-
}
|
|
35856
|
-
for (const track of this._score.tracks) {
|
|
35857
|
-
this._handler.finishTrack(track.index, controller.currentTick);
|
|
35858
|
-
}
|
|
35859
|
+
});
|
|
35859
35860
|
Logger.debug('Midi', 'Midi generation done');
|
|
35860
35861
|
}
|
|
35861
35862
|
generateTrack(track) {
|
|
@@ -35900,6 +35901,81 @@ class MidiFileGenerator {
|
|
|
35900
35901
|
this._handler.addControlChange(track.index, 0, channel, ControllerType.DataEntryCoarse, MidiFileGenerator.PitchBendRangeInSemitones);
|
|
35901
35902
|
this.addProgramChange(track, 0, channel, playbackInfo.program);
|
|
35902
35903
|
}
|
|
35904
|
+
/**
|
|
35905
|
+
* Generates the sync points for the given score without re-generating the midi itself.
|
|
35906
|
+
* @remarks
|
|
35907
|
+
* Use this method if a re-generation of the sync points after modification is required.
|
|
35908
|
+
* It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
|
|
35909
|
+
* need to be considered for synchronization.
|
|
35910
|
+
* @param score The song for which to regenerate the sync points.
|
|
35911
|
+
* @returns The generated sync points for usage in the backing track playback.
|
|
35912
|
+
*/
|
|
35913
|
+
static generateSyncPoints(score) {
|
|
35914
|
+
const syncPoints = [];
|
|
35915
|
+
MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
35916
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
35917
|
+
}, _endTick => {
|
|
35918
|
+
});
|
|
35919
|
+
return syncPoints;
|
|
35920
|
+
}
|
|
35921
|
+
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35922
|
+
const controller = new MidiPlaybackController(score);
|
|
35923
|
+
let currentTempo = score.tempo;
|
|
35924
|
+
let previousMasterBar = null;
|
|
35925
|
+
// store the previous played bar for repeats
|
|
35926
|
+
const barOccurence = new Map();
|
|
35927
|
+
while (!controller.finished) {
|
|
35928
|
+
const index = controller.index;
|
|
35929
|
+
const bar = score.masterBars[index];
|
|
35930
|
+
const currentTick = controller.currentTick;
|
|
35931
|
+
controller.processCurrent();
|
|
35932
|
+
if (controller.shouldPlay) {
|
|
35933
|
+
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35934
|
+
occurence++;
|
|
35935
|
+
barOccurence.set(index, occurence);
|
|
35936
|
+
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35937
|
+
const barSyncPoints = bar.syncPoints;
|
|
35938
|
+
if (barSyncPoints) {
|
|
35939
|
+
for (const syncPoint of barSyncPoints) {
|
|
35940
|
+
if (syncPoint.syncPointValue.barOccurence === occurence) {
|
|
35941
|
+
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
35942
|
+
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
35943
|
+
}
|
|
35944
|
+
}
|
|
35945
|
+
}
|
|
35946
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35947
|
+
currentTempo = bar.tempoAutomations[0].value;
|
|
35948
|
+
}
|
|
35949
|
+
generateTracks(index, currentTick, currentTempo);
|
|
35950
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35951
|
+
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35952
|
+
}
|
|
35953
|
+
}
|
|
35954
|
+
controller.moveNext();
|
|
35955
|
+
previousMasterBar = bar;
|
|
35956
|
+
}
|
|
35957
|
+
// here we interpolate the sync point which marks the end of the sync.
|
|
35958
|
+
// Sync points define new tempos at certain positions.
|
|
35959
|
+
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35960
|
+
// but where it ends according to the BPM and the remaining ticks.
|
|
35961
|
+
if (syncPoints.length > 0) {
|
|
35962
|
+
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
35963
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.tick;
|
|
35964
|
+
if (remainingTicks > 0) {
|
|
35965
|
+
const syncPointData = new SyncPointData();
|
|
35966
|
+
// last occurence of the last bar
|
|
35967
|
+
syncPointData.barOccurence = barOccurence.get(score.masterBars.length - 1);
|
|
35968
|
+
// same tempo as last point
|
|
35969
|
+
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35970
|
+
// interpolated end from last syncPoint
|
|
35971
|
+
syncPointData.millisecondOffset =
|
|
35972
|
+
lastSyncPoint.data.millisecondOffset +
|
|
35973
|
+
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35974
|
+
syncPoints.push(new BackingTrackSyncPoint(controller.currentTick, syncPointData));
|
|
35975
|
+
}
|
|
35976
|
+
}
|
|
35977
|
+
finish(controller.currentTick);
|
|
35978
|
+
}
|
|
35903
35979
|
static toChannelShort(data) {
|
|
35904
35980
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
35905
35981
|
return Math.max(value, -1) + 1;
|
|
@@ -38236,21 +38312,23 @@ class BackingTrackPlayer extends AlphaSynthBase {
|
|
|
38236
38312
|
this._backingTrackOutput.seekTo(this.sequencer.mainTimePositionToBackingTrack(timePosition, this._backingTrackOutput.backingTrackDuration));
|
|
38237
38313
|
}
|
|
38238
38314
|
}
|
|
38239
|
-
loadBackingTrack(score
|
|
38315
|
+
loadBackingTrack(score) {
|
|
38240
38316
|
const backingTrackInfo = score.backingTrack;
|
|
38241
38317
|
if (backingTrackInfo) {
|
|
38242
38318
|
this._backingTrackOutput.loadBackingTrack(backingTrackInfo);
|
|
38243
|
-
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38244
38319
|
this.timePosition = 0;
|
|
38245
38320
|
}
|
|
38246
38321
|
}
|
|
38322
|
+
updateSyncPoints(syncPoints) {
|
|
38323
|
+
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38324
|
+
this.tickPosition = this.tickPosition;
|
|
38325
|
+
}
|
|
38247
38326
|
}
|
|
38248
38327
|
|
|
38249
38328
|
class ExternalMediaSynthOutput {
|
|
38250
38329
|
constructor() {
|
|
38251
38330
|
// fake rate
|
|
38252
38331
|
this.sampleRate = 44100;
|
|
38253
|
-
this._padding = 0;
|
|
38254
38332
|
this._seekPosition = 0;
|
|
38255
38333
|
this.ready = new EventEmitter();
|
|
38256
38334
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -38293,20 +38371,19 @@ class ExternalMediaSynthOutput {
|
|
|
38293
38371
|
seekTo(time) {
|
|
38294
38372
|
const handler = this.handler;
|
|
38295
38373
|
if (handler) {
|
|
38296
|
-
handler.seekTo(time
|
|
38374
|
+
handler.seekTo(time);
|
|
38297
38375
|
}
|
|
38298
38376
|
else {
|
|
38299
|
-
this._seekPosition = time
|
|
38377
|
+
this._seekPosition = time;
|
|
38300
38378
|
}
|
|
38301
38379
|
}
|
|
38302
|
-
loadBackingTrack(
|
|
38303
|
-
this._padding = backingTrack.padding;
|
|
38380
|
+
loadBackingTrack(_backingTrack) {
|
|
38304
38381
|
}
|
|
38305
38382
|
open(_bufferTimeInMilliseconds) {
|
|
38306
38383
|
this.ready.trigger();
|
|
38307
38384
|
}
|
|
38308
38385
|
updatePosition(currentTime) {
|
|
38309
|
-
this.timeUpdate.trigger(currentTime
|
|
38386
|
+
this.timeUpdate.trigger(currentTime);
|
|
38310
38387
|
}
|
|
38311
38388
|
play() {
|
|
38312
38389
|
this.handler?.play();
|
|
@@ -38571,9 +38648,14 @@ class AlphaSynthWrapper {
|
|
|
38571
38648
|
this._instance.loadMidiFile(midi);
|
|
38572
38649
|
}
|
|
38573
38650
|
}
|
|
38574
|
-
loadBackingTrack(score
|
|
38651
|
+
loadBackingTrack(score) {
|
|
38575
38652
|
if (this._instance) {
|
|
38576
|
-
this._instance.loadBackingTrack(score
|
|
38653
|
+
this._instance.loadBackingTrack(score);
|
|
38654
|
+
}
|
|
38655
|
+
}
|
|
38656
|
+
updateSyncPoints(syncPoints) {
|
|
38657
|
+
if (this._instance) {
|
|
38658
|
+
this._instance.updateSyncPoints(syncPoints);
|
|
38577
38659
|
}
|
|
38578
38660
|
}
|
|
38579
38661
|
applyTranspositionPitches(transpositionPitches) {
|
|
@@ -39902,7 +39984,7 @@ class AlphaTabApiBase {
|
|
|
39902
39984
|
}
|
|
39903
39985
|
appendRenderResult(result, isLast) {
|
|
39904
39986
|
// resizing the canvas and wrapper elements at the end is enough
|
|
39905
|
-
// it avoids flickering on resizes and re-renders.
|
|
39987
|
+
// it avoids flickering on resizes and re-renders.
|
|
39906
39988
|
// the individual partials are anyhow sized correctly
|
|
39907
39989
|
if (isLast) {
|
|
39908
39990
|
this.canvasElement.width = result.totalWidth;
|
|
@@ -40664,9 +40746,23 @@ class AlphaTabApiBase {
|
|
|
40664
40746
|
this.onMidiLoad(midiFile);
|
|
40665
40747
|
const player = this._player;
|
|
40666
40748
|
player.loadMidiFile(midiFile);
|
|
40667
|
-
player.loadBackingTrack(score
|
|
40749
|
+
player.loadBackingTrack(score);
|
|
40750
|
+
player.updateSyncPoints(generator.syncPoints);
|
|
40668
40751
|
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40669
40752
|
}
|
|
40753
|
+
/**
|
|
40754
|
+
* Triggers an update of the sync points for the current score after modification within the data model
|
|
40755
|
+
* @category Methods - Player
|
|
40756
|
+
* @since 1.6.0
|
|
40757
|
+
*/
|
|
40758
|
+
updateSyncPoints() {
|
|
40759
|
+
if (!this.score) {
|
|
40760
|
+
return;
|
|
40761
|
+
}
|
|
40762
|
+
const score = this.score;
|
|
40763
|
+
const player = this._player;
|
|
40764
|
+
player.updateSyncPoints(MidiFileGenerator.generateSyncPoints(score));
|
|
40765
|
+
}
|
|
40670
40766
|
/**
|
|
40671
40767
|
* Changes the volume of the given tracks.
|
|
40672
40768
|
* @param tracks The tracks for which the volume should be changed.
|
|
@@ -43334,6 +43430,8 @@ class AlphaSynthWebWorkerApi {
|
|
|
43334
43430
|
}
|
|
43335
43431
|
loadBackingTrack(_score) {
|
|
43336
43432
|
}
|
|
43433
|
+
updateSyncPoints(_syncPoints) {
|
|
43434
|
+
}
|
|
43337
43435
|
}
|
|
43338
43436
|
|
|
43339
43437
|
/**
|
|
@@ -43708,7 +43806,6 @@ class AudioElementBackingTrackSynthOutput {
|
|
|
43708
43806
|
constructor() {
|
|
43709
43807
|
// fake rate
|
|
43710
43808
|
this.sampleRate = 44100;
|
|
43711
|
-
this._padding = 0;
|
|
43712
43809
|
this._updateInterval = 0;
|
|
43713
43810
|
this.ready = new EventEmitter();
|
|
43714
43811
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -43732,13 +43829,12 @@ class AudioElementBackingTrackSynthOutput {
|
|
|
43732
43829
|
this.audioElement.volume = value;
|
|
43733
43830
|
}
|
|
43734
43831
|
seekTo(time) {
|
|
43735
|
-
this.audioElement.currentTime = time / 1000
|
|
43832
|
+
this.audioElement.currentTime = time / 1000;
|
|
43736
43833
|
}
|
|
43737
43834
|
loadBackingTrack(backingTrack) {
|
|
43738
43835
|
if (this.audioElement?.src) {
|
|
43739
43836
|
URL.revokeObjectURL(this.audioElement.src);
|
|
43740
43837
|
}
|
|
43741
|
-
this._padding = backingTrack.padding / 1000;
|
|
43742
43838
|
const blob = new Blob([backingTrack.rawAudioFile]);
|
|
43743
43839
|
// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
|
|
43744
43840
|
// Step 8. resets the playbackRate, we need to remember and restore it.
|
|
@@ -43757,7 +43853,7 @@ class AudioElementBackingTrackSynthOutput {
|
|
|
43757
43853
|
this.ready.trigger();
|
|
43758
43854
|
}
|
|
43759
43855
|
updatePosition() {
|
|
43760
|
-
const timePos =
|
|
43856
|
+
const timePos = this.audioElement.currentTime * 1000;
|
|
43761
43857
|
this.timeUpdate.trigger(timePos);
|
|
43762
43858
|
}
|
|
43763
43859
|
play() {
|
|
@@ -43843,10 +43939,6 @@ class BrowserUiFacade {
|
|
|
43843
43939
|
return this.areAllFontsLoaded();
|
|
43844
43940
|
}
|
|
43845
43941
|
areAllFontsLoaded() {
|
|
43846
|
-
Environment.bravuraFontChecker.checkForFontAvailability();
|
|
43847
|
-
if (!Environment.bravuraFontChecker.isFontLoaded) {
|
|
43848
|
-
return false;
|
|
43849
|
-
}
|
|
43850
43942
|
let isAnyNotLoaded = false;
|
|
43851
43943
|
for (const checker of this._fontCheckers.values()) {
|
|
43852
43944
|
if (!checker.isFontLoaded) {
|
|
@@ -43883,7 +43975,6 @@ class BrowserUiFacade {
|
|
|
43883
43975
|
rootElement.classList.add('alphaTab');
|
|
43884
43976
|
this.rootContainer = new HtmlElementContainer(rootElement);
|
|
43885
43977
|
this.areWorkersSupported = 'Worker' in window;
|
|
43886
|
-
Environment.bravuraFontChecker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
43887
43978
|
this._intersectionObserver = new IntersectionObserver(this.onElementVisibilityChanged.bind(this), {
|
|
43888
43979
|
threshold: [0, 0.01, 1]
|
|
43889
43980
|
});
|
|
@@ -43951,7 +44042,7 @@ class BrowserUiFacade {
|
|
|
43951
44042
|
this._contents = element.element.innerHTML;
|
|
43952
44043
|
element.element.innerHTML = '';
|
|
43953
44044
|
}
|
|
43954
|
-
this.
|
|
44045
|
+
this.createStyleElements(settings);
|
|
43955
44046
|
this._file = settings.core.file;
|
|
43956
44047
|
}
|
|
43957
44048
|
setupFontCheckers(settings) {
|
|
@@ -43977,10 +44068,16 @@ class BrowserUiFacade {
|
|
|
43977
44068
|
}
|
|
43978
44069
|
destroy() {
|
|
43979
44070
|
this.rootContainer.element.innerHTML = '';
|
|
44071
|
+
const webFont = this._webFont;
|
|
44072
|
+
webFont.usages--;
|
|
44073
|
+
if (webFont.usages <= 0) {
|
|
44074
|
+
webFont.element.remove();
|
|
44075
|
+
BrowserUiFacade._registeredWebFonts.delete(webFont.hash);
|
|
44076
|
+
}
|
|
43980
44077
|
}
|
|
43981
44078
|
createCanvasElement() {
|
|
43982
44079
|
const canvasElement = document.createElement('div');
|
|
43983
|
-
canvasElement.
|
|
44080
|
+
canvasElement.classList.add('at-surface', `at${this._webFont.fontSuffix}`);
|
|
43984
44081
|
canvasElement.style.fontSize = '0';
|
|
43985
44082
|
canvasElement.style.overflow = 'hidden';
|
|
43986
44083
|
canvasElement.style.lineHeight = '0';
|
|
@@ -44078,9 +44175,133 @@ class BrowserUiFacade {
|
|
|
44078
44175
|
initialRender();
|
|
44079
44176
|
}
|
|
44080
44177
|
}
|
|
44081
|
-
|
|
44082
|
-
const
|
|
44083
|
-
|
|
44178
|
+
createStyleElements(settings) {
|
|
44179
|
+
const root = this._api.container.element.ownerDocument;
|
|
44180
|
+
BrowserUiFacade.createSharedStyleElement(root);
|
|
44181
|
+
// SmuFl Font Specific style
|
|
44182
|
+
const smuflFontSources = settings.core.smuflFontSources ?? CoreSettings.buildDefaultSmuflFontSources(settings.core.fontDirectory);
|
|
44183
|
+
// create a simple unique hash for the font source definition
|
|
44184
|
+
// as data urls might be used we don't want to just use the plain strings.
|
|
44185
|
+
const hash = BrowserUiFacade.cyrb53(smuflFontSources.values());
|
|
44186
|
+
// reuse existing style if available
|
|
44187
|
+
const registeredWebFonts = BrowserUiFacade._registeredWebFonts;
|
|
44188
|
+
if (registeredWebFonts.has(hash)) {
|
|
44189
|
+
const webFont = registeredWebFonts.get(hash);
|
|
44190
|
+
webFont.usages++;
|
|
44191
|
+
webFont.checker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
44192
|
+
this._webFont = webFont;
|
|
44193
|
+
return;
|
|
44194
|
+
}
|
|
44195
|
+
const fontSuffix = registeredWebFonts.size === 0 ? '' : String(registeredWebFonts.size);
|
|
44196
|
+
const familyName = `alphaTab${fontSuffix}`;
|
|
44197
|
+
const src = Array.from(smuflFontSources.entries())
|
|
44198
|
+
.map(e => `url(${JSON.stringify(e[1])}) format('${BrowserUiFacade.cssFormat(e[0])}')`)
|
|
44199
|
+
.join(',');
|
|
44200
|
+
const css = `
|
|
44201
|
+
@font-face {
|
|
44202
|
+
font-display: block;
|
|
44203
|
+
font-family: '${familyName}';
|
|
44204
|
+
src: ${src};
|
|
44205
|
+
font-weight: normal;
|
|
44206
|
+
font-style: normal;
|
|
44207
|
+
}
|
|
44208
|
+
.at-surface.at${fontSuffix} .at {
|
|
44209
|
+
font-family: '${familyName}';
|
|
44210
|
+
speak: none;
|
|
44211
|
+
font-style: normal;
|
|
44212
|
+
font-weight: normal;
|
|
44213
|
+
font-variant: normal;
|
|
44214
|
+
text-transform: none;
|
|
44215
|
+
line-height: 1;
|
|
44216
|
+
line-height: 1;
|
|
44217
|
+
-webkit-font-smoothing: antialiased;
|
|
44218
|
+
-moz-osx-font-smoothing: grayscale;
|
|
44219
|
+
font-size: ${Environment.MusicFontSize}px;
|
|
44220
|
+
overflow: visible !important;
|
|
44221
|
+
}`;
|
|
44222
|
+
const styleElement = root.createElement('style');
|
|
44223
|
+
styleElement.id = `alphaTabStyle${fontSuffix}`;
|
|
44224
|
+
styleElement.innerHTML = css;
|
|
44225
|
+
root.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
44226
|
+
const checker = new FontLoadingChecker([familyName]);
|
|
44227
|
+
checker.fontLoaded.on(this.onFontLoaded.bind(this));
|
|
44228
|
+
this._fontCheckers.set(familyName, checker);
|
|
44229
|
+
checker.checkForFontAvailability();
|
|
44230
|
+
settings.display.resources.smuflFont = new Font(familyName, Environment.MusicFontSize, FontStyle.Plain, FontWeight.Regular);
|
|
44231
|
+
const webFont = {
|
|
44232
|
+
hash,
|
|
44233
|
+
element: styleElement,
|
|
44234
|
+
fontSuffix,
|
|
44235
|
+
usages: 1,
|
|
44236
|
+
checker
|
|
44237
|
+
};
|
|
44238
|
+
registeredWebFonts.set(hash, webFont);
|
|
44239
|
+
this._webFont = webFont;
|
|
44240
|
+
}
|
|
44241
|
+
static cssFormat(format) {
|
|
44242
|
+
switch (format) {
|
|
44243
|
+
case FontFileFormat.EmbeddedOpenType:
|
|
44244
|
+
return 'embedded-opentype';
|
|
44245
|
+
case FontFileFormat.Woff:
|
|
44246
|
+
return 'woff';
|
|
44247
|
+
case FontFileFormat.Woff2:
|
|
44248
|
+
return 'woff2';
|
|
44249
|
+
case FontFileFormat.OpenType:
|
|
44250
|
+
return 'opentype';
|
|
44251
|
+
case FontFileFormat.TrueType:
|
|
44252
|
+
return 'truetype';
|
|
44253
|
+
case FontFileFormat.Svg:
|
|
44254
|
+
return 'svg';
|
|
44255
|
+
}
|
|
44256
|
+
}
|
|
44257
|
+
/**
|
|
44258
|
+
* cyrb53 (c) 2018 bryc (github.com/bryc)
|
|
44259
|
+
* License: Public domain (or MIT if needed). Attribution appreciated.
|
|
44260
|
+
* A fast and simple 53-bit string hash function with decent collision resistance.
|
|
44261
|
+
* Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity
|
|
44262
|
+
* @param str
|
|
44263
|
+
* @param seed
|
|
44264
|
+
* @returns
|
|
44265
|
+
*/
|
|
44266
|
+
static cyrb53(strings, seed = 0) {
|
|
44267
|
+
let h1 = 0xdeadbeef ^ seed;
|
|
44268
|
+
let h2 = 0x41c6ce57 ^ seed;
|
|
44269
|
+
for (const str of strings) {
|
|
44270
|
+
for (let i = 0; i < str.length; i++) {
|
|
44271
|
+
const ch = str.charCodeAt(i);
|
|
44272
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
44273
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
44274
|
+
}
|
|
44275
|
+
}
|
|
44276
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
44277
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
44278
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
44279
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
44280
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
44281
|
+
}
|
|
44282
|
+
/**
|
|
44283
|
+
* Creates the default CSS styles used across all alphaTab instances.
|
|
44284
|
+
* @target web
|
|
44285
|
+
* @internal
|
|
44286
|
+
*/
|
|
44287
|
+
static createSharedStyleElement(root) {
|
|
44288
|
+
let styleElement = root.getElementById('alphaTabStyle');
|
|
44289
|
+
if (!styleElement) {
|
|
44290
|
+
styleElement = document.createElement('style');
|
|
44291
|
+
styleElement.id = 'alphaTabStyleShared';
|
|
44292
|
+
const css = `
|
|
44293
|
+
.at-surface * {
|
|
44294
|
+
cursor: default;
|
|
44295
|
+
vertical-align: top;
|
|
44296
|
+
overflow: visible;
|
|
44297
|
+
}
|
|
44298
|
+
.at-surface-svg text {
|
|
44299
|
+
dominant-baseline: central;
|
|
44300
|
+
white-space:pre;
|
|
44301
|
+
}`;
|
|
44302
|
+
styleElement.innerHTML = css;
|
|
44303
|
+
document.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
44304
|
+
}
|
|
44084
44305
|
}
|
|
44085
44306
|
parseTracks(tracksData) {
|
|
44086
44307
|
if (!tracksData) {
|
|
@@ -44462,6 +44683,7 @@ class BrowserUiFacade {
|
|
|
44462
44683
|
return new BackingTrackPlayer(new AudioElementBackingTrackSynthOutput(), this._api.settings.player.bufferTimeInMilliseconds);
|
|
44463
44684
|
}
|
|
44464
44685
|
}
|
|
44686
|
+
BrowserUiFacade._registeredWebFonts = new Map();
|
|
44465
44687
|
|
|
44466
44688
|
/**
|
|
44467
44689
|
* Represents the progress of any data being loaded.
|
|
@@ -60719,9 +60941,9 @@ class VersionInfo {
|
|
|
60719
60941
|
print(`build date: ${VersionInfo.date}`);
|
|
60720
60942
|
}
|
|
60721
60943
|
}
|
|
60722
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
60723
|
-
VersionInfo.date = '2025-05-
|
|
60724
|
-
VersionInfo.commit = '
|
|
60944
|
+
VersionInfo.version = '1.6.0-alpha.1420';
|
|
60945
|
+
VersionInfo.date = '2025-05-22T03:03:04.057Z';
|
|
60946
|
+
VersionInfo.commit = '230bdd455a1906e6f334b63bad3b8cf773f890e6';
|
|
60725
60947
|
|
|
60726
60948
|
/**
|
|
60727
60949
|
* A factory for custom layout engines.
|
|
@@ -60753,59 +60975,6 @@ class RenderEngineFactory {
|
|
|
60753
60975
|
* @partial
|
|
60754
60976
|
*/
|
|
60755
60977
|
class Environment {
|
|
60756
|
-
/**
|
|
60757
|
-
* @target web
|
|
60758
|
-
* @internal
|
|
60759
|
-
*/
|
|
60760
|
-
static createStyleElement(elementDocument, fontDirectory) {
|
|
60761
|
-
let styleElement = elementDocument.getElementById('alphaTabStyle');
|
|
60762
|
-
if (!styleElement) {
|
|
60763
|
-
if (!fontDirectory) {
|
|
60764
|
-
Logger.error('AlphaTab', 'Font directory could not be detected, cannot create style element');
|
|
60765
|
-
return;
|
|
60766
|
-
}
|
|
60767
|
-
styleElement = elementDocument.createElement('style');
|
|
60768
|
-
styleElement.id = 'alphaTabStyle';
|
|
60769
|
-
const css = `
|
|
60770
|
-
@font-face {
|
|
60771
|
-
font-display: block;
|
|
60772
|
-
font-family: 'alphaTab';
|
|
60773
|
-
src: url('${fontDirectory}Bravura.eot');
|
|
60774
|
-
src: url('${fontDirectory}Bravura.eot?#iefix') format('embedded-opentype')
|
|
60775
|
-
, url('${fontDirectory}Bravura.woff') format('woff')
|
|
60776
|
-
, url('${fontDirectory}Bravura.otf') format('opentype')
|
|
60777
|
-
, url('${fontDirectory}Bravura.svg#Bravura') format('svg');
|
|
60778
|
-
font-weight: normal;
|
|
60779
|
-
font-style: normal;
|
|
60780
|
-
}
|
|
60781
|
-
.at-surface * {
|
|
60782
|
-
cursor: default;
|
|
60783
|
-
vertical-align: top;
|
|
60784
|
-
overflow: visible;
|
|
60785
|
-
}
|
|
60786
|
-
.at-surface-svg text {
|
|
60787
|
-
dominant-baseline: central;
|
|
60788
|
-
white-space:pre;
|
|
60789
|
-
}
|
|
60790
|
-
.at {
|
|
60791
|
-
font-family: 'alphaTab';
|
|
60792
|
-
speak: none;
|
|
60793
|
-
font-style: normal;
|
|
60794
|
-
font-weight: normal;
|
|
60795
|
-
font-variant: normal;
|
|
60796
|
-
text-transform: none;
|
|
60797
|
-
line-height: 1;
|
|
60798
|
-
line-height: 1;
|
|
60799
|
-
-webkit-font-smoothing: antialiased;
|
|
60800
|
-
-moz-osx-font-smoothing: grayscale;
|
|
60801
|
-
font-size: ${Environment.MusicFontSize}px;
|
|
60802
|
-
overflow: visible !important;
|
|
60803
|
-
}`;
|
|
60804
|
-
styleElement.innerHTML = css;
|
|
60805
|
-
elementDocument.getElementsByTagName('head').item(0).appendChild(styleElement);
|
|
60806
|
-
Environment.bravuraFontChecker.checkForFontAvailability();
|
|
60807
|
-
}
|
|
60808
|
-
}
|
|
60809
60978
|
/**
|
|
60810
60979
|
* @target web
|
|
60811
60980
|
* @internal
|
|
@@ -60881,7 +61050,9 @@ class Environment {
|
|
|
60881
61050
|
catch (e) {
|
|
60882
61051
|
}
|
|
60883
61052
|
// normal browser include as <script>
|
|
60884
|
-
if ('document' in Environment.globalThis &&
|
|
61053
|
+
if ('document' in Environment.globalThis &&
|
|
61054
|
+
document.currentScript &&
|
|
61055
|
+
document.currentScript instanceof HTMLScriptElement) {
|
|
60885
61056
|
return document.currentScript.src;
|
|
60886
61057
|
}
|
|
60887
61058
|
return null;
|
|
@@ -61359,11 +61530,6 @@ Environment.scriptFile = Environment.detectScriptFile();
|
|
|
61359
61530
|
* @target web
|
|
61360
61531
|
*/
|
|
61361
61532
|
Environment.fontDirectory = Environment.detectFontDirectory();
|
|
61362
|
-
/**
|
|
61363
|
-
* @target web
|
|
61364
|
-
* @internal
|
|
61365
|
-
*/
|
|
61366
|
-
Environment.bravuraFontChecker = new FontLoadingChecker(['alphaTab']);
|
|
61367
61533
|
Environment.renderEngines = Environment.createDefaultRenderEngines();
|
|
61368
61534
|
/**
|
|
61369
61535
|
* @internal
|
|
@@ -61374,12 +61540,58 @@ Environment.layoutEngines = Environment.createDefaultLayoutEngines();
|
|
|
61374
61540
|
*/
|
|
61375
61541
|
Environment.staveProfiles = Environment.createDefaultStaveProfiles();
|
|
61376
61542
|
|
|
61543
|
+
/**
|
|
61544
|
+
* Lists the known file formats for font files.
|
|
61545
|
+
* @target web
|
|
61546
|
+
*/
|
|
61547
|
+
var FontFileFormat;
|
|
61548
|
+
(function (FontFileFormat) {
|
|
61549
|
+
/**
|
|
61550
|
+
* .eot
|
|
61551
|
+
*/
|
|
61552
|
+
FontFileFormat[FontFileFormat["EmbeddedOpenType"] = 0] = "EmbeddedOpenType";
|
|
61553
|
+
/**
|
|
61554
|
+
* .woff
|
|
61555
|
+
*/
|
|
61556
|
+
FontFileFormat[FontFileFormat["Woff"] = 1] = "Woff";
|
|
61557
|
+
/**
|
|
61558
|
+
* .woff2
|
|
61559
|
+
*/
|
|
61560
|
+
FontFileFormat[FontFileFormat["Woff2"] = 2] = "Woff2";
|
|
61561
|
+
/**
|
|
61562
|
+
* .otf
|
|
61563
|
+
*/
|
|
61564
|
+
FontFileFormat[FontFileFormat["OpenType"] = 3] = "OpenType";
|
|
61565
|
+
/**
|
|
61566
|
+
* .ttf
|
|
61567
|
+
*/
|
|
61568
|
+
FontFileFormat[FontFileFormat["TrueType"] = 4] = "TrueType";
|
|
61569
|
+
/**
|
|
61570
|
+
* .svg
|
|
61571
|
+
*/
|
|
61572
|
+
FontFileFormat[FontFileFormat["Svg"] = 5] = "Svg";
|
|
61573
|
+
})(FontFileFormat || (FontFileFormat = {}));
|
|
61377
61574
|
/**
|
|
61378
61575
|
* All main settings of alphaTab controlling rather general aspects of its behavior.
|
|
61379
61576
|
* @json
|
|
61380
61577
|
* @json_declaration
|
|
61381
61578
|
*/
|
|
61382
61579
|
class CoreSettings {
|
|
61580
|
+
/**
|
|
61581
|
+
* Builds the default SMuFL font sources for the usage with alphaTab in cases
|
|
61582
|
+
* where no custom {@link smuflFontSources} are provided.
|
|
61583
|
+
* @param fontDirectory The {@link fontDirectory} configured.
|
|
61584
|
+
* @target web
|
|
61585
|
+
*/
|
|
61586
|
+
static buildDefaultSmuflFontSources(fontDirectory) {
|
|
61587
|
+
const map = new Map();
|
|
61588
|
+
// WOFF, WOFF2 and OTF should cover all our platform needs
|
|
61589
|
+
const prefix = fontDirectory ?? '';
|
|
61590
|
+
map.set(FontFileFormat.Woff2, `${prefix}Bravura.woff2`);
|
|
61591
|
+
map.set(FontFileFormat.Woff, `${prefix}Bravura.woff`);
|
|
61592
|
+
map.set(FontFileFormat.OpenType, `${prefix}Bravura.otf`);
|
|
61593
|
+
return map;
|
|
61594
|
+
}
|
|
61383
61595
|
/**
|
|
61384
61596
|
* @target web
|
|
61385
61597
|
*/
|
|
@@ -61405,12 +61617,27 @@ class CoreSettings {
|
|
|
61405
61617
|
* where the Web Font files of [Bravura](https://github.com/steinbergmedia/bravura) are. Normally alphaTab expects
|
|
61406
61618
|
* 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.
|
|
61407
61619
|
* Alternatively also a global variable `ALPHATAB_FONT` can be set on the page before initializing alphaTab.
|
|
61620
|
+
*
|
|
61621
|
+
* Use {@link smuflFontSources} for more flexible font configuration.
|
|
61408
61622
|
* @defaultValue `"${AlphaTabScriptFolder}/font/"`
|
|
61409
61623
|
* @category Core - JavaScript Specific
|
|
61410
61624
|
* @target web
|
|
61411
61625
|
* @since 0.9.6
|
|
61412
61626
|
*/
|
|
61413
61627
|
this.fontDirectory = null;
|
|
61628
|
+
/**
|
|
61629
|
+
* Defines the URLs from which to load the SMuFL compliant font files.
|
|
61630
|
+
* @remarks
|
|
61631
|
+
* These sources will be used to load and register the webfonts on the page so
|
|
61632
|
+
* they are available for rendering the music sheet. The sources can be set to any
|
|
61633
|
+
* CSS compatible URL which can be passed into `url()`.
|
|
61634
|
+
* See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src#url
|
|
61635
|
+
* @defaultValue Bravura files located at {@link fontDirectory} .
|
|
61636
|
+
* @category Core - JavaScript Specific
|
|
61637
|
+
* @target web
|
|
61638
|
+
* @since 1.6.0
|
|
61639
|
+
*/
|
|
61640
|
+
this.smuflFontSources = null;
|
|
61414
61641
|
/**
|
|
61415
61642
|
* The full URL to the input file to be loaded.
|
|
61416
61643
|
* @remarks
|
|
@@ -65053,4 +65280,4 @@ const _jsonbarrel = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.definePropert
|
|
|
65053
65280
|
__proto__: null
|
|
65054
65281
|
}, Symbol.toStringTag, { value: 'Module' }));
|
|
65055
65282
|
|
|
65056
|
-
export { AlphaTabApi, AlphaTabApiBase, AlphaTabError, AlphaTabErrorType, ConsoleLogger, CoreSettings, DisplaySettings, Environment, FileLoadError, FingeringMode, FormatError, ImporterSettings, LayoutMode, LogLevel, Logger, NotationElement, NotationMode, NotationSettings, PlayerMode, PlayerOutputMode, PlayerSettings, ProgressEventArgs, RenderEngineFactory, RenderingResources, ResizeEventArgs, ScrollMode, Settings, SlidePlaybackSettings, StaveProfile, SystemsLayoutMode, TabRhythmMode, VibratoPlaybackSettings, WebPlatform, _barrel$5 as exporter, _barrel$7 as importer, _barrel$6 as io, _jsonbarrel as json, VersionInfo as meta, _barrel$4 as midi, _barrel$3 as model, _barrel$1 as platform, _barrel$2 as rendering, _barrel as synth };
|
|
65283
|
+
export { AlphaTabApi, AlphaTabApiBase, AlphaTabError, AlphaTabErrorType, ConsoleLogger, CoreSettings, DisplaySettings, Environment, FileLoadError, FingeringMode, FontFileFormat, FormatError, ImporterSettings, LayoutMode, LogLevel, Logger, NotationElement, NotationMode, NotationSettings, PlayerMode, PlayerOutputMode, PlayerSettings, ProgressEventArgs, RenderEngineFactory, RenderingResources, ResizeEventArgs, ScrollMode, Settings, SlidePlaybackSettings, StaveProfile, SystemsLayoutMode, TabRhythmMode, VibratoPlaybackSettings, WebPlatform, _barrel$5 as exporter, _barrel$7 as importer, _barrel$6 as io, _jsonbarrel as json, VersionInfo as meta, _barrel$4 as midi, _barrel$3 as model, _barrel$1 as platform, _barrel$2 as rendering, _barrel as synth };
|