@coderline/alphatab 1.6.0-alpha.1449 → 1.6.0

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.6.0-alpha.1449 (develop, build 1449)
2
+ * alphaTab v1.6.0 (, build 19)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -22887,6 +22887,9 @@ class MidiFileSequencer {
22887
22887
  if (tempoChangeIndex !== state.tempoChangeIndex) {
22888
22888
  state.tempoChangeIndex = tempoChangeIndex;
22889
22889
  state.currentTempo = state.tempoChanges[state.tempoChangeIndex].bpm;
22890
+ if (state.syncPoints.length === 0) {
22891
+ state.syncPointTempo = state.currentTempo;
22892
+ }
22890
22893
  }
22891
22894
  }
22892
22895
  currentUpdateSyncPoints(timePosition) {
@@ -36331,6 +36334,7 @@ class PlayThroughContext {
36331
36334
  this.synthTime = 0;
36332
36335
  this.currentTempo = 0;
36333
36336
  this.automationToSyncPoint = new Map();
36337
+ this.createNewSyncPoints = false;
36334
36338
  }
36335
36339
  }
36336
36340
  /**
@@ -36383,7 +36387,7 @@ class MidiFileGenerator {
36383
36387
  }
36384
36388
  Logger.debug('Midi', 'Begin midi generation');
36385
36389
  this.syncPoints = [];
36386
- MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
36390
+ MidiFileGenerator.playThroughSong(this._score, this.syncPoints, false, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
36387
36391
  this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
36388
36392
  }, (index, currentTick, currentTempo) => {
36389
36393
  for (const track of this._score.tracks) {
@@ -36449,11 +36453,12 @@ class MidiFileGenerator {
36449
36453
  * It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
36450
36454
  * need to be considered for synchronization.
36451
36455
  * @param score The song for which to regenerate the sync points.
36456
+ * @param createNew Whether a new set of sync points should be generated for the sync (start, stop and tempo changes).
36452
36457
  * @returns The generated sync points for usage in the backing track playback.
36453
36458
  */
