@coderline/alphatab 1.6.0-alpha.1409 → 1.6.0-alpha.1415

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.1409 (develop, build 1409)
2
+ * alphaTab v1.6.0-alpha.1415 (develop, build 1415)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -3371,18 +3371,26 @@ class ModelUtils {
3371
3371
  const lookup = new Map();
3372
3372
  const score = tracks[0].score;
3373
3373
  let currentIndex = startIndex;
3374
+ let tempo = score.tempo;
3374
3375
  while (currentIndex <= endIndexInclusive) {
3375
3376
  const currentGroupStartIndex = currentIndex;
3376
3377
  let currentGroup = null;
3377
3378
  while (currentIndex <= endIndexInclusive) {
3378
3379
  const masterBar = score.masterBars[currentIndex];
3380
+ let hasTempoChange = false;
3381
+ for (const a of masterBar.tempoAutomations) {
3382
+ if (a.value !== tempo) {
3383
+ hasTempoChange = true;
3384
+ }
3385
+ tempo = a.value;
3386
+ }
3379
3387
  // check if masterbar breaks multibar rests, it must be fully empty with no annotations
3380
3388
  if (masterBar.alternateEndings ||
3381
3389
  (masterBar.isRepeatStart && masterBar.index !== currentGroupStartIndex) ||
3382
3390
  masterBar.isFreeTime ||
3383
3391
  masterBar.isAnacrusis ||
3384
3392
  masterBar.section !== null ||
3385
- (masterBar.index !== currentGroupStartIndex && masterBar.tempoAutomations.length > 0) ||
3393
+ (masterBar.index !== currentGroupStartIndex && hasTempoChange) ||
3386
3394
  (masterBar.fermata !== null && masterBar.fermata.size > 0) ||
3387
3395
  (masterBar.directions !== null && masterBar.directions.size > 0)) {
3388
3396
  break;
@@ -20215,7 +20223,11 @@ class MusicXmlImporter extends ScoreImporter {
20215
20223
  for (const c of element.childElements()) {
20216
20224
  switch (c.localName) {
20217
20225
  case 'direction-type':
20218
- directionTypes.push(c.firstElement);
20226
+ // See https://github.com/CoderLine/alphaTab/issues/2102
20227
+ const type = c.firstElement;
20228
+ if (type) {
20229
+ directionTypes.push(type);
20230
+ }
20219
20231
  break;
20220
20232
  case 'offset':
20221
20233
  offset = Number.parseFloat(c.innerText);
@@ -38596,6 +38608,93 @@ class AlphaSynthWrapper {
38596
38608
  }
38597
38609
  }
38598
38610
 
38611
+ /**
38612
+ * A {@link IScoreRenderer} implementation wrapping and underling other {@link IScoreRenderer}
38613
+ * allowing dynamic changing of the underlying instance without loosing aspects like the
38614
+ * event listeners.
38615
+ */
38616
+ class ScoreRendererWrapper {
38617
+ constructor() {
38618
+ this._width = 0;
38619
+ this._score = null;
38620
+ this._trackIndexes = null;
38621
+ this.preRender = new EventEmitterOfT();
38622
+ this.renderFinished = new EventEmitterOfT();
38623
+ this.partialRenderFinished = new EventEmitterOfT();
38624
+ this.partialLayoutFinished = new EventEmitterOfT();
38625
+ this.postRenderFinished = new EventEmitter();
38626
+ this.error = new EventEmitterOfT();
38627
+ }
38628
+ get instance() {
38629
+ return this._instance;
38630
+ }
38631
+ set instance(value) {
38632
+ this._instance = value;
38633
+ // unregister all events from previous instance
38634
+ const unregister = this._instanceEventUnregister;
38635
+ if (unregister) {
38636
+ for (const e of unregister) {
38637
+ e();
38638
+ }
38639
+ }
38640
+ if (value) {
38641
+ // regsiter to events of new player and forward them to existing listeners
38642
+ const newUnregister = [];
38643
+ newUnregister.push(value.preRender.on(v => this.preRender.trigger(v)));
38644
+ newUnregister.push(value.renderFinished.on(v => this.renderFinished.trigger(v)));
38645
+ newUnregister.push(value.partialRenderFinished.on(v => this.partialRenderFinished.trigger(v)));
38646
+ newUnregister.push(value.partialLayoutFinished.on(v => this.partialLayoutFinished.trigger(v)));
38647
+ newUnregister.push(value.postRenderFinished.on(() => this.postRenderFinished.trigger()));
38648
+ newUnregister.push(value.error.on(v => this.error.trigger(v)));
38649
+ this._instanceEventUnregister = newUnregister;
38650
+ if (this._settings) {
38651
+ value.updateSettings(this._settings);
38652
+ }
38653
+ value.width = this._width;
38654
+ if (this._score !== null) {
38655
+ value.renderScore(this._score, this._trackIndexes);
38656
+ }
38657
+ }
38658
+ else {
38659
+ this._instanceEventUnregister = undefined;
38660
+ }
38661
+ }
38662
+ get boundsLookup() {
38663
+ return this._instance ? this._instance.boundsLookup : null;
38664
+ }
38665
+ get width() {
38666
+ return this._instance ? this._instance.width : 0;
38667
+ }
38668
+ set width(value) {
38669
+ this._width = value;
38670
+ if (this._instance) {
38671
+ this._instance.width = value;
38672
+ }
38673
+ }
38674
+ render() {
38675
+ this._instance?.render();
38676
+ }
38677
+ resizeRender() {
38678
+ this._instance?.resizeRender();
38679
+ }
38680
+ renderScore(score, trackIndexes) {
38681
+ this._score = score;
38682
+ this._trackIndexes = trackIndexes;
38683
+ this._instance?.renderScore(score, trackIndexes);
38684
+ }
38685
+ renderResult(resultId) {
38686
+ this._instance?.renderResult(resultId);
38687
+ }
38688
+ updateSettings(settings) {
38689
+ this._settings = settings;
38690
+ this._instance?.updateSettings(settings);
38691
+ }
38692
+ destroy() {
38693
+ this._instance?.destroy();
38694
+ this._instance = undefined;
38695
+ }
38696
+ }
38697
+
38599
38698
  class SelectionInfo {
38600
38699
  constructor(beat) {
38601
38700
  this.bounds = null;
@@ -38615,6 +38714,18 @@ class AlphaTabApiBase {
38615
38714
  get actualPlayerMode() {
38616
38715
  return this._actualPlayerMode;
38617
38716
  }
38717
+ /**
38718
+ * The score renderer used for rendering the music sheet.
38719
+ * @remarks
38720
+ * This is the low-level API responsible for the actual rendering engine.
38721
+ * Gets access to the underling {@link IScoreRenderer} that is used for the rendering.
38722
+ *
38723
+ * @category Properties - Core
38724
+ * @since 0.9.4
38725
+ */
38726
+ get renderer() {
38727
+ return this._renderer;
38728
+ }
38618
38729
  /**
38619
38730
  * The score holding all information about the song being rendered
38620
38731
  * @category Properties - Core
@@ -38693,6 +38804,7 @@ class AlphaTabApiBase {
38693
38804
  this._previousTick = 0;
38694
38805
  this._currentBeat = null;
38695
38806
  this._currentBeatBounds = null;
38807
+ this._isInitialBeatCursorUpdate = true;
38696
38808
  this._previousStateForCursor = PlayerState.Paused;
38697
38809
  this._previousCursorCache = null;
38698
38810
  this._lastScroll = 0;
@@ -39351,7 +39463,40 @@ class AlphaTabApiBase {
39351
39463
  */
39352
39464
  this.midiLoaded = new EventEmitterOfT();
39353
39465
  /**
39354
- * @internal
39466
+ * This event is fired when a settings update was requested.
39467
+ *
39468
+ * @eventProperty
39469
+ * @category Events - Core
39470
+ * @since 1.6.0
39471
+ *
39472
+ * @example
39473
+ * JavaScript
39474
+ * ```js
39475
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
39476
+ * api.settingsUpdated.on(() => {
39477
+ * updateSettingsUI(api.settings);
39478
+ * });
39479
+ * ```
39480
+ *
39481
+ * @example
39482
+ * C#
39483
+ * ```cs
39484
+ * var api = new AlphaTabApi<MyControl>(...);
39485
+ * api.SettingsUpdated.On(() =>
39486
+ * {
39487
+ * UpdateSettingsUI(api.settings);
39488
+ * });
39489
+ * ```
39490
+ *
39491
+ * @example
39492
+ * Android
39493
+ * ```kotlin
39494
+ * val api = AlphaTabApi<MyControl>(...)
39495
+ * api.SettingsUpdated.on {
39496
+ * updateSettingsUI(api.settings)
39497
+ * }
39498
+ * ```
39499
+ *
39355
39500
  */
39356
39501
  this.settingsUpdated = new EventEmitter();
39357
39502
  this.uiFacade = uiFacade;
@@ -39365,46 +39510,46 @@ class AlphaTabApiBase {
39365
39510
  Environment.printEnvironmentInfo(false);
39366
39511
  this.canvasElement = uiFacade.createCanvasElement();
39367
39512
  this.container.appendChild(this.canvasElement);
39513
+ this._renderer = new ScoreRendererWrapper();
39368
39514
  if (this.settings.core.useWorkers &&
39369
39515
  this.uiFacade.areWorkersSupported &&
39370
39516
  Environment.getRenderEngineFactory(this.settings.core.engine).supportsWorkers) {
39371
- this.renderer = this.uiFacade.createWorkerRenderer();
39517
+ this._renderer.instance = this.uiFacade.createWorkerRenderer();
39372
39518
  }
39373
39519
  else {
39374
- this.renderer = new ScoreRenderer(this.settings);
39520
+ this._renderer.instance = new ScoreRenderer(this.settings);
39375
39521
  }
39376
39522
  this.container.resize.on(Environment.throttle(() => {
39377
39523
  if (this._isDestroyed) {
39378
39524
  return;
39379
39525
  }
39380
- if (this.container.width !== this.renderer.width) {
39526
+ if (this.container.width !== this._renderer.width) {
39381
39527
  this.triggerResize();
39382
39528
  }
39383
39529
  }, uiFacade.resizeThrottle));
39384
39530
  const initialResizeEventInfo = new ResizeEventArgs();
39385
- initialResizeEventInfo.oldWidth = this.renderer.width;
39531
+ initialResizeEventInfo.oldWidth = this._renderer.width;
39386
39532
  initialResizeEventInfo.newWidth = this.container.width | 0;
39387
39533
  initialResizeEventInfo.settings = this.settings;
39388
39534
  this.onResize(initialResizeEventInfo);
39389
- this.renderer.preRender.on(this.onRenderStarted.bind(this));
39390
- this.renderer.renderFinished.on(renderingResult => {
39535
+ this._renderer.preRender.on(this.onRenderStarted.bind(this));
39536
+ this._renderer.renderFinished.on(renderingResult => {
39391
39537
  this.onRenderFinished(renderingResult);
39392
39538
  });
39393
- this.renderer.postRenderFinished.on(() => {
39539
+ this._renderer.postRenderFinished.on(() => {
39394
39540
  const duration = Date.now() - this._startTime;
39395
39541
  Logger.debug('rendering', `Rendering completed in ${duration}ms`);
39396
39542
  this.onPostRenderFinished();
39397
39543
  });
39398
- this.renderer.preRender.on(_ => {
39544
+ this._renderer.preRender.on(_ => {
39399
39545
  this._startTime = Date.now();
39400
39546
  });
39401
- this.renderer.partialLayoutFinished.on(this.appendRenderResult.bind(this));
39402
- this.renderer.partialRenderFinished.on(this.updateRenderResult.bind(this));
39403
- this.renderer.renderFinished.on(r => {
39404
- this.appendRenderResult(r);
39405
- this.appendRenderResult(null); // marks last element
39547
+ this._renderer.partialLayoutFinished.on(r => this.appendRenderResult(r, false));
39548
+ this._renderer.partialRenderFinished.on(this.updateRenderResult.bind(this));
39549
+ this._renderer.renderFinished.on(r => {
39550
+ this.appendRenderResult(r, true);
39406
39551
  });
39407
- this.renderer.error.on(this.onError.bind(this));
39552
+ this._renderer.error.on(this.onError.bind(this));
39408
39553
  this.setupPlayerWrapper();
39409
39554
  if (this.settings.player.playerMode !== PlayerMode.Disabled) {
39410
39555
  this.setupOrDestroyPlayer();
@@ -39479,7 +39624,7 @@ class AlphaTabApiBase {
39479
39624
  this._isDestroyed = true;
39480
39625
  this._player.destroy();
39481
39626
  this.uiFacade.destroy();
39482
- this.renderer.destroy();
39627
+ this._renderer.destroy();
39483
39628
  }
39484
39629
  /**
39485
39630
  * Applies any changes that were done to the settings object.
@@ -39524,12 +39669,30 @@ class AlphaTabApiBase {
39524
39669
  if (score) {
39525
39670
  ModelUtils.applyPitchOffsets(this.settings, score);
39526
39671
  }
39527
- this.renderer.updateSettings(this.settings);
39528
- if (this.setupOrDestroyPlayer()) {
39529
- this.loadMidiForScore();
39530
- }
39672
+ this.updateRenderer();
39673
+ this._renderer.updateSettings(this.settings);
39674
+ this.setupOrDestroyPlayer();
39531
39675
  this.onSettingsUpdated();
39532
39676
  }
39677
+ updateRenderer() {
39678
+ const renderer = this._renderer;
39679
+ if (this.settings.core.useWorkers &&
39680
+ this.uiFacade.areWorkersSupported &&
39681
+ Environment.getRenderEngineFactory(this.settings.core.engine).supportsWorkers) {
39682
+ // switch from non-worker to worker renderer
39683
+ if (renderer.instance instanceof ScoreRenderer) {
39684
+ renderer.destroy();
39685
+ renderer.instance = this.uiFacade.createWorkerRenderer();
39686
+ }
39687
+ }
39688
+ else {
39689
+ // switch from worker to non-worker renderer
39690
+ if (!(renderer.instance instanceof ScoreRenderer)) {
39691
+ renderer.destroy();
39692
+ renderer.instance = new ScoreRenderer(this.settings);
39693
+ }
39694
+ }
39695
+ }
39533
39696
  /**
39534
39697
  * Initiates a load of the score using the given data.
39535
39698
  * @returns true if the data object is supported and a load was initiated, otherwise false
@@ -39724,30 +39887,33 @@ class AlphaTabApiBase {
39724
39887
  }
39725
39888
  else {
39726
39889
  const resizeEventInfo = new ResizeEventArgs();
39727
- resizeEventInfo.oldWidth = this.renderer.width;
39890
+ resizeEventInfo.oldWidth = this._renderer.width;
39728
39891
  resizeEventInfo.newWidth = this.container.width;
39729
39892
  resizeEventInfo.settings = this.settings;
39730
39893
  this.onResize(resizeEventInfo);
39731
- this.renderer.updateSettings(this.settings);
39732
- this.renderer.width = this.container.width;
39733
- this.renderer.resizeRender();
39894
+ this._renderer.updateSettings(this.settings);
39895
+ this._renderer.width = this.container.width;
39896
+ this._renderer.resizeRender();
39734
39897
  }
39735
39898
  }
39736
- appendRenderResult(result) {
39737
- if (result) {
39899
+ appendRenderResult(result, isLast) {
39900
+ // resizing the canvas and wrapper elements at the end is enough
39901
+ // it avoids flickering on resizes and re-renders.
39902
+ // the individual partials are anyhow sized correctly
39903
+ if (isLast) {
39738
39904
  this.canvasElement.width = result.totalWidth;
39739
39905
  this.canvasElement.height = result.totalHeight;
39740
39906
  if (this._cursorWrapper) {
39741
39907
  this._cursorWrapper.width = result.totalWidth;
39742
39908
  this._cursorWrapper.height = result.totalHeight;
39743
39909
  }
39744
- if (result.width > 0 || result.height > 0) {
39745
- this.uiFacade.beginAppendRenderResults(result);
39746
- }
39747
39910
  }
39748
- else {
39911
+ if (result.width > 0 || result.height > 0) {
39749
39912
  this.uiFacade.beginAppendRenderResults(result);
39750
39913
  }
39914
+ if (isLast) {
39915
+ this.uiFacade.beginAppendRenderResults(null);
39916
+ }
39751
39917
  }
39752
39918
  updateRenderResult(result) {
39753
39919
  if (result && result.renderResult) {
@@ -39909,13 +40075,10 @@ class AlphaTabApiBase {
39909
40075
  * ```
39910
40076
  */
39911
40077
  render() {
39912
- if (!this.renderer) {
39913
- return;
39914
- }
39915
40078
  if (this.uiFacade.canRender) {
39916
40079
  // when font is finally loaded, start rendering
39917
- this.renderer.width = this.container.width;
39918
- this.renderer.renderScore(this.score, this._trackIndexes);
40080
+ this._renderer.width = this.container.width;
40081
+ this._renderer.renderScore(this.score, this._trackIndexes);
39919
40082
  }
39920
40083
  else {
39921
40084
  this.uiFacade.canRenderChanged.on(() => this.render());
@@ -39990,7 +40153,7 @@ class AlphaTabApiBase {
39990
40153
  * @since 1.5.0
39991
40154
  */
39992
40155
  get boundsLookup() {
39993
- return this.renderer.boundsLookup;
40156
+ return this._renderer.boundsLookup;
39994
40157
  }
39995
40158
  /**
39996
40159
  * The alphaSynth player used for playback.
@@ -40422,6 +40585,10 @@ class AlphaTabApiBase {
40422
40585
  this._previousTick = 0;
40423
40586
  this.destroyCursors();
40424
40587
  }
40588
+ /**
40589
+ *
40590
+ * @returns true if a new player was created, false if no player was created (includes destroy & reuse of the current one)
40591
+ */
40425
40592
  setupOrDestroyPlayer() {
40426
40593
  let mode = this.settings.player.playerMode;
40427
40594
  if (mode === PlayerMode.EnabledAutomatic) {
@@ -40439,6 +40606,7 @@ class AlphaTabApiBase {
40439
40606
  let newPlayer = null;
40440
40607
  if (mode !== this._actualPlayerMode) {
40441
40608
  this.destroyPlayer();
40609
+ this.updateCursors();
40442
40610
  switch (mode) {
40443
40611
  case PlayerMode.Disabled:
40444
40612
  newPlayer = null;
@@ -40456,9 +40624,9 @@ class AlphaTabApiBase {
40456
40624
  }
40457
40625
  else {
40458
40626
  // no change in player mode, just update song info if needed
40459
- return true;
40627
+ this.updateCursors();
40628
+ return false;
40460
40629
  }
40461
- this.updateCursors();
40462
40630
  this._actualPlayerMode = mode;
40463
40631
  if (!newPlayer) {
40464
40632
  return false;
@@ -40466,6 +40634,13 @@ class AlphaTabApiBase {
40466
40634
  this._player.instance = newPlayer;
40467
40635
  return false;
40468
40636
  }
40637
+ /**
40638
+ * Re-creates the midi for the current score and loads it.
40639
+ * @remarks
40640
+ * This will result in the player to stop playback. Some setting changes require re-genration of the midi song.
40641
+ * @category Methods - Player
40642
+ * @since 1.6.0
40643
+ */
40469
40644
  loadMidiForScore() {
40470
40645
  if (!this.score) {
40471
40646
  return;
@@ -40869,6 +41044,7 @@ class AlphaTabApiBase {
40869
41044
  this._barCursor = cursors.barCursor;
40870
41045
  this._beatCursor = cursors.beatCursor;
40871
41046
  this._selectionWrapper = cursors.selectionWrapper;
41047
+ this._isInitialBeatCursorUpdate = true;
40872
41048
  }
40873
41049
  if (this._currentBeat !== null) {
40874
41050
  this.cursorUpdateBeat(this._currentBeat, false, this._previousTick > 10, 1, true);
@@ -40908,7 +41084,7 @@ class AlphaTabApiBase {
40908
41084
  if (!beat) {
40909
41085
  return;
40910
41086
  }
40911
- const cache = this.renderer.boundsLookup;
41087
+ const cache = this._renderer.boundsLookup;
40912
41088
  if (!cache) {
40913
41089
  return;
40914
41090
  }
@@ -41031,6 +41207,7 @@ class AlphaTabApiBase {
41031
41207
  if (isPlayingUpdate) {
41032
41208
  // we do not "reset" the cursor if we are smoothly moving from left to right.
41033
41209
  const jumpCursor = !previousBeatBounds ||
41210
+ this._isInitialBeatCursorUpdate ||
41034
41211
  barBounds.y !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.y ||
41035
41212
  startBeatX < previousBeatBounds.onNotesX ||
41036
41213
  barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1;
@@ -41055,13 +41232,16 @@ class AlphaTabApiBase {
41055
41232
  }
41056
41233
  else {
41057
41234
  // ticking cursor
41235
+ beatCursor.transitionToX(0, startBeatX);
41058
41236
  beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
41059
41237
  }
41238
+ this._isInitialBeatCursorUpdate = false;
41060
41239
  }
41061
- // if playing, animate the cursor to the next beat
41062
- if (this.settings.player.enableElementHighlighting) {
41063
- this.uiFacade.removeHighlights();
41240
+ else {
41241
+ this._isInitialBeatCursorUpdate = true;
41064
41242
  }
41243
+ // if playing, animate the cursor to the next beat
41244
+ this.uiFacade.removeHighlights();
41065
41245
  // actively playing? -> animate cursor and highlight items
41066
41246
  let shouldNotifyBeatChange = false;
41067
41247
  if (isPlayingUpdate) {
@@ -41225,11 +41405,11 @@ class AlphaTabApiBase {
41225
41405
  }
41226
41406
  const relX = e.getX(this.canvasElement);
41227
41407
  const relY = e.getY(this.canvasElement);
41228
- const beat = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41408
+ const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41229
41409
  if (beat) {
41230
41410
  this.onBeatMouseDown(e, beat);
41231
41411
  if (this.settings.core.includeNoteBounds) {
41232
- const note = this.renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
41412
+ const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
41233
41413
  if (note) {
41234
41414
  this.onNoteMouseDown(e, note);
41235
41415
  }
@@ -41242,11 +41422,11 @@ class AlphaTabApiBase {
41242
41422
  }
41243
41423
  const relX = e.getX(this.canvasElement);
41244
41424
  const relY = e.getY(this.canvasElement);
41245
- const beat = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41425
+ const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41246
41426
  if (beat) {
41247
41427
  this.onBeatMouseMove(e, beat);
41248
41428
  if (this._noteMouseDown) {
41249
- const note = this.renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
41429
+ const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
41250
41430
  if (note) {
41251
41431
  this.onNoteMouseMove(e, note);
41252
41432
  }
@@ -41262,11 +41442,11 @@ class AlphaTabApiBase {
41262
41442
  }
41263
41443
  const relX = e.getX(this.canvasElement);
41264
41444
  const relY = e.getY(this.canvasElement);
41265
- const beat = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41445
+ const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
41266
41446
  this.onBeatMouseUp(e, beat);
41267
41447
  if (this._noteMouseDown) {
41268
41448
  if (beat) {
41269
- const note = this.renderer.boundsLookup?.getNoteAtPos(beat, relX, relY) ?? null;
41449
+ const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY) ?? null;
41270
41450
  this.onNoteMouseUp(e, note);
41271
41451
  }
41272
41452
  else {
@@ -41274,7 +41454,7 @@ class AlphaTabApiBase {
41274
41454
  }
41275
41455
  }
41276
41456
  });
41277
- this.renderer.postRenderFinished.on(() => {
41457
+ this._renderer.postRenderFinished.on(() => {
41278
41458
  if (!this._selectionStart ||
41279
41459
  this.settings.player.playerMode === PlayerMode.Disabled ||
41280
41460
  !this.settings.player.enableCursor ||
@@ -41285,7 +41465,7 @@ class AlphaTabApiBase {
41285
41465
  });
41286
41466
  }
41287
41467
  cursorSelectRange(startBeat, endBeat) {
41288
- const cache = this.renderer.boundsLookup;
41468
+ const cache = this._renderer.boundsLookup;
41289
41469
  if (!cache) {
41290
41470
  return;
41291
41471
  }
@@ -41354,7 +41534,8 @@ class AlphaTabApiBase {
41354
41534
  }
41355
41535
  this.scoreLoaded.trigger(score);
41356
41536
  this.uiFacade.triggerEvent(this.container, 'scoreLoaded', score);
41357
- if (this.setupOrDestroyPlayer()) {
41537
+ if (!this.setupOrDestroyPlayer()) {
41538
+ // feed midi into current player (a new player will trigger a midi generation once the player is ready)
41358
41539
  this.loadMidiForScore();
41359
41540
  }
41360
41541
  }
@@ -41384,7 +41565,7 @@ class AlphaTabApiBase {
41384
41565
  return;
41385
41566
  }
41386
41567
  this._currentBeat = null;
41387
- this.cursorUpdateTick(this._previousTick, false, 1, this._previousTick > 10);
41568
+ this.cursorUpdateTick(this._previousTick, false, 1, true, true);
41388
41569
  this.postRenderFinished.trigger();
41389
41570
  this.uiFacade.triggerEvent(this.container, 'postRenderFinished', null);
41390
41571
  }
@@ -46940,7 +47121,7 @@ class StaffSystem {
46940
47121
  return this.masterBarsRenderers[0].masterBar.index;
46941
47122
  }
46942
47123
  get lastBarIndex() {
46943
- return this.masterBarsRenderers[this.masterBarsRenderers.length - 1].masterBar.index;
47124
+ return this.masterBarsRenderers[this.masterBarsRenderers.length - 1].lastMasterBarIndex;
46944
47125
  }
46945
47126
  addMasterBarRenderers(tracks, renderers) {
46946
47127
  if (tracks.length === 0) {
@@ -47754,6 +47935,9 @@ class ScoreLayout {
47754
47935
  }
47755
47936
  }
47756
47937
  }
47938
+ else {
47939
+ this.tuningGlyph = null;
47940
+ }
47757
47941
  }
47758
47942
  // chord diagram glyphs
47759
47943
  if (notation.isNotationElementVisible(NotationElement.ChordDiagrams)) {
@@ -47782,6 +47966,12 @@ class ScoreLayout {
47782
47966
  }
47783
47967
  }
47784
47968
  }
47969
+ if (this.chordDiagrams.isEmpty) {
47970
+ this.chordDiagrams = null;
47971
+ }
47972
+ }
47973
+ else {
47974
+ this.chordDiagrams = null;
47785
47975
  }
47786
47976
  }
47787
47977
  createEmptyStaffSystem() {
@@ -60525,9 +60715,9 @@ class VersionInfo {
60525
60715
  print(`build date: ${VersionInfo.date}`);
60526
60716
  }
60527
60717
  }
60528
- VersionInfo.version = '1.6.0-alpha.1409';
60529
- VersionInfo.date = '2025-05-14T02:06:32.990Z';
60530
- VersionInfo.commit = 'f91fed13b0a0946b3fa9306b480ddf8044b948e2';
60718
+ VersionInfo.version = '1.6.0-alpha.1415';
60719
+ VersionInfo.date = '2025-05-19T16:08:22.342Z';
60720
+ VersionInfo.commit = '459db69f8896a2ea8822ce5d49dcc824edd36521';
60531
60721
 
60532
60722
  /**
60533
60723
  * A factory for custom layout engines.
@@ -471,6 +471,7 @@ export declare class AlphaTabApiBase<TSettings> {
471
471
  private _tracks;
472
472
  private _actualPlayerMode;
473
473
  private _player;
474
+ private _renderer;
474
475
  /**
475
476
  * The actual player mode which is currently active (e.g. allows determining whether a backing track or the synthesizer is active).
476
477
  */
@@ -516,7 +517,7 @@ export declare class AlphaTabApiBase<TSettings> {
516
517
  * @category Properties - Core
517
518
  * @since 0.9.4
518
519
  */
519
- readonly renderer: IScoreRenderer;
520
+ get renderer(): IScoreRenderer;
520
521
  /**
521
522
  * The score holding all information about the song being rendered
522
523
  * @category Properties - Core
@@ -682,6 +683,7 @@ export declare class AlphaTabApiBase<TSettings> {
682
683
  * ```
683
684
  */
684
685
  updateSettings(): void;
686
+ private updateRenderer;
685
687
  /**
686
688
  * Initiates a load of the score using the given data.
687
689
  * @returns true if the data object is supported and a load was initiated, otherwise false
@@ -1376,8 +1378,19 @@ export declare class AlphaTabApiBase<TSettings> {
1376
1378
  get isLooping(): boolean;
1377
1379
  set isLooping(value: boolean);
1378
1380
  private destroyPlayer;
1381
+ /**
1382
+ *
1383
+ * @returns true if a new player was created, false if no player was created (includes destroy & reuse of the current one)
1384
+ */
1379
1385
  private setupOrDestroyPlayer;
1380
- private loadMidiForScore;
1386
+ /**
1387
+ * Re-creates the midi for the current score and loads it.
1388
+ * @remarks
1389
+ * This will result in the player to stop playback. Some setting changes require re-genration of the midi song.
1390
+ * @category Methods - Player
1391
+ * @since 1.6.0
1392
+ */
1393
+ loadMidiForScore(): void;
1381
1394
  /**
1382
1395
  * Changes the volume of the given tracks.
1383
1396
  * @param tracks The tracks for which the volume should be changed.
@@ -1703,6 +1716,7 @@ export declare class AlphaTabApiBase<TSettings> {
1703
1716
  private _previousTick;
1704
1717
  private _currentBeat;
1705
1718
  private _currentBeatBounds;
1719
+ private _isInitialBeatCursorUpdate;
1706
1720
  private _previousStateForCursor;
1707
1721
  private _previousCursorCache;
1708
1722
  private _lastScroll;
@@ -2741,7 +2755,43 @@ export declare class AlphaTabApiBase<TSettings> {
2741
2755
  */
2742
2756
  get playbackRangeChanged(): IEventEmitterOfT<PlaybackRangeChangedEventArgs>;
2743
2757
  private onPlaybackRangeChanged;
2744
- /* Excluded from this release type: settingsUpdated */
2758
+ /**
2759
+ * This event is fired when a settings update was requested.
2760
+ *
2761
+ * @eventProperty
2762
+ * @category Events - Core
2763
+ * @since 1.6.0
2764
+ *
2765
+ * @example
2766
+ * JavaScript
2767
+ * ```js
2768
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
2769
+ * api.settingsUpdated.on(() => {
2770
+ * updateSettingsUI(api.settings);
2771
+ * });
2772
+ * ```
2773
+ *
2774
+ * @example
2775
+ * C#
2776
+ * ```cs
2777
+ * var api = new AlphaTabApi<MyControl>(...);
2778
+ * api.SettingsUpdated.On(() =>
2779
+ * {
2780
+ * UpdateSettingsUI(api.settings);
2781
+ * });
2782
+ * ```
2783
+ *
2784
+ * @example
2785
+ * Android
2786
+ * ```kotlin
2787
+ * val api = AlphaTabApi<MyControl>(...)
2788
+ * api.SettingsUpdated.on {
2789
+ * updateSettingsUI(api.settings)
2790
+ * }
2791
+ * ```
2792
+ *
2793
+ */
2794
+ settingsUpdated: IEventEmitter;
2745
2795
  private onSettingsUpdated;
2746
2796
  /**
2747
2797
  * Loads and lists the available output devices which can be used by the player.