@coderline/alphatab 1.6.0-alpha.1428 → 1.6.0-alpha.1432
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 +415 -114
- package/dist/alphaTab.d.ts +218 -26
- package/dist/alphaTab.js +415 -114
- 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.1432 (develop, build 1432)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -1006,6 +1006,26 @@ class Bar {
|
|
|
1006
1006
|
get isEmpty() {
|
|
1007
1007
|
return this._isEmpty;
|
|
1008
1008
|
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Whether this bar has any changes applied which are not related to the voices in it.
|
|
1011
|
+
* (e.g. new key signatures)
|
|
1012
|
+
*/
|
|
1013
|
+
get hasChanges() {
|
|
1014
|
+
if (this.index === 0) {
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
const hasChangesToPrevious = this.keySignature !== this.previousBar.keySignature ||
|
|
1018
|
+
this.keySignatureType !== this.previousBar.keySignatureType ||
|
|
1019
|
+
this.clef !== this.previousBar.clef ||
|
|
1020
|
+
this.clefOttava !== this.previousBar.clefOttava;
|
|
1021
|
+
if (hasChangesToPrevious) {
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
return (this.simileMark !== SimileMark.None ||
|
|
1025
|
+
this.sustainPedals.length > 0 ||
|
|
1026
|
+
this.barLineLeft !== BarLineStyle.Automatic ||
|
|
1027
|
+
this.barLineRight !== BarLineStyle.Automatic);
|
|
1028
|
+
}
|
|
1009
1029
|
/**
|
|
1010
1030
|
* Whether this bar is empty or has only rests.
|
|
1011
1031
|
*/
|
|
@@ -1411,12 +1431,6 @@ class SyncPointData {
|
|
|
1411
1431
|
* Indicates for which repeat occurence this sync point is valid (e.g. 0 on the first time played, 1 on the second time played)
|
|
1412
1432
|
*/
|
|
1413
1433
|
this.barOccurence = 0;
|
|
1414
|
-
/**
|
|
1415
|
-
* The modified tempo at which the cursor should move (aka. the tempo played within the external audio track).
|
|
1416
|
-
* This information is used together with normal tempo changes to calculate how much faster/slower the
|
|
1417
|
-
* cursor playback is performed to align with the audio track.
|
|
1418
|
-
*/
|
|
1419
|
-
this.modifiedTempo = 0;
|
|
1420
1434
|
/**
|
|
1421
1435
|
* The audio offset marking the position within the audio track in milliseconds.
|
|
1422
1436
|
* This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
|
|
@@ -2625,6 +2639,33 @@ class MasterBar {
|
|
|
2625
2639
|
*/
|
|
2626
2640
|
this.directions = null;
|
|
2627
2641
|
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Whether the masterbar is has any changes applied to it (e.g. tempo changes, time signature changes etc)
|
|
2644
|
+
* The first bar is always considered changed due to initial setup of values. It does not consider
|
|
2645
|
+
* elements like whether the tempo really changes to the previous bar.
|
|
2646
|
+
*/
|
|
2647
|
+
get hasChanges() {
|
|
2648
|
+
if (this.index === 0) {
|
|
2649
|
+
return false;
|
|
2650
|
+
}
|
|
2651
|
+
const hasChangesToPrevious = this.timeSignatureCommon !== this.previousMasterBar.timeSignatureCommon ||
|
|
2652
|
+
this.timeSignatureNumerator !== this.previousMasterBar.timeSignatureNumerator ||
|
|
2653
|
+
this.timeSignatureDenominator !== this.previousMasterBar.timeSignatureDenominator ||
|
|
2654
|
+
this.tripletFeel !== this.previousMasterBar.tripletFeel;
|
|
2655
|
+
if (hasChangesToPrevious) {
|
|
2656
|
+
return true;
|
|
2657
|
+
}
|
|
2658
|
+
return (this.alternateEndings !== 0 ||
|
|
2659
|
+
this.isRepeatStart ||
|
|
2660
|
+
this.isRepeatEnd ||
|
|
2661
|
+
this.isFreeTime ||
|
|
2662
|
+
this.isSectionStart ||
|
|
2663
|
+
this.tempoAutomations.length > 0 ||
|
|
2664
|
+
this.syncPoints && this.syncPoints.length > 0 ||
|
|
2665
|
+
(this.fermata !== null && this.fermata.size > 0) ||
|
|
2666
|
+
(this.directions !== null && this.directions.size > 0) ||
|
|
2667
|
+
this.isAnacrusis);
|
|
2668
|
+
}
|
|
2628
2669
|
/**
|
|
2629
2670
|
* The key signature used on all bars.
|
|
2630
2671
|
* @deprecated Use key signatures on bar level
|
|
@@ -3566,6 +3607,43 @@ class ModelUtils {
|
|
|
3566
3607
|
}
|
|
3567
3608
|
}
|
|
3568
3609
|
}
|
|
3610
|
+
/**
|
|
3611
|
+
* Trims any empty bars at the end of the song.
|
|
3612
|
+
* @param score
|
|
3613
|
+
*/
|
|
3614
|
+
static trimEmptyBarsAtEnd(score) {
|
|
3615
|
+
while (score.masterBars.length > 1) {
|
|
3616
|
+
const barIndex = score.masterBars.length - 1;
|
|
3617
|
+
const masterBar = score.masterBars[barIndex];
|
|
3618
|
+
if (masterBar.hasChanges) {
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
for (const track of score.tracks) {
|
|
3622
|
+
for (const staff of track.staves) {
|
|
3623
|
+
if (barIndex < staff.bars.length) {
|
|
3624
|
+
const bar = staff.bars[barIndex];
|
|
3625
|
+
if (!bar.isEmpty || bar.hasChanges) {
|
|
3626
|
+
// found a non-empty bar, stop whole cleanup
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
// if we reach here, all found bars are empty, remove the bar
|
|
3633
|
+
for (const track of score.tracks) {
|
|
3634
|
+
for (const staff of track.staves) {
|
|
3635
|
+
if (barIndex < staff.bars.length) {
|
|
3636
|
+
const bar = staff.bars[barIndex];
|
|
3637
|
+
staff.bars.pop();
|
|
3638
|
+
// unlink
|
|
3639
|
+
bar.previousBar.nextBar = null;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
score.masterBars.pop();
|
|
3644
|
+
masterBar.previousMasterBar.nextMasterBar = null;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3569
3647
|
}
|
|
3570
3648
|
ModelUtils.TuningLetters = new Set([
|
|
3571
3649
|
0x43 /* C */, 0x44 /* D */, 0x45 /* E */, 0x46 /* F */, 0x47 /* G */, 0x41 /* A */, 0x42 /* B */, 0x63 /* c */,
|
|
@@ -5843,7 +5921,6 @@ class SyncPointDataCloner {
|
|
|
5843
5921
|
static clone(original) {
|
|
5844
5922
|
const clone = new SyncPointData();
|
|
5845
5923
|
clone.barOccurence = original.barOccurence;
|
|
5846
|
-
clone.modifiedTempo = original.modifiedTempo;
|
|
5847
5924
|
clone.millisecondOffset = original.millisecondOffset;
|
|
5848
5925
|
return clone;
|
|
5849
5926
|
}
|
|
@@ -7313,7 +7390,6 @@ class Score {
|
|
|
7313
7390
|
automation.ratioPosition = Math.min(1, Math.max(0, syncPoint.barPosition));
|
|
7314
7391
|
automation.type = AutomationType.SyncPoint;
|
|
7315
7392
|
automation.syncPointValue = new SyncPointData();
|
|
7316
|
-
automation.syncPointValue.modifiedTempo = syncPoint.modifiedTempo;
|
|
7317
7393
|
automation.syncPointValue.millisecondOffset = syncPoint.millisecondOffset;
|
|
7318
7394
|
automation.syncPointValue.barOccurence = syncPoint.barOccurence;
|
|
7319
7395
|
if (syncPoint.barIndex < this.masterBars.length) {
|
|
@@ -7346,8 +7422,7 @@ class Score {
|
|
|
7346
7422
|
barIndex: masterBar.index,
|
|
7347
7423
|
barOccurence: syncPoint.syncPointValue.barOccurence,
|
|
7348
7424
|
barPosition: syncPoint.ratioPosition,
|
|
7349
|
-
millisecondOffset: syncPoint.syncPointValue.millisecondOffset
|
|
7350
|
-
modifiedTempo: syncPoint.syncPointValue.modifiedTempo
|
|
7425
|
+
millisecondOffset: syncPoint.syncPointValue.millisecondOffset
|
|
7351
7426
|
});
|
|
7352
7427
|
}
|
|
7353
7428
|
}
|
|
@@ -8686,6 +8761,7 @@ class AlphaTexImporter extends ScoreImporter {
|
|
|
8686
8761
|
this._slurs = new Map();
|
|
8687
8762
|
this._articulationValueToIndex = new Map();
|
|
8688
8763
|
this._accidentalMode = AlphaTexAccidentalMode.Explicit;
|
|
8764
|
+
this._syncPoints = [];
|
|
8689
8765
|
this.logErrors = false;
|
|
8690
8766
|
}
|
|
8691
8767
|
get name() {
|
|
@@ -8726,10 +8802,16 @@ class AlphaTexImporter extends ScoreImporter {
|
|
|
8726
8802
|
if (!anyMetaRead && !anyBarsRead) {
|
|
8727
8803
|
throw new UnsupportedFormatError('No alphaTex data found');
|
|
8728
8804
|
}
|
|
8805
|
+
if (this._sy === AlphaTexSymbols.Dot) {
|
|
8806
|
+
this._sy = this.newSy();
|
|
8807
|
+
this.syncPoints();
|
|
8808
|
+
}
|
|
8729
8809
|
}
|
|
8730
8810
|
ModelUtils.consolidate(this._score);
|
|
8731
8811
|
this._score.finish(this.settings);
|
|
8812
|
+
ModelUtils.trimEmptyBarsAtEnd(this._score);
|
|
8732
8813
|
this._score.rebuildRepeatGroups();
|
|
8814
|
+
this._score.applyFlatSyncPoints(this._syncPoints);
|
|
8733
8815
|
for (const [track, lyrics] of this._lyrics) {
|
|
8734
8816
|
this._score.tracks[track].applyLyrics(lyrics);
|
|
8735
8817
|
}
|
|
@@ -8746,6 +8828,47 @@ class AlphaTexImporter extends ScoreImporter {
|
|
|
8746
8828
|
throw e;
|
|
8747
8829
|
}
|
|
8748
8830
|
}
|
|
8831
|
+
syncPoints() {
|
|
8832
|
+
while (this._sy !== AlphaTexSymbols.Eof) {
|
|
8833
|
+
this.syncPoint();
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
syncPoint() {
|
|
8837
|
+
// \sync BarIndex Occurence MillisecondOffset
|
|
8838
|
+
// \sync BarIndex Occurence MillisecondOffset RatioPosition
|
|
8839
|
+
if (this._sy !== AlphaTexSymbols.MetaCommand || this._syData !== 'sync') {
|
|
8840
|
+
this.error('syncPoint', AlphaTexSymbols.MetaCommand, true);
|
|
8841
|
+
}
|
|
8842
|
+
this._sy = this.newSy();
|
|
8843
|
+
if (this._sy !== AlphaTexSymbols.Number) {
|
|
8844
|
+
this.error('syncPointBarIndex', AlphaTexSymbols.Number, true);
|
|
8845
|
+
}
|
|
8846
|
+
const barIndex = this._syData;
|
|
8847
|
+
this._sy = this.newSy();
|
|
8848
|
+
if (this._sy !== AlphaTexSymbols.Number) {
|
|
8849
|
+
this.error('syncPointBarOccurence', AlphaTexSymbols.Number, true);
|
|
8850
|
+
}
|
|
8851
|
+
const barOccurence = this._syData;
|
|
8852
|
+
this._sy = this.newSy();
|
|
8853
|
+
if (this._sy !== AlphaTexSymbols.Number) {
|
|
8854
|
+
this.error('syncPointBarMillis', AlphaTexSymbols.Number, true);
|
|
8855
|
+
}
|
|
8856
|
+
const millisecondOffset = this._syData;
|
|
8857
|
+
this._allowFloat = true;
|
|
8858
|
+
this._sy = this.newSy();
|
|
8859
|
+
this._allowFloat = false;
|
|
8860
|
+
let barPosition = 0;
|
|
8861
|
+
if (this._sy === AlphaTexSymbols.Number) {
|
|
8862
|
+
barPosition = this._syData;
|
|
8863
|
+
this._sy = this.newSy();
|
|
8864
|
+
}
|
|
8865
|
+
this._syncPoints.push({
|
|
8866
|
+
barIndex,
|
|
8867
|
+
barOccurence,
|
|
8868
|
+
barPosition,
|
|
8869
|
+
millisecondOffset
|
|
8870
|
+
});
|
|
8871
|
+
}
|
|
8749
8872
|
error(nonterm, expected, wrongSymbol = true) {
|
|
8750
8873
|
let receivedSymbol;
|
|
8751
8874
|
let showSyData = false;
|
|
@@ -14714,9 +14837,6 @@ class GpifParser {
|
|
|
14714
14837
|
case 'BarOccurrence':
|
|
14715
14838
|
syncPointValue.barOccurence = GpifParser.parseIntSafe(vc.innerText, 0);
|
|
14716
14839
|
break;
|
|
14717
|
-
case 'ModifiedTempo':
|
|
14718
|
-
syncPointValue.modifiedTempo = GpifParser.parseFloatSafe(vc.innerText, 0);
|
|
14719
|
-
break;
|
|
14720
14840
|
case 'FrameOffset':
|
|
14721
14841
|
const frameOffset = GpifParser.parseFloatSafe(vc.innerText, 0);
|
|
14722
14842
|
syncPointValue.millisecondOffset = (frameOffset / GpifParser.SampleRate) * 1000;
|
|
@@ -22339,6 +22459,61 @@ class SynthEvent {
|
|
|
22339
22459
|
}
|
|
22340
22460
|
}
|
|
22341
22461
|
|
|
22462
|
+
/**
|
|
22463
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22464
|
+
*/
|
|
22465
|
+
class BackingTrackSyncPoint {
|
|
22466
|
+
constructor() {
|
|
22467
|
+
/**
|
|
22468
|
+
* The index of the masterbar to which this sync point belongs to.
|
|
22469
|
+
* @remarks
|
|
22470
|
+
* This property is purely informative for external use like in editors.
|
|
22471
|
+
* It has no impact to the synchronization itself.
|
|
22472
|
+
*/
|
|
22473
|
+
this.masterBarIndex = 0;
|
|
22474
|
+
/**
|
|
22475
|
+
* The occurence of the masterbar to which this sync point belongs to. The occurence
|
|
22476
|
+
* is 0-based and increases with every repeated play of a masterbar (e.g. on repeats or jumps).
|
|
22477
|
+
* @remarks
|
|
22478
|
+
* This property is purely informative for external use like in editors.
|
|
22479
|
+
* It has no impact to the synchronization itself.
|
|
22480
|
+
*/
|
|
22481
|
+
this.masterBarOccurence = 0;
|
|
22482
|
+
/**
|
|
22483
|
+
* The BPM the synthesizer has at the exact tick position of this sync point.
|
|
22484
|
+
*/
|
|
22485
|
+
this.synthBpm = 0;
|
|
22486
|
+
/**
|
|
22487
|
+
* The millisecond time position of the synthesizer when this sync point is reached.
|
|
22488
|
+
*/
|
|
22489
|
+
this.synthTime = 0;
|
|
22490
|
+
/**
|
|
22491
|
+
* The midi tick position of the synthesizer when this sync point is reached.
|
|
22492
|
+
*/
|
|
22493
|
+
this.synthTick = 0;
|
|
22494
|
+
/**
|
|
22495
|
+
* The millisecond time in the external media marking the synchronization point.
|
|
22496
|
+
*/
|
|
22497
|
+
this.syncTime = 0;
|
|
22498
|
+
/**
|
|
22499
|
+
* The BPM the song will have virtually after this sync point to align the external media time axis
|
|
22500
|
+
* with the one from the synthesizer.
|
|
22501
|
+
*/
|
|
22502
|
+
this.syncBpm = 0;
|
|
22503
|
+
}
|
|
22504
|
+
/**
|
|
22505
|
+
* Updates the synchronization BPM that will apply after this sync point.
|
|
22506
|
+
* @param nextSyncPointSynthTime The synthesizer time of the next sync point after this one.
|
|
22507
|
+
* @param nextSyncPointSyncTime The synchronization time of the next sync point after this one.
|
|
22508
|
+
*/
|
|
22509
|
+
updateSyncBpm(nextSyncPointSynthTime, nextSyncPointSyncTime) {
|
|
22510
|
+
const synthDuration = nextSyncPointSynthTime - this.synthTime;
|
|
22511
|
+
const syncedDuration = nextSyncPointSyncTime - this.syncTime;
|
|
22512
|
+
const modifiedTempo = (synthDuration / syncedDuration) * this.synthBpm;
|
|
22513
|
+
this.syncBpm = modifiedTempo;
|
|
22514
|
+
}
|
|
22515
|
+
}
|
|
22516
|
+
|
|
22342
22517
|
class MidiFileSequencerTempoChange {
|
|
22343
22518
|
constructor(bpm, ticks, time) {
|
|
22344
22519
|
this.bpm = bpm;
|
|
@@ -22346,14 +22521,6 @@ class MidiFileSequencerTempoChange {
|
|
|
22346
22521
|
this.time = time;
|
|
22347
22522
|
}
|
|
22348
22523
|
}
|
|
22349
|
-
class BackingTrackSyncPointWithTime {
|
|
22350
|
-
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22351
|
-
this.alphaTabTick = tick;
|
|
22352
|
-
this.alphaTabTime = time;
|
|
22353
|
-
this.modifiedTempo = modifiedTempo;
|
|
22354
|
-
this.millisecondOffset = millisecondOffset;
|
|
22355
|
-
}
|
|
22356
|
-
}
|
|
22357
22524
|
class MidiSequencerState {
|
|
22358
22525
|
constructor() {
|
|
22359
22526
|
this.tempoChanges = [];
|
|
@@ -22457,7 +22624,7 @@ class MidiFileSequencer {
|
|
|
22457
22624
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22458
22625
|
this._mainState.modifiedTempo =
|
|
22459
22626
|
this._mainState.syncPoints.length > 0
|
|
22460
|
-
? this._mainState.syncPoints[0].
|
|
22627
|
+
? this._mainState.syncPoints[0].syncBpm
|
|
22461
22628
|
: this._mainState.currentTempo;
|
|
22462
22629
|
if (this.isPlayingMain) {
|
|
22463
22630
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22628,7 +22795,7 @@ class MidiFileSequencer {
|
|
|
22628
22795
|
}
|
|
22629
22796
|
mainUpdateSyncPoints(syncPoints) {
|
|
22630
22797
|
const state = this._mainState;
|
|
22631
|
-
syncPoints.sort((a, b) => a.
|
|
22798
|
+
syncPoints.sort((a, b) => a.synthTick - b.synthTick); // just in case
|
|
22632
22799
|
state.syncPoints = [];
|
|
22633
22800
|
if (syncPoints.length >= 0) {
|
|
22634
22801
|
let bpm = 120;
|
|
@@ -22638,6 +22805,8 @@ class MidiFileSequencer {
|
|
|
22638
22805
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22639
22806
|
const p = syncPoints[i];
|
|
22640
22807
|
let deltaTick = 0;
|
|
22808
|
+
// TODO: merge interpolation into MidiFileGenerator where we already play through
|
|
22809
|
+
// the time axis.
|
|
22641
22810
|
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22642
22811
|
let previousModifiedTempo;
|
|
22643
22812
|
let previousMillisecondOffset;
|
|
@@ -22649,9 +22818,9 @@ class MidiFileSequencer {
|
|
|
22649
22818
|
}
|
|
22650
22819
|
else {
|
|
22651
22820
|
const previousSyncPoint = syncPoints[i - 1];
|
|
22652
|
-
previousModifiedTempo = previousSyncPoint.
|
|
22653
|
-
previousMillisecondOffset = previousSyncPoint.
|
|
22654
|
-
previousTick = previousSyncPoint.
|
|
22821
|
+
previousModifiedTempo = previousSyncPoint.syncBpm;
|
|
22822
|
+
previousMillisecondOffset = previousSyncPoint.syncTime;
|
|
22823
|
+
previousTick = previousSyncPoint.synthTick;
|
|
22655
22824
|
}
|
|
22656
22825
|
// process time until sync point
|
|
22657
22826
|
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
@@ -22659,27 +22828,31 @@ class MidiFileSequencer {
|
|
|
22659
22828
|
// otherwise the linear interpolation later in the lookup will fail.
|
|
22660
22829
|
// 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
|
|
22661
22830
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22662
|
-
state.tempoChanges[tempoChangeIndex].ticks <= p.
|
|
22831
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.synthTick) {
|
|
22663
22832
|
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22664
22833
|
if (deltaTick > 0) {
|
|
22665
22834
|
absTick += deltaTick;
|
|
22666
22835
|
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22667
|
-
const millisPerTick = (p.
|
|
22836
|
+
const millisPerTick = (p.syncTime - previousMillisecondOffset) / (p.synthTick - previousTick);
|
|
22668
22837
|
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22669
|
-
|
|
22838
|
+
const syncPoint = new BackingTrackSyncPoint();
|
|
22839
|
+
syncPoint.synthTick = absTick;
|
|
22840
|
+
syncPoint.synthBpm = bpm;
|
|
22841
|
+
syncPoint.synthTime = absTime;
|
|
22842
|
+
syncPoint.syncTime = interpolatedMillisecondOffset;
|
|
22843
|
+
syncPoint.syncBpm = previousModifiedTempo;
|
|
22670
22844
|
}
|
|
22671
22845
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22672
22846
|
tempoChangeIndex++;
|
|
22673
22847
|
}
|
|
22674
|
-
deltaTick = p.
|
|
22848
|
+
deltaTick = p.synthTick - absTick;
|
|
22675
22849
|
absTick += deltaTick;
|
|
22676
22850
|
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22677
|
-
state.syncPoints.push(
|
|
22851
|
+
state.syncPoints.push(p);
|
|
22678
22852
|
}
|
|
22679
22853
|
}
|
|
22680
22854
|
state.syncPointIndex = 0;
|
|
22681
|
-
state.modifiedTempo =
|
|
22682
|
-
state.syncPoints.length > 0 ? state.syncPoints[0].modifiedTempo : state.currentTempo;
|
|
22855
|
+
state.modifiedTempo = state.syncPoints.length > 0 ? state.syncPoints[0].syncBpm : state.currentTempo;
|
|
22683
22856
|
}
|
|
22684
22857
|
currentTimePositionToTickPosition(timePosition) {
|
|
22685
22858
|
const state = this._currentState;
|
|
@@ -22712,16 +22885,15 @@ class MidiFileSequencer {
|
|
|
22712
22885
|
const syncPoints = state.syncPoints;
|
|
22713
22886
|
if (syncPoints.length > 0) {
|
|
22714
22887
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22715
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22888
|
+
if (timePosition < syncPoints[syncPointIndex].syncTime) {
|
|
22716
22889
|
syncPointIndex = 0;
|
|
22717
22890
|
}
|
|
22718
|
-
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22719
|
-
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22891
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].syncTime <= timePosition) {
|
|
22720
22892
|
syncPointIndex++;
|
|
22721
22893
|
}
|
|
22722
22894
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22723
22895
|
state.syncPointIndex = syncPointIndex;
|
|
22724
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22896
|
+
state.modifiedTempo = syncPoints[syncPointIndex].syncBpm;
|
|
22725
22897
|
}
|
|
22726
22898
|
}
|
|
22727
22899
|
else {
|
|
@@ -22737,18 +22909,18 @@ class MidiFileSequencer {
|
|
|
22737
22909
|
this.updateSyncPoints(this._mainState, timePosition);
|
|
22738
22910
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22739
22911
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22740
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22912
|
+
const timeDiff = timePosition - currentSyncPoint.syncTime;
|
|
22741
22913
|
let alphaTabTimeDiff;
|
|
22742
22914
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22743
22915
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22744
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22745
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22916
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.syncTime - currentSyncPoint.syncTime);
|
|
22917
|
+
alphaTabTimeDiff = (nextSyncPoint.synthTime - currentSyncPoint.synthTime) * relativeTimeDiff;
|
|
22746
22918
|
}
|
|
22747
22919
|
else {
|
|
22748
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22749
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22920
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.syncTime);
|
|
22921
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.synthTime) * relativeTimeDiff;
|
|
22750
22922
|
}
|
|
22751
|
-
return (currentSyncPoint.
|
|
22923
|
+
return (currentSyncPoint.synthTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22752
22924
|
}
|
|
22753
22925
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22754
22926
|
const mainState = this._mainState;
|
|
@@ -22758,27 +22930,27 @@ class MidiFileSequencer {
|
|
|
22758
22930
|
}
|
|
22759
22931
|
timePosition *= this.playbackSpeed;
|
|
22760
22932
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22761
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22933
|
+
if (timePosition < syncPoints[syncPointIndex].synthTime) {
|
|
22762
22934
|
syncPointIndex = 0;
|
|
22763
22935
|
}
|
|
22764
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22936
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].synthTime <= timePosition) {
|
|
22765
22937
|
syncPointIndex++;
|
|
22766
22938
|
}
|
|
22767
22939
|
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22768
22940
|
// we ensure that we have a linear increase between two points
|
|
22769
22941
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22770
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22942
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.synthTime;
|
|
22771
22943
|
let backingTrackPos;
|
|
22772
22944
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22773
22945
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22774
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22775
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22776
|
-
backingTrackPos = currentSyncPoint.
|
|
22946
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.synthTime - currentSyncPoint.synthTime);
|
|
22947
|
+
const backingTrackDiff = nextSyncPoint.syncTime - currentSyncPoint.syncTime;
|
|
22948
|
+
backingTrackPos = currentSyncPoint.syncTime + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22777
22949
|
}
|
|
22778
22950
|
else {
|
|
22779
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22780
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22781
|
-
backingTrackPos = currentSyncPoint.
|
|
22951
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.synthTime);
|
|
22952
|
+
const frameDiff = backingTrackLength - currentSyncPoint.syncTime;
|
|
22953
|
+
backingTrackPos = currentSyncPoint.syncTime + frameDiff * relativeAlphaTabTimeDiff;
|
|
22782
22954
|
}
|
|
22783
22955
|
return backingTrackPos;
|
|
22784
22956
|
}
|
|
@@ -31050,7 +31222,6 @@ class SyncPointDataSerializer {
|
|
|
31050
31222
|
}
|
|
31051
31223
|
const o = new Map();
|
|
31052
31224
|
o.set("baroccurence", obj.barOccurence);
|
|
31053
|
-
o.set("modifiedtempo", obj.modifiedTempo);
|
|
31054
31225
|
o.set("millisecondoffset", obj.millisecondOffset);
|
|
31055
31226
|
return o;
|
|
31056
31227
|
}
|
|
@@ -31059,9 +31230,6 @@ class SyncPointDataSerializer {
|
|
|
31059
31230
|
case "baroccurence":
|
|
31060
31231
|
obj.barOccurence = v;
|
|
31061
31232
|
return true;
|
|
31062
|
-
case "modifiedtempo":
|
|
31063
|
-
obj.modifiedTempo = v;
|
|
31064
|
-
return true;
|
|
31065
31233
|
case "millisecondoffset":
|
|
31066
31234
|
obj.millisecondOffset = v;
|
|
31067
31235
|
return true;
|
|
@@ -33066,7 +33234,7 @@ class AlphaSynthWorkerSynthOutput {
|
|
|
33066
33234
|
addSamples(samples) {
|
|
33067
33235
|
this._worker.postMessage({
|
|
33068
33236
|
cmd: 'alphaSynth.output.addSamples',
|
|
33069
|
-
samples: samples
|
|
33237
|
+
samples: Environment.prepareForPostMessage(samples)
|
|
33070
33238
|
});
|
|
33071
33239
|
}
|
|
33072
33240
|
play() {
|
|
@@ -33262,7 +33430,7 @@ class AlphaSynthWebWorker {
|
|
|
33262
33430
|
onSoundFontLoadFailed(e) {
|
|
33263
33431
|
this._main.postMessage({
|
|
33264
33432
|
cmd: 'alphaSynth.soundFontLoadFailed',
|
|
33265
|
-
error: this.serializeException(e)
|
|
33433
|
+
error: this.serializeException(Environment.prepareForPostMessage(e))
|
|
33266
33434
|
});
|
|
33267
33435
|
}
|
|
33268
33436
|
serializeException(e) {
|
|
@@ -33293,7 +33461,7 @@ class AlphaSynthWebWorker {
|
|
|
33293
33461
|
onMidiLoadFailed(e) {
|
|
33294
33462
|
this._main.postMessage({
|
|
33295
33463
|
cmd: 'alphaSynth.midiLoaded',
|
|
33296
|
-
error: this.serializeException(e)
|
|
33464
|
+
error: this.serializeException(Environment.prepareForPostMessage(e))
|
|
33297
33465
|
});
|
|
33298
33466
|
}
|
|
33299
33467
|
onReadyForPlayback() {
|
|
@@ -35824,17 +35992,6 @@ class MidiTickLookup {
|
|
|
35824
35992
|
}
|
|
35825
35993
|
}
|
|
35826
35994
|
|
|
35827
|
-
/**
|
|
35828
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35829
|
-
*/
|
|
35830
|
-
class BackingTrackSyncPoint {
|
|
35831
|
-
constructor(tick, data) {
|
|
35832
|
-
this.tick = 0;
|
|
35833
|
-
this.tick = tick;
|
|
35834
|
-
this.data = data;
|
|
35835
|
-
}
|
|
35836
|
-
}
|
|
35837
|
-
|
|
35838
35995
|
class MidiNoteDuration {
|
|
35839
35996
|
constructor() {
|
|
35840
35997
|
this.noteOnly = 0;
|
|
@@ -35855,6 +36012,14 @@ class RasgueadoInfo {
|
|
|
35855
36012
|
this.brushInfos = [];
|
|
35856
36013
|
}
|
|
35857
36014
|
}
|
|
36015
|
+
class PlayThroughContext {
|
|
36016
|
+
constructor() {
|
|
36017
|
+
this.synthTick = 0;
|
|
36018
|
+
this.synthTime = 0;
|
|
36019
|
+
this.currentTempo = 0;
|
|
36020
|
+
this.automationToSyncPoint = new Map();
|
|
36021
|
+
}
|
|
36022
|
+
}
|
|
35858
36023
|
/**
|
|
35859
36024
|
* This generator creates a midi file using a score.
|
|
35860
36025
|
*/
|
|
@@ -35981,9 +36146,22 @@ class MidiFileGenerator {
|
|
|
35981
36146
|
});
|
|
35982
36147
|
return syncPoints;
|
|
35983
36148
|
}
|
|
36149
|
+
/**
|
|
36150
|
+
* @internal
|
|
36151
|
+
*/
|
|
36152
|
+
static buildModifiedTempoLookup(score) {
|
|
36153
|
+
const syncPoints = [];
|
|
36154
|
+
const context = MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
36155
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
36156
|
+
}, _endTick => {
|
|
36157
|
+
});
|
|
36158
|
+
return context.automationToSyncPoint;
|
|
36159
|
+
}
|
|
35984
36160
|
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35985
36161
|
const controller = new MidiPlaybackController(score);
|
|
35986
|
-
|
|
36162
|
+
const playContext = new PlayThroughContext();
|
|
36163
|
+
playContext.currentTempo = score.tempo;
|
|
36164
|
+
playContext.syncPoints = syncPoints;
|
|
35987
36165
|
let previousMasterBar = null;
|
|
35988
36166
|
// store the previous played bar for repeats
|
|
35989
36167
|
const barOccurence = new Map();
|
|
@@ -35996,23 +36174,11 @@ class MidiFileGenerator {
|
|
|
35996
36174
|
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35997
36175
|
occurence++;
|
|
35998
36176
|
barOccurence.set(index, occurence);
|
|
35999
|
-
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
36000
|
-
const
|
|
36001
|
-
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
36005
|
-
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
36006
|
-
}
|
|
36007
|
-
}
|
|
36008
|
-
}
|
|
36009
|
-
if (bar.tempoAutomations.length > 0) {
|
|
36010
|
-
currentTempo = bar.tempoAutomations[0].value;
|
|
36011
|
-
}
|
|
36012
|
-
generateTracks(index, currentTick, currentTempo);
|
|
36013
|
-
if (bar.tempoAutomations.length > 0) {
|
|
36014
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
36015
|
-
}
|
|
36177
|
+
generateMasterBar(bar, previousMasterBar, currentTick, playContext.currentTempo, occurence);
|
|
36178
|
+
const trackTempo = bar.tempoAutomations.length > 0 ? bar.tempoAutomations[0].value : playContext.currentTempo;
|
|
36179
|
+
generateTracks(index, currentTick, trackTempo);
|
|
36180
|
+
playContext.synthTick = currentTick;
|
|
36181
|
+
MidiFileGenerator.processBarTime(bar, occurence, playContext);
|
|
36016
36182
|
}
|
|
36017
36183
|
controller.moveNext();
|
|
36018
36184
|
previousMasterBar = bar;
|
|
@@ -36023,21 +36189,119 @@ class MidiFileGenerator {
|
|
|
36023
36189
|
// but where it ends according to the BPM and the remaining ticks.
|
|
36024
36190
|
if (syncPoints.length > 0) {
|
|
36025
36191
|
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
36026
|
-
const remainingTicks = controller.currentTick - lastSyncPoint.
|
|
36192
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.synthTick;
|
|
36027
36193
|
if (remainingTicks > 0) {
|
|
36028
|
-
const
|
|
36029
|
-
|
|
36030
|
-
|
|
36031
|
-
|
|
36032
|
-
|
|
36033
|
-
//
|
|
36034
|
-
|
|
36035
|
-
|
|
36036
|
-
|
|
36037
|
-
|
|
36194
|
+
const backingTrackSyncPoint = new BackingTrackSyncPoint();
|
|
36195
|
+
backingTrackSyncPoint.masterBarIndex = previousMasterBar.index;
|
|
36196
|
+
backingTrackSyncPoint.masterBarOccurence = barOccurence.get(previousMasterBar.index) - 1;
|
|
36197
|
+
backingTrackSyncPoint.synthTick = controller.currentTick;
|
|
36198
|
+
backingTrackSyncPoint.synthBpm = playContext.currentTempo;
|
|
36199
|
+
// we need to assume some BPM for the last interpolated point.
|
|
36200
|
+
// if we have more than just a start point, we keep the BPM before the last manual sync point
|
|
36201
|
+
// otherwise we have no customized sync BPM known and keep the synthesizer one.
|
|
36202
|
+
backingTrackSyncPoint.syncBpm =
|
|
36203
|
+
syncPoints.length > 1 ? syncPoints[syncPoints.length - 2].syncBpm : lastSyncPoint.synthBpm;
|
|
36204
|
+
backingTrackSyncPoint.synthTime =
|
|
36205
|
+
lastSyncPoint.synthTime + MidiUtils.ticksToMillis(remainingTicks, lastSyncPoint.synthBpm);
|
|
36206
|
+
backingTrackSyncPoint.syncTime =
|
|
36207
|
+
lastSyncPoint.syncTime + MidiUtils.ticksToMillis(remainingTicks, backingTrackSyncPoint.syncBpm);
|
|
36208
|
+
// update the previous sync point according to the new time
|
|
36209
|
+
lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
|
|
36210
|
+
syncPoints.push(backingTrackSyncPoint);
|
|
36038
36211
|
}
|
|
36039
36212
|
}
|
|
36040
36213
|
finish(controller.currentTick);
|
|
36214
|
+
return playContext;
|
|
36215
|
+
}
|
|
36216
|
+
static processBarTime(bar, occurence, context) {
|
|
36217
|
+
const duration = bar.calculateDuration();
|
|
36218
|
+
const barSyncPoints = bar.syncPoints;
|
|
36219
|
+
const barStartTick = context.synthTick;
|
|
36220
|
+
if (barSyncPoints) {
|
|
36221
|
+
MidiFileGenerator.processBarTimeWithSyncPoints(bar, occurence, context);
|
|
36222
|
+
}
|
|
36223
|
+
else {
|
|
36224
|
+
MidiFileGenerator.processBarTimeNoSyncPoints(bar, context);
|
|
36225
|
+
}
|
|
36226
|
+
// don't forget the part after the last tempo change
|
|
36227
|
+
const endTick = barStartTick + duration;
|
|
36228
|
+
const tickOffset = endTick - context.synthTick;
|
|
36229
|
+
if (tickOffset > 0) {
|
|
36230
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36231
|
+
context.synthTick = endTick;
|
|
36232
|
+
}
|
|
36233
|
+
}
|
|
36234
|
+
static processBarTimeWithSyncPoints(bar, occurence, context) {
|
|
36235
|
+
const barStartTick = context.synthTick;
|
|
36236
|
+
const duration = bar.calculateDuration();
|
|
36237
|
+
let tempoChangeIndex = 0;
|
|
36238
|
+
let tickOffset;
|
|
36239
|
+
for (const syncPoint of bar.syncPoints) {
|
|
36240
|
+
if (syncPoint.syncPointValue.barOccurence !== occurence) {
|
|
36241
|
+
continue;
|
|
36242
|
+
}
|
|
36243
|
+
const syncPointTick = barStartTick + syncPoint.ratioPosition * duration;
|
|
36244
|
+
// first process all tempo changes until this sync point
|
|
36245
|
+
while (tempoChangeIndex < bar.tempoAutomations.length &&
|
|
36246
|
+
bar.tempoAutomations[tempoChangeIndex].ratioPosition <= syncPoint.ratioPosition) {
|
|
36247
|
+
const tempoChange = bar.tempoAutomations[tempoChangeIndex];
|
|
36248
|
+
const absoluteTick = barStartTick + tempoChange.ratioPosition * duration;
|
|
36249
|
+
tickOffset = absoluteTick - context.synthTick;
|
|
36250
|
+
if (tickOffset > 0) {
|
|
36251
|
+
context.synthTick = absoluteTick;
|
|
36252
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36253
|
+
}
|
|
36254
|
+
context.currentTempo = tempoChange.value;
|
|
36255
|
+
tempoChangeIndex++;
|
|
36256
|
+
}
|
|
36257
|
+
// process time until sync point
|
|
36258
|
+
tickOffset = syncPointTick - context.synthTick;
|
|
36259
|
+
if (tickOffset > 0) {
|
|
36260
|
+
context.synthTick = syncPointTick;
|
|
36261
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36262
|
+
}
|
|
36263
|
+
// update the previous sync point according to the new time
|
|
36264
|
+
if (context.syncPoints.length > 0) {
|
|
36265
|
+
context.syncPoints[context.syncPoints.length - 1].updateSyncBpm(context.synthTime, syncPoint.syncPointValue.millisecondOffset);
|
|
36266
|
+
}
|
|
36267
|
+
// create the new sync point
|
|
36268
|
+
const backingTrackSyncPoint = new BackingTrackSyncPoint();
|
|
36269
|
+
backingTrackSyncPoint.masterBarIndex = bar.index;
|
|
36270
|
+
backingTrackSyncPoint.masterBarOccurence = occurence;
|
|
36271
|
+
backingTrackSyncPoint.synthTick = syncPointTick;
|
|
36272
|
+
backingTrackSyncPoint.synthBpm = context.currentTempo;
|
|
36273
|
+
backingTrackSyncPoint.synthTime = context.synthTime;
|
|
36274
|
+
backingTrackSyncPoint.syncTime = syncPoint.syncPointValue.millisecondOffset;
|
|
36275
|
+
backingTrackSyncPoint.syncBpm = 0 /* calculated by next sync point */;
|
|
36276
|
+
context.syncPoints.push(backingTrackSyncPoint);
|
|
36277
|
+
context.automationToSyncPoint.set(syncPoint, backingTrackSyncPoint);
|
|
36278
|
+
}
|
|
36279
|
+
// process remaining tempo changes after all sync points
|
|
36280
|
+
while (tempoChangeIndex < bar.tempoAutomations.length) {
|
|
36281
|
+
const tempoChange = bar.tempoAutomations[tempoChangeIndex];
|
|
36282
|
+
const absoluteTick = barStartTick + tempoChange.ratioPosition * duration;
|
|
36283
|
+
tickOffset = absoluteTick - context.synthTick;
|
|
36284
|
+
if (tickOffset > 0) {
|
|
36285
|
+
context.synthTick = absoluteTick;
|
|
36286
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36287
|
+
}
|
|
36288
|
+
context.currentTempo = tempoChange.value;
|
|
36289
|
+
tempoChangeIndex++;
|
|
36290
|
+
}
|
|
36291
|
+
}
|
|
36292
|
+
static processBarTimeNoSyncPoints(bar, context) {
|
|
36293
|
+
// walk through the tempo changes
|
|
36294
|
+
const barStartTick = context.synthTick;
|
|
36295
|
+
const duration = bar.calculateDuration();
|
|
36296
|
+
for (const changes of bar.tempoAutomations) {
|
|
36297
|
+
const absoluteTick = barStartTick + changes.ratioPosition * duration;
|
|
36298
|
+
const tickOffset = absoluteTick - context.synthTick;
|
|
36299
|
+
if (tickOffset > 0) {
|
|
36300
|
+
context.synthTick = absoluteTick;
|
|
36301
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36302
|
+
}
|
|
36303
|
+
context.currentTempo = changes.value;
|
|
36304
|
+
}
|
|
36041
36305
|
}
|
|
36042
36306
|
static toChannelShort(data) {
|
|
36043
36307
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
@@ -42322,6 +42586,18 @@ class FileLoadError extends AlphaTabError {
|
|
|
42322
42586
|
* available importers
|
|
42323
42587
|
*/
|
|
42324
42588
|
class ScoreLoader {
|
|
42589
|
+
/**
|
|
42590
|
+
* Loads the given alphaTex string.
|
|
42591
|
+
* @param tex The alphaTex string.
|
|
42592
|
+
* @param settings The settings to use for parsing.
|
|
42593
|
+
* @returns The parsed {@see Score}.
|
|
42594
|
+
*/
|
|
42595
|
+
static loadAlphaTex(tex, settings) {
|
|
42596
|
+
const parser = new AlphaTexImporter();
|
|
42597
|
+
parser.logErrors = true;
|
|
42598
|
+
parser.initFromString(tex, settings ?? new Settings());
|
|
42599
|
+
return parser.readScore();
|
|
42600
|
+
}
|
|
42325
42601
|
/**
|
|
42326
42602
|
* Loads a score asynchronously from the given datasource
|
|
42327
42603
|
* @param path the source path to load the binary file from
|
|
@@ -43172,7 +43448,7 @@ class AlphaSynthWebWorkerApi {
|
|
|
43172
43448
|
this._midiEventsPlayedFilter = value;
|
|
43173
43449
|
this._synth.postMessage({
|
|
43174
43450
|
cmd: 'alphaSynth.setMidiEventsPlayedFilter',
|
|
43175
|
-
value: value
|
|
43451
|
+
value: Environment.prepareForPostMessage(value)
|
|
43176
43452
|
});
|
|
43177
43453
|
}
|
|
43178
43454
|
get playbackSpeed() {
|
|
@@ -43237,7 +43513,7 @@ class AlphaSynthWebWorkerApi {
|
|
|
43237
43513
|
this._playbackRange = value;
|
|
43238
43514
|
this._synth.postMessage({
|
|
43239
43515
|
cmd: 'alphaSynth.setPlaybackRange',
|
|
43240
|
-
value: value
|
|
43516
|
+
value: Environment.prepareForPostMessage(value)
|
|
43241
43517
|
});
|
|
43242
43518
|
}
|
|
43243
43519
|
constructor(player, settings) {
|
|
@@ -43331,13 +43607,13 @@ class AlphaSynthWebWorkerApi {
|
|
|
43331
43607
|
playOneTimeMidiFile(midi) {
|
|
43332
43608
|
this._synth.postMessage({
|
|
43333
43609
|
cmd: 'alphaSynth.playOneTimeMidiFile',
|
|
43334
|
-
midi: JsonConverter.midiFileToJsObject(midi)
|
|
43610
|
+
midi: JsonConverter.midiFileToJsObject(Environment.prepareForPostMessage(midi))
|
|
43335
43611
|
});
|
|
43336
43612
|
}
|
|
43337
43613
|
loadSoundFont(data, append) {
|
|
43338
43614
|
this._synth.postMessage({
|
|
43339
43615
|
cmd: 'alphaSynth.loadSoundFontBytes',
|
|
43340
|
-
data: data,
|
|
43616
|
+
data: Environment.prepareForPostMessage(data),
|
|
43341
43617
|
append: append
|
|
43342
43618
|
});
|
|
43343
43619
|
}
|
|
@@ -43349,13 +43625,13 @@ class AlphaSynthWebWorkerApi {
|
|
|
43349
43625
|
loadMidiFile(midi) {
|
|
43350
43626
|
this._synth.postMessage({
|
|
43351
43627
|
cmd: 'alphaSynth.loadMidi',
|
|
43352
|
-
midi: JsonConverter.midiFileToJsObject(midi)
|
|
43628
|
+
midi: JsonConverter.midiFileToJsObject(Environment.prepareForPostMessage(midi))
|
|
43353
43629
|
});
|
|
43354
43630
|
}
|
|
43355
43631
|
applyTranspositionPitches(transpositionPitches) {
|
|
43356
43632
|
this._synth.postMessage({
|
|
43357
43633
|
cmd: 'alphaSynth.applyTranspositionPitches',
|
|
43358
|
-
transpositionPitches: JSON.stringify(Array.from(transpositionPitches.entries()))
|
|
43634
|
+
transpositionPitches: JSON.stringify(Array.from(Environment.prepareForPostMessage(transpositionPitches).entries()))
|
|
43359
43635
|
});
|
|
43360
43636
|
}
|
|
43361
43637
|
setChannelTranspositionPitch(channel, semitones) {
|
|
@@ -43527,7 +43803,7 @@ class AlphaTabWorkerScoreRenderer {
|
|
|
43527
43803
|
});
|
|
43528
43804
|
}
|
|
43529
43805
|
serializeSettingsForWorker(settings) {
|
|
43530
|
-
const jsObject = JsonConverter.settingsToJsObject(settings);
|
|
43806
|
+
const jsObject = JsonConverter.settingsToJsObject(Environment.prepareForPostMessage(settings));
|
|
43531
43807
|
// cut out player settings, they are only needed on UI thread side
|
|
43532
43808
|
jsObject.delete('player');
|
|
43533
43809
|
return jsObject;
|
|
@@ -43585,11 +43861,11 @@ class AlphaTabWorkerScoreRenderer {
|
|
|
43585
43861
|
}
|
|
43586
43862
|
}
|
|
43587
43863
|
renderScore(score, trackIndexes) {
|
|
43588
|
-
const jsObject = score == null ? null : JsonConverter.scoreToJsObject(score);
|
|
43864
|
+
const jsObject = score == null ? null : JsonConverter.scoreToJsObject(Environment.prepareForPostMessage(score));
|
|
43589
43865
|
this._worker.postMessage({
|
|
43590
43866
|
cmd: 'alphaTab.renderScore',
|
|
43591
43867
|
score: jsObject,
|
|
43592
|
-
trackIndexes: trackIndexes,
|
|
43868
|
+
trackIndexes: Environment.prepareForPostMessage(trackIndexes),
|
|
43593
43869
|
fontSizes: FontSizes.FontSizeLookupTables
|
|
43594
43870
|
});
|
|
43595
43871
|
}
|
|
@@ -43780,7 +44056,7 @@ class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase {
|
|
|
43780
44056
|
addSamples(f) {
|
|
43781
44057
|
this._worklet?.port.postMessage({
|
|
43782
44058
|
cmd: AlphaSynthWorkerSynthOutput.CmdOutputAddSamples,
|
|
43783
|
-
samples: f
|
|
44059
|
+
samples: Environment.prepareForPostMessage(f)
|
|
43784
44060
|
});
|
|
43785
44061
|
}
|
|
43786
44062
|
resetSamples() {
|
|
@@ -61000,9 +61276,9 @@ class VersionInfo {
|
|
|
61000
61276
|
print(`build date: ${VersionInfo.date}`);
|
|
61001
61277
|
}
|
|
61002
61278
|
}
|
|
61003
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
61004
|
-
VersionInfo.date = '2025-05-
|
|
61005
|
-
VersionInfo.commit = '
|
|
61279
|
+
VersionInfo.version = '1.6.0-alpha.1432';
|
|
61280
|
+
VersionInfo.date = '2025-05-31T02:06:00.256Z';
|
|
61281
|
+
VersionInfo.commit = '91e41f1b2cf0b786a1ca2053f51cf3f370da4bc6';
|
|
61006
61282
|
|
|
61007
61283
|
/**
|
|
61008
61284
|
* A factory for custom layout engines.
|
|
@@ -61547,6 +61823,29 @@ class Environment {
|
|
|
61547
61823
|
print(`Screen Size: ${window.screen.width}x${window.screen.height}`);
|
|
61548
61824
|
}
|
|
61549
61825
|
}
|
|
61826
|
+
/**
|
|
61827
|
+
* Prepares the given object to be sent to workers. Web Frameworks like Vue might
|
|
61828
|
+
* create proxy objects for all objects used. This code handles the necessary unwrapping.
|
|
61829
|
+
* @internal
|
|
61830
|
+
* @target web
|
|
61831
|
+
*/
|
|
61832
|
+
static prepareForPostMessage(object) {
|
|
61833
|
+
if (!object) {
|
|
61834
|
+
return object;
|
|
61835
|
+
}
|
|
61836
|
+
// Vue toRaw:
|
|
61837
|
+
// https://github.com/vuejs/core/blob/e7381761cc7971c0d40ae0a0a72687a500fd8db3/packages/reactivity/src/reactive.ts#L378-L381
|
|
61838
|
+
if (typeof object === 'object') {
|
|
61839
|
+
const unwrapped = object.__v_raw;
|
|
61840
|
+
if (unwrapped) {
|
|
61841
|
+
return Environment.prepareForPostMessage(unwrapped);
|
|
61842
|
+
}
|
|
61843
|
+
}
|
|
61844
|
+
// Solidjs unwrap: the symbol required to access the raw object is unfortunately hidden and we cannot unwrap it without importing
|
|
61845
|
+
// import { unwrap } from "solid-js/store"
|
|
61846
|
+
// alternative for users is to replace this method during runtime.
|
|
61847
|
+
return object;
|
|
61848
|
+
}
|
|
61550
61849
|
}
|
|
61551
61850
|
Environment.StaffIdBeforeSlashAlways = 'before-slash-always';
|
|
61552
61851
|
Environment.StaffIdBeforeScoreAlways = 'before-score-always';
|
|
@@ -61818,6 +62117,7 @@ class CoreSettings {
|
|
|
61818
62117
|
|
|
61819
62118
|
const _barrel$7 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
61820
62119
|
__proto__: null,
|
|
62120
|
+
AlphaTexImporter,
|
|
61821
62121
|
ScoreImporter,
|
|
61822
62122
|
ScoreLoader,
|
|
61823
62123
|
UnsupportedFormatError
|
|
@@ -62652,6 +62952,7 @@ class GpifWriter {
|
|
|
62652
62952
|
: undefined;
|
|
62653
62953
|
const millisecondPadding = initialSyncPoint ? initialSyncPoint.syncPointValue.millisecondOffset : 0;
|
|
62654
62954
|
this.backingTrackFramePadding = (-1 * ((millisecondPadding / 1000) * GpifWriter.SampleRate)) | 0;
|
|
62955
|
+
const modifiedTempoLookup = new Lazy(() => MidiFileGenerator.buildModifiedTempoLookup(score));
|
|
62655
62956
|
for (const mb of score.masterBars) {
|
|
62656
62957
|
for (const automation of mb.tempoAutomations) {
|
|
62657
62958
|
const tempoAutomation = automations.addElement('Automation');
|
|
@@ -62676,7 +62977,7 @@ class GpifWriter {
|
|
|
62676
62977
|
const value = syncPointAutomation.addElement('Value');
|
|
62677
62978
|
value.addElement('BarIndex').innerText = mb.index.toString();
|
|
62678
62979
|
value.addElement('BarOccurrence').innerText = syncPoint.syncPointValue.barOccurence.toString();
|
|
62679
|
-
value.addElement('ModifiedTempo').innerText = syncPoint.
|
|
62980
|
+
value.addElement('ModifiedTempo').innerText = modifiedTempoLookup.value.get(syncPoint).syncBpm.toString();
|
|
62680
62981
|
value.addElement('OriginalTempo').innerText = score.tempo.toString();
|
|
62681
62982
|
const frameOffset = (((syncPoint.syncPointValue.millisecondOffset - millisecondPadding) / 1000) *
|
|
62682
62983
|
GpifWriter.SampleRate) |
|