@coderline/alphatab 1.6.0-alpha.1418 → 1.6.0-alpha.1420
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/alphaTab.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +217 -133
- package/dist/alphaTab.d.ts +42 -11
- package/dist/alphaTab.js +217 -133
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.vite.js +1 -1
- package/dist/alphaTab.vite.mjs +1 -1
- package/dist/alphaTab.webpack.js +1 -1
- package/dist/alphaTab.webpack.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +1 -1
package/dist/alphaTab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.6.0-alpha.
|
|
2
|
+
* alphaTab v1.6.0-alpha.1420 (develop, build 1420)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -14318,13 +14318,6 @@
|
|
|
14318
14318
|
* @json_strict
|
|
14319
14319
|
*/
|
|
14320
14320
|
class BackingTrack {
|
|
14321
|
-
constructor() {
|
|
14322
|
-
/**
|
|
14323
|
-
* The number of milliseconds the audio should be shifted to align with the song.
|
|
14324
|
-
* (e.g. negative values allow skipping potential silent parts at the start of the file and directly start with the first note).
|
|
14325
|
-
*/
|
|
14326
|
-
this.padding = 0;
|
|
14327
|
-
}
|
|
14328
14321
|
}
|
|
14329
14322
|
|
|
14330
14323
|
/**
|
|
@@ -14357,6 +14350,7 @@
|
|
|
14357
14350
|
constructor() {
|
|
14358
14351
|
this._hasAnacrusis = false;
|
|
14359
14352
|
this._skipApplyLyrics = false;
|
|
14353
|
+
this._backingTrackPadding = 0;
|
|
14360
14354
|
this._doubleBars = new Set();
|
|
14361
14355
|
this._keySignatures = new Map();
|
|
14362
14356
|
}
|
|
@@ -14589,7 +14583,7 @@
|
|
|
14589
14583
|
assetId = c.innerText;
|
|
14590
14584
|
break;
|
|
14591
14585
|
case 'FramePadding':
|
|
14592
|
-
|
|
14586
|
+
this._backingTrackPadding = GpifParser.parseIntSafe(c.innerText, 0) / GpifParser.SampleRate * 1000;
|
|
14593
14587
|
break;
|
|
14594
14588
|
}
|
|
14595
14589
|
}
|
|
@@ -16803,6 +16797,7 @@
|
|
|
16803
16797
|
masterBar.tempoAutomations.push(automation);
|
|
16804
16798
|
break;
|
|
16805
16799
|
case AutomationType.SyncPoint:
|
|
16800
|
+
automation.syncPointValue.millisecondOffset -= this._backingTrackPadding;
|
|
16806
16801
|
masterBar.addSyncPoint(automation);
|
|
16807
16802
|
break;
|
|
16808
16803
|
}
|
|
@@ -22291,17 +22286,6 @@
|
|
|
22291
22286
|
}
|
|
22292
22287
|
}
|
|
22293
22288
|
|
|
22294
|
-
/**
|
|
22295
|
-
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
22296
|
-
*/
|
|
22297
|
-
class BackingTrackSyncPoint {
|
|
22298
|
-
constructor(tick, data) {
|
|
22299
|
-
this.tick = 0;
|
|
22300
|
-
this.tick = tick;
|
|
22301
|
-
this.data = data;
|
|
22302
|
-
}
|
|
22303
|
-
}
|
|
22304
|
-
|
|
22305
22289
|
class MidiFileSequencerTempoChange {
|
|
22306
22290
|
constructor(bpm, ticks, time) {
|
|
22307
22291
|
this.bpm = bpm;
|
|
@@ -22309,10 +22293,12 @@
|
|
|
22309
22293
|
this.time = time;
|
|
22310
22294
|
}
|
|
22311
22295
|
}
|
|
22312
|
-
class BackingTrackSyncPointWithTime
|
|
22313
|
-
constructor(tick,
|
|
22314
|
-
|
|
22315
|
-
this.
|
|
22296
|
+
class BackingTrackSyncPointWithTime {
|
|
22297
|
+
constructor(tick, time, modifiedTempo, millisecondOffset) {
|
|
22298
|
+
this.alphaTabTick = tick;
|
|
22299
|
+
this.alphaTabTime = time;
|
|
22300
|
+
this.modifiedTempo = modifiedTempo;
|
|
22301
|
+
this.millisecondOffset = millisecondOffset;
|
|
22316
22302
|
}
|
|
22317
22303
|
}
|
|
22318
22304
|
class MidiSequencerState {
|
|
@@ -22418,7 +22404,7 @@
|
|
|
22418
22404
|
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22419
22405
|
this._mainState.modifiedTempo =
|
|
22420
22406
|
this._mainState.syncPoints.length > 0
|
|
22421
|
-
? this._mainState.syncPoints[0].
|
|
22407
|
+
? this._mainState.syncPoints[0].modifiedTempo
|
|
22422
22408
|
: this._mainState.currentTempo;
|
|
22423
22409
|
if (this.isPlayingMain) {
|
|
22424
22410
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
@@ -22590,25 +22576,52 @@
|
|
|
22590
22576
|
mainUpdateSyncPoints(syncPoints) {
|
|
22591
22577
|
const state = this._mainState;
|
|
22592
22578
|
syncPoints.sort((a, b) => a.tick - b.tick); // just in case
|
|
22593
|
-
state.syncPoints =
|
|
22579
|
+
state.syncPoints = [];
|
|
22594
22580
|
if (syncPoints.length >= 0) {
|
|
22595
22581
|
let bpm = 120;
|
|
22596
22582
|
let absTick = 0;
|
|
22597
22583
|
let absTime = 0.0;
|
|
22598
|
-
let previousTick = 0;
|
|
22599
22584
|
let tempoChangeIndex = 0;
|
|
22600
22585
|
for (let i = 0; i < syncPoints.length; i++) {
|
|
22601
22586
|
const p = syncPoints[i];
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
previousTick
|
|
22587
|
+
let deltaTick = 0;
|
|
22588
|
+
// remember state from previous sync point (or start). to handle linear interpolation
|
|
22589
|
+
let previousModifiedTempo;
|
|
22590
|
+
let previousMillisecondOffset;
|
|
22591
|
+
let previousTick;
|
|
22592
|
+
if (i === 0) {
|
|
22593
|
+
previousModifiedTempo = bpm;
|
|
22594
|
+
previousMillisecondOffset = 0;
|
|
22595
|
+
previousTick = 0;
|
|
22596
|
+
}
|
|
22597
|
+
else {
|
|
22598
|
+
const previousSyncPoint = syncPoints[i - 1];
|
|
22599
|
+
previousModifiedTempo = previousSyncPoint.data.modifiedTempo;
|
|
22600
|
+
previousMillisecondOffset = previousSyncPoint.data.millisecondOffset;
|
|
22601
|
+
previousTick = previousSyncPoint.tick;
|
|
22602
|
+
}
|
|
22603
|
+
// process time until sync point
|
|
22604
|
+
// here it gets a bit tricky. if we have tempo changes on the synthesizer time axis (inbetween two sync points)
|
|
22605
|
+
// we have to calculate a interpolated sync point on the alphaTab time axis.
|
|
22606
|
+
// otherwise the linear interpolation later in the lookup will fail.
|
|
22607
|
+
// goal is to have always a linear increase between two points, no matter if the time axis is sliced by tempo changes or sync points
|
|
22607
22608
|
while (tempoChangeIndex < state.tempoChanges.length &&
|
|
22608
|
-
state.tempoChanges[tempoChangeIndex].ticks <=
|
|
22609
|
+
state.tempoChanges[tempoChangeIndex].ticks <= p.tick) {
|
|
22610
|
+
deltaTick = state.tempoChanges[tempoChangeIndex].ticks - absTick;
|
|
22611
|
+
if (deltaTick > 0) {
|
|
22612
|
+
absTick += deltaTick;
|
|
22613
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22614
|
+
const millisPerTick = (p.data.millisecondOffset - previousMillisecondOffset) / (p.tick - previousTick);
|
|
22615
|
+
const interpolatedMillisecondOffset = (absTick - previousTick) * millisPerTick + previousMillisecondOffset;
|
|
22616
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(absTick, absTime, previousModifiedTempo, interpolatedMillisecondOffset));
|
|
22617
|
+
}
|
|
22609
22618
|
bpm = state.tempoChanges[tempoChangeIndex].bpm;
|
|
22610
22619
|
tempoChangeIndex++;
|
|
22611
22620
|
}
|
|
22621
|
+
deltaTick = p.tick - absTick;
|
|
22622
|
+
absTick += deltaTick;
|
|
22623
|
+
absTime += deltaTick * (60000.0 / (bpm * state.division));
|
|
22624
|
+
state.syncPoints.push(new BackingTrackSyncPointWithTime(p.tick, absTime, p.data.modifiedTempo, p.data.millisecondOffset));
|
|
22612
22625
|
}
|
|
22613
22626
|
}
|
|
22614
22627
|
state.syncPointIndex = 0;
|
|
@@ -22622,7 +22635,7 @@
|
|
|
22622
22635
|
this.updateCurrentTempo(state, timePosition);
|
|
22623
22636
|
const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
|
|
22624
22637
|
const timeDiff = timePosition - lastTempoChange.time;
|
|
22625
|
-
const ticks = (
|
|
22638
|
+
const ticks = (timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0;
|
|
22626
22639
|
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22627
22640
|
return lastTempoChange.ticks + ticks + 1;
|
|
22628
22641
|
}
|
|
@@ -22642,16 +22655,16 @@
|
|
|
22642
22655
|
const syncPoints = state.syncPoints;
|
|
22643
22656
|
if (syncPoints.length > 0) {
|
|
22644
22657
|
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22645
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22658
|
+
if (timePosition < syncPoints[syncPointIndex].millisecondOffset) {
|
|
22646
22659
|
syncPointIndex = 0;
|
|
22647
22660
|
}
|
|
22648
22661
|
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22649
|
-
syncPoints[syncPointIndex + 1].
|
|
22662
|
+
syncPoints[syncPointIndex + 1].millisecondOffset <= timePosition) {
|
|
22650
22663
|
syncPointIndex++;
|
|
22651
22664
|
}
|
|
22652
22665
|
if (syncPointIndex !== state.syncPointIndex) {
|
|
22653
22666
|
state.syncPointIndex = syncPointIndex;
|
|
22654
|
-
state.modifiedTempo = syncPoints[syncPointIndex].
|
|
22667
|
+
state.modifiedTempo = syncPoints[syncPointIndex].modifiedTempo;
|
|
22655
22668
|
}
|
|
22656
22669
|
}
|
|
22657
22670
|
else {
|
|
@@ -22667,18 +22680,18 @@
|
|
|
22667
22680
|
this.updateCurrentTempo(this._mainState, timePosition);
|
|
22668
22681
|
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22669
22682
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22670
|
-
const timeDiff = timePosition - currentSyncPoint.
|
|
22683
|
+
const timeDiff = timePosition - currentSyncPoint.millisecondOffset;
|
|
22671
22684
|
let alphaTabTimeDiff;
|
|
22672
22685
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22673
22686
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22674
|
-
const relativeTimeDiff = timeDiff / (nextSyncPoint.
|
|
22675
|
-
alphaTabTimeDiff = (nextSyncPoint.
|
|
22687
|
+
const relativeTimeDiff = timeDiff / (nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset);
|
|
22688
|
+
alphaTabTimeDiff = (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22676
22689
|
}
|
|
22677
22690
|
else {
|
|
22678
|
-
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.
|
|
22679
|
-
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.
|
|
22691
|
+
const relativeTimeDiff = timeDiff / (backingTrackLength - currentSyncPoint.millisecondOffset);
|
|
22692
|
+
alphaTabTimeDiff = (mainState.endTime - currentSyncPoint.alphaTabTime) * relativeTimeDiff;
|
|
22680
22693
|
}
|
|
22681
|
-
return (currentSyncPoint.
|
|
22694
|
+
return (currentSyncPoint.alphaTabTime + alphaTabTimeDiff) / this.playbackSpeed;
|
|
22682
22695
|
}
|
|
22683
22696
|
mainTimePositionToBackingTrack(timePosition, backingTrackLength) {
|
|
22684
22697
|
const mainState = this._mainState;
|
|
@@ -22688,25 +22701,27 @@
|
|
|
22688
22701
|
}
|
|
22689
22702
|
timePosition *= this.playbackSpeed;
|
|
22690
22703
|
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22691
|
-
if (timePosition < syncPoints[syncPointIndex].
|
|
22704
|
+
if (timePosition < syncPoints[syncPointIndex].alphaTabTime) {
|
|
22692
22705
|
syncPointIndex = 0;
|
|
22693
22706
|
}
|
|
22694
|
-
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].
|
|
22707
|
+
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].alphaTabTime <= timePosition) {
|
|
22695
22708
|
syncPointIndex++;
|
|
22696
22709
|
}
|
|
22710
|
+
// NOTE: this logic heavily relies on the interpolation done in mainUpdateSyncPoints
|
|
22711
|
+
// we ensure that we have a linear increase between two points
|
|
22697
22712
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22698
|
-
const alphaTabTimeDiff = timePosition - currentSyncPoint.
|
|
22713
|
+
const alphaTabTimeDiff = timePosition - currentSyncPoint.alphaTabTime;
|
|
22699
22714
|
let backingTrackPos;
|
|
22700
22715
|
if (syncPointIndex + 1 < syncPoints.length) {
|
|
22701
22716
|
const nextSyncPoint = syncPoints[syncPointIndex + 1];
|
|
22702
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.
|
|
22703
|
-
const backingTrackDiff = nextSyncPoint.
|
|
22704
|
-
backingTrackPos = currentSyncPoint.
|
|
22717
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (nextSyncPoint.alphaTabTime - currentSyncPoint.alphaTabTime);
|
|
22718
|
+
const backingTrackDiff = nextSyncPoint.millisecondOffset - currentSyncPoint.millisecondOffset;
|
|
22719
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + backingTrackDiff * relativeAlphaTabTimeDiff;
|
|
22705
22720
|
}
|
|
22706
22721
|
else {
|
|
22707
|
-
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.
|
|
22708
|
-
const frameDiff = backingTrackLength - currentSyncPoint.
|
|
22709
|
-
backingTrackPos = currentSyncPoint.
|
|
22722
|
+
const relativeAlphaTabTimeDiff = alphaTabTimeDiff / (mainState.endTime - currentSyncPoint.alphaTabTime);
|
|
22723
|
+
const frameDiff = backingTrackLength - currentSyncPoint.millisecondOffset;
|
|
22724
|
+
backingTrackPos = currentSyncPoint.millisecondOffset + frameDiff * relativeAlphaTabTimeDiff;
|
|
22710
22725
|
}
|
|
22711
22726
|
return backingTrackPos;
|
|
22712
22727
|
}
|
|
@@ -28588,7 +28603,9 @@
|
|
|
28588
28603
|
hasSamplesForPercussion(key) {
|
|
28589
28604
|
return this.synthesizer.hasSamplesForPercussion(key);
|
|
28590
28605
|
}
|
|
28591
|
-
loadBackingTrack(_score
|
|
28606
|
+
loadBackingTrack(_score) {
|
|
28607
|
+
}
|
|
28608
|
+
updateSyncPoints(_syncPoints) {
|
|
28592
28609
|
}
|
|
28593
28610
|
}
|
|
28594
28611
|
/**
|
|
@@ -32485,15 +32502,9 @@
|
|
|
32485
32502
|
return null;
|
|
32486
32503
|
}
|
|
32487
32504
|
const o = new Map();
|
|
32488
|
-
o.set("padding", obj.padding);
|
|
32489
32505
|
return o;
|
|
32490
32506
|
}
|
|
32491
32507
|
static setProperty(obj, property, v) {
|
|
32492
|
-
switch (property) {
|
|
32493
|
-
case "padding":
|
|
32494
|
-
obj.padding = v;
|
|
32495
|
-
return true;
|
|
32496
|
-
}
|
|
32497
32508
|
return false;
|
|
32498
32509
|
}
|
|
32499
32510
|
}
|
|
@@ -34956,7 +34967,13 @@
|
|
|
34956
34967
|
* Gets the list of tempo changes within the tick lookup.
|
|
34957
34968
|
*/
|
|
34958
34969
|
this.tempoChanges = [];
|
|
34970
|
+
/**
|
|
34971
|
+
* The first beat in the bar.
|
|
34972
|
+
*/
|
|
34959
34973
|
this.firstBeat = null;
|
|
34974
|
+
/**
|
|
34975
|
+
* The last beat in the bar.
|
|
34976
|
+
*/
|
|
34960
34977
|
this.lastBeat = null;
|
|
34961
34978
|
/**
|
|
34962
34979
|
* Gets or sets the {@link MasterBarTickLookup} of the next masterbar in the {@link Score}
|
|
@@ -35370,14 +35387,13 @@
|
|
|
35370
35387
|
constructor() {
|
|
35371
35388
|
this._currentMasterBar = null;
|
|
35372
35389
|
/**
|
|
35373
|
-
*
|
|
35390
|
+
* A dictionary of all master bars played. The index is the index equals to {@link MasterBar.index}.
|
|
35374
35391
|
* This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to {@link MasterBars}.
|
|
35375
35392
|
* @internal
|
|
35376
35393
|
*/
|
|
35377
35394
|
this.masterBarLookup = new Map();
|
|
35378
35395
|
/**
|
|
35379
|
-
*
|
|
35380
|
-
* @internal
|
|
35396
|
+
* A list of all {@link MasterBarTickLookup} sorted by time.
|
|
35381
35397
|
*/
|
|
35382
35398
|
this.masterBars = [];
|
|
35383
35399
|
/**
|
|
@@ -35751,6 +35767,17 @@
|
|
|
35751
35767
|
}
|
|
35752
35768
|
}
|
|
35753
35769
|
|
|
35770
|
+
/**
|
|
35771
|
+
* Rerpresents a point to sync the alphaTab time axis with an external backing track.
|
|
35772
|
+
*/
|
|
35773
|
+
class BackingTrackSyncPoint {
|
|
35774
|
+
constructor(tick, data) {
|
|
35775
|
+
this.tick = 0;
|
|
35776
|
+
this.tick = tick;
|
|
35777
|
+
this.data = data;
|
|
35778
|
+
}
|
|
35779
|
+
}
|
|
35780
|
+
|
|
35754
35781
|
class MidiNoteDuration {
|
|
35755
35782
|
constructor() {
|
|
35756
35783
|
this.noteOnly = 0;
|
|
@@ -35820,62 +35847,22 @@
|
|
|
35820
35847
|
this.generateTrack(track);
|
|
35821
35848
|
}
|
|
35822
35849
|
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
|
-
}
|
|
35850
|
+
this.syncPoints = [];
|
|
35851
|
+
MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
|
|
35852
|
+
this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35853
|
+
}, (index, currentTick, currentTempo) => {
|
|
35854
|
+
for (const track of this._score.tracks) {
|
|
35855
|
+
for (const staff of track.staves) {
|
|
35856
|
+
if (index < staff.bars.length) {
|
|
35857
|
+
this.generateBar(staff.bars[index], currentTick, currentTempo);
|
|
35846
35858
|
}
|
|
35847
35859
|
}
|
|
35848
|
-
if (bar.tempoAutomations.length > 0) {
|
|
35849
|
-
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35850
|
-
}
|
|
35851
35860
|
}
|
|
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));
|
|
35861
|
+
}, endTick => {
|
|
35862
|
+
for (const track of this._score.tracks) {
|
|
35863
|
+
this._handler.finishTrack(track.index, endTick);
|
|
35874
35864
|
}
|
|
35875
|
-
}
|
|
35876
|
-
for (const track of this._score.tracks) {
|
|
35877
|
-
this._handler.finishTrack(track.index, controller.currentTick);
|
|
35878
|
-
}
|
|
35865
|
+
});
|
|
35879
35866
|
Logger.debug('Midi', 'Midi generation done');
|
|
35880
35867
|
}
|
|
35881
35868
|
generateTrack(track) {
|
|
@@ -35920,6 +35907,81 @@
|
|
|
35920
35907
|
this._handler.addControlChange(track.index, 0, channel, ControllerType.DataEntryCoarse, MidiFileGenerator.PitchBendRangeInSemitones);
|
|
35921
35908
|
this.addProgramChange(track, 0, channel, playbackInfo.program);
|
|
35922
35909
|
}
|
|
35910
|
+
/**
|
|
35911
|
+
* Generates the sync points for the given score without re-generating the midi itself.
|
|
35912
|
+
* @remarks
|
|
35913
|
+
* Use this method if a re-generation of the sync points after modification is required.
|
|
35914
|
+
* It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
|
|
35915
|
+
* need to be considered for synchronization.
|
|
35916
|
+
* @param score The song for which to regenerate the sync points.
|
|
35917
|
+
* @returns The generated sync points for usage in the backing track playback.
|
|
35918
|
+
*/
|
|
35919
|
+
static generateSyncPoints(score) {
|
|
35920
|
+
const syncPoints = [];
|
|
35921
|
+
MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
|
|
35922
|
+
}, (_barIndex, _currentTick, _currentTempo) => {
|
|
35923
|
+
}, _endTick => {
|
|
35924
|
+
});
|
|
35925
|
+
return syncPoints;
|
|
35926
|
+
}
|
|
35927
|
+
static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
|
|
35928
|
+
const controller = new MidiPlaybackController(score);
|
|
35929
|
+
let currentTempo = score.tempo;
|
|
35930
|
+
let previousMasterBar = null;
|
|
35931
|
+
// store the previous played bar for repeats
|
|
35932
|
+
const barOccurence = new Map();
|
|
35933
|
+
while (!controller.finished) {
|
|
35934
|
+
const index = controller.index;
|
|
35935
|
+
const bar = score.masterBars[index];
|
|
35936
|
+
const currentTick = controller.currentTick;
|
|
35937
|
+
controller.processCurrent();
|
|
35938
|
+
if (controller.shouldPlay) {
|
|
35939
|
+
let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
|
|
35940
|
+
occurence++;
|
|
35941
|
+
barOccurence.set(index, occurence);
|
|
35942
|
+
generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
|
|
35943
|
+
const barSyncPoints = bar.syncPoints;
|
|
35944
|
+
if (barSyncPoints) {
|
|
35945
|
+
for (const syncPoint of barSyncPoints) {
|
|
35946
|
+
if (syncPoint.syncPointValue.barOccurence === occurence) {
|
|
35947
|
+
const tick = currentTick + bar.calculateDuration() * syncPoint.ratioPosition;
|
|
35948
|
+
syncPoints.push(new BackingTrackSyncPoint(tick, syncPoint.syncPointValue));
|
|
35949
|
+
}
|
|
35950
|
+
}
|
|
35951
|
+
}
|
|
35952
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35953
|
+
currentTempo = bar.tempoAutomations[0].value;
|
|
35954
|
+
}
|
|
35955
|
+
generateTracks(index, currentTick, currentTempo);
|
|
35956
|
+
if (bar.tempoAutomations.length > 0) {
|
|
35957
|
+
currentTempo = bar.tempoAutomations[bar.tempoAutomations.length - 1].value;
|
|
35958
|
+
}
|
|
35959
|
+
}
|
|
35960
|
+
controller.moveNext();
|
|
35961
|
+
previousMasterBar = bar;
|
|
35962
|
+
}
|
|
35963
|
+
// here we interpolate the sync point which marks the end of the sync.
|
|
35964
|
+
// Sync points define new tempos at certain positions.
|
|
35965
|
+
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35966
|
+
// but where it ends according to the BPM and the remaining ticks.
|
|
35967
|
+
if (syncPoints.length > 0) {
|
|
35968
|
+
const lastSyncPoint = syncPoints[syncPoints.length - 1];
|
|
35969
|
+
const remainingTicks = controller.currentTick - lastSyncPoint.tick;
|
|
35970
|
+
if (remainingTicks > 0) {
|
|
35971
|
+
const syncPointData = new SyncPointData();
|
|
35972
|
+
// last occurence of the last bar
|
|
35973
|
+
syncPointData.barOccurence = barOccurence.get(score.masterBars.length - 1);
|
|
35974
|
+
// same tempo as last point
|
|
35975
|
+
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35976
|
+
// interpolated end from last syncPoint
|
|
35977
|
+
syncPointData.millisecondOffset =
|
|
35978
|
+
lastSyncPoint.data.millisecondOffset +
|
|
35979
|
+
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35980
|
+
syncPoints.push(new BackingTrackSyncPoint(controller.currentTick, syncPointData));
|
|
35981
|
+
}
|
|
35982
|
+
}
|
|
35983
|
+
finish(controller.currentTick);
|
|
35984
|
+
}
|
|
35923
35985
|
static toChannelShort(data) {
|
|
35924
35986
|
const value = Math.max(-32768, Math.min(32767, data * 8 - 1));
|
|
35925
35987
|
return Math.max(value, -1) + 1;
|
|
@@ -38256,21 +38318,23 @@
|
|
|
38256
38318
|
this._backingTrackOutput.seekTo(this.sequencer.mainTimePositionToBackingTrack(timePosition, this._backingTrackOutput.backingTrackDuration));
|
|
38257
38319
|
}
|
|
38258
38320
|
}
|
|
38259
|
-
loadBackingTrack(score
|
|
38321
|
+
loadBackingTrack(score) {
|
|
38260
38322
|
const backingTrackInfo = score.backingTrack;
|
|
38261
38323
|
if (backingTrackInfo) {
|
|
38262
38324
|
this._backingTrackOutput.loadBackingTrack(backingTrackInfo);
|
|
38263
|
-
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38264
38325
|
this.timePosition = 0;
|
|
38265
38326
|
}
|
|
38266
38327
|
}
|
|
38328
|
+
updateSyncPoints(syncPoints) {
|
|
38329
|
+
this.sequencer.mainUpdateSyncPoints(syncPoints);
|
|
38330
|
+
this.tickPosition = this.tickPosition;
|
|
38331
|
+
}
|
|
38267
38332
|
}
|
|
38268
38333
|
|
|
38269
38334
|
class ExternalMediaSynthOutput {
|
|
38270
38335
|
constructor() {
|
|
38271
38336
|
// fake rate
|
|
38272
38337
|
this.sampleRate = 44100;
|
|
38273
|
-
this._padding = 0;
|
|
38274
38338
|
this._seekPosition = 0;
|
|
38275
38339
|
this.ready = new EventEmitter();
|
|
38276
38340
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -38313,20 +38377,19 @@
|
|
|
38313
38377
|
seekTo(time) {
|
|
38314
38378
|
const handler = this.handler;
|
|
38315
38379
|
if (handler) {
|
|
38316
|
-
handler.seekTo(time
|
|
38380
|
+
handler.seekTo(time);
|
|
38317
38381
|
}
|
|
38318
38382
|
else {
|
|
38319
|
-
this._seekPosition = time
|
|
38383
|
+
this._seekPosition = time;
|
|
38320
38384
|
}
|
|
38321
38385
|
}
|
|
38322
|
-
loadBackingTrack(
|
|
38323
|
-
this._padding = backingTrack.padding;
|
|
38386
|
+
loadBackingTrack(_backingTrack) {
|
|
38324
38387
|
}
|
|
38325
38388
|
open(_bufferTimeInMilliseconds) {
|
|
38326
38389
|
this.ready.trigger();
|
|
38327
38390
|
}
|
|
38328
38391
|
updatePosition(currentTime) {
|
|
38329
|
-
this.timeUpdate.trigger(currentTime
|
|
38392
|
+
this.timeUpdate.trigger(currentTime);
|
|
38330
38393
|
}
|
|
38331
38394
|
play() {
|
|
38332
38395
|
this.handler?.play();
|
|
@@ -38591,9 +38654,14 @@
|
|
|
38591
38654
|
this._instance.loadMidiFile(midi);
|
|
38592
38655
|
}
|
|
38593
38656
|
}
|
|
38594
|
-
loadBackingTrack(score
|
|
38657
|
+
loadBackingTrack(score) {
|
|
38595
38658
|
if (this._instance) {
|
|
38596
|
-
this._instance.loadBackingTrack(score
|
|
38659
|
+
this._instance.loadBackingTrack(score);
|
|
38660
|
+
}
|
|
38661
|
+
}
|
|
38662
|
+
updateSyncPoints(syncPoints) {
|
|
38663
|
+
if (this._instance) {
|
|
38664
|
+
this._instance.updateSyncPoints(syncPoints);
|
|
38597
38665
|
}
|
|
38598
38666
|
}
|
|
38599
38667
|
applyTranspositionPitches(transpositionPitches) {
|
|
@@ -39922,7 +39990,7 @@
|
|
|
39922
39990
|
}
|
|
39923
39991
|
appendRenderResult(result, isLast) {
|
|
39924
39992
|
// resizing the canvas and wrapper elements at the end is enough
|
|
39925
|
-
// it avoids flickering on resizes and re-renders.
|
|
39993
|
+
// it avoids flickering on resizes and re-renders.
|
|
39926
39994
|
// the individual partials are anyhow sized correctly
|
|
39927
39995
|
if (isLast) {
|
|
39928
39996
|
this.canvasElement.width = result.totalWidth;
|
|
@@ -40684,9 +40752,23 @@
|
|
|
40684
40752
|
this.onMidiLoad(midiFile);
|
|
40685
40753
|
const player = this._player;
|
|
40686
40754
|
player.loadMidiFile(midiFile);
|
|
40687
|
-
player.loadBackingTrack(score
|
|
40755
|
+
player.loadBackingTrack(score);
|
|
40756
|
+
player.updateSyncPoints(generator.syncPoints);
|
|
40688
40757
|
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40689
40758
|
}
|
|
40759
|
+
/**
|
|
40760
|
+
* Triggers an update of the sync points for the current score after modification within the data model
|
|
40761
|
+
* @category Methods - Player
|
|
40762
|
+
* @since 1.6.0
|
|
40763
|
+
*/
|
|
40764
|
+
updateSyncPoints() {
|
|
40765
|
+
if (!this.score) {
|
|
40766
|
+
return;
|
|
40767
|
+
}
|
|
40768
|
+
const score = this.score;
|
|
40769
|
+
const player = this._player;
|
|
40770
|
+
player.updateSyncPoints(MidiFileGenerator.generateSyncPoints(score));
|
|
40771
|
+
}
|
|
40690
40772
|
/**
|
|
40691
40773
|
* Changes the volume of the given tracks.
|
|
40692
40774
|
* @param tracks The tracks for which the volume should be changed.
|
|
@@ -43354,6 +43436,8 @@
|
|
|
43354
43436
|
}
|
|
43355
43437
|
loadBackingTrack(_score) {
|
|
43356
43438
|
}
|
|
43439
|
+
updateSyncPoints(_syncPoints) {
|
|
43440
|
+
}
|
|
43357
43441
|
}
|
|
43358
43442
|
|
|
43359
43443
|
/**
|
|
@@ -43728,7 +43812,6 @@
|
|
|
43728
43812
|
constructor() {
|
|
43729
43813
|
// fake rate
|
|
43730
43814
|
this.sampleRate = 44100;
|
|
43731
|
-
this._padding = 0;
|
|
43732
43815
|
this._updateInterval = 0;
|
|
43733
43816
|
this.ready = new EventEmitter();
|
|
43734
43817
|
this.samplesPlayed = new EventEmitterOfT();
|
|
@@ -43752,13 +43835,12 @@
|
|
|
43752
43835
|
this.audioElement.volume = value;
|
|
43753
43836
|
}
|
|
43754
43837
|
seekTo(time) {
|
|
43755
|
-
this.audioElement.currentTime = time / 1000
|
|
43838
|
+
this.audioElement.currentTime = time / 1000;
|
|
43756
43839
|
}
|
|
43757
43840
|
loadBackingTrack(backingTrack) {
|
|
43758
43841
|
if (this.audioElement?.src) {
|
|
43759
43842
|
URL.revokeObjectURL(this.audioElement.src);
|
|
43760
43843
|
}
|
|
43761
|
-
this._padding = backingTrack.padding / 1000;
|
|
43762
43844
|
const blob = new Blob([backingTrack.rawAudioFile]);
|
|
43763
43845
|
// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
|
|
43764
43846
|
// Step 8. resets the playbackRate, we need to remember and restore it.
|
|
@@ -43777,7 +43859,7 @@
|
|
|
43777
43859
|
this.ready.trigger();
|
|
43778
43860
|
}
|
|
43779
43861
|
updatePosition() {
|
|
43780
|
-
const timePos =
|
|
43862
|
+
const timePos = this.audioElement.currentTime * 1000;
|
|
43781
43863
|
this.timeUpdate.trigger(timePos);
|
|
43782
43864
|
}
|
|
43783
43865
|
play() {
|
|
@@ -43996,6 +44078,7 @@
|
|
|
43996
44078
|
webFont.usages--;
|
|
43997
44079
|
if (webFont.usages <= 0) {
|
|
43998
44080
|
webFont.element.remove();
|
|
44081
|
+
BrowserUiFacade._registeredWebFonts.delete(webFont.hash);
|
|
43999
44082
|
}
|
|
44000
44083
|
}
|
|
44001
44084
|
createCanvasElement() {
|
|
@@ -44152,6 +44235,7 @@
|
|
|
44152
44235
|
checker.checkForFontAvailability();
|
|
44153
44236
|
settings.display.resources.smuflFont = new Font(familyName, Environment.MusicFontSize, FontStyle.Plain, FontWeight.Regular);
|
|
44154
44237
|
const webFont = {
|
|
44238
|
+
hash,
|
|
44155
44239
|
element: styleElement,
|
|
44156
44240
|
fontSuffix,
|
|
44157
44241
|
usages: 1,
|
|
@@ -60863,9 +60947,9 @@
|
|
|
60863
60947
|
print(`build date: ${VersionInfo.date}`);
|
|
60864
60948
|
}
|
|
60865
60949
|
}
|
|
60866
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
60867
|
-
VersionInfo.date = '2025-05-
|
|
60868
|
-
VersionInfo.commit = '
|
|
60950
|
+
VersionInfo.version = '1.6.0-alpha.1420';
|
|
60951
|
+
VersionInfo.date = '2025-05-22T03:03:04.057Z';
|
|
60952
|
+
VersionInfo.commit = '230bdd455a1906e6f334b63bad3b8cf773f890e6';
|
|
60869
60953
|
|
|
60870
60954
|
/**
|
|
60871
60955
|
* A factory for custom layout engines.
|