@coderline/alphatab 1.6.0-alpha.1428 → 1.6.0-alpha.1430
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 +234 -101
- package/dist/alphaTab.d.ts +48 -25
- package/dist/alphaTab.js +234 -101
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.vite.js +1 -1
- package/dist/alphaTab.vite.mjs +1 -1
- package/dist/alphaTab.webpack.js +1 -1
- package/dist/alphaTab.webpack.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +1 -1
package/dist/alphaTab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.6.0-alpha.
|
|
2
|
+
* alphaTab v1.6.0-alpha.1430 (develop, build 1430)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -1417,12 +1417,6 @@
|
|
|
1417
1417
|
* Indicates for which repeat occurence this sync point is valid (e.g. 0 on the first time played, 1 on the second time played)
|
|
1418
1418
|
*/
|
|
1419
1419
|
this.barOccurence = 0;
|
|
1420
|
-
/**
|
|
1421
|
-
* The modified tempo at which the cursor should move (aka. the tempo played within the external audio track).
|
|
1422
|
-
* This information is used together with normal tempo changes to calculate how much faster/slower the
|
|
1423
|
-
* cursor playback is performed to align with the audio track.
|
|
1424
|
-
*/
|
|
1425
|
-
this.modifiedTempo = 0;
|
|
1426
1420
|
/**
|
|
1427
1421
|
* The audio offset marking the position within the audio track in milliseconds.
|
|
1428
1422
|
* This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
|
|
@@ -5849,7 +5843,6 @@
|
|
|
5849
5843
|
static clone(original) {
|
|
5850
5844
|
const clone = new SyncPointData();
|
|
5851
5845
|
clone.barOccurence = original.barOccurence;
|
|
5852
|
-
clone.modifiedTempo = original.modifiedTempo;
|
|
5853
5846
|
clone.millisecondOffset = original.millisecondOffset;
|
|
5854
5847
|
return clone;
|
|
5855
5848
|
}
|
|
@@ -7319,7 +7312,6 @@
|
|
|
7319
7312
|
automation.ratioPosition = Math.min(1, Math.max(0, syncPoint.barPosition));
|
|
7320
7313
|
automation.type = AutomationType.SyncPoint;
|
|
7321
7314
|
automation.syncPointValue = new SyncPointData();
|
|
7322
|
-
automation.syncPointValue.modifiedTempo = syncPoint.modifiedTempo;
|
|
7323
7315
|
automation.syncPointValue.millisecondOffset = syncPoint.millisecondOffset;
|
|
7324
7316
|
automation.syncPointValue.barOccurence = syncPoint.barOccurence;
|
|
7325
7317
|
if (syncPoint.barIndex < this.masterBars.length) {
|
|
@@ -7352,8 +7344,7 @@
|
|
|
7352
7344
|
barIndex: masterBar.index,
|
|
7353
7345
|
barOccurence: syncPoint.syncPointValue.barOccurence,
|
|
7354
7346
|
barPosition: syncPoint.ratioPosition,
|
|
7355
|
-
millisecondOffset: syncPoint.syncPointValue.millisecondOffset
|
|
7356
|
-
modifiedTempo: syncPoint.syncPointValue.modifiedTempo
|
|
7347
|
+
millisecondOffset: syncPoint.syncPointValue.millisecondOffset
|
|
7357
7348
|
});
|
|
7358
7349
|
}
|
|
7359
7350
|
}
|
|
@@ -14720,9 +14711,6 @@
|
|
|
14720
14711
|
case 'BarOccurrence':
|
|
14721
14712
|
syncPointValue.barOccurence = GpifParser.parseIntSafe(vc.innerText, 0);
|
|
14722
14713
|
break;
|
|
14723
|
-
case 'ModifiedTempo':
|
|
14724
|
-
syncPointValue.modifiedTempo = GpifParser.parseFloatSafe(vc.innerText, 0);
|
|
14725
|
-
break;
|
|
14726
14714
|
case 'FrameOffset':
|
|
14727
14715
|
const frameOffset = GpifParser.parseFloatSafe(vc.innerText, 0);
|
|
14728
14716
|
syncPointValue.millisecondOffset = (frameOffset / GpifParser.SampleRate) * 1000;
|
|
@@ -22345,6 +22333,61 @@
|
|
|
22345
22333
|
}
|
|
22346
22334
|
}
|
|
22347
22335
|
|
|
22336
|
+
/**
|
|
22337
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22338
|
+
*/
|
|
22339
|
+
class BackingTrackSyncPoint {
|
|
22340
|
+
constructor() {
|
|
22341
|
+
/**
|
|
22342
|
+
* The index of the masterbar to which this sync point belongs to.
|
|
22343
|
+
* @remarks
|
|
22344
|
+
* This property is purely informative for external use like in editors.
|
|
22345
|
+
* It has no impact to the synchronization itself.
|
|
22346
|
+
*/
|
|
22347
|
+
this.masterBarIndex = 0;
|
|
22348
|
+
/**
|
|
22349
|
+
* The occurence of the masterbar to which this sync point belongs to. The occurence
|
|
22350
|
+
* is 0-based and increases with every repeated play of a masterbar (e.g. on repeats or jumps).
|
|
22351
|
+
* @remarks
|
|
22352
|
+
* This property is purely informative for external use like in editors.
|
|
22353
|
+
* It has no impact to the synchronization itself.
|
|
22354
|
+
*/
|
|
22355
|
+
this.masterBarOccurence = 0;
|
|
22356
|
+
/**
|
|
22357
|
+
* The BPM the synthesizer has at the exact tick position of this sync point.
|
|
22358
|
+
*/
|
|
22359
|
+
this.synthBpm = 0;
|
|
22360
|
+
/**
|
|
22361
|
+
* The millisecond time position of the synthesizer when this sync point is reached.
|
|
22362
|
+
*/
|
|
22363
|
+
this.synthTime = 0;
|
|
22364
|
+
/**
|
|
22365
|
+
* The midi tick position of the synthesizer when this sync point is reached.
|
|
22366
|
+
*/
|
|
22367
|
+
this.synthTick = 0;
|
|
22368
|
+
/**
|
|
22369
|
+
* The millisecond time in the external media marking the synchronization point.
|
|
22370
|
+
*/
|
|
22371
|
+
this.syncTime = 0;
|
|
22372
|
+
/**
|
|
22373
|
+
* The BPM the song will have virtually after this sync point to align the external media time axis
|
|
22374
|
+
* with the one from the synthesizer.
|
|
22375
|
+
*/
|
|
22376
|
+
this.syncBpm = 0;
|
|
22377
|
+
}
|
|
22378
|
+
/**
|
|
22379
|
+
* Updates the synchronization BPM that will apply after this sync point.
|
|
22380
|
+
* @param nextSyncPointSynthTime The synthesizer time of the next sync point after this one.
|
|
22381
|
+
* @param nextSyncPointSyncTime The synchronization time of the next sync point after this one.
|
|
22382
|
+
*/
|
|
22383
|
+
updateSyncBpm(nextSyncPointSynthTime, nextSyncPointSyncTime) {
|
|
22384
|
+
const synthDuration = nextSyncPointSynthTime - this.synthTime;
|
|
22385
|
+
const syncedDuration = nextSyncPointSyncTime - this.syncTime;
|
|
22386
|
+
const modifiedTempo = (synthDuration / syncedDuration) * this.synthBpm;
|
|
22387
|
+
this.syncBpm = modifiedTempo;
|
|
22388
|
+
}
|
|
22389
|
+
}
|
|
22390
|
+
|
|
22348
22391
|
class MidiFileSequencerTempoChange {
|
|
22349
22392
|
constructor(bpm, ticks, time) {
|
|
22350
22393
|
this.bpm = bpm;
|
|
@@ -22352,14 +22395,6 @@
|
|
|
22352
22395
|
this.time = time;
|
|
22353
22396
|
}
|
|
22354
22397
|
}
|
|
22355
|
-
class BackingTrackSyncPointWithTime {
|
|
22356
|
-
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22357
|
-
this.alphaTabTick = tick;
|
|
22358
|
-
this.alphaTabTime = time;
|
|
22359
|
-
this.modifiedTempo = modifiedTempo;
|
|
22360
|
-
this.millisecondOffset = millisecondOffset;
|
|
22361
|
-
}
|
|
22362
|
-
}
|
|
22363
22398
|
class MidiSequencerState {
|
|
22364
22399
|
constructor() {
|
|
22365
22400
|
this.tempoChanges = [];
|
|
@@ -22463,7 +22498,7 @@
|
|
|
22463
22498
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22464
22499
|
this._mainState.modifiedTempo =
|
|
22465
22500
|
this._mainState.syncPoints.length > 0
|
|
22466
|
-
? this._mainState.syncPoints[0].
|
|
22501
|
+
? this._mainState.syncPoints[0].syncBpm
|
|
22467
22502
|
: this._mainState.currentTempo;
|
|
22468
22503
|
if (this.isPlayingMain) {
|
|
22469
22504
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22634,7 +22669,7 @@
|
|
|
22634
22669
|
}
|
|
22635
22670
|
mainUpdateSyncPoints(syncPoints) {
|
|
22636
22671
|
const state = this._mainState;
|
|
22637
|
-
syncPoints.sort((a, b) => a.
|
|
22672
|
+
syncPoints.sort((a, b) => a.synthTick - b.synthTick); // just in case
|
|
22638
22673
|
state.syncPoints = [];
|
|
22639
22674
|
if (syncPoints.length >= 0) {
|
|
22640
22675
|
let bpm = 120;
|
|
@@ -22644,6 +22679,8 @@
|
|
|
22644
22679
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22645
22680
|
const p = syncPoints[i];
|
|
22646
22681
|
let deltaTick = 0;
|
|
22682
|
+
// TODO: merge interpolation into MidiFileGenerator where we already play through
|
|
22683
|
+
// the time axis.
|
|
22647
22684
|
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22648
22685
|
let previousModifiedTempo;
|
|
22649
22686
|
let previousMillisecondOffset;
|
|
@@ -22655,9 +22692,9 @@
|
|
|
22655
22692
|
}
|
|
22656
22693
|
else {
|
|
22657
22694
|
const previousSyncPoint = syncPoints[i - 1];
|
|
22658
|
-
previousModifiedTempo = previousSyncPoint.
|
|
22659
|
-
previousMillisecondOffset = previousSyncPoint.
|
|
22660
|
-
previousTick = previousSyncPoint.
|
|
22695
|
+
previousModifiedTempo = previousSyncPoint.syncBpm;
|
|
22696
|
+
previousMillisecondOffset = previousSyncPoint.syncTime;
|
|
22697
|
+
previousTick = previousSyncPoint.synthTick;
|
|
22661
22698
|
}
|
|
22662
22699
|
// process time until sync point
|
|
22663
22700
|
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
@@ -22665,27 +22702,31 @@
|
|
|
22665
22702
|
// otherwise the linear interpolation later in the lookup will fail.
|
|
22666
22703
|
// 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
|
|
22667
22704
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22668
|
-
state.tempoChanges[tempoChangeIndex].ticks <= p.
|
|
22705
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.synthTick) {
|
|
22669
22706
|
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22670
22707
|
if (deltaTick > 0) {
|
|
22671
22708
|
absTick += deltaTick;
|
|
22672
22709
|
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22673
|
-
const millisPerTick = (p.
|
|
22710
|
+
const millisPerTick = (p.syncTime - previousMillisecondOffset) / (p.synthTick - previousTick);
|
|
22674
22711
|
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22675
|
-
|
|
22712
|
+
const syncPoint = new BackingTrackSyncPoint();
|
|
22713
|
+
syncPoint.synthTick = absTick;
|
|
22714
|
+
syncPoint.synthBpm = bpm;
|
|
22715
|
+
syncPoint.synthTime = absTime;
|
|
22716
|
+
syncPoint.syncTime = interpolatedMillisecondOffset;
|
|
22717
|
+
syncPoint.syncBpm = previousModifiedTempo;
|
|
22676
22718
|
}
|
|
22677
22719
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22678
22720
|
tempoChangeIndex++;
|
|
22679
22721
|
}
|
|
22680
|
-
deltaTick = p.
|
|
22722
|
+
deltaTick = p.synthTick - absTick;
|
|
22681
22723
|
absTick += deltaTick;
|
|
22682
22724
|
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22683
|
-
state.syncPoints.push(
|
|
22725
|
+
state.syncPoints.push(p);
|
|
22684
22726
|
}
|
|
22685
22727
|
}
|
|
22686
22728
|
state.syncPointIndex = 0;
|
|
22687
|
-
state.modifiedTempo =
|
|
22688
|
-
state.syncPoints.length > 0 ? state.syncPoints[0].modifiedTempo : state.currentTempo;
|
|
22729
|
+
state.modifiedTempo = state.syncPoints.length > 0 ? state.syncPoints[0].syncBpm : state.currentTempo;
|
|
22689
22730
|
}
|
|
22690
22731
|
currentTimePositionToTickPosition(timePosition) {
|
|
22691
22732
|
const state = this._currentState;
|
|
@@ -22718,16 +22759,15 @@
|
|
|
22718
22759
|
const syncPoints = state.syncPoints;
|
|
22719
22760
|
if (syncPoints.length > 0) {
|
|
22720
22761
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22721
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22762
|
+
if (timePosition < syncPoints[syncPointIndex].syncTime) {
|
|
22722
22763
|
syncPointIndex = 0;
|
|
22723
22764
|
}
|
|
22724
|
-
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22725
|
-
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22765
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].syncTime <= timePosition) {
|
|
22726
22766
|
syncPointIndex++;
|
|
22727
22767
|
}
|
|
22728
22768
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22729
22769
|
state.syncPointIndex = syncPointIndex;
|
|
22730
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22770
|
+
state.modifiedTempo = syncPoints[syncPointIndex].syncBpm;
|
|
22731
22771
|
}
|
|
22732
22772
|
}
|
|
22733
22773
|
else {
|
|
@@ -22743,18 +22783,18 @@
|
|
|
22743
22783
|
this.updateSyncPoints(this._mainState, timePosition);
|
|
22744
22784
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22745
22785
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22746
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22786
|
+
const timeDiff = timePosition - currentSyncPoint.syncTime;
|
|
22747
22787
|
let alphaTabTimeDiff;
|
|
22748
22788
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22749
22789
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22750
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22751
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22790
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.syncTime - currentSyncPoint.syncTime);
|
|
22791
|
+
alphaTabTimeDiff = (nextSyncPoint.synthTime - currentSyncPoint.synthTime) * relativeTimeDiff;
|
|
22752
22792
|
}
|
|
22753
22793
|
else {
|
|
22754
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22755
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22794
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.syncTime);
|
|
22795
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.synthTime) * relativeTimeDiff;
|
|
22756
22796
|
}
|
|
22757
|
-
return (currentSyncPoint.
|
|
22797
|
+
return (currentSyncPoint.synthTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22758
22798
|
}
|
|
22759
22799
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22760
22800
|
const mainState = this._mainState;
|
|
@@ -22764,27 +22804,27 @@
|
|
|
22764
22804
|
}
|
|
22765
22805
|
timePosition *= this.playbackSpeed;
|
|
22766
22806
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22767
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22807
|
+
if (timePosition < syncPoints[syncPointIndex].synthTime) {
|
|
22768
22808
|
syncPointIndex = 0;
|
|
22769
22809
|
}
|
|
22770
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22810
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].synthTime <= timePosition) {
|
|
22771
22811
|
syncPointIndex++;
|
|
22772
22812
|
}
|
|
22773
22813
|
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22774
22814
|
// we ensure that we have a linear increase between two points
|
|
22775
22815
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22776
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22816
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.synthTime;
|
|
22777
22817
|
let backingTrackPos;
|
|
22778
22818
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22779
22819
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22780
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22781
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22782
|
-
backingTrackPos = currentSyncPoint.
|
|
22820
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.synthTime - currentSyncPoint.synthTime);
|
|
22821
|
+
const backingTrackDiff = nextSyncPoint.syncTime - currentSyncPoint.syncTime;
|
|
22822
|
+
backingTrackPos = currentSyncPoint.syncTime + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22783
22823
|
}
|
|
22784
22824
|
else {
|
|
22785
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22786
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22787
|
-
backingTrackPos = currentSyncPoint.
|
|
22825
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.synthTime);
|
|
22826
|
+
const frameDiff = backingTrackLength - currentSyncPoint.syncTime;
|
|
22827
|
+
backingTrackPos = currentSyncPoint.syncTime + frameDiff * relativeAlphaTabTimeDiff;
|
|
22788
22828
|
}
|
|
22789
22829
|
return backingTrackPos;
|
|
22790
22830
|
}
|
|
@@ -31056,7 +31096,6 @@
|
|
|
31056
31096
|
}
|
|
31057
31097
|
const o = new Map();
|
|
31058
31098
|
o.set("baroccurence", obj.barOccurence);
|
|
31059
|
-
o.set("modifiedtempo", obj.modifiedTempo);
|
|
31060
31099
|
o.set("millisecondoffset", obj.millisecondOffset);
|
|
31061
31100
|
return o;
|
|
31062
31101
|
}
|
|
@@ -31065,9 +31104,6 @@
|
|
|
31065
31104
|
case "baroccurence":
|
|
31066
31105
|
obj.barOccurence = v;
|
|
31067
31106
|
return true;
|
|
31068
|
-
case "modifiedtempo":
|
|
31069
|
-
obj.modifiedTempo = v;
|
|
31070
|
-
return true;
|
|
31071
31107
|
case "millisecondoffset":
|
|
31072
31108
|
obj.millisecondOffset = v;
|
|
31073
31109
|
return true;
|
|
@@ -35830,17 +35866,6 @@
|
|
|
35830
35866
|
}
|
|
35831
35867
|
}
|
|
35832
35868
|
|
|
35833
|
-
/**
|
|
35834
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35835
|
-
*/
|
|
35836
|
-
class BackingTrackSyncPoint {
|
|
35837
|
-
constructor(tick, data) {
|
|
35838
|
-
this.tick = 0;
|
|
35839
|
-
this.tick = tick;
|
|
35840
|
-
this.data = data;
|
|
35841
|
-
}
|
|
35842
|
-
}
|
|
35843
|
-
|
|
35844
35869
|
class MidiNoteDuration {
|
|
35845
35870
|
constructor() {
|
|
35846
35871
|
this.noteOnly = 0;
|
|
@@ -35861,6 +35886,14 @@
|
|
|
35861
35886
|
this.brushInfos = [];
|
|
35862
35887
|
}
|
|
35863
35888
|
}
|
|
35889
|
+
class PlayThroughContext {
|
|
35890
|
+
constructor() {
|
|
35891
|
+
this.synthTick = 0;
|
|
35892
|
+
this.synthTime = 0;
|
|
35893
|
+
this.currentTempo = 0;
|
|
35894
|
+
this.automationToSyncPoint = new Map();
|
|
35895
|
+
}
|
|
35896
|
+
}
|
|
35864
35897
|
/**
|
|
35865
35898
|
* This generator creates a midi file using a score.
|
|
35866
35899
|
*/
|
|
@@ -35987,9 +36020,22 @@
|
|
|
35987
36020
|
});
|
|
35988
36021
|
return syncPoints;
|
|
35989
36022
|
}
|
|
36023
|
+
/**
|
|
36024
|
+
* @internal
|
|
36025
|
+
*/
|
|
36026
|
+
static buildModifiedTempoLookup(score) {
|
|
36027
|
+
const syncPoints = [];
|
|
36028
|
+
const context = MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
36029
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
36030
|
+
}, _endTick => {
|
|
36031
|
+
});
|
|
36032
|
+
return context.automationToSyncPoint;
|
|
36033
|
+
}
|
|
35990
36034
|
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35991
36035
|
const controller = new MidiPlaybackController(score);
|
|
35992
|
-
|
|
36036
|
+
const playContext = new PlayThroughContext();
|
|
36037
|
+
playContext.currentTempo = score.tempo;
|
|
36038
|
+
playContext.syncPoints = syncPoints;
|
|
35993
36039
|
let previousMasterBar = null;
|
|
35994
36040
|
// store the previous played bar for repeats
|
|
35995
36041
|
const barOccurence = new Map();
|
|
@@ -36002,23 +36048,11 @@
|
|
|
36002
36048
|
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
36003
36049
|
occurence++;
|
|
36004
36050
|
barOccurence.set(index, occurence);
|
|
36005
|
-
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
36006
|
-
const
|
|
36007
|
-
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
36011
|
-
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
36012
|
-
}
|
|
36013
|
-
}
|
|
36014
|
-
}
|
|
36015
|
-
if (bar.tempoAutomations.length > 0) {
|
|
36016
|
-
currentTempo = bar.tempoAutomations[0].value;
|
|
36017
|
-
}
|
|
36018
|
-
generateTracks(index, currentTick, currentTempo);
|
|
36019
|
-
if (bar.tempoAutomations.length > 0) {
|
|
36020
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
36021
|
-
}
|
|
36051
|
+
generateMasterBar(bar, previousMasterBar, currentTick, playContext.currentTempo, occurence);
|
|
36052
|
+
const trackTempo = bar.tempoAutomations.length > 0 ? bar.tempoAutomations[0].value : playContext.currentTempo;
|
|
36053
|
+
generateTracks(index, currentTick, trackTempo);
|
|
36054
|
+
playContext.synthTick = currentTick;
|
|
36055
|
+
MidiFileGenerator.processBarTime(bar, occurence, playContext);
|
|
36022
36056
|
}
|
|
36023
36057
|
controller.moveNext();
|
|
36024
36058
|
previousMasterBar = bar;
|
|
@@ -36029,21 +36063,119 @@
|
|
|
36029
36063
|
// but where it ends according to the BPM and the remaining ticks.
|
|
36030
36064
|
if (syncPoints.length > 0) {
|
|
36031
36065
|
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
36032
|
-
const remainingTicks = controller.currentTick - lastSyncPoint.
|
|
36066
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.synthTick;
|
|
36033
36067
|
if (remainingTicks > 0) {
|
|
36034
|
-
const
|
|
36035
|
-
|
|
36036
|
-
|
|
36037
|
-
|
|
36038
|
-
|
|
36039
|
-
//
|
|
36040
|
-
|
|
36041
|
-
|
|
36042
|
-
|
|
36043
|
-
|
|
36068
|
+
const backingTrackSyncPoint = new BackingTrackSyncPoint();
|
|
36069
|
+
backingTrackSyncPoint.masterBarIndex = previousMasterBar.index;
|
|
36070
|
+
backingTrackSyncPoint.masterBarOccurence = barOccurence.get(previousMasterBar.index) - 1;
|
|
36071
|
+
backingTrackSyncPoint.synthTick = controller.currentTick;
|
|
36072
|
+
backingTrackSyncPoint.synthBpm = playContext.currentTempo;
|
|
36073
|
+
// we need to assume some BPM for the last interpolated point.
|
|
36074
|
+
// if we have more than just a start point, we keep the BPM before the last manual sync point
|
|
36075
|
+
// otherwise we have no customized sync BPM known and keep the synthesizer one.
|
|
36076
|
+
backingTrackSyncPoint.syncBpm =
|
|
36077
|
+
syncPoints.length > 1 ? syncPoints[syncPoints.length - 2].syncBpm : lastSyncPoint.synthBpm;
|
|
36078
|
+
backingTrackSyncPoint.synthTime =
|
|
36079
|
+
lastSyncPoint.synthTime + MidiUtils.ticksToMillis(remainingTicks, lastSyncPoint.synthBpm);
|
|
36080
|
+
backingTrackSyncPoint.syncTime =
|
|
36081
|
+
lastSyncPoint.syncTime + MidiUtils.ticksToMillis(remainingTicks, backingTrackSyncPoint.syncBpm);
|
|
36082
|
+
// update the previous sync point according to the new time
|
|
36083
|
+
lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
|
|
36084
|
+
syncPoints.push(backingTrackSyncPoint);
|
|
36044
36085
|
}
|
|
36045
36086
|
}
|
|
36046
36087
|
finish(controller.currentTick);
|
|
36088
|
+
return playContext;
|
|
36089
|
+
}
|
|
36090
|
+
static processBarTime(bar, occurence, context) {
|
|
36091
|
+
const duration = bar.calculateDuration();
|
|
36092
|
+
const barSyncPoints = bar.syncPoints;
|
|
36093
|
+
const barStartTick = context.synthTick;
|
|
36094
|
+
if (barSyncPoints) {
|
|
36095
|
+
MidiFileGenerator.processBarTimeWithSyncPoints(bar, occurence, context);
|
|
36096
|
+
}
|
|
36097
|
+
else {
|
|
36098
|
+
MidiFileGenerator.processBarTimeNoSyncPoints(bar, context);
|
|
36099
|
+
}
|
|
36100
|
+
// don't forget the part after the last tempo change
|
|
36101
|
+
const endTick = barStartTick + duration;
|
|
36102
|
+
const tickOffset = endTick - context.synthTick;
|
|
36103
|
+
if (tickOffset > 0) {
|
|
36104
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36105
|
+
context.synthTick = endTick;
|
|
36106
|
+
}
|
|
36107
|
+
}
|
|
36108
|
+
static processBarTimeWithSyncPoints(bar, occurence, context) {
|
|
36109
|
+
const barStartTick = context.synthTick;
|
|
36110
|
+
const duration = bar.calculateDuration();
|
|
36111
|
+
let tempoChangeIndex = 0;
|
|
36112
|
+
let tickOffset;
|
|
36113
|
+
for (const syncPoint of bar.syncPoints) {
|
|
36114
|
+
if (syncPoint.syncPointValue.barOccurence !== occurence) {
|
|
36115
|
+
continue;
|
|
36116
|
+
}
|
|
36117
|
+
const syncPointTick = barStartTick + syncPoint.ratioPosition * duration;
|
|
36118
|
+
// first process all tempo changes until this sync point
|
|
36119
|
+
while (tempoChangeIndex < bar.tempoAutomations.length &&
|
|
36120
|
+
bar.tempoAutomations[tempoChangeIndex].ratioPosition <= syncPoint.ratioPosition) {
|
|
36121
|
+
const tempoChange = bar.tempoAutomations[tempoChangeIndex];
|
|
36122
|
+
const absoluteTick = barStartTick + tempoChange.ratioPosition * duration;
|
|
36123
|
+
tickOffset = absoluteTick - context.synthTick;
|
|
36124
|
+
if (tickOffset > 0) {
|
|
36125
|
+
context.synthTick = absoluteTick;
|
|
36126
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36127
|
+
}
|
|
36128
|
+
context.currentTempo = tempoChange.value;
|
|
36129
|
+
tempoChangeIndex++;
|
|
36130
|
+
}
|
|
36131
|
+
// process time until sync point
|
|
36132
|
+
tickOffset = syncPointTick - context.synthTick;
|
|
36133
|
+
if (tickOffset > 0) {
|
|
36134
|
+
context.synthTick = syncPointTick;
|
|
36135
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36136
|
+
}
|
|
36137
|
+
// update the previous sync point according to the new time
|
|
36138
|
+
if (context.syncPoints.length > 0) {
|
|
36139
|
+
context.syncPoints[context.syncPoints.length - 1].updateSyncBpm(context.synthTime, syncPoint.syncPointValue.millisecondOffset);
|
|
36140
|
+
}
|
|
36141
|
+
// create the new sync point
|
|
36142
|
+
const backingTrackSyncPoint = new BackingTrackSyncPoint();
|
|
36143
|
+
backingTrackSyncPoint.masterBarIndex = bar.index;
|
|
36144
|
+
backingTrackSyncPoint.masterBarOccurence = occurence;
|
|
36145
|
+
backingTrackSyncPoint.synthTick = syncPointTick;
|
|
36146
|
+
backingTrackSyncPoint.synthBpm = context.currentTempo;
|
|
36147
|
+
backingTrackSyncPoint.synthTime = context.synthTime;
|
|
36148
|
+
backingTrackSyncPoint.syncTime = syncPoint.syncPointValue.millisecondOffset;
|
|
36149
|
+
backingTrackSyncPoint.syncBpm = 0 /* calculated by next sync point */;
|
|
36150
|
+
context.syncPoints.push(backingTrackSyncPoint);
|
|
36151
|
+
context.automationToSyncPoint.set(syncPoint, backingTrackSyncPoint);
|
|
36152
|
+
}
|
|
36153
|
+
// process remaining tempo changes after all sync points
|
|
36154
|
+
while (tempoChangeIndex < bar.tempoAutomations.length) {
|
|
36155
|
+
const tempoChange = bar.tempoAutomations[tempoChangeIndex];
|
|
36156
|
+
const absoluteTick = barStartTick + tempoChange.ratioPosition * duration;
|
|
36157
|
+
tickOffset = absoluteTick - context.synthTick;
|
|
36158
|
+
if (tickOffset > 0) {
|
|
36159
|
+
context.synthTick = absoluteTick;
|
|
36160
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36161
|
+
}
|
|
36162
|
+
context.currentTempo = tempoChange.value;
|
|
36163
|
+
tempoChangeIndex++;
|
|
36164
|
+
}
|
|
36165
|
+
}
|
|
36166
|
+
static processBarTimeNoSyncPoints(bar, context) {
|
|
36167
|
+
// walk through the tempo changes
|
|
36168
|
+
const barStartTick = context.synthTick;
|
|
36169
|
+
const duration = bar.calculateDuration();
|
|
36170
|
+
for (const changes of bar.tempoAutomations) {
|
|
36171
|
+
const absoluteTick = barStartTick + changes.ratioPosition * duration;
|
|
36172
|
+
const tickOffset = absoluteTick - context.synthTick;
|
|
36173
|
+
if (tickOffset > 0) {
|
|
36174
|
+
context.synthTick = absoluteTick;
|
|
36175
|
+
context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
|
|
36176
|
+
}
|
|
36177
|
+
context.currentTempo = changes.value;
|
|
36178
|
+
}
|
|
36047
36179
|
}
|
|
36048
36180
|
static toChannelShort(data) {
|
|
36049
36181
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
@@ -61006,9 +61138,9 @@
|
|
|
61006
61138
|
print(`build date: ${VersionInfo.date}`);
|
|
61007
61139
|
}
|
|
61008
61140
|
}
|
|
61009
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
61010
|
-
VersionInfo.date = '2025-05-
|
|
61011
|
-
VersionInfo.commit = '
|
|
61141
|
+
VersionInfo.version = '1.6.0-alpha.1430';
|
|
61142
|
+
VersionInfo.date = '2025-05-29T22:21:21.689Z';
|
|
61143
|
+
VersionInfo.commit = '98a4c2bec8d71f2645008118d3b77fb40973e7fe';
|
|
61012
61144
|
|
|
61013
61145
|
/**
|
|
61014
61146
|
* A factory for custom layout engines.
|
|
@@ -62658,6 +62790,7 @@
|
|
|
62658
62790
|
: undefined;
|
|
62659
62791
|
const millisecondPadding = initialSyncPoint ? initialSyncPoint.syncPointValue.millisecondOffset : 0;
|
|
62660
62792
|
this.backingTrackFramePadding = (-1 * ((millisecondPadding / 1000) * GpifWriter.SampleRate)) | 0;
|
|
62793
|
+
const modifiedTempoLookup = new Lazy(() => MidiFileGenerator.buildModifiedTempoLookup(score));
|
|
62661
62794
|
for (const mb of score.masterBars) {
|
|
62662
62795
|
for (const automation of mb.tempoAutomations) {
|
|
62663
62796
|
const tempoAutomation = automations.addElement('Automation');
|
|
@@ -62682,7 +62815,7 @@
|
|
|
62682
62815
|
const value = syncPointAutomation.addElement('Value');
|
|
62683
62816
|
value.addElement('BarIndex').innerText = mb.index.toString();
|
|
62684
62817
|
value.addElement('BarOccurrence').innerText = syncPoint.syncPointValue.barOccurence.toString();
|
|
62685
|
-
value.addElement('ModifiedTempo').innerText = syncPoint.
|
|
62818
|
+
value.addElement('ModifiedTempo').innerText = modifiedTempoLookup.value.get(syncPoint).syncBpm.toString();
|
|
62686
62819
|
value.addElement('OriginalTempo').innerText = score.tempo.toString();
|
|
62687
62820
|
const frameOffset = (((syncPoint.syncPointValue.millisecondOffset - millisecondPadding) / 1000) *
|
|
62688
62821
|
GpifWriter.SampleRate) |
|