@coderline/alphatab 1.6.0-alpha.1418 → 1.6.0-alpha.1421
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 +223 -145
- package/dist/alphaTab.d.ts +43 -11
- package/dist/alphaTab.js +223 -145
- 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.1421 (develop, build 1421)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -14318,13 +14318,6 @@
|
|
|
14318
14318
|
* @json_strict
|
|
14319
14319
|
*/
|
|
14320
14320
|
class BackingTrack {
|
|
14321
|
-
constructor() {
|
|
14322
|
-
/**
|
|
14323
|
-
* The number of milliseconds the audio should be shifted to align with the song.
|
|
14324
|
-
* (e.g. negative values allow skipping potential silent parts at the start of the file and directly start with the first note).
|
|
14325
|
-
*/
|
|
14326
|
-
this.padding = 0;
|
|
14327
|
-
}
|
|
14328
14321
|
}
|
|
14329
14322
|
|
|
14330
14323
|
/**
|
|
@@ -14357,6 +14350,7 @@
|
|
|
14357
14350
|
constructor() {
|
|
14358
14351
|
this._hasAnacrusis = false;
|
|
14359
14352
|
this._skipApplyLyrics = false;
|
|
14353
|
+
this._backingTrackPadding = 0;
|
|
14360
14354
|
this._doubleBars = new Set();
|
|
14361
14355
|
this._keySignatures = new Map();
|
|
14362
14356
|
}
|
|
@@ -14589,7 +14583,7 @@
|
|
|
14589
14583
|
assetId = c.innerText;
|
|
14590
14584
|
break;
|
|
14591
14585
|
case 'FramePadding':
|
|
14592
|
-
|
|
14586
|
+
this._backingTrackPadding = GpifParser.parseIntSafe(c.innerText, 0) / GpifParser.SampleRate * 1000;
|
|
14593
14587
|
break;
|
|
14594
14588
|
}
|
|
14595
14589
|
}
|
|
@@ -16803,6 +16797,7 @@
|
|
|
16803
16797
|
masterBar.tempoAutomations.push(automation);
|
|
16804
16798
|
break;
|
|
16805
16799
|
case AutomationType.SyncPoint:
|
|
16800
|
+
automation.syncPointValue.millisecondOffset -= this._backingTrackPadding;
|
|
16806
16801
|
masterBar.addSyncPoint(automation);
|
|
16807
16802
|
break;
|
|
16808
16803
|
}
|
|
@@ -22291,17 +22286,6 @@
|
|
|
22291
22286
|
}
|
|
22292
22287
|
}
|
|
22293
22288
|
|
|
22294
|
-
/**
|
|
22295
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22296
|
-
*/
|
|
22297
|
-
class BackingTrackSyncPoint {
|
|
22298
|
-
constructor(tick, data) {
|
|
22299
|
-
this.tick = 0;
|
|
22300
|
-
this.tick = tick;
|
|
22301
|
-
this.data = data;
|
|
22302
|
-
}
|
|
22303
|
-
}
|
|
22304
|
-
|
|
22305
22289
|
class MidiFileSequencerTempoChange {
|
|
22306
22290
|
constructor(bpm, ticks, time) {
|
|
22307
22291
|
this.bpm = bpm;
|
|
@@ -22309,10 +22293,12 @@
|
|
|
22309
22293
|
this.time = time;
|
|
22310
22294
|
}
|
|
22311
22295
|
}
|
|
22312
|
-
class BackingTrackSyncPointWithTime
|
|
22313
|
-
constructor(tick,
|
|
22314
|
-
|
|
22315
|
-
this.
|
|
22296
|
+
class BackingTrackSyncPointWithTime {
|
|
22297
|
+
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22298
|
+
this.alphaTabTick = tick;
|
|
22299
|
+
this.alphaTabTime = time;
|
|
22300
|
+
this.modifiedTempo = modifiedTempo;
|
|
22301
|
+
this.millisecondOffset = millisecondOffset;
|
|
22316
22302
|
}
|
|
22317
22303
|
}
|
|
22318
22304
|
class MidiSequencerState {
|
|
@@ -22418,7 +22404,7 @@
|
|
|
22418
22404
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22419
22405
|
this._mainState.modifiedTempo =
|
|
22420
22406
|
this._mainState.syncPoints.length > 0
|
|
22421
|
-
? this._mainState.syncPoints[0].
|
|
22407
|
+
? this._mainState.syncPoints[0].modifiedTempo
|
|
22422
22408
|
: this._mainState.currentTempo;
|
|
22423
22409
|
if (this.isPlayingMain) {
|
|
22424
22410
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22590,25 +22576,52 @@
|
|
|
22590
22576
|
mainUpdateSyncPoints(syncPoints) {
|
|
22591
22577
|
const state = this._mainState;
|
|
22592
22578
|
syncPoints.sort((a, b) => a.tick - b.tick); // just in case
|
|
22593
|
-
state.syncPoints =
|
|
22579
|
+
state.syncPoints = [];
|
|
22594
22580
|
if (syncPoints.length >= 0) {
|
|
22595
22581
|
let bpm = 120;
|
|
22596
22582
|
let absTick = 0;
|
|
22597
22583
|
let absTime = 0.0;
|
|
22598
|
-
let previousTick = 0;
|
|
22599
22584
|
let tempoChangeIndex = 0;
|
|
22600
22585
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22601
22586
|
const p = syncPoints[i];
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
previousTick
|
|
22587
|
+
let deltaTick = 0;
|
|
22588
|
+
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22589
|
+
let previousModifiedTempo;
|
|
22590
|
+
let previousMillisecondOffset;
|
|
22591
|
+
let previousTick;
|
|
22592
|
+
if (i === 0) {
|
|
22593
|
+
previousModifiedTempo = bpm;
|
|
22594
|
+
previousMillisecondOffset = 0;
|
|
22595
|
+
previousTick = 0;
|
|
22596
|
+
}
|
|
22597
|
+
else {
|
|
22598
|
+
const previousSyncPoint = syncPoints[i - 1];
|
|
22599
|
+
previousModifiedTempo = previousSyncPoint.data.modifiedTempo;
|
|
22600
|
+
previousMillisecondOffset = previousSyncPoint.data.millisecondOffset;
|
|
22601
|
+
previousTick = previousSyncPoint.tick;
|
|
22602
|
+
}
|
|
22603
|
+
// process time until sync point
|
|
22604
|
+
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
22605
|
+
// we have to calculate a interpolated sync point on the alphaTab time axis.
|
|
22606
|
+
// otherwise the linear interpolation later in the lookup will fail.
|
|
22607
|
+
// goal is to have always a linear increase between two points, no matter if the time axis is sliced by tempo changes or sync points
|
|
22607
22608
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22608
|
-
state.tempoChanges[tempoChangeIndex].ticks <=
|
|
22609
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.tick) {
|
|
22610
|
+
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22611
|
+
if (deltaTick > 0) {
|
|
22612
|
+
absTick += deltaTick;
|
|
22613
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22614
|
+
const millisPerTick = (p.data.millisecondOffset - previousMillisecondOffset) / (p.tick - previousTick);
|
|
22615
|
+
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22616
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(absTick, absTime, previousModifiedTempo, interpolatedMillisecondOffset));
|
|
22617
|
+
}
|
|
22609
22618
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22610
22619
|
tempoChangeIndex++;
|
|
22611
22620
|
}
|
|
22621
|
+
deltaTick = p.tick - absTick;
|
|
22622
|
+
absTick += deltaTick;
|
|
22623
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22624
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(p.tick, absTime, p.data.modifiedTempo, p.data.millisecondOffset));
|
|
22612
22625
|
}
|
|
22613
22626
|
}
|
|
22614
22627
|
state.syncPointIndex = 0;
|
|
@@ -22622,7 +22635,7 @@
|
|
|
22622
22635
|
this.updateCurrentTempo(state, timePosition);
|
|
22623
22636
|
const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
|
|
22624
22637
|
const timeDiff = timePosition - lastTempoChange.time;
|
|
22625
|
-
const ticks = (
|
|
22638
|
+
const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
|
|
22626
22639
|
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22627
22640
|
return lastTempoChange.ticks + ticks + 1;
|
|
22628
22641
|
}
|
|
@@ -22639,19 +22652,21 @@
|
|
|
22639
22652
|
state.tempoChangeIndex = tempoChangeIndex;
|
|
22640
22653
|
state.currentTempo = state.tempoChanges[state.tempoChangeIndex].bpm;
|
|
22641
22654
|
}
|
|
22655
|
+
}
|
|
22656
|
+
updateSyncPoints(state, timePosition) {
|
|
22642
22657
|
const syncPoints = state.syncPoints;
|
|
22643
22658
|
if (syncPoints.length > 0) {
|
|
22644
22659
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22645
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22660
|
+
if (timePosition < syncPoints[syncPointIndex].millisecondOffset) {
|
|
22646
22661
|
syncPointIndex = 0;
|
|
22647
22662
|
}
|
|
22648
22663
|
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22649
|
-
syncPoints[syncPointIndex + 1].
|
|
22664
|
+
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22650
22665
|
syncPointIndex++;
|
|
22651
22666
|
}
|
|
22652
22667
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22653
22668
|
state.syncPointIndex = syncPointIndex;
|
|
22654
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22669
|
+
state.modifiedTempo = syncPoints[syncPointIndex].modifiedTempo;
|
|
22655
22670
|
}
|
|
22656
22671
|
}
|
|
22657
22672
|
else {
|
|
@@ -22664,21 +22679,21 @@
|
|
|
22664
22679
|
if (timePosition < 0 || syncPoints.length === 0) {
|
|
22665
22680
|
return timePosition;
|
|
22666
22681
|
}
|
|
22667
|
-
this.
|
|
22682
|
+
this.updateSyncPoints(this._mainState, timePosition);
|
|
22668
22683
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22669
22684
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22670
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22685
|
+
const timeDiff = timePosition - currentSyncPoint.millisecondOffset;
|
|
22671
22686
|
let alphaTabTimeDiff;
|
|
22672
22687
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22673
22688
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22674
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22675
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22689
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset);
|
|
22690
|
+
alphaTabTimeDiff = (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22676
22691
|
}
|
|
22677
22692
|
else {
|
|
22678
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22679
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22693
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.millisecondOffset);
|
|
22694
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22680
22695
|
}
|
|
22681
|
-
return (currentSyncPoint.
|
|
22696
|
+
return (currentSyncPoint.alphaTabTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22682
22697
|
}
|
|
22683
22698
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22684
22699
|
const mainState = this._mainState;
|
|
@@ -22688,25 +22703,27 @@
|
|
|
22688
22703
|
}
|
|
22689
22704
|
timePosition *= this.playbackSpeed;
|
|
22690
22705
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22691
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22706
|
+
if (timePosition < syncPoints[syncPointIndex].alphaTabTime) {
|
|
22692
22707
|
syncPointIndex = 0;
|
|
22693
22708
|
}
|
|
22694
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22709
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].alphaTabTime <= timePosition) {
|
|
22695
22710
|
syncPointIndex++;
|
|
22696
22711
|
}
|
|
22712
|
+
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22713
|
+
// we ensure that we have a linear increase between two points
|
|
22697
22714
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22698
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22715
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.alphaTabTime;
|
|
22699
22716
|
let backingTrackPos;
|
|
22700
22717
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22701
22718
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22702
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22703
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22704
|
-
backingTrackPos = currentSyncPoint.
|
|
22719
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime);
|
|
22720
|
+
const backingTrackDiff = nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset;
|
|
22721
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22705
22722
|
}
|
|
22706
22723
|
else {
|
|
22707
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22708
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22709
|
-
backingTrackPos = currentSyncPoint.
|
|
22724
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.alphaTabTime);
|
|
22725
|
+
const frameDiff = backingTrackLength - currentSyncPoint.millisecondOffset;
|
|
22726
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + frameDiff * relativeAlphaTabTimeDiff;
|
|
22710
22727
|
}
|
|
22711
22728
|
return backingTrackPos;
|
|
22712
22729
|
}
|
|
@@ -28588,7 +28605,9 @@
|
|
|
28588
28605
|
hasSamplesForPercussion(key) {
|
|
28589
28606
|
return this.synthesizer.hasSamplesForPercussion(key);
|
|
28590
28607
|
}
|
|
28591
|
-
loadBackingTrack(_score
|
|
28608
|
+
loadBackingTrack(_score) {
|
|
28609
|
+
}
|
|
28610
|
+
updateSyncPoints(_syncPoints) {
|
|
28592
28611
|
}
|
|
28593
28612
|
}
|
|
28594
28613
|
/**
|
|
@@ -32485,15 +32504,9 @@
|
|
|
32485
32504
|
return null;
|
|
32486
32505
|
}
|
|
32487
32506
|
const o = new Map();
|
|
32488
|
-
o.set("padding", obj.padding);
|
|
32489
32507
|
return o;
|
|
32490
32508
|
}
|
|
32491
32509
|
static setProperty(obj, property, v) {
|
|
32492
|
-
switch (property) {
|
|
32493
|
-
case "padding":
|
|
32494
|
-
obj.padding = v;
|
|
32495
|
-
return true;
|
|
32496
|
-
}
|
|
32497
32510
|
return false;
|
|
32498
32511
|
}
|
|
32499
32512
|
}
|
|
@@ -34956,7 +34969,13 @@
|
|
|
34956
34969
|
* Gets the list of tempo changes within the tick lookup.
|
|
34957
34970
|
*/
|
|
34958
34971
|
this.tempoChanges = [];
|
|
34972
|
+
/**
|
|
34973
|
+
* The first beat in the bar.
|
|
34974
|
+
*/
|
|
34959
34975
|
this.firstBeat = null;
|
|
34976
|
+
/**
|
|
34977
|
+
* The last beat in the bar.
|
|
34978
|
+
*/
|
|
34960
34979
|
this.lastBeat = null;
|
|
34961
34980
|
/**
|
|
34962
34981
|
* Gets or sets the {@link MasterBarTickLookup} of the next masterbar in the {@link Score}
|
|
@@ -35370,14 +35389,13 @@
|
|
|
35370
35389
|
constructor() {
|
|
35371
35390
|
this._currentMasterBar = null;
|
|
35372
35391
|
/**
|
|
35373
|
-
*
|
|
35392
|
+
* A dictionary of all master bars played. The index is the index equals to {@link MasterBar.index}.
|
|
35374
35393
|
* This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to {@link MasterBars}.
|
|
35375
35394
|
* @internal
|
|
35376
35395
|
*/
|
|
35377
35396
|
this.masterBarLookup = new Map();
|
|
35378
35397
|
/**
|
|
35379
|
-
*
|
|
35380
|
-
* @internal
|
|
35398
|
+
* A list of all {@link MasterBarTickLookup} sorted by time.
|
|
35381
35399
|
*/
|
|
35382
35400
|
this.masterBars = [];
|
|
35383
35401
|
/**
|
|
@@ -35751,6 +35769,17 @@
|
|
|
35751
35769
|
}
|
|
35752
35770
|
}
|
|
35753
35771
|
|
|
35772
|
+
/**
|
|
35773
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35774
|
+
*/
|
|
35775
|
+
class BackingTrackSyncPoint {
|
|
35776
|
+
constructor(tick, data) {
|
|
35777
|
+
this.tick = 0;
|
|
35778
|
+
this.tick = tick;
|
|
35779
|
+
this.data = data;
|
|
35780
|
+
}
|
|
35781
|
+
}
|
|
35782
|
+
|
|
35754
35783
|
class MidiNoteDuration {
|
|
35755
35784
|
constructor() {
|
|
35756
35785
|
this.noteOnly = 0;
|
|
@@ -35820,62 +35849,22 @@
|
|
|
35820
35849
|
this.generateTrack(track);
|
|
35821
35850
|
}
|
|
35822
35851
|
Logger.debug('Midi', 'Begin midi generation');
|
|
35823
|
-
|
|
35824
|
-
|
|
35825
|
-
|
|
35826
|
-
|
|
35827
|
-
|
|
35828
|
-
|
|
35829
|
-
|
|
35830
|
-
|
|
35831
|
-
const currentTick = controller.currentTick;
|
|
35832
|
-
controller.processCurrent();
|
|
35833
|
-
if (controller.shouldPlay) {
|
|
35834
|
-
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35835
|
-
occurence++;
|
|
35836
|
-
barOccurence.set(index, occurence);
|
|
35837
|
-
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35838
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35839
|
-
currentTempo = bar.tempoAutomations[0].value;
|
|
35840
|
-
}
|
|
35841
|
-
for (const track of this._score.tracks) {
|
|
35842
|
-
for (const staff of track.staves) {
|
|
35843
|
-
if (index < staff.bars.length) {
|
|
35844
|
-
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35845
|
-
}
|
|
35852
|
+
this.syncPoints = [];
|
|
35853
|
+
MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
|
|
35854
|
+
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35855
|
+
}, (index, currentTick, currentTempo) => {
|
|
35856
|
+
for (const track of this._score.tracks) {
|
|
35857
|
+
for (const staff of track.staves) {
|
|
35858
|
+
if (index < staff.bars.length) {
|
|
35859
|
+
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35846
35860
|
}
|
|
35847
35861
|
}
|
|
35848
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35849
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35850
|
-
}
|
|
35851
35862
|
}
|
|
35852
|
-
|
|
35853
|
-
|
|
35854
|
-
|
|
35855
|
-
// here we interpolate the sync point which marks the end of the sync.
|
|
35856
|
-
// Sync points define new tempos at certain positions.
|
|
35857
|
-
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35858
|
-
// but where it ends according to the BPM and the remaining ticks.
|
|
35859
|
-
if (this.syncPoints.length > 0) {
|
|
35860
|
-
const lastSyncPoint = this.syncPoints[this.syncPoints.length - 1];
|
|
35861
|
-
const endTick = controller.currentTick;
|
|
35862
|
-
const remainingTicks = endTick - lastSyncPoint.tick;
|
|
35863
|
-
if (remainingTicks > 0) {
|
|
35864
|
-
const syncPointData = new SyncPointData();
|
|
35865
|
-
// last occurence of the last bar
|
|
35866
|
-
syncPointData.barOccurence = barOccurence.get(this._score.masterBars.length - 1);
|
|
35867
|
-
// same tempo as last point
|
|
35868
|
-
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35869
|
-
// interpolated end from last syncPoint
|
|
35870
|
-
syncPointData.millisecondOffset =
|
|
35871
|
-
lastSyncPoint.data.millisecondOffset +
|
|
35872
|
-
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35873
|
-
this.syncPoints.push(new BackingTrackSyncPoint(endTick, syncPointData));
|
|
35863
|
+
}, endTick => {
|
|
35864
|
+
for (const track of this._score.tracks) {
|
|
35865
|
+
this._handler.finishTrack(track.index, endTick);
|
|
35874
35866
|
}
|
|
35875
|
-
}
|
|
35876
|
-
for (const track of this._score.tracks) {
|
|
35877
|
-
this._handler.finishTrack(track.index, controller.currentTick);
|
|
35878
|
-
}
|
|
35867
|
+
});
|
|
35879
35868
|
Logger.debug('Midi', 'Midi generation done');
|
|
35880
35869
|
}
|
|
35881
35870
|
generateTrack(track) {
|
|
@@ -35920,6 +35909,81 @@
|
|
|
35920
35909
|
this._handler.addControlChange(track.index, 0, channel, ControllerType.DataEntryCoarse, MidiFileGenerator.PitchBendRangeInSemitones);
|
|
35921
35910
|
this.addProgramChange(track, 0, channel, playbackInfo.program);
|
|
35922
35911
|
}
|
|
35912
|
+
/**
|
|
35913
|
+
* Generates the sync points for the given score without re-generating the midi itself.
|
|
35914
|
+
* @remarks
|
|
35915
|
+
* Use this method if a re-generation of the sync points after modification is required.
|
|
35916
|
+
* It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
|
|
35917
|
+
* need to be considered for synchronization.
|
|
35918
|
+
* @param score The song for which to regenerate the sync points.
|
|
35919
|
+
* @returns The generated sync points for usage in the backing track playback.
|
|
35920
|
+
*/
|
|
35921
|
+
static generateSyncPoints(score) {
|
|
35922
|
+
const syncPoints = [];
|
|
35923
|
+
MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
35924
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
35925
|
+
}, _endTick => {
|
|
35926
|
+
});
|
|
35927
|
+
return syncPoints;
|
|
35928
|
+
}
|
|
35929
|
+
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35930
|
+
const controller = new MidiPlaybackController(score);
|
|
35931
|
+
let currentTempo = score.tempo;
|
|
35932
|
+
let previousMasterBar = null;
|
|
35933
|
+
// store the previous played bar for repeats
|
|
35934
|
+
const barOccurence = new Map();
|
|
35935
|
+
while (!controller.finished) {
|
|
35936
|
+
const index = controller.index;
|
|
35937
|
+
const bar = score.masterBars[index];
|
|
35938
|
+
const currentTick = controller.currentTick;
|
|
35939
|
+
controller.processCurrent();
|
|
35940
|
+
if (controller.shouldPlay) {
|
|
35941
|
+
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35942
|
+
occurence++;
|
|
35943
|
+
barOccurence.set(index, occurence);
|
|
35944
|
+
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35945
|
+
const barSyncPoints = bar.syncPoints;
|
|
35946
|
+
if (barSyncPoints) {
|
|
35947
|
+
for (const syncPoint of barSyncPoints) {
|
|
35948
|
+
if (syncPoint.syncPointValue.barOccurence === occurence) {
|
|
35949
|
+
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
35950
|
+
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
35951
|
+
}
|
|
35952
|
+
}
|
|
35953
|
+
}
|
|
35954
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35955
|
+
currentTempo = bar.tempoAutomations[0].value;
|
|
35956
|
+
}
|
|
35957
|
+
generateTracks(index, currentTick, currentTempo);
|
|
35958
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35959
|
+
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35960
|
+
}
|
|
35961
|
+
}
|
|
35962
|
+
controller.moveNext();
|
|
35963
|
+
previousMasterBar = bar;
|
|
35964
|
+
}
|
|
35965
|
+
// here we interpolate the sync point which marks the end of the sync.
|
|
35966
|
+
// Sync points define new tempos at certain positions.
|
|
35967
|
+
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35968
|
+
// but where it ends according to the BPM and the remaining ticks.
|
|
35969
|
+
if (syncPoints.length > 0) {
|
|
35970
|
+
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
35971
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.tick;
|
|
35972
|
+
if (remainingTicks > 0) {
|
|
35973
|
+
const syncPointData = new SyncPointData();
|
|
35974
|
+
// last occurence of the last bar
|
|
35975
|
+
syncPointData.barOccurence = barOccurence.get(score.masterBars.length - 1);
|
|
35976
|
+
// same tempo as last point
|
|
35977
|
+
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35978
|
+
// interpolated end from last syncPoint
|
|
35979
|
+
syncPointData.millisecondOffset =
|
|
35980
|
+
lastSyncPoint.data.millisecondOffset +
|
|
35981
|
+
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35982
|
+
syncPoints.push(new BackingTrackSyncPoint(controller.currentTick, syncPointData));
|
|
35983
|
+
}
|
|
35984
|
+
}
|
|
35985
|
+
finish(controller.currentTick);
|
|
35986
|
+
}
|
|
35923
35987
|
static toChannelShort(data) {
|
|
35924
35988
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
35925
35989
|
return Math.max(value, -1) + 1;
|
|
@@ -35951,15 +36015,6 @@
|
|
|
35951
36015
|
else {
|
|
35952
36016
|
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, currentTempo));
|
|
35953
36017
|
}
|
|
35954
|
-
const syncPoints = masterBar.syncPoints;
|
|
35955
|
-
if (syncPoints) {
|
|
35956
|
-
for (const syncPoint of syncPoints) {
|
|
35957
|
-
if (syncPoint.syncPointValue.barOccurence === barOccurence) {
|
|
35958
|
-
const tick = currentTick + masterBarDuration * syncPoint.ratioPosition;
|
|
35959
|
-
this.syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
35960
|
-
}
|
|
35961
|
-
}
|
|
35962
|
-
}
|
|
35963
36018
|
masterBarLookup.masterBar = masterBar;
|
|
35964
36019
|
masterBarLookup.start = currentTick;
|
|
35965
36020
|
masterBarLookup.end = masterBarLookup.start + masterBarDuration;
|
|
@@ -38256,21 +38311,23 @@
|
|
|
38256
38311
|
this._backingTrackOutput.seekTo(this.sequencer.mainTimePositionToBackingTrack(timePosition, this._backingTrackOutput.backingTrackDuration));
|
|
38257
38312
|
}
|
|
38258
38313
|
}
|
|
38259
|
-
loadBackingTrack(score
|
|
38314
|
+
loadBackingTrack(score) {
|
|
38260
38315
|
const backingTrackInfo = score.backingTrack;
|
|
38261
38316
|
if (backingTrackInfo) {
|
|
38262
38317
|
this._backingTrackOutput.loadBackingTrack(backingTrackInfo);
|
|
38263
|
-
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38264
38318
|
this.timePosition = 0;
|
|
38265
38319
|
}
|
|
38266
38320
|
}
|
|
38321
|
+
updateSyncPoints(syncPoints) {
|
|
38322
|
+
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38323
|
+
this.tickPosition = this.tickPosition;
|
|
38324
|
+
}
|
|
38267
38325
|
}
|
|
38268
38326
|
|
|
38269
38327
|
class ExternalMediaSynthOutput {
|
|
38270
38328
|
constructor() {
|
|
38271
38329
|
// fake rate
|
|
38272
38330
|
this.sampleRate = 44100;
|
|
38273
|
-
this._padding = 0;
|
|
38274
38331
|
this._seekPosition = 0;
|
|
38275
38332
|
this.ready = new EventEmitter();
|
|
38276
38333
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -38313,20 +38370,19 @@
|
|
|
38313
38370
|
seekTo(time) {
|
|
38314
38371
|
const handler = this.handler;
|
|
38315
38372
|
if (handler) {
|
|
38316
|
-
handler.seekTo(time
|
|
38373
|
+
handler.seekTo(time);
|
|
38317
38374
|
}
|
|
38318
38375
|
else {
|
|
38319
|
-
this._seekPosition = time
|
|
38376
|
+
this._seekPosition = time;
|
|
38320
38377
|
}
|
|
38321
38378
|
}
|
|
38322
|
-
loadBackingTrack(
|
|
38323
|
-
this._padding = backingTrack.padding;
|
|
38379
|
+
loadBackingTrack(_backingTrack) {
|
|
38324
38380
|
}
|
|
38325
38381
|
open(_bufferTimeInMilliseconds) {
|
|
38326
38382
|
this.ready.trigger();
|
|
38327
38383
|
}
|
|
38328
38384
|
updatePosition(currentTime) {
|
|
38329
|
-
this.timeUpdate.trigger(currentTime
|
|
38385
|
+
this.timeUpdate.trigger(currentTime);
|
|
38330
38386
|
}
|
|
38331
38387
|
play() {
|
|
38332
38388
|
this.handler?.play();
|
|
@@ -38591,9 +38647,14 @@
|
|
|
38591
38647
|
this._instance.loadMidiFile(midi);
|
|
38592
38648
|
}
|
|
38593
38649
|
}
|
|
38594
|
-
loadBackingTrack(score
|
|
38650
|
+
loadBackingTrack(score) {
|
|
38595
38651
|
if (this._instance) {
|
|
38596
|
-
this._instance.loadBackingTrack(score
|
|
38652
|
+
this._instance.loadBackingTrack(score);
|
|
38653
|
+
}
|
|
38654
|
+
}
|
|
38655
|
+
updateSyncPoints(syncPoints) {
|
|
38656
|
+
if (this._instance) {
|
|
38657
|
+
this._instance.updateSyncPoints(syncPoints);
|
|
38597
38658
|
}
|
|
38598
38659
|
}
|
|
38599
38660
|
applyTranspositionPitches(transpositionPitches) {
|
|
@@ -39922,7 +39983,7 @@
|
|
|
39922
39983
|
}
|
|
39923
39984
|
appendRenderResult(result, isLast) {
|
|
39924
39985
|
// resizing the canvas and wrapper elements at the end is enough
|
|
39925
|
-
// it avoids flickering on resizes and re-renders.
|
|
39986
|
+
// it avoids flickering on resizes and re-renders.
|
|
39926
39987
|
// the individual partials are anyhow sized correctly
|
|
39927
39988
|
if (isLast) {
|
|
39928
39989
|
this.canvasElement.width = result.totalWidth;
|
|
@@ -40684,9 +40745,23 @@
|
|
|
40684
40745
|
this.onMidiLoad(midiFile);
|
|
40685
40746
|
const player = this._player;
|
|
40686
40747
|
player.loadMidiFile(midiFile);
|
|
40687
|
-
player.loadBackingTrack(score
|
|
40748
|
+
player.loadBackingTrack(score);
|
|
40749
|
+
player.updateSyncPoints(generator.syncPoints);
|
|
40688
40750
|
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40689
40751
|
}
|
|
40752
|
+
/**
|
|
40753
|
+
* Triggers an update of the sync points for the current score after modification within the data model
|
|
40754
|
+
* @category Methods - Player
|
|
40755
|
+
* @since 1.6.0
|
|
40756
|
+
*/
|
|
40757
|
+
updateSyncPoints() {
|
|
40758
|
+
if (!this.score) {
|
|
40759
|
+
return;
|
|
40760
|
+
}
|
|
40761
|
+
const score = this.score;
|
|
40762
|
+
const player = this._player;
|
|
40763
|
+
player.updateSyncPoints(MidiFileGenerator.generateSyncPoints(score));
|
|
40764
|
+
}
|
|
40690
40765
|
/**
|
|
40691
40766
|
* Changes the volume of the given tracks.
|
|
40692
40767
|
* @param tracks The tracks for which the volume should be changed.
|
|
@@ -41245,8 +41320,9 @@
|
|
|
41245
41320
|
// it can happen that the cursor reaches the target position slightly too early (especially on backing tracks)
|
|
41246
41321
|
// to avoid the cursor stopping, causing a wierd look, we animate the cursor to the double position in double time.
|
|
41247
41322
|
// beatCursor!.transitionToX((duration / cursorSpeed), nextBeatX);
|
|
41248
|
-
const
|
|
41249
|
-
|
|
41323
|
+
const factor = cursorMode === MidiTickLookupFindBeatResultCursorMode.ToNextBext ? 2 : 1;
|
|
41324
|
+
const doubleEndBeatX = startBeatX + (nextBeatX - startBeatX) * factor;
|
|
41325
|
+
beatCursor.transitionToX((duration / cursorSpeed) * factor, doubleEndBeatX);
|
|
41250
41326
|
});
|
|
41251
41327
|
}
|
|
41252
41328
|
else {
|
|
@@ -43354,6 +43430,8 @@
|
|
|
43354
43430
|
}
|
|
43355
43431
|
loadBackingTrack(_score) {
|
|
43356
43432
|
}
|
|
43433
|
+
updateSyncPoints(_syncPoints) {
|
|
43434
|
+
}
|
|
43357
43435
|
}
|
|
43358
43436
|
|
|
43359
43437
|
/**
|
|
@@ -43728,7 +43806,6 @@
|
|
|
43728
43806
|
constructor() {
|
|
43729
43807
|
// fake rate
|
|
43730
43808
|
this.sampleRate = 44100;
|
|
43731
|
-
this._padding = 0;
|
|
43732
43809
|
this._updateInterval = 0;
|
|
43733
43810
|
this.ready = new EventEmitter();
|
|
43734
43811
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -43752,13 +43829,12 @@
|
|
|
43752
43829
|
this.audioElement.volume = value;
|
|
43753
43830
|
}
|
|
43754
43831
|
seekTo(time) {
|
|
43755
|
-
this.audioElement.currentTime = time / 1000
|
|
43832
|
+
this.audioElement.currentTime = time / 1000;
|
|
43756
43833
|
}
|
|
43757
43834
|
loadBackingTrack(backingTrack) {
|
|
43758
43835
|
if (this.audioElement?.src) {
|
|
43759
43836
|
URL.revokeObjectURL(this.audioElement.src);
|
|
43760
43837
|
}
|
|
43761
|
-
this._padding = backingTrack.padding / 1000;
|
|
43762
43838
|
const blob = new Blob([backingTrack.rawAudioFile]);
|
|
43763
43839
|
// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
|
|
43764
43840
|
// Step 8. resets the playbackRate, we need to remember and restore it.
|
|
@@ -43777,7 +43853,7 @@
|
|
|
43777
43853
|
this.ready.trigger();
|
|
43778
43854
|
}
|
|
43779
43855
|
updatePosition() {
|
|
43780
|
-
const timePos =
|
|
43856
|
+
const timePos = this.audioElement.currentTime * 1000;
|
|
43781
43857
|
this.timeUpdate.trigger(timePos);
|
|
43782
43858
|
}
|
|
43783
43859
|
play() {
|
|
@@ -43996,6 +44072,7 @@
|
|
|
43996
44072
|
webFont.usages--;
|
|
43997
44073
|
if (webFont.usages <= 0) {
|
|
43998
44074
|
webFont.element.remove();
|
|
44075
|
+
BrowserUiFacade._registeredWebFonts.delete(webFont.hash);
|
|
43999
44076
|
}
|
|
44000
44077
|
}
|
|
44001
44078
|
createCanvasElement() {
|
|
@@ -44152,6 +44229,7 @@
|
|
|
44152
44229
|
checker.checkForFontAvailability();
|
|
44153
44230
|
settings.display.resources.smuflFont = new Font(familyName, Environment.MusicFontSize, FontStyle.Plain, FontWeight.Regular);
|
|
44154
44231
|
const webFont = {
|
|
44232
|
+
hash,
|
|
44155
44233
|
element: styleElement,
|
|
44156
44234
|
fontSuffix,
|
|
44157
44235
|
usages: 1,
|
|
@@ -60863,9 +60941,9 @@
|
|
|
60863
60941
|
print(`build date: ${VersionInfo.date}`);
|
|
60864
60942
|
}
|
|
60865
60943
|
}
|
|
60866
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
60867
|
-
VersionInfo.date = '2025-05-
|
|
60868
|
-
VersionInfo.commit = '
|
|
60944
|
+
VersionInfo.version = '1.6.0-alpha.1421';
|
|
60945
|
+
VersionInfo.date = '2025-05-22T19:50:36.984Z';
|
|
60946
|
+
VersionInfo.commit = '0b4cd7b2a2c0d244d65c0c0c5254fe4814976bc4';
|
|
60869
60947
|
|
|
60870
60948
|
/**
|
|
60871
60949
|
* A factory for custom layout engines.
|