@coderline/alphatab 1.8.0-alpha.1636 → 1.8.0-alpha.1637

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.8.0-alpha.1636 (develop, build 1636)
2
+ * alphaTab v1.8.0-alpha.1637 (develop, build 1637)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -209,9 +209,9 @@
209
209
  * @internal
210
210
  */
211
211
  class VersionInfo {
212
- static version = '1.8.0-alpha.1636';
213
- static date = '2025-12-06T02:04:52.462Z';
214
- static commit = '49c68ed64403080028c852fe371cc6bdcca8dc3a';
212
+ static version = '1.8.0-alpha.1637';
213
+ static date = '2025-12-07T02:26:27.382Z';
214
+ static commit = 'aa2c8101456d3ddfc777491468bc483789cc6c12';
215
215
  static print(print) {
216
216
  print(`alphaTab ${VersionInfo.version}`);
217
217
  print(`commit: ${VersionInfo.commit}`);
@@ -12956,25 +12956,31 @@
12956
12956
  /**
12957
12957
  * Gets or sets the X-position of the rectangle within the music notation.
12958
12958
  */
12959
- x = 0;
12959
+ x;
12960
12960
  /**
12961
12961
  * Gets or sets the Y-position of the rectangle within the music notation.
12962
12962
  */
12963
- y = 0;
12963
+ y;
12964
12964
  /**
12965
12965
  * Gets or sets the width of the rectangle.
12966
12966
  */
12967
- w = 0;
12967
+ w;
12968
12968
  /**
12969
12969
  * Gets or sets the height of the rectangle.
12970
12970
  */
12971
- h = 0;
12971
+ h;
12972
12972
  scaleWith(scale) {
12973
12973
  this.x *= scale;
12974
12974
  this.y *= scale;
12975
12975
  this.w *= scale;
12976
12976
  this.h *= scale;
12977
12977
  }
12978
+ constructor(x = 0, y = 0, w = 0, h = 0) {
12979
+ this.x = x;
12980
+ this.y = y;
12981
+ this.h = h;
12982
+ this.w = w;
12983
+ }
12978
12984
  }
12979
12985
 
12980
12986
  /**
@@ -49460,16 +49466,6 @@
49460
49466
  }
49461
49467
  }
49462
49468
 
49463
- /**
49464
- * @internal
49465
- */
49466
- class SelectionInfo {
49467
- beat;
49468
- bounds = null;
49469
- constructor(beat) {
49470
- this.beat = beat;
49471
- }
49472
- }
49473
49469
  /**
49474
49470
  * This class represents the public API of alphaTab and provides all logic to display
49475
49471
  * a music sheet in any UI using the given {@link IUiFacade}
@@ -51569,8 +51565,8 @@
51569
51565
  }
51570
51566
  _isBeatMouseDown = false;
51571
51567
  _isNoteMouseDown = false;
51572
- _selectionStart = null;
51573
- _selectionEnd = null;
51568
+ _selectionStart;
51569
+ _selectionEnd;
51574
51570
  /**
51575
51571
  * This event is fired whenever a the user presses the mouse button on a beat.
51576
51572
  * @eventProperty
@@ -51813,8 +51809,8 @@
51813
51809
  return;
51814
51810
  }
51815
51811
  if (this._hasCursor && this.settings.player.enableUserInteraction) {
51816
- this._selectionStart = new SelectionInfo(beat);
51817
- this._selectionEnd = null;
51812
+ this._selectionStart = { beat };
51813
+ this._selectionEnd = undefined;
51818
51814
  }
51819
51815
  this._isBeatMouseDown = true;
51820
51816
  this.beatMouseDown.trigger(beat);
@@ -51834,7 +51830,7 @@
51834
51830
  }
51835
51831
  if (this.settings.player.enableUserInteraction) {
51836
51832
  if (!this._selectionEnd || this._selectionEnd.beat !== beat) {
51837
- this._selectionEnd = new SelectionInfo(beat);
51833
+ this._selectionEnd = { beat };
51838
51834
  this._cursorSelectRange(this._selectionStart, this._selectionEnd);
51839
51835
  }
51840
51836
  }
@@ -51853,45 +51849,7 @@
51853
51849
  return;
51854
51850
  }
51855
51851
  if (this._hasCursor && this.settings.player.enableUserInteraction) {
51856
- if (this._selectionEnd) {
51857
- const startTick = this._tickCache?.getBeatStart(this._selectionStart.beat) ??
51858
- this._selectionStart.beat.absolutePlaybackStart;
51859
- const endTick = this._tickCache?.getBeatStart(this._selectionEnd.beat) ??
51860
- this._selectionEnd.beat.absolutePlaybackStart;
51861
- if (endTick < startTick) {
51862
- const t = this._selectionStart;
51863
- this._selectionStart = this._selectionEnd;
51864
- this._selectionEnd = t;
51865
- }
51866
- }
51867
- if (this._selectionStart && this._tickCache) {
51868
- // get the start and stop ticks (which consider properly repeats)
51869
- const tickCache = this._tickCache;
51870
- const realMasterBarStart = tickCache.getMasterBarStart(this._selectionStart.beat.voice.bar.masterBar);
51871
- // move to selection start
51872
- this._currentBeat = null; // reset current beat so it is updating the cursor
51873
- if (this._player.state === PlayerState.Paused) {
51874
- this._cursorUpdateTick(this._tickCache.getBeatStart(this._selectionStart.beat), false, 1);
51875
- }
51876
- this.tickPosition = realMasterBarStart + this._selectionStart.beat.playbackStart;
51877
- // set playback range
51878
- if (this._selectionEnd && this._selectionStart.beat !== this._selectionEnd.beat) {
51879
- const realMasterBarEnd = tickCache.getMasterBarStart(this._selectionEnd.beat.voice.bar.masterBar);
51880
- const range = new PlaybackRange();
51881
- range.startTick = realMasterBarStart + this._selectionStart.beat.playbackStart;
51882
- range.endTick =
51883
- realMasterBarEnd +
51884
- this._selectionEnd.beat.playbackStart +
51885
- this._selectionEnd.beat.playbackDuration -
51886
- 50;
51887
- this.playbackRange = range;
51888
- }
51889
- else {
51890
- this._selectionStart = null;
51891
- this.playbackRange = null;
51892
- this._cursorSelectRange(this._selectionStart, this._selectionEnd);
51893
- }
51894
- }
51852
+ this.applyPlaybackRangeFromHighlight();
51895
51853
  }
51896
51854
  this.beatMouseUp.trigger(beat);
51897
51855
  this.uiFacade.triggerEvent(this.container, 'beatMouseUp', beat, originalEvent);
@@ -51913,13 +51871,13 @@
51913
51871
  const startBeat = this._tickCache.findBeat(this._trackIndexLookup, range.startTick);
51914
51872
  const endBeat = this._tickCache.findBeat(this._trackIndexLookup, range.endTick);
51915
51873
  if (startBeat && endBeat) {
51916
- const selectionStart = new SelectionInfo(startBeat.beat);
51917
- const selectionEnd = new SelectionInfo(endBeat.beat);
51874
+ const selectionStart = { beat: startBeat.beat };
51875
+ const selectionEnd = { beat: endBeat.beat };
51918
51876
  this._cursorSelectRange(selectionStart, selectionEnd);
51919
51877
  }
51920
51878
  }
51921
51879
  else {
51922
- this._cursorSelectRange(null, null);
51880
+ this._cursorSelectRange(undefined, undefined);
51923
51881
  }
51924
51882
  }
51925
51883
  _setupClickHandling() {
@@ -51988,24 +51946,230 @@
51988
51946
  this._cursorSelectRange(this._selectionStart, this._selectionEnd);
51989
51947
  });
51990
51948
  }
51949
+ /**
51950
+ * Places the highlight markers at the specified start and end-beat range.
51951
+ * @param startBeat The start beat where the selection should start
51952
+ * @param endBeat The end beat where the selection should end.
51953
+ *
51954
+ * @remarks
51955
+ * Unlike actually setting {@link playbackRange} this method only places the selection markers without actually
51956
+ * changing the playback range. This method can be used when building custom selection systems (e.g. having draggable handles).
51957
+ *
51958
+ * @category Methods - Player
51959
+ * @since 1.8.0
51960
+ *
51961
+ * @example
51962
+ * JavaScript
51963
+ * ```js
51964
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
51965
+ * const startBeat = api.score.tracks[0].staves[0].bars[0].voices[0].beats[0];
51966
+ * const endBeat = api.score.tracks[0].staves[0].bars[3].voices[0].beats[0];
51967
+ * api.highlightPlaybackRange(startBeat, endBeat);
51968
+ * ```
51969
+ *
51970
+ * @example
51971
+ * C#
51972
+ * ```cs
51973
+ * var api = new AlphaTabApi<MyControl>(...);
51974
+ * api.ChangeTrackVolume(new Track[] { api.Score.Tracks[0], api.Score.Tracks[1] }, 1.5);
51975
+ * api.ChangeTrackVolume(new Track[] { api.Score.Tracks[2] }, 0.5);
51976
+ * var startBeat = api.Score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0];
51977
+ * var endBeat = api.Score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0];
51978
+ * api.HighlightPlaybackRange(startBeat, endBeat);
51979
+ * ```
51980
+ *
51981
+ * @example
51982
+ * Android
51983
+ * ```kotlin
51984
+ * val api = AlphaTabApi<MyControl>(...)
51985
+ * val startBeat = api.score.tracks[0].staves[0].bars[0].voices[0].beats[0]
51986
+ * val endBeat = api.score.tracks[0].staves[0].bars[3].voices[0].beats[0]
51987
+ * api.highlightPlaybackRange(startBeat, endBeat)
51988
+ * ```
51989
+ */
51990
+ highlightPlaybackRange(startBeat, endBeat) {
51991
+ this._selectionStart = { beat: startBeat };
51992
+ this._selectionEnd = { beat: endBeat };
51993
+ this._cursorSelectRange(this._selectionStart, this._selectionEnd);
51994
+ }
51995
+ /**
51996
+ * Applies the playback range from the currently highlighted range.
51997
+ *
51998
+ * @remarks
51999
+ * This method can be used when building custom selection systems (e.g. having draggable handles).
52000
+ *
52001
+ * @category Methods - Player
52002
+ * @since 1.8.0
52003
+ *
52004
+ * @example
52005
+ * JavaScript
52006
+ * ```js
52007
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
52008
+ * const startBeat = api.score.tracks[0].staves[0].bars[0].voices[0].beats[0];
52009
+ * const endBeat = api.score.tracks[0].staves[0].bars[3].voices[0].beats[0];
52010
+ * api.highlightPlaybackRange(startBeat, endBeat);
52011
+ * api.applyPlaybackRangeFromHighlight();
52012
+ * ```
52013
+ *
52014
+ * @example
52015
+ * C#
52016
+ * ```cs
52017
+ * var api = new AlphaTabApi<MyControl>(...);
52018
+ * api.ChangeTrackVolume(new Track[] { api.Score.Tracks[0], api.Score.Tracks[1] }, 1.5);
52019
+ * api.ChangeTrackVolume(new Track[] { api.Score.Tracks[2] }, 0.5);
52020
+ * var startBeat = api.Score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0];
52021
+ * var endBeat = api.Score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0];
52022
+ * api.HighlightPlaybackRange(startBeat, endBeat);
52023
+ * api.ApplyPlaybackRangeFromHighlight();
52024
+ * ```
52025
+ *
52026
+ * @example
52027
+ * Android
52028
+ * ```kotlin
52029
+ * val api = AlphaTabApi<MyControl>(...)
52030
+ * val startBeat = api.score.tracks[0].staves[0].bars[0].voices[0].beats[0]
52031
+ * val endBeat = api.score.tracks[0].staves[0].bars[3].voices[0].beats[0]
52032
+ * api.highlightPlaybackRange(startBeat, endBeat)
52033
+ * api.applyPlaybackRangeFromHighlight()
52034
+ * ```
52035
+ */
52036
+ applyPlaybackRangeFromHighlight() {
52037
+ if (this._selectionEnd) {
52038
+ const startTick = this._tickCache?.getBeatStart(this._selectionStart.beat) ??
52039
+ this._selectionStart.beat.absolutePlaybackStart;
52040
+ const endTick = this._tickCache?.getBeatStart(this._selectionEnd.beat) ??
52041
+ this._selectionEnd.beat.absolutePlaybackStart;
52042
+ if (endTick < startTick) {
52043
+ const t = this._selectionStart;
52044
+ this._selectionStart = this._selectionEnd;
52045
+ this._selectionEnd = t;
52046
+ }
52047
+ }
52048
+ if (this._selectionStart && this._tickCache) {
52049
+ // get the start and stop ticks (which consider properly repeats)
52050
+ const tickCache = this._tickCache;
52051
+ const realMasterBarStart = tickCache.getMasterBarStart(this._selectionStart.beat.voice.bar.masterBar);
52052
+ // move to selection start
52053
+ this._currentBeat = null; // reset current beat so it is updating the cursor
52054
+ if (this._player.state === PlayerState.Paused) {
52055
+ this._cursorUpdateTick(this._tickCache.getBeatStart(this._selectionStart.beat), false, 1);
52056
+ }
52057
+ this.tickPosition = realMasterBarStart + this._selectionStart.beat.playbackStart;
52058
+ // set playback range
52059
+ if (this._selectionEnd && this._selectionStart.beat !== this._selectionEnd.beat) {
52060
+ const realMasterBarEnd = tickCache.getMasterBarStart(this._selectionEnd.beat.voice.bar.masterBar);
52061
+ const range = new PlaybackRange();
52062
+ range.startTick = realMasterBarStart + this._selectionStart.beat.playbackStart;
52063
+ range.endTick =
52064
+ realMasterBarEnd +
52065
+ this._selectionEnd.beat.playbackStart +
52066
+ this._selectionEnd.beat.playbackDuration -
52067
+ 50;
52068
+ this.playbackRange = range;
52069
+ }
52070
+ else {
52071
+ this._selectionStart = undefined;
52072
+ this.playbackRange = null;
52073
+ this._cursorSelectRange(this._selectionStart, this._selectionEnd);
52074
+ }
52075
+ }
52076
+ }
52077
+ /**
52078
+ * Clears the highlight markers marking the currently selected playback range.
52079
+ *
52080
+ * @remarks
52081
+ * Unlike actually setting {@link playbackRange} this method only clears the selection markers without actually
52082
+ * changing the playback range. This method can be used when building custom selection systems (e.g. having draggable handles).
52083
+ *
52084
+ * @category Methods - Player
52085
+ * @since 1.8.0
52086
+ *
52087
+ * @example
52088
+ * JavaScript
52089
+ * ```js
52090
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
52091
+ * api.clearPlaybackRangeHighlight();
52092
+ * ```
52093
+ *
52094
+ * @example
52095
+ * C#
52096
+ * ```cs
52097
+ * var api = new AlphaTabApi<MyControl>(...);
52098
+ * api.clearPlaybackRangeHighlight();
52099
+ * ```
52100
+ *
52101
+ * @example
52102
+ * Android
52103
+ * ```kotlin
52104
+ * val api = AlphaTabApi<MyControl>(...)
52105
+ * api.clearPlaybackRangeHighlight()
52106
+ * ```
52107
+ */
52108
+ clearPlaybackRangeHighlight() {
52109
+ this._cursorSelectRange(undefined, undefined);
52110
+ }
52111
+ /**
52112
+ * This event is fired the shown highlights for the selected playback range changes.
52113
+ *
52114
+ * @remarks
52115
+ * This event is fired already during selection and not only when the selection is completed.
52116
+ * This event can be used to place additional custom selection markers (like drag handles).
52117
+ *
52118
+ * @eventProperty
52119
+ * @category Events - Player
52120
+ * @since 1.8.0
52121
+ *
52122
+ * @example
52123
+ * JavaScript
52124
+ * ```js
52125
+ * const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
52126
+ * api.playbackRangeHighlightChanged.on(e => {
52127
+ * updateSelectionHandles(e);
52128
+ * });
52129
+ * ```
52130
+ *
52131
+ * @example
52132
+ * C#
52133
+ * ```cs
52134
+ * var api = new AlphaTabApi<MyControl>(...);
52135
+ * api.PlaybackRangeHighlightChanged.On(e =>
52136
+ * {
52137
+ * UpdateSelectionHandles(e);
52138
+ * });
52139
+ * ```
52140
+ *
52141
+ * @example
52142
+ * Android
52143
+ * ```kotlin
52144
+ * val api = AlphaTabApi<MyControl>(...)
52145
+ * api.playbackRangeHighlightChanged.on { e ->
52146
+ * updateSelectionHandles(e)
52147
+ * }
52148
+ * ```
52149
+ *
52150
+ */
52151
+ playbackRangeHighlightChanged = new EventEmitterOfT();
51991
52152
  _cursorSelectRange(startBeat, endBeat) {
51992
52153
  const cache = this._renderer.boundsLookup;
51993
52154
  if (!cache) {
52155
+ this.playbackRangeHighlightChanged.trigger({});
51994
52156
  return;
51995
52157
  }
51996
52158
  const selectionWrapper = this._selectionWrapper;
51997
52159
  if (!selectionWrapper) {
52160
+ this.playbackRangeHighlightChanged.trigger({});
51998
52161
  return;
51999
52162
  }
52000
52163
  selectionWrapper.clear();
52001
52164
  if (!startBeat || !endBeat || startBeat.beat === endBeat.beat) {
52165
+ this.playbackRangeHighlightChanged.trigger({});
52002
52166
  return;
52003
52167
  }
52004
52168
  if (!startBeat.bounds) {
52005
- startBeat.bounds = cache.findBeat(startBeat.beat);
52169
+ startBeat.bounds = cache.findBeat(startBeat.beat) ?? undefined;
52006
52170
  }
52007
52171
  if (!endBeat.bounds) {
52008
- endBeat.bounds = cache.findBeat(endBeat.beat);
52172
+ endBeat.bounds = cache.findBeat(endBeat.beat) ?? undefined;
52009
52173
  }
52010
52174
  const startTick = this._tickCache?.getBeatStart(startBeat.beat) ?? startBeat.beat.absolutePlaybackStart;
52011
52175
  const endTick = this._tickCache?.getBeatStart(endBeat.beat) ?? endBeat.beat.absolutePlaybackStart;
@@ -52014,7 +52178,17 @@
52014
52178
  startBeat = endBeat;
52015
52179
  endBeat = t;
52016
52180
  }
52017
- const startX = startBeat.bounds.realBounds.x;
52181
+ const eventArgs = {
52182
+ startBeat: startBeat.beat,
52183
+ startBeatBounds: startBeat.bounds,
52184
+ endBeat: endBeat.beat,
52185
+ endBeatBounds: endBeat.bounds,
52186
+ highlightBlocks: []
52187
+ };
52188
+ let startX = startBeat.bounds.realBounds.x;
52189
+ if (startBeat.beat.index === 0) {
52190
+ startX = startBeat.bounds.barBounds.masterBarBounds.realBounds.x;
52191
+ }
52018
52192
  let endX = endBeat.bounds.realBounds.x + endBeat.bounds.realBounds.w;
52019
52193
  if (endBeat.beat.index === endBeat.beat.voice.beats.length - 1) {
52020
52194
  endX =
@@ -52031,18 +52205,24 @@
52031
52205
  const staffEndX = startBeat.bounds.barBounds.masterBarBounds.staffSystemBounds.visualBounds.x +
52032
52206
  startBeat.bounds.barBounds.masterBarBounds.staffSystemBounds.visualBounds.w;
52033
52207
  const startSelection = this.uiFacade.createSelectionElement();
52034
- startSelection.setBounds(startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.y, staffEndX - startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.h);
52208
+ const startSelectionBounds = new Bounds(startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.y, staffEndX - startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.h);
52209
+ startSelection.setBounds(startSelectionBounds.x, startSelectionBounds.y, startSelectionBounds.w, startSelectionBounds.h);
52210
+ eventArgs.highlightBlocks.push(startSelectionBounds);
52035
52211
  selectionWrapper.appendChild(startSelection);
52036
52212
  const staffStartIndex = startBeat.bounds.barBounds.masterBarBounds.staffSystemBounds.index + 1;
52037
52213
  const staffEndIndex = endBeat.bounds.barBounds.masterBarBounds.staffSystemBounds.index;
52038
52214
  for (let staffIndex = staffStartIndex; staffIndex < staffEndIndex; staffIndex++) {
52039
52215
  const staffBounds = cache.staffSystems[staffIndex];
52040
52216
  const middleSelection = this.uiFacade.createSelectionElement();
52041
- middleSelection.setBounds(staffStartX, staffBounds.visualBounds.y, staffEndX - staffStartX, staffBounds.visualBounds.h);
52217
+ const middleSelectionBounds = new Bounds(staffStartX, staffBounds.visualBounds.y, staffEndX - staffStartX, staffBounds.visualBounds.h);
52218
+ eventArgs.highlightBlocks.push(middleSelectionBounds);
52219
+ middleSelection.setBounds(middleSelectionBounds.x, middleSelectionBounds.y, middleSelectionBounds.w, middleSelectionBounds.h);
52042
52220
  selectionWrapper.appendChild(middleSelection);
52043
52221
  }
52044
52222
  const endSelection = this.uiFacade.createSelectionElement();
52045
- endSelection.setBounds(staffStartX, endBeat.bounds.barBounds.masterBarBounds.visualBounds.y, endX - staffStartX, endBeat.bounds.barBounds.masterBarBounds.visualBounds.h);
52223
+ const endSelectionBounds = new Bounds(staffStartX, endBeat.bounds.barBounds.masterBarBounds.visualBounds.y, endX - staffStartX, endBeat.bounds.barBounds.masterBarBounds.visualBounds.h);
52224
+ eventArgs.highlightBlocks.push(endSelectionBounds);
52225
+ endSelection.setBounds(endSelectionBounds.x, endSelectionBounds.y, endSelectionBounds.w, endSelectionBounds.h);
52046
52226
  selectionWrapper.appendChild(endSelection);
52047
52227
  }
52048
52228
  else {
@@ -52051,6 +52231,7 @@
52051
52231
  selection.setBounds(startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.y, endX - startX, startBeat.bounds.barBounds.masterBarBounds.visualBounds.h);
52052
52232
  selectionWrapper.appendChild(selection);
52053
52233
  }
52234
+ this.playbackRangeHighlightChanged.trigger(eventArgs);
52054
52235
  }
52055
52236
  /**
52056
52237
  * This event is fired whenever a new song is loaded.