36454
- static generateSyncPoints(score) {
36459
+ static generateSyncPoints(score, createNew = false) {
36455
36460
  const syncPoints = [];
36456
- MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36461
+ MidiFileGenerator.playThroughSong(score, syncPoints, createNew, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36457
36462
  }, (_barIndex, _currentTick, _currentTempo) => {
36458
36463
  }, _endTick => {
36459
36464
  });
@@ -36464,17 +36469,18 @@ class MidiFileGenerator {
36464
36469
  */
36465
36470
  static buildModifiedTempoLookup(score) {
36466
36471
  const syncPoints = [];
36467
- const context = MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36472
+ const context = MidiFileGenerator.playThroughSong(score, syncPoints, false, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36468
36473
  }, (_barIndex, _currentTick, _currentTempo) => {
36469
36474
  }, _endTick => {
36470
36475
  });
36471
36476
  return context.automationToSyncPoint;
36472
36477
  }
36473
- static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
36478
+ static playThroughSong(score, syncPoints, createNewSyncPoints, generateMasterBar, generateTracks, finish) {
36474
36479
  const controller = new MidiPlaybackController(score);
36475
36480
  const playContext = new PlayThroughContext();
36476
36481
  playContext.currentTempo = score.tempo;
36477
36482
  playContext.syncPoints = syncPoints;
36483
+ playContext.createNewSyncPoints = createNewSyncPoints;
36478
36484
  let previousMasterBar = null;
36479
36485
  // store the previous played bar for repeats
36480
36486
  const barOccurence = new Map();
@@ -36512,14 +36518,24 @@ class MidiFileGenerator {
36512
36518
  // we need to assume some BPM for the last interpolated point.
36513
36519
  // if we have more than just a start point, we keep the BPM before the last manual sync point
36514
36520
  // otherwise we have no customized sync BPM known and keep the synthesizer one.
36515
- backingTrackSyncPoint.syncBpm =
36516
- syncPoints.length > 1 ? syncPoints[syncPoints.length - 2].syncBpm : lastSyncPoint.synthBpm;
36521
+ if (playContext.createNewSyncPoints) {
36522
+ backingTrackSyncPoint.syncBpm = lastSyncPoint.synthBpm;
36523
+ backingTrackSyncPoint.synthBpm = lastSyncPoint.synthBpm;
36524
+ }
36525
+ else if (syncPoints.length === 1) {
36526
+ backingTrackSyncPoint.syncBpm = lastSyncPoint.synthBpm;
36527
+ }
36528
+ else {
36529
+ backingTrackSyncPoint.syncBpm = syncPoints[syncPoints.length - 2].syncBpm;
36530
+ }
36517
36531
  backingTrackSyncPoint.synthTime =
36518
36532
  lastSyncPoint.synthTime + MidiUtils.ticksToMillis(remainingTicks, lastSyncPoint.synthBpm);
36519
36533
  backingTrackSyncPoint.syncTime =
36520
36534
  lastSyncPoint.syncTime + MidiUtils.ticksToMillis(remainingTicks, backingTrackSyncPoint.syncBpm);
36521
36535
  // update the previous sync point according to the new time
36522
- lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
36536
+ if (!playContext.createNewSyncPoints) {
36537
+ lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
36538
+ }
36523
36539
  syncPoints.push(backingTrackSyncPoint);
36524
36540
  }
36525
36541
  }
@@ -36530,7 +36546,10 @@ class MidiFileGenerator {
36530
36546
  const duration = bar.calculateDuration();
36531
36547
  const barSyncPoints = bar.syncPoints;
36532
36548
  const barStartTick = context.synthTick;
36533
- if (barSyncPoints) {
36549
+ if (context.createNewSyncPoints) {
36550
+ MidiFileGenerator.processBarTimeWithNewSyncPoints(bar, occurence, context);
36551
+ }
36552
+ else if (barSyncPoints) {
36534
36553
  MidiFileGenerator.processBarTimeWithSyncPoints(bar, occurence, context);
36535
36554
  }
36536
36555
  else {
@@ -36544,6 +36563,44 @@ class MidiFileGenerator {
36544
36563
  context.synthTick = endTick;
36545
36564
  }
36546
36565
  }
36566
+ static processBarTimeWithNewSyncPoints(bar, occurence, context) {
36567
+ // start marker
36568
+ const barStartTick = context.synthTick;
36569
+ if (bar.index === 0 && occurence === 0) {
36570
+ context.currentTempo = bar.score.tempo;
36571
+ const backingTrackSyncPoint = new BackingTrackSyncPoint();
36572
+ backingTrackSyncPoint.masterBarIndex = bar.index;
36573
+ backingTrackSyncPoint.masterBarOccurence = occurence;
36574
+ backingTrackSyncPoint.synthTick = barStartTick;
36575
+ backingTrackSyncPoint.synthBpm = context.currentTempo;
36576
+ backingTrackSyncPoint.synthTime = context.synthTime;
36577
+ backingTrackSyncPoint.syncBpm = context.currentTempo;
36578
+ backingTrackSyncPoint.syncTime = context.synthTime;
36579
+ context.syncPoints.push(backingTrackSyncPoint);
36580
+ }
36581
+ // walk tempo changes and create points
36582
+ const duration = bar.calculateDuration();
36583
+ for (const change of bar.tempoAutomations) {
36584
+ const absoluteTick = barStartTick + change.ratioPosition * duration;
36585
+ const tickOffset = absoluteTick - context.synthTick;
36586
+ if (tickOffset > 0) {
36587
+ context.synthTick = absoluteTick;
36588
+ context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
36589
+ }
36590
+ if (change.value !== context.currentTempo) {
36591
+ context.currentTempo = change.value;
36592
+ const backingTrackSyncPoint = new BackingTrackSyncPoint();
36593
+ backingTrackSyncPoint.masterBarIndex = bar.index;
36594
+ backingTrackSyncPoint.masterBarOccurence = occurence;
36595
+ backingTrackSyncPoint.synthTick = absoluteTick;
36596
+ backingTrackSyncPoint.synthBpm = context.currentTempo;
36597
+ backingTrackSyncPoint.synthTime = context.synthTime;
36598
+ backingTrackSyncPoint.syncBpm = context.currentTempo;
36599
+ backingTrackSyncPoint.syncTime = context.synthTime;
36600
+ context.syncPoints.push(backingTrackSyncPoint);
36601
+ }
36602
+ }
36603
+ }
36547
36604
  static processBarTimeWithSyncPoints(bar, occurence, context) {
36548
36605
  const barStartTick = context.synthTick;
36549
36606
  const duration = bar.calculateDuration();
@@ -61732,9 +61789,9 @@ class VersionInfo {
61732
61789
  print(`build date: ${VersionInfo.date}`);
61733
61790
  }
61734
61791
  }
61735
- VersionInfo.version = '1.6.0-alpha.1449';
61736
- VersionInfo.date = '2025-06-14T22:28:30.062Z';
61737
- VersionInfo.commit = 'b08102e1e209cfc7880c524366eae49d3b23e5db';
61792
+ VersionInfo.version = '1.6.0';
61793
+ VersionInfo.date = '2025-06-15T14:48:58.455Z';
61794
+ VersionInfo.commit = 'f43cd971d7911b75277a9b3d98ca0855a129ad4d';
61738
61795
 
61739
61796
  /**
61740
61797
  * A factory for custom layout engines.
@@ -9633,12 +9633,14 @@ declare class MidiFileGenerator {
9633
9633
  * It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
9634
9634
  * need to be considered for synchronization.
9635
9635
  * @param score The song for which to regenerate the sync points.
9636
+ * @param createNew Whether a new set of sync points should be generated for the sync (start, stop and tempo changes).
9636
9637
  * @returns The generated sync points for usage in the backing track playback.
9637
9638
  */
9638
- static generateSyncPoints(score: Score): BackingTrackSyncPoint[];
9639
+ static generateSyncPoints(score: Score, createNew?: boolean): BackingTrackSyncPoint[];
9639
9640
  /* Excluded from this release type: buildModifiedTempoLookup */
9640
9641
  private static playThroughSong;
9641
9642
  private static processBarTime;
9643
+ private static processBarTimeWithNewSyncPoints;
9642
9644
  private static processBarTimeWithSyncPoints;
9643
9645
  private static processBarTimeNoSyncPoints;
9644
9646
  private static toChannelShort;
package/dist/alphaTab.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.6.0-alpha.1449 (develop, build 1449)
2
+ * alphaTab v1.6.0 (, build 19)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -22893,6 +22893,9 @@
22893
22893
  if (tempoChangeIndex !== state.tempoChangeIndex) {
22894
22894
  state.tempoChangeIndex = tempoChangeIndex;
22895
22895
  state.currentTempo = state.tempoChanges[state.tempoChangeIndex].bpm;
22896
+ if (state.syncPoints.length === 0) {
22897
+ state.syncPointTempo = state.currentTempo;
22898
+ }
22896
22899
  }
22897
22900
  }
22898
22901
  currentUpdateSyncPoints(timePosition) {
@@ -36337,6 +36340,7 @@
36337
36340
  this.synthTime = 0;
36338
36341
  this.currentTempo = 0;
36339
36342
  this.automationToSyncPoint = new Map();
36343
+ this.createNewSyncPoints = false;
36340
36344
  }
36341
36345
  }
36342
36346
  /**
@@ -36389,7 +36393,7 @@
36389
36393
  }
36390
36394
  Logger.debug('Midi', 'Begin midi generation');
36391
36395
  this.syncPoints = [];
36392
- MidiFileGenerator.playThroughSong(this._score, this.syncPoints, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
36396
+ MidiFileGenerator.playThroughSong(this._score, this.syncPoints, false, (bar, previousMasterBar, currentTick, currentTempo, occurence) => {
36393
36397
  this.generateMasterBar(bar, previousMasterBar, currentTick, currentTempo, occurence);
36394
36398
  }, (index, currentTick, currentTempo) => {
36395
36399
  for (const track of this._score.tracks) {
@@ -36455,11 +36459,12 @@
36455
36459
  * It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
36456
36460
  * need to be considered for synchronization.
36457
36461
  * @param score The song for which to regenerate the sync points.
36462
+ * @param createNew Whether a new set of sync points should be generated for the sync (start, stop and tempo changes).
36458
36463
  * @returns The generated sync points for usage in the backing track playback.
36459
36464
  */
36460
- static generateSyncPoints(score) {
36465
+ static generateSyncPoints(score, createNew = false) {
36461
36466
  const syncPoints = [];
36462
- MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36467
+ MidiFileGenerator.playThroughSong(score, syncPoints, createNew, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36463
36468
  }, (_barIndex, _currentTick, _currentTempo) => {
36464
36469
  }, _endTick => {
36465
36470
  });
@@ -36470,17 +36475,18 @@
36470
36475
  */
36471
36476
  static buildModifiedTempoLookup(score) {
36472
36477
  const syncPoints = [];
36473
- const context = MidiFileGenerator.playThroughSong(score, syncPoints, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36478
+ const context = MidiFileGenerator.playThroughSong(score, syncPoints, false, (_masterBar, _previousMasterBar, _currentTick, _currentTempo, _barOccurence) => {
36474
36479
  }, (_barIndex, _currentTick, _currentTempo) => {
36475
36480
  }, _endTick => {
36476
36481
  });
36477
36482
  return context.automationToSyncPoint;
36478
36483
  }
36479
- static playThroughSong(score, syncPoints, generateMasterBar, generateTracks, finish) {
36484
+ static playThroughSong(score, syncPoints, createNewSyncPoints, generateMasterBar, generateTracks, finish) {
36480
36485
  const controller = new MidiPlaybackController(score);
36481
36486
  const playContext = new PlayThroughContext();
36482
36487
  playContext.currentTempo = score.tempo;
36483
36488
  playContext.syncPoints = syncPoints;
36489
+ playContext.createNewSyncPoints = createNewSyncPoints;
36484
36490
  let previousMasterBar = null;
36485
36491
  // store the previous played bar for repeats
36486
36492
  const barOccurence = new Map();
@@ -36518,14 +36524,24 @@
36518
36524
  // we need to assume some BPM for the last interpolated point.
36519
36525
  // if we have more than just a start point, we keep the BPM before the last manual sync point
36520
36526
  // otherwise we have no customized sync BPM known and keep the synthesizer one.
36521
- backingTrackSyncPoint.syncBpm =
36522
- syncPoints.length > 1 ? syncPoints[syncPoints.length - 2].syncBpm : lastSyncPoint.synthBpm;
36527
+ if (playContext.createNewSyncPoints) {
36528
+ backingTrackSyncPoint.syncBpm = lastSyncPoint.synthBpm;
36529
+ backingTrackSyncPoint.synthBpm = lastSyncPoint.synthBpm;
36530
+ }
36531
+ else if (syncPoints.length === 1) {
36532
+ backingTrackSyncPoint.syncBpm = lastSyncPoint.synthBpm;
36533
+ }
36534
+ else {
36535
+ backingTrackSyncPoint.syncBpm = syncPoints[syncPoints.length - 2].syncBpm;
36536
+ }
36523
36537
  backingTrackSyncPoint.synthTime =
36524
36538
  lastSyncPoint.synthTime + MidiUtils.ticksToMillis(remainingTicks, lastSyncPoint.synthBpm);
36525
36539
  backingTrackSyncPoint.syncTime =
36526
36540
  lastSyncPoint.syncTime + MidiUtils.ticksToMillis(remainingTicks, backingTrackSyncPoint.syncBpm);
36527
36541
  // update the previous sync point according to the new time
36528
- lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
36542
+ if (!playContext.createNewSyncPoints) {
36543
+ lastSyncPoint.updateSyncBpm(backingTrackSyncPoint.synthTime, backingTrackSyncPoint.syncTime);
36544
+ }
36529
36545
  syncPoints.push(backingTrackSyncPoint);
36530
36546
  }
36531
36547
  }
@@ -36536,7 +36552,10 @@
36536
36552
  const duration = bar.calculateDuration();
36537
36553
  const barSyncPoints = bar.syncPoints;
36538
36554
  const barStartTick = context.synthTick;
36539
- if (barSyncPoints) {
36555
+ if (context.createNewSyncPoints) {
36556
+ MidiFileGenerator.processBarTimeWithNewSyncPoints(bar, occurence, context);
36557
+ }
36558
+ else if (barSyncPoints) {
36540
36559
  MidiFileGenerator.processBarTimeWithSyncPoints(bar, occurence, context);
36541
36560
  }
36542
36561
  else {
@@ -36550,6 +36569,44 @@
36550
36569
  context.synthTick = endTick;
36551
36570
  }
36552
36571
  }
36572
+ static processBarTimeWithNewSyncPoints(bar, occurence, context) {
36573
+ // start marker
36574
+ const barStartTick = context.synthTick;
36575
+ if (bar.index === 0 && occurence === 0) {
36576
+ context.currentTempo = bar.score.tempo;
36577
+ const backingTrackSyncPoint = new BackingTrackSyncPoint();
36578
+ backingTrackSyncPoint.masterBarIndex = bar.index;
36579
+ backingTrackSyncPoint.masterBarOccurence = occurence;
36580
+ backingTrackSyncPoint.synthTick = barStartTick;
36581
+ backingTrackSyncPoint.synthBpm = context.currentTempo;
36582
+ backingTrackSyncPoint.synthTime = context.synthTime;
36583
+ backingTrackSyncPoint.syncBpm = context.currentTempo;
36584
+ backingTrackSyncPoint.syncTime = context.synthTime;
36585
+ context.syncPoints.push(backingTrackSyncPoint);
36586
+ }
36587
+ // walk tempo changes and create points
36588
+ const duration = bar.calculateDuration();
36589
+ for (const change of bar.tempoAutomations) {
36590
+ const absoluteTick = barStartTick + change.ratioPosition * duration;
36591
+ const tickOffset = absoluteTick - context.synthTick;
36592
+ if (tickOffset > 0) {
36593
+ context.synthTick = absoluteTick;
36594
+ context.synthTime += MidiUtils.ticksToMillis(tickOffset, context.currentTempo);
36595
+ }
36596
+ if (change.value !== context.currentTempo) {
36597
+ context.currentTempo = change.value;
36598
+ const backingTrackSyncPoint = new BackingTrackSyncPoint();
36599
+ backingTrackSyncPoint.masterBarIndex = bar.index;
36600
+ backingTrackSyncPoint.masterBarOccurence = occurence;
36601
+ backingTrackSyncPoint.synthTick = absoluteTick;
36602
+ backingTrackSyncPoint.synthBpm = context.currentTempo;
36603
+ backingTrackSyncPoint.synthTime = context.synthTime;
36604
+ backingTrackSyncPoint.syncBpm = context.currentTempo;
36605
+ backingTrackSyncPoint.syncTime = context.synthTime;
36606
+ context.syncPoints.push(backingTrackSyncPoint);
36607
+ }
36608
+ }
36609
+ }
36553
36610
  static processBarTimeWithSyncPoints(bar, occurence, context) {
36554
36611
  const barStartTick = context.synthTick;
36555
36612
  const duration = bar.calculateDuration();
@@ -61738,9 +61795,9 @@
61738
61795
  print(`build date: ${VersionInfo.date}`);
61739
61796
  }
61740
61797
  }
61741
- VersionInfo.version = '1.6.0-alpha.1449';
61742
- VersionInfo.date = '2025-06-14T22:28:30.062Z';
61743
- VersionInfo.commit = 'b08102e1e209cfc7880c524366eae49d3b23e5db';
61798
+ VersionInfo.version = '1.6.0';
61799
+ VersionInfo.date = '2025-06-15T14:48:58.455Z';
61800
+ VersionInfo.commit = 'f43cd971d7911b75277a9b3d98ca0855a129ad4d';
61744
61801
 
61745
61802
  /**
61746
61803
  * A factory for custom layout engines.