@coderline/alphatab 1.6.0-alpha.1408 → 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.
- package/dist/alphaTab.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +1163 -758
- package/dist/alphaTab.d.ts +94 -20
- package/dist/alphaTab.js +1163 -758
- 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.core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.6.0-alpha.
|
|
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 &&
|
|
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
|
-
|
|
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);
|
|
@@ -22397,6 +22409,11 @@ class MidiFileSequencer {
|
|
|
22397
22409
|
this._mainState.eventIndex = 0;
|
|
22398
22410
|
this._mainState.syncPointIndex = 0;
|
|
22399
22411
|
this._mainState.tempoChangeIndex = 0;
|
|
22412
|
+
this._mainState.currentTempo = this._mainState.tempoChanges[0].bpm;
|
|
22413
|
+
this._mainState.modifiedTempo =
|
|
22414
|
+
this._mainState.syncPoints.length > 0
|
|
22415
|
+
? this._mainState.syncPoints[0].data.modifiedTempo
|
|
22416
|
+
: this._mainState.currentTempo;
|
|
22400
22417
|
if (this.isPlayingMain) {
|
|
22401
22418
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
22402
22419
|
this._synthesizer.noteOffAll(true);
|
|
@@ -22535,18 +22552,6 @@ class MidiFileSequencer {
|
|
|
22535
22552
|
this._currentState.synthData[this._currentState.eventIndex].time < this._currentState.currentTime) {
|
|
22536
22553
|
const synthEvent = this._currentState.synthData[this._currentState.eventIndex];
|
|
22537
22554
|
this._synthesizer.dispatchEvent(synthEvent);
|
|
22538
|
-
while (this._currentState.syncPointIndex < this._currentState.syncPoints.length &&
|
|
22539
|
-
this._currentState.syncPoints[this._currentState.syncPointIndex].tick < synthEvent.event.tick) {
|
|
22540
|
-
this._currentState.modifiedTempo =
|
|
22541
|
-
this._currentState.syncPoints[this._currentState.syncPointIndex].data.modifiedTempo;
|
|
22542
|
-
this._currentState.syncPointIndex++;
|
|
22543
|
-
}
|
|
22544
|
-
while (this._currentState.tempoChangeIndex < this._currentState.tempoChanges.length &&
|
|
22545
|
-
this._currentState.tempoChanges[this._currentState.tempoChangeIndex].time <= synthEvent.time) {
|
|
22546
|
-
this._currentState.currentTempo =
|
|
22547
|
-
this._currentState.tempoChanges[this._currentState.tempoChangeIndex].bpm;
|
|
22548
|
-
this._currentState.tempoChangeIndex++;
|
|
22549
|
-
}
|
|
22550
22555
|
this._currentState.eventIndex++;
|
|
22551
22556
|
anyEventsDispatched = true;
|
|
22552
22557
|
}
|
|
@@ -22576,9 +22581,6 @@ class MidiFileSequencer {
|
|
|
22576
22581
|
mainTickPositionToTimePosition(tickPosition) {
|
|
22577
22582
|
return this.tickPositionToTimePositionWithSpeed(this._mainState, tickPosition, this.playbackSpeed);
|
|
22578
22583
|
}
|
|
22579
|
-
mainTimePositionToTickPosition(timePosition) {
|
|
22580
|
-
return this.timePositionToTickPositionWithSpeed(this._mainState, timePosition, this.playbackSpeed);
|
|
22581
|
-
}
|
|
22582
22584
|
mainUpdateSyncPoints(syncPoints) {
|
|
22583
22585
|
const state = this._mainState;
|
|
22584
22586
|
syncPoints.sort((a, b) => a.tick - b.tick); // just in case
|
|
@@ -22606,7 +22608,49 @@ class MidiFileSequencer {
|
|
|
22606
22608
|
state.syncPointIndex = 0;
|
|
22607
22609
|
}
|
|
22608
22610
|
currentTimePositionToTickPosition(timePosition) {
|
|
22609
|
-
|
|
22611
|
+
const state = this._currentState;
|
|
22612
|
+
if (state.tempoChanges.length === 0) {
|
|
22613
|
+
return 0;
|
|
22614
|
+
}
|
|
22615
|
+
timePosition *= this.playbackSpeed;
|
|
22616
|
+
this.updateCurrentTempo(state, timePosition);
|
|
22617
|
+
const lastTempoChange = state.tempoChanges[state.tempoChangeIndex];
|
|
22618
|
+
const timeDiff = timePosition - lastTempoChange.time;
|
|
22619
|
+
const ticks = ((timeDiff / (60000.0 / (lastTempoChange.bpm * state.division))) | 0);
|
|
22620
|
+
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22621
|
+
return lastTempoChange.ticks + ticks + 1;
|
|
22622
|
+
}
|
|
22623
|
+
updateCurrentTempo(state, timePosition) {
|
|
22624
|
+
let tempoChangeIndex = state.tempoChangeIndex;
|
|
22625
|
+
if (timePosition < state.tempoChanges[tempoChangeIndex].time) {
|
|
22626
|
+
tempoChangeIndex = 0;
|
|
22627
|
+
}
|
|
22628
|
+
while (tempoChangeIndex + 1 < state.tempoChanges.length &&
|
|
22629
|
+
state.tempoChanges[tempoChangeIndex + 1].time <= timePosition) {
|
|
22630
|
+
tempoChangeIndex++;
|
|
22631
|
+
}
|
|
22632
|
+
if (tempoChangeIndex !== state.tempoChangeIndex) {
|
|
22633
|
+
state.tempoChangeIndex = tempoChangeIndex;
|
|
22634
|
+
state.currentTempo = state.tempoChanges[state.tempoChangeIndex].bpm;
|
|
22635
|
+
}
|
|
22636
|
+
const syncPoints = state.syncPoints;
|
|
22637
|
+
if (syncPoints.length > 0) {
|
|
22638
|
+
let syncPointIndex = Math.min(state.syncPointIndex, syncPoints.length - 1);
|
|
22639
|
+
if (timePosition < syncPoints[syncPointIndex].data.millisecondOffset) {
|
|
22640
|
+
syncPointIndex = 0;
|
|
22641
|
+
}
|
|
22642
|
+
while (syncPointIndex + 1 < syncPoints.length &&
|
|
22643
|
+
syncPoints[syncPointIndex + 1].data.millisecondOffset <= timePosition) {
|
|
22644
|
+
syncPointIndex++;
|
|
22645
|
+
}
|
|
22646
|
+
if (syncPointIndex !== state.syncPointIndex) {
|
|
22647
|
+
state.syncPointIndex = syncPointIndex;
|
|
22648
|
+
state.modifiedTempo = syncPoints[syncPointIndex].data.modifiedTempo;
|
|
22649
|
+
}
|
|
22650
|
+
}
|
|
22651
|
+
else {
|
|
22652
|
+
state.modifiedTempo = state.currentTempo;
|
|
22653
|
+
}
|
|
22610
22654
|
}
|
|
22611
22655
|
mainTimePositionFromBackingTrack(timePosition, backingTrackLength) {
|
|
22612
22656
|
const mainState = this._mainState;
|
|
@@ -22614,11 +22658,8 @@ class MidiFileSequencer {
|
|
|
22614
22658
|
if (timePosition < 0 || syncPoints.length === 0) {
|
|
22615
22659
|
return timePosition;
|
|
22616
22660
|
}
|
|
22617
|
-
|
|
22618
|
-
|
|
22619
|
-
syncPoints[syncPointIndex + 1].data.millisecondOffset <= timePosition) {
|
|
22620
|
-
syncPointIndex++;
|
|
22621
|
-
}
|
|
22661
|
+
this.updateCurrentTempo(this._mainState, timePosition);
|
|
22662
|
+
const syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22622
22663
|
const currentSyncPoint = syncPoints[syncPointIndex];
|
|
22623
22664
|
const timeDiff = timePosition - currentSyncPoint.data.millisecondOffset;
|
|
22624
22665
|
let alphaTabTimeDiff;
|
|
@@ -22640,7 +22681,10 @@ class MidiFileSequencer {
|
|
|
22640
22681
|
return timePosition;
|
|
22641
22682
|
}
|
|
22642
22683
|
timePosition *= this.playbackSpeed;
|
|
22643
|
-
let syncPointIndex =
|
|
22684
|
+
let syncPointIndex = Math.min(mainState.syncPointIndex, syncPoints.length - 1);
|
|
22685
|
+
if (timePosition < syncPoints[syncPointIndex].time) {
|
|
22686
|
+
syncPointIndex = 0;
|
|
22687
|
+
}
|
|
22644
22688
|
while (syncPointIndex + 1 < syncPoints.length && syncPoints[syncPointIndex + 1].time <= timePosition) {
|
|
22645
22689
|
syncPointIndex++;
|
|
22646
22690
|
}
|
|
@@ -22678,26 +22722,6 @@ class MidiFileSequencer {
|
|
|
22678
22722
|
timePosition += tickPosition * (60000.0 / (bpm * state.division));
|
|
22679
22723
|
return timePosition / playbackSpeed;
|
|
22680
22724
|
}
|
|
22681
|
-
timePositionToTickPositionWithSpeed(state, timePosition, playbackSpeed) {
|
|
22682
|
-
timePosition *= playbackSpeed;
|
|
22683
|
-
let ticks = 0;
|
|
22684
|
-
let bpm = 120.0;
|
|
22685
|
-
let lastChange = 0;
|
|
22686
|
-
// find start and bpm of last tempo change before time
|
|
22687
|
-
for (const c of state.tempoChanges) {
|
|
22688
|
-
if (timePosition < c.time) {
|
|
22689
|
-
break;
|
|
22690
|
-
}
|
|
22691
|
-
ticks = c.ticks;
|
|
22692
|
-
bpm = c.bpm;
|
|
22693
|
-
lastChange = c.time;
|
|
22694
|
-
}
|
|
22695
|
-
// add the missing ticks
|
|
22696
|
-
timePosition -= lastChange;
|
|
22697
|
-
ticks += (timePosition / (60000.0 / (bpm * state.division))) | 0;
|
|
22698
|
-
// we add 1 for possible rounding errors.(floating point issuses)
|
|
22699
|
-
return ticks + 1;
|
|
22700
|
-
}
|
|
22701
22725
|
get internalEndTime() {
|
|
22702
22726
|
if (this.isPlayingMain) {
|
|
22703
22727
|
return !this.mainPlaybackRange ? this._currentState.endTime : this._currentState.playbackRangeEndTime;
|
|
@@ -26916,7 +26940,7 @@ class TinySoundFont {
|
|
|
26916
26940
|
return processedEvents;
|
|
26917
26941
|
}
|
|
26918
26942
|
processMidiMessage(e) {
|
|
26919
|
-
Logger.debug('Midi', `Processing Midi message ${MidiEventType[e.type]}/${e.tick}`);
|
|
26943
|
+
//Logger.debug('Midi', `Processing Midi message ${MidiEventType[e.type]}/${e.tick}`);
|
|
26920
26944
|
const command = e.type;
|
|
26921
26945
|
switch (command) {
|
|
26922
26946
|
case MidiEventType.TimeSignature:
|
|
@@ -28037,6 +28061,9 @@ class EventEmitter {
|
|
|
28037
28061
|
}
|
|
28038
28062
|
on(value) {
|
|
28039
28063
|
this._listeners.push(value);
|
|
28064
|
+
return () => {
|
|
28065
|
+
this.off(value);
|
|
28066
|
+
};
|
|
28040
28067
|
}
|
|
28041
28068
|
off(value) {
|
|
28042
28069
|
this._listeners = this._listeners.filter(l => l !== value);
|
|
@@ -28056,6 +28083,9 @@ class EventEmitterOfT {
|
|
|
28056
28083
|
}
|
|
28057
28084
|
on(value) {
|
|
28058
28085
|
this._listeners.push(value);
|
|
28086
|
+
return () => {
|
|
28087
|
+
this.off(value);
|
|
28088
|
+
};
|
|
28059
28089
|
}
|
|
28060
28090
|
off(value) {
|
|
28061
28091
|
this._listeners = this._listeners.filter(l => l !== value);
|
|
@@ -28452,7 +28482,7 @@ class AlphaSynthBase {
|
|
|
28452
28482
|
endTick = this.sequencer.currentEndTick;
|
|
28453
28483
|
}
|
|
28454
28484
|
if (this._tickPosition >= endTick) {
|
|
28455
|
-
// fully done with playback of remaining samples?
|
|
28485
|
+
// fully done with playback of remaining samples?
|
|
28456
28486
|
if (this._notPlayedSamples <= 0) {
|
|
28457
28487
|
this._notPlayedSamples = 0;
|
|
28458
28488
|
if (this.sequencer.isPlayingCountIn) {
|
|
@@ -35802,6 +35832,27 @@ class MidiFileGenerator {
|
|
|
35802
35832
|
controller.moveNext();
|
|
35803
35833
|
previousMasterBar = bar;
|
|
35804
35834
|
}
|
|
35835
|
+
// here we interpolate the sync point which marks the end of the sync.
|
|
35836
|
+
// Sync points define new tempos at certain positions.
|
|
35837
|
+
// looking from the last sync point to the end we do not assume the end where the audio ends,
|
|
35838
|
+
// but where it ends according to the BPM and the remaining ticks.
|
|
35839
|
+
if (this.syncPoints.length > 0) {
|
|
35840
|
+
const lastSyncPoint = this.syncPoints[this.syncPoints.length - 1];
|
|
35841
|
+
const endTick = controller.currentTick;
|
|
35842
|
+
const remainingTicks = endTick - lastSyncPoint.tick;
|
|
35843
|
+
if (remainingTicks > 0) {
|
|
35844
|
+
const syncPointData = new SyncPointData();
|
|
35845
|
+
// last occurence of the last bar
|
|
35846
|
+
syncPointData.barOccurence = barOccurence.get(this._score.masterBars.length - 1);
|
|
35847
|
+
// same tempo as last point
|
|
35848
|
+
syncPointData.modifiedTempo = lastSyncPoint.data.modifiedTempo;
|
|
35849
|
+
// interpolated end from last syncPoint
|
|
35850
|
+
syncPointData.millisecondOffset =
|
|
35851
|
+
lastSyncPoint.data.millisecondOffset +
|
|
35852
|
+
MidiUtils.ticksToMillis(remainingTicks, syncPointData.modifiedTempo);
|
|
35853
|
+
this.syncPoints.push(new BackingTrackSyncPoint(endTick, syncPointData));
|
|
35854
|
+
}
|
|
35855
|
+
}
|
|
35805
35856
|
for (const track of this._score.tracks) {
|
|
35806
35857
|
this._handler.finishTrack(track.index, controller.currentTick);
|
|
35807
35858
|
}
|
|
@@ -38293,6 +38344,357 @@ class ExternalMediaPlayer extends BackingTrackPlayer {
|
|
|
38293
38344
|
}
|
|
38294
38345
|
}
|
|
38295
38346
|
|
|
38347
|
+
/**
|
|
38348
|
+
* A {@link IAlphaSynth} implementation wrapping and underling other {@link IAlphaSynth}
|
|
38349
|
+
* allowing dynamic changing of the underlying instance without loosing aspects like the
|
|
38350
|
+
* main playback information and event listeners.
|
|
38351
|
+
*
|
|
38352
|
+
* @remarks
|
|
38353
|
+
* This wrapper is used when re-exposing the underlying player via {@link AlphaTabApiBase} to integrators.
|
|
38354
|
+
* Even with dynamic switching between synthesizer, backing tracks etc. aspects like volume, playbackspeed,
|
|
38355
|
+
* event listeners etc. should not be lost.
|
|
38356
|
+
*/
|
|
38357
|
+
class AlphaSynthWrapper {
|
|
38358
|
+
constructor() {
|
|
38359
|
+
// relevant state information we want to remember when switching between player instances
|
|
38360
|
+
this._masterVolume = 1;
|
|
38361
|
+
this._metronomeVolume = 0;
|
|
38362
|
+
this._countInVolume = 0;
|
|
38363
|
+
this._playbackSpeed = 1;
|
|
38364
|
+
this._isLooping = false;
|
|
38365
|
+
this._midiEventsPlayedFilter = [];
|
|
38366
|
+
this.ready = new EventEmitter();
|
|
38367
|
+
this.readyForPlayback = new EventEmitter();
|
|
38368
|
+
this.finished = new EventEmitter();
|
|
38369
|
+
this.soundFontLoaded = new EventEmitter();
|
|
38370
|
+
this.soundFontLoadFailed = new EventEmitterOfT();
|
|
38371
|
+
this.midiLoaded = new EventEmitterOfT();
|
|
38372
|
+
this.midiLoadFailed = new EventEmitterOfT();
|
|
38373
|
+
this.stateChanged = new EventEmitterOfT();
|
|
38374
|
+
this.positionChanged = new EventEmitterOfT();
|
|
38375
|
+
this.midiEventsPlayed = new EventEmitterOfT();
|
|
38376
|
+
this.playbackRangeChanged = new EventEmitterOfT();
|
|
38377
|
+
}
|
|
38378
|
+
get instance() {
|
|
38379
|
+
return this._instance;
|
|
38380
|
+
}
|
|
38381
|
+
set instance(value) {
|
|
38382
|
+
this._instance = value;
|
|
38383
|
+
// unregister all events from previous instance
|
|
38384
|
+
const unregister = this._instanceEventUnregister;
|
|
38385
|
+
if (unregister) {
|
|
38386
|
+
for (const e of unregister) {
|
|
38387
|
+
e();
|
|
38388
|
+
}
|
|
38389
|
+
}
|
|
38390
|
+
if (value) {
|
|
38391
|
+
// regsiter to events of new player and forward them to existing listeners
|
|
38392
|
+
const newUnregister = [];
|
|
38393
|
+
newUnregister.push(value.ready.on(() => this.ready.trigger()));
|
|
38394
|
+
newUnregister.push(value.readyForPlayback.on(() => this.readyForPlayback.trigger()));
|
|
38395
|
+
newUnregister.push(value.finished.on(() => this.finished.trigger()));
|
|
38396
|
+
newUnregister.push(value.soundFontLoaded.on(() => this.soundFontLoaded.trigger()));
|
|
38397
|
+
newUnregister.push(value.soundFontLoadFailed.on(e => this.soundFontLoadFailed.trigger(e)));
|
|
38398
|
+
newUnregister.push(value.midiLoaded.on(e => this.midiLoaded.trigger(e)));
|
|
38399
|
+
newUnregister.push(value.midiLoadFailed.on(e => this.midiLoadFailed.trigger(e)));
|
|
38400
|
+
newUnregister.push(value.stateChanged.on(e => this.stateChanged.trigger(e)));
|
|
38401
|
+
newUnregister.push(value.positionChanged.on(e => this.positionChanged.trigger(e)));
|
|
38402
|
+
newUnregister.push(value.midiEventsPlayed.on(e => this.midiEventsPlayed.trigger(e)));
|
|
38403
|
+
newUnregister.push(value.playbackRangeChanged.on(e => this.playbackRangeChanged.trigger(e)));
|
|
38404
|
+
this._instanceEventUnregister = newUnregister;
|
|
38405
|
+
// restore state on new player
|
|
38406
|
+
if (this.isReady) {
|
|
38407
|
+
value.masterVolume = this._masterVolume;
|
|
38408
|
+
value.metronomeVolume = this._metronomeVolume;
|
|
38409
|
+
value.countInVolume = this._countInVolume;
|
|
38410
|
+
value.playbackSpeed = this._playbackSpeed;
|
|
38411
|
+
value.isLooping = this._isLooping;
|
|
38412
|
+
value.midiEventsPlayedFilter = this._midiEventsPlayedFilter;
|
|
38413
|
+
}
|
|
38414
|
+
else {
|
|
38415
|
+
newUnregister.push(value.ready.on(() => {
|
|
38416
|
+
value.masterVolume = this._masterVolume;
|
|
38417
|
+
value.metronomeVolume = this._metronomeVolume;
|
|
38418
|
+
value.countInVolume = this._countInVolume;
|
|
38419
|
+
value.playbackSpeed = this._playbackSpeed;
|
|
38420
|
+
value.isLooping = this._isLooping;
|
|
38421
|
+
value.midiEventsPlayedFilter = this._midiEventsPlayedFilter;
|
|
38422
|
+
}));
|
|
38423
|
+
}
|
|
38424
|
+
}
|
|
38425
|
+
else {
|
|
38426
|
+
this._instanceEventUnregister = undefined;
|
|
38427
|
+
}
|
|
38428
|
+
}
|
|
38429
|
+
get output() {
|
|
38430
|
+
return this._instance.output;
|
|
38431
|
+
}
|
|
38432
|
+
get isReady() {
|
|
38433
|
+
return this._instance ? this._instance.isReady : false;
|
|
38434
|
+
}
|
|
38435
|
+
get isReadyForPlayback() {
|
|
38436
|
+
return this._instance ? this._instance.isReadyForPlayback : false;
|
|
38437
|
+
}
|
|
38438
|
+
get state() {
|
|
38439
|
+
return this._instance ? this._instance.state : PlayerState.Paused;
|
|
38440
|
+
}
|
|
38441
|
+
get logLevel() {
|
|
38442
|
+
return Logger.logLevel;
|
|
38443
|
+
}
|
|
38444
|
+
set logLevel(value) {
|
|
38445
|
+
Logger.logLevel = value;
|
|
38446
|
+
if (this._instance) {
|
|
38447
|
+
this._instance.logLevel = value;
|
|
38448
|
+
}
|
|
38449
|
+
}
|
|
38450
|
+
get masterVolume() {
|
|
38451
|
+
return this._masterVolume;
|
|
38452
|
+
}
|
|
38453
|
+
set masterVolume(value) {
|
|
38454
|
+
value = Math.max(value, SynthConstants.MinVolume);
|
|
38455
|
+
this._masterVolume = value;
|
|
38456
|
+
if (this._instance) {
|
|
38457
|
+
this._instance.masterVolume = value;
|
|
38458
|
+
}
|
|
38459
|
+
}
|
|
38460
|
+
get metronomeVolume() {
|
|
38461
|
+
return this._metronomeVolume;
|
|
38462
|
+
}
|
|
38463
|
+
set metronomeVolume(value) {
|
|
38464
|
+
value = Math.max(value, SynthConstants.MinVolume);
|
|
38465
|
+
this._metronomeVolume = value;
|
|
38466
|
+
if (this._instance) {
|
|
38467
|
+
this._instance.metronomeVolume = value;
|
|
38468
|
+
}
|
|
38469
|
+
}
|
|
38470
|
+
get playbackSpeed() {
|
|
38471
|
+
return this._playbackSpeed;
|
|
38472
|
+
}
|
|
38473
|
+
set playbackSpeed(value) {
|
|
38474
|
+
this._playbackSpeed = value;
|
|
38475
|
+
if (this._instance) {
|
|
38476
|
+
this._instance.playbackSpeed = value;
|
|
38477
|
+
}
|
|
38478
|
+
}
|
|
38479
|
+
get tickPosition() {
|
|
38480
|
+
return this._instance ? this._instance.tickPosition : 0;
|
|
38481
|
+
}
|
|
38482
|
+
set tickPosition(value) {
|
|
38483
|
+
if (this._instance) {
|
|
38484
|
+
this._instance.tickPosition = value;
|
|
38485
|
+
}
|
|
38486
|
+
}
|
|
38487
|
+
get timePosition() {
|
|
38488
|
+
return this._instance ? this._instance.timePosition : 0;
|
|
38489
|
+
}
|
|
38490
|
+
set timePosition(value) {
|
|
38491
|
+
if (this._instance) {
|
|
38492
|
+
this._instance.timePosition = value;
|
|
38493
|
+
}
|
|
38494
|
+
}
|
|
38495
|
+
get playbackRange() {
|
|
38496
|
+
return this._instance ? this._instance.playbackRange : null;
|
|
38497
|
+
}
|
|
38498
|
+
set playbackRange(value) {
|
|
38499
|
+
if (this._instance) {
|
|
38500
|
+
this._instance.playbackRange = value;
|
|
38501
|
+
}
|
|
38502
|
+
}
|
|
38503
|
+
get isLooping() {
|
|
38504
|
+
return this._isLooping;
|
|
38505
|
+
}
|
|
38506
|
+
set isLooping(value) {
|
|
38507
|
+
this._isLooping = value;
|
|
38508
|
+
if (this._instance) {
|
|
38509
|
+
this._instance.isLooping = value;
|
|
38510
|
+
}
|
|
38511
|
+
}
|
|
38512
|
+
get countInVolume() {
|
|
38513
|
+
return this._countInVolume;
|
|
38514
|
+
}
|
|
38515
|
+
set countInVolume(value) {
|
|
38516
|
+
this._countInVolume = value;
|
|
38517
|
+
if (this._instance) {
|
|
38518
|
+
this._instance.countInVolume = value;
|
|
38519
|
+
}
|
|
38520
|
+
}
|
|
38521
|
+
get midiEventsPlayedFilter() {
|
|
38522
|
+
return this._midiEventsPlayedFilter;
|
|
38523
|
+
}
|
|
38524
|
+
set midiEventsPlayedFilter(value) {
|
|
38525
|
+
this._midiEventsPlayedFilter = value;
|
|
38526
|
+
if (this._instance) {
|
|
38527
|
+
this._instance.midiEventsPlayedFilter = value;
|
|
38528
|
+
}
|
|
38529
|
+
}
|
|
38530
|
+
destroy() {
|
|
38531
|
+
if (this._instance) {
|
|
38532
|
+
this._instance.destroy();
|
|
38533
|
+
this._instance = undefined;
|
|
38534
|
+
}
|
|
38535
|
+
}
|
|
38536
|
+
play() {
|
|
38537
|
+
return this._instance ? this._instance.play() : false;
|
|
38538
|
+
}
|
|
38539
|
+
pause() {
|
|
38540
|
+
if (this._instance) {
|
|
38541
|
+
this._instance.pause();
|
|
38542
|
+
}
|
|
38543
|
+
}
|
|
38544
|
+
playPause() {
|
|
38545
|
+
if (this._instance) {
|
|
38546
|
+
this._instance.playPause();
|
|
38547
|
+
}
|
|
38548
|
+
}
|
|
38549
|
+
stop() {
|
|
38550
|
+
if (this._instance) {
|
|
38551
|
+
this._instance.stop();
|
|
38552
|
+
}
|
|
38553
|
+
}
|
|
38554
|
+
playOneTimeMidiFile(midi) {
|
|
38555
|
+
if (this._instance) {
|
|
38556
|
+
this._instance.playOneTimeMidiFile(midi);
|
|
38557
|
+
}
|
|
38558
|
+
}
|
|
38559
|
+
loadSoundFont(data, append) {
|
|
38560
|
+
if (this._instance) {
|
|
38561
|
+
this._instance.loadSoundFont(data, append);
|
|
38562
|
+
}
|
|
38563
|
+
}
|
|
38564
|
+
resetSoundFonts() {
|
|
38565
|
+
if (this._instance) {
|
|
38566
|
+
this._instance.resetSoundFonts();
|
|
38567
|
+
}
|
|
38568
|
+
}
|
|
38569
|
+
loadMidiFile(midi) {
|
|
38570
|
+
if (this._instance) {
|
|
38571
|
+
this._instance.loadMidiFile(midi);
|
|
38572
|
+
}
|
|
38573
|
+
}
|
|
38574
|
+
loadBackingTrack(score, syncPoints) {
|
|
38575
|
+
if (this._instance) {
|
|
38576
|
+
this._instance.loadBackingTrack(score, syncPoints);
|
|
38577
|
+
}
|
|
38578
|
+
}
|
|
38579
|
+
applyTranspositionPitches(transpositionPitches) {
|
|
38580
|
+
if (this._instance) {
|
|
38581
|
+
this._instance.applyTranspositionPitches(transpositionPitches);
|
|
38582
|
+
}
|
|
38583
|
+
}
|
|
38584
|
+
setChannelTranspositionPitch(channel, semitones) {
|
|
38585
|
+
if (this._instance) {
|
|
38586
|
+
this._instance.setChannelTranspositionPitch(channel, semitones);
|
|
38587
|
+
}
|
|
38588
|
+
}
|
|
38589
|
+
setChannelMute(channel, mute) {
|
|
38590
|
+
if (this._instance) {
|
|
38591
|
+
this._instance.setChannelMute(channel, mute);
|
|
38592
|
+
}
|
|
38593
|
+
}
|
|
38594
|
+
resetChannelStates() {
|
|
38595
|
+
if (this._instance) {
|
|
38596
|
+
this._instance.resetChannelStates();
|
|
38597
|
+
}
|
|
38598
|
+
}
|
|
38599
|
+
setChannelSolo(channel, solo) {
|
|
38600
|
+
if (this._instance) {
|
|
38601
|
+
this._instance.setChannelSolo(channel, solo);
|
|
38602
|
+
}
|
|
38603
|
+
}
|
|
38604
|
+
setChannelVolume(channel, volume) {
|
|
38605
|
+
if (this._instance) {
|
|
38606
|
+
this._instance.setChannelVolume(channel, volume);
|
|
38607
|
+
}
|
|
38608
|
+
}
|
|
38609
|
+
}
|
|
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
|
+
|
|
38296
38698
|
class SelectionInfo {
|
|
38297
38699
|
constructor(beat) {
|
|
38298
38700
|
this.bounds = null;
|
|
@@ -38312,6 +38714,18 @@ class AlphaTabApiBase {
|
|
|
38312
38714
|
get actualPlayerMode() {
|
|
38313
38715
|
return this._actualPlayerMode;
|
|
38314
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
|
+
}
|
|
38315
38729
|
/**
|
|
38316
38730
|
* The score holding all information about the song being rendered
|
|
38317
38731
|
* @category Properties - Core
|
|
@@ -38383,43 +38797,14 @@ class AlphaTabApiBase {
|
|
|
38383
38797
|
this._tracks = [];
|
|
38384
38798
|
this._actualPlayerMode = PlayerMode.Disabled;
|
|
38385
38799
|
this._tickCache = null;
|
|
38386
|
-
/**
|
|
38387
|
-
* The alphaSynth player used for playback.
|
|
38388
|
-
* @remarks
|
|
38389
|
-
* This is the low-level API to the Midi synthesizer used for playback.
|
|
38390
|
-
* Gets access to the underling {@link IAlphaSynth} that is used for the audio playback.
|
|
38391
|
-
* @category Properties - Player
|
|
38392
|
-
* @since 0.9.4
|
|
38393
|
-
* @example
|
|
38394
|
-
* JavaScript
|
|
38395
|
-
* ```js
|
|
38396
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
38397
|
-
* setupPlayerEvents(api.settings);
|
|
38398
|
-
* ```
|
|
38399
|
-
*
|
|
38400
|
-
* @example
|
|
38401
|
-
* C#
|
|
38402
|
-
* ```cs
|
|
38403
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
38404
|
-
* SetupPlayerEvents(api.Player);
|
|
38405
|
-
* ```
|
|
38406
|
-
*
|
|
38407
|
-
* @example
|
|
38408
|
-
* Android
|
|
38409
|
-
* ```kotlin
|
|
38410
|
-
* val api = AlphaTabApi<MyControl>(...)
|
|
38411
|
-
* setupPlayerEvents(api.player)
|
|
38412
|
-
* ```
|
|
38413
|
-
*/
|
|
38414
|
-
this.player = null;
|
|
38415
38800
|
this._cursorWrapper = null;
|
|
38416
38801
|
this._barCursor = null;
|
|
38417
38802
|
this._beatCursor = null;
|
|
38418
38803
|
this._selectionWrapper = null;
|
|
38419
38804
|
this._previousTick = 0;
|
|
38420
|
-
this._playerState = PlayerState.Paused;
|
|
38421
38805
|
this._currentBeat = null;
|
|
38422
|
-
this.
|
|
38806
|
+
this._currentBeatBounds = null;
|
|
38807
|
+
this._isInitialBeatCursorUpdate = true;
|
|
38423
38808
|
this._previousStateForCursor = PlayerState.Paused;
|
|
38424
38809
|
this._previousCursorCache = null;
|
|
38425
38810
|
this._lastScroll = 0;
|
|
@@ -38990,133 +39375,6 @@ class AlphaTabApiBase {
|
|
|
38990
39375
|
*
|
|
38991
39376
|
*/
|
|
38992
39377
|
this.error = new EventEmitterOfT();
|
|
38993
|
-
/**
|
|
38994
|
-
* This event is fired when all required data for playback is loaded and ready.
|
|
38995
|
-
* @remarks
|
|
38996
|
-
* This event is fired when all required data for playback is loaded and ready. The player is ready for playback when
|
|
38997
|
-
* all background workers are started, the audio output is initialized, a soundfont is loaded, and a song was loaded into the player as midi file.
|
|
38998
|
-
*
|
|
38999
|
-
* @eventProperty
|
|
39000
|
-
* @category Events - Player
|
|
39001
|
-
* @since 0.9.4
|
|
39002
|
-
*
|
|
39003
|
-
* @example
|
|
39004
|
-
* JavaScript
|
|
39005
|
-
* ```js
|
|
39006
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39007
|
-
* api.playerReady.on(() => {
|
|
39008
|
-
* enablePlayerControls();
|
|
39009
|
-
* });
|
|
39010
|
-
* ```
|
|
39011
|
-
*
|
|
39012
|
-
* @example
|
|
39013
|
-
* C#
|
|
39014
|
-
* ```cs
|
|
39015
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39016
|
-
* api.PlayerReady.On(() =>
|
|
39017
|
-
* {
|
|
39018
|
-
* EnablePlayerControls()
|
|
39019
|
-
* });
|
|
39020
|
-
* ```
|
|
39021
|
-
*
|
|
39022
|
-
* @example
|
|
39023
|
-
* Android
|
|
39024
|
-
* ```kotlin
|
|
39025
|
-
* val api = AlphaTabApi<MyControl>(...)
|
|
39026
|
-
* api.playerReady.on {
|
|
39027
|
-
* enablePlayerControls()
|
|
39028
|
-
* }
|
|
39029
|
-
* ```
|
|
39030
|
-
*/
|
|
39031
|
-
this.playerReady = new EventEmitter();
|
|
39032
|
-
/**
|
|
39033
|
-
* This event is fired when the playback of the whole song finished.
|
|
39034
|
-
* @remarks
|
|
39035
|
-
* This event is finished regardless on whether looping is enabled or not.
|
|
39036
|
-
*
|
|
39037
|
-
* @eventProperty
|
|
39038
|
-
* @category Events - Player
|
|
39039
|
-
* @since 0.9.4
|
|
39040
|
-
*
|
|
39041
|
-
* @example
|
|
39042
|
-
* JavaScript
|
|
39043
|
-
* ```js
|
|
39044
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39045
|
-
* api.playerFinished.on((args) => {
|
|
39046
|
-
* // speed trainer
|
|
39047
|
-
* api.playbackSpeed = Math.min(1.0, api.playbackSpeed + 0.1);
|
|
39048
|
-
* });
|
|
39049
|
-
* api.isLooping = true;
|
|
39050
|
-
* api.playbackSpeed = 0.5;
|
|
39051
|
-
* api.play()
|
|
39052
|
-
* ```
|
|
39053
|
-
*
|
|
39054
|
-
* @example
|
|
39055
|
-
* C#
|
|
39056
|
-
* ```cs
|
|
39057
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39058
|
-
* api.PlayerFinished.On(() =>
|
|
39059
|
-
* {
|
|
39060
|
-
* // speed trainer
|
|
39061
|
-
* api.PlaybackSpeed = Math.Min(1.0, api.PlaybackSpeed + 0.1);
|
|
39062
|
-
* });
|
|
39063
|
-
* api.IsLooping = true;
|
|
39064
|
-
* api.PlaybackSpeed = 0.5;
|
|
39065
|
-
* api.Play();
|
|
39066
|
-
* ```
|
|
39067
|
-
*
|
|
39068
|
-
* @example
|
|
39069
|
-
* Android
|
|
39070
|
-
* ```kotlin
|
|
39071
|
-
* val api = AlphaTabApi<MyControl>(...)
|
|
39072
|
-
* api.playerFinished.on {
|
|
39073
|
-
* // speed trainer
|
|
39074
|
-
* api.playbackSpeed = min(1.0, api.playbackSpeed + 0.1);
|
|
39075
|
-
* }
|
|
39076
|
-
* api.isLooping = true
|
|
39077
|
-
* api.playbackSpeed = 0.5
|
|
39078
|
-
* api.play()
|
|
39079
|
-
* ```
|
|
39080
|
-
*
|
|
39081
|
-
*/
|
|
39082
|
-
this.playerFinished = new EventEmitter();
|
|
39083
|
-
/**
|
|
39084
|
-
* This event is fired when the SoundFont needed for playback was loaded.
|
|
39085
|
-
*
|
|
39086
|
-
* @eventProperty
|
|
39087
|
-
* @category Events - Player
|
|
39088
|
-
* @since 0.9.4
|
|
39089
|
-
*
|
|
39090
|
-
* @example
|
|
39091
|
-
* JavaScript
|
|
39092
|
-
* ```js
|
|
39093
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39094
|
-
* api.soundFontLoaded.on(() => {
|
|
39095
|
-
* hideSoundFontLoadingIndicator();
|
|
39096
|
-
* });
|
|
39097
|
-
* ```
|
|
39098
|
-
*
|
|
39099
|
-
* @example
|
|
39100
|
-
* C#
|
|
39101
|
-
* ```cs
|
|
39102
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39103
|
-
* api.SoundFontLoaded.On(() =>
|
|
39104
|
-
* {
|
|
39105
|
-
* HideSoundFontLoadingIndicator();
|
|
39106
|
-
* });
|
|
39107
|
-
* ```
|
|
39108
|
-
*
|
|
39109
|
-
* @example
|
|
39110
|
-
* Android
|
|
39111
|
-
* ```kotlin
|
|
39112
|
-
* val api = AlphaTabApi<MyControl>(...);
|
|
39113
|
-
* api.soundFontLoaded.on {
|
|
39114
|
-
* hideSoundFontLoadingIndicator();
|
|
39115
|
-
* }
|
|
39116
|
-
* ```
|
|
39117
|
-
*
|
|
39118
|
-
*/
|
|
39119
|
-
this.soundFontLoaded = new EventEmitter();
|
|
39120
39378
|
/**
|
|
39121
39379
|
* This event is fired when a Midi file is being loaded.
|
|
39122
39380
|
*
|
|
@@ -39205,176 +39463,18 @@ class AlphaTabApiBase {
|
|
|
39205
39463
|
*/
|
|
39206
39464
|
this.midiLoaded = new EventEmitterOfT();
|
|
39207
39465
|
/**
|
|
39208
|
-
* This event is fired when
|
|
39209
|
-
*
|
|
39210
|
-
* @eventProperty
|
|
39211
|
-
* @category Events - Player
|
|
39212
|
-
* @since 0.9.4
|
|
39213
|
-
*
|
|
39214
|
-
* @example
|
|
39215
|
-
* JavaScript
|
|
39216
|
-
* ```js
|
|
39217
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39218
|
-
* api.playerStateChanged.on((args) => {
|
|
39219
|
-
* updatePlayerControls(args.state, args.stopped);
|
|
39220
|
-
* });
|
|
39221
|
-
* ```
|
|
39222
|
-
*
|
|
39223
|
-
* @example
|
|
39224
|
-
* C#
|
|
39225
|
-
* ```cs
|
|
39226
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39227
|
-
* api.PlayerStateChanged.On(args =>
|
|
39228
|
-
* {
|
|
39229
|
-
* UpdatePlayerControls(args);
|
|
39230
|
-
* });
|
|
39231
|
-
* ```
|
|
39232
|
-
*
|
|
39233
|
-
* @example
|
|
39234
|
-
* Android
|
|
39235
|
-
* ```kotlin
|
|
39236
|
-
* val api = AlphaTabApi<MyControl>(...)
|
|
39237
|
-
* api.playerStateChanged.on { args ->
|
|
39238
|
-
* updatePlayerControls(args)
|
|
39239
|
-
* }
|
|
39240
|
-
* ```
|
|
39241
|
-
*
|
|
39242
|
-
*/
|
|
39243
|
-
this.playerStateChanged = new EventEmitterOfT();
|
|
39244
|
-
/**
|
|
39245
|
-
* This event is fired when the current playback position of the song changed.
|
|
39246
|
-
*
|
|
39247
|
-
* @eventProperty
|
|
39248
|
-
* @category Events - Player
|
|
39249
|
-
* @since 0.9.4
|
|
39250
|
-
*
|
|
39251
|
-
* @example
|
|
39252
|
-
* JavaScript
|
|
39253
|
-
* ```js
|
|
39254
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39255
|
-
* api.playerPositionChanged.on((args) => {
|
|
39256
|
-
* updatePlayerPosition(args);
|
|
39257
|
-
* });
|
|
39258
|
-
* ```
|
|
39259
|
-
*
|
|
39260
|
-
* @example
|
|
39261
|
-
* C#
|
|
39262
|
-
* ```cs
|
|
39263
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39264
|
-
* api.PlayerPositionChanged.On(args =>
|
|
39265
|
-
* {
|
|
39266
|
-
* UpdatePlayerPosition(args);
|
|
39267
|
-
* });
|
|
39268
|
-
* ```
|
|
39269
|
-
*
|
|
39270
|
-
* @example
|
|
39271
|
-
* Android
|
|
39272
|
-
* ```kotlin
|
|
39273
|
-
* val api = AlphaTabApi<MyControl>(...)
|
|
39274
|
-
* api.playerPositionChanged.on { args ->
|
|
39275
|
-
* updatePlayerPosition(args)
|
|
39276
|
-
* }
|
|
39277
|
-
* ```
|
|
39278
|
-
*
|
|
39279
|
-
*/
|
|
39280
|
-
this.playerPositionChanged = new EventEmitterOfT();
|
|
39281
|
-
/**
|
|
39282
|
-
* This event is fired when the synthesizer played certain midi events.
|
|
39283
|
-
*
|
|
39284
|
-
* @remarks
|
|
39285
|
-
* This event is fired when the synthesizer played certain midi events. This allows reacing on various low level
|
|
39286
|
-
* audio playback elements like notes/rests played or metronome ticks.
|
|
39287
|
-
*
|
|
39288
|
-
* Refer to the [related guide](https://www.alphatab.net/docs/guides/handling-midi-events) to learn more about this feature.
|
|
39289
|
-
*
|
|
39290
|
-
* Also note that the provided data models changed significantly in {@version 1.3.0}. We try to provide backwards compatibility
|
|
39291
|
-
* until some extend but highly encourage changing to the new models in case of problems.
|
|
39466
|
+
* This event is fired when a settings update was requested.
|
|
39292
39467
|
*
|
|
39293
39468
|
* @eventProperty
|
|
39294
|
-
* @category Events -
|
|
39295
|
-
* @since 1.
|
|
39296
|
-
*
|
|
39297
|
-
* @example
|
|
39298
|
-
* JavaScript
|
|
39299
|
-
* ```js
|
|
39300
|
-
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39301
|
-
* api.midiEventsPlayedFilter = [alphaTab.midi.MidiEventType.AlphaTabMetronome];
|
|
39302
|
-
* api.midiEventsPlayed.on(function(e) {
|
|
39303
|
-
* for(const midi of e.events) {
|
|
39304
|
-
* if(midi.isMetronome) {
|
|
39305
|
-
* console.log('Metronome tick ' + midi.tick);
|
|
39306
|
-
* }
|
|
39307
|
-
* }
|
|
39308
|
-
* });
|
|
39309
|
-
* ```
|
|
39310
|
-
*
|
|
39311
|
-
* @example
|
|
39312
|
-
* C#
|
|
39313
|
-
* ```cs
|
|
39314
|
-
* var api = new AlphaTabApi<MyControl>(...);
|
|
39315
|
-
* api.MidiEventsPlayedFilter = new MidiEventType[] { AlphaTab.Midi.MidiEventType.AlphaTabMetronome };
|
|
39316
|
-
* api.MidiEventsPlayed.On(e =>
|
|
39317
|
-
* {
|
|
39318
|
-
* foreach(var midi of e.events)
|
|
39319
|
-
* {
|
|
39320
|
-
* if(midi is AlphaTab.Midi.AlphaTabMetronomeEvent sysex && sysex.IsMetronome)
|
|
39321
|
-
* {
|
|
39322
|
-
* Console.WriteLine("Metronome tick " + midi.Tick);
|
|
39323
|
-
* }
|
|
39324
|
-
* }
|
|
39325
|
-
* });
|
|
39326
|
-
* ```
|
|
39327
|
-
*
|
|
39328
|
-
* @example
|
|
39329
|
-
* Android
|
|
39330
|
-
* ```kotlin
|
|
39331
|
-
* val api = AlphaTabApi<MyControl>(...);
|
|
39332
|
-
* api.midiEventsPlayedFilter = alphaTab.collections.List<alphaTab.midi.MidiEventType>( alphaTab.midi.MidiEventType.AlphaTabMetronome )
|
|
39333
|
-
* api.midiEventsPlayed.on { e ->
|
|
39334
|
-
* for (midi in e.events) {
|
|
39335
|
-
* if(midi instanceof alphaTab.midi.AlphaTabMetronomeEvent && midi.isMetronome) {
|
|
39336
|
-
* println("Metronome tick " + midi.tick);
|
|
39337
|
-
* }
|
|
39338
|
-
* }
|
|
39339
|
-
* }
|
|
39340
|
-
* ```
|
|
39341
|
-
* @see {@link MidiEvent}
|
|
39342
|
-
* @see {@link TimeSignatureEvent}
|
|
39343
|
-
* @see {@link AlphaTabMetronomeEvent}
|
|
39344
|
-
* @see {@link AlphaTabRestEvent}
|
|
39345
|
-
* @see {@link NoteOnEvent}
|
|
39346
|
-
* @see {@link NoteOffEvent}
|
|
39347
|
-
* @see {@link ControlChangeEvent}
|
|
39348
|
-
* @see {@link ProgramChangeEvent}
|
|
39349
|
-
* @see {@link TempoChangeEvent}
|
|
39350
|
-
* @see {@link PitchBendEvent}
|
|
39351
|
-
* @see {@link NoteBendEvent}
|
|
39352
|
-
* @see {@link EndOfTrackEvent}
|
|
39353
|
-
* @see {@link MetaEvent}
|
|
39354
|
-
* @see {@link MetaDataEvent}
|
|
39355
|
-
* @see {@link MetaNumberEvent}
|
|
39356
|
-
* @see {@link Midi20PerNotePitchBendEvent}
|
|
39357
|
-
* @see {@link SystemCommonEvent}
|
|
39358
|
-
* @see {@link SystemExclusiveEvent}
|
|
39359
|
-
*/
|
|
39360
|
-
this.midiEventsPlayed = new EventEmitterOfT();
|
|
39361
|
-
/**
|
|
39362
|
-
* This event is fired when the playback range changed.
|
|
39363
|
-
*
|
|
39364
|
-
* @eventProperty
|
|
39365
|
-
* @category Events - Player
|
|
39366
|
-
* @since 1.2.3
|
|
39469
|
+
* @category Events - Core
|
|
39470
|
+
* @since 1.6.0
|
|
39367
39471
|
*
|
|
39368
39472
|
* @example
|
|
39369
39473
|
* JavaScript
|
|
39370
39474
|
* ```js
|
|
39371
39475
|
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
39372
|
-
* api.
|
|
39373
|
-
*
|
|
39374
|
-
* highlightRangeInProgressBar(args.playbackRange.startTick, args.playbackRange.endTick);
|
|
39375
|
-
* } else {
|
|
39376
|
-
* clearHighlightInProgressBar();
|
|
39377
|
-
* }
|
|
39476
|
+
* api.settingsUpdated.on(() => {
|
|
39477
|
+
* updateSettingsUI(api.settings);
|
|
39378
39478
|
* });
|
|
39379
39479
|
* ```
|
|
39380
39480
|
*
|
|
@@ -39382,16 +39482,9 @@ class AlphaTabApiBase {
|
|
|
39382
39482
|
* C#
|
|
39383
39483
|
* ```cs
|
|
39384
39484
|
* var api = new AlphaTabApi<MyControl>(...);
|
|
39385
|
-
* api.
|
|
39485
|
+
* api.SettingsUpdated.On(() =>
|
|
39386
39486
|
* {
|
|
39387
|
-
*
|
|
39388
|
-
* {
|
|
39389
|
-
* HighlightRangeInProgressBar(args.PlaybackRange.StartTick, args.PlaybackRange.EndTick);
|
|
39390
|
-
* }
|
|
39391
|
-
* else
|
|
39392
|
-
* {
|
|
39393
|
-
* ClearHighlightInProgressBar();
|
|
39394
|
-
* }
|
|
39487
|
+
* UpdateSettingsUI(api.settings);
|
|
39395
39488
|
* });
|
|
39396
39489
|
* ```
|
|
39397
39490
|
*
|
|
@@ -39399,21 +39492,12 @@ class AlphaTabApiBase {
|
|
|
39399
39492
|
* Android
|
|
39400
39493
|
* ```kotlin
|
|
39401
39494
|
* val api = AlphaTabApi<MyControl>(...)
|
|
39402
|
-
* api.
|
|
39403
|
-
*
|
|
39404
|
-
* if (playbackRange != null) {
|
|
39405
|
-
* highlightRangeInProgressBar(playbackRange.startTick, playbackRange.endTick)
|
|
39406
|
-
* } else {
|
|
39407
|
-
* clearHighlightInProgressBar()
|
|
39408
|
-
* }
|
|
39495
|
+
* api.SettingsUpdated.on {
|
|
39496
|
+
* updateSettingsUI(api.settings)
|
|
39409
39497
|
* }
|
|
39410
39498
|
* ```
|
|
39411
39499
|
*
|
|
39412
39500
|
*/
|
|
39413
|
-
this.playbackRangeChanged = new EventEmitterOfT();
|
|
39414
|
-
/**
|
|
39415
|
-
* @internal
|
|
39416
|
-
*/
|
|
39417
39501
|
this.settingsUpdated = new EventEmitter();
|
|
39418
39502
|
this.uiFacade = uiFacade;
|
|
39419
39503
|
this.container = uiFacade.rootContainer;
|
|
@@ -39426,48 +39510,49 @@ class AlphaTabApiBase {
|
|
|
39426
39510
|
Environment.printEnvironmentInfo(false);
|
|
39427
39511
|
this.canvasElement = uiFacade.createCanvasElement();
|
|
39428
39512
|
this.container.appendChild(this.canvasElement);
|
|
39513
|
+
this._renderer = new ScoreRendererWrapper();
|
|
39429
39514
|
if (this.settings.core.useWorkers &&
|
|
39430
39515
|
this.uiFacade.areWorkersSupported &&
|
|
39431
39516
|
Environment.getRenderEngineFactory(this.settings.core.engine).supportsWorkers) {
|
|
39432
|
-
this.
|
|
39517
|
+
this._renderer.instance = this.uiFacade.createWorkerRenderer();
|
|
39433
39518
|
}
|
|
39434
39519
|
else {
|
|
39435
|
-
this.
|
|
39520
|
+
this._renderer.instance = new ScoreRenderer(this.settings);
|
|
39436
39521
|
}
|
|
39437
39522
|
this.container.resize.on(Environment.throttle(() => {
|
|
39438
39523
|
if (this._isDestroyed) {
|
|
39439
39524
|
return;
|
|
39440
39525
|
}
|
|
39441
|
-
if (this.container.width !== this.
|
|
39526
|
+
if (this.container.width !== this._renderer.width) {
|
|
39442
39527
|
this.triggerResize();
|
|
39443
39528
|
}
|
|
39444
39529
|
}, uiFacade.resizeThrottle));
|
|
39445
39530
|
const initialResizeEventInfo = new ResizeEventArgs();
|
|
39446
|
-
initialResizeEventInfo.oldWidth = this.
|
|
39531
|
+
initialResizeEventInfo.oldWidth = this._renderer.width;
|
|
39447
39532
|
initialResizeEventInfo.newWidth = this.container.width | 0;
|
|
39448
39533
|
initialResizeEventInfo.settings = this.settings;
|
|
39449
39534
|
this.onResize(initialResizeEventInfo);
|
|
39450
|
-
this.
|
|
39451
|
-
this.
|
|
39535
|
+
this._renderer.preRender.on(this.onRenderStarted.bind(this));
|
|
39536
|
+
this._renderer.renderFinished.on(renderingResult => {
|
|
39452
39537
|
this.onRenderFinished(renderingResult);
|
|
39453
39538
|
});
|
|
39454
|
-
this.
|
|
39539
|
+
this._renderer.postRenderFinished.on(() => {
|
|
39455
39540
|
const duration = Date.now() - this._startTime;
|
|
39456
39541
|
Logger.debug('rendering', `Rendering completed in ${duration}ms`);
|
|
39457
39542
|
this.onPostRenderFinished();
|
|
39458
39543
|
});
|
|
39459
|
-
this.
|
|
39544
|
+
this._renderer.preRender.on(_ => {
|
|
39460
39545
|
this._startTime = Date.now();
|
|
39461
39546
|
});
|
|
39462
|
-
this.
|
|
39463
|
-
this.
|
|
39464
|
-
this.
|
|
39465
|
-
this.appendRenderResult(r);
|
|
39466
|
-
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);
|
|
39467
39551
|
});
|
|
39468
|
-
this.
|
|
39552
|
+
this._renderer.error.on(this.onError.bind(this));
|
|
39553
|
+
this.setupPlayerWrapper();
|
|
39469
39554
|
if (this.settings.player.playerMode !== PlayerMode.Disabled) {
|
|
39470
|
-
this.
|
|
39555
|
+
this.setupOrDestroyPlayer();
|
|
39471
39556
|
}
|
|
39472
39557
|
this.setupClickHandling();
|
|
39473
39558
|
// delay rendering to allow ui to hook up with events first.
|
|
@@ -39475,6 +39560,36 @@ class AlphaTabApiBase {
|
|
|
39475
39560
|
this.uiFacade.initialRender();
|
|
39476
39561
|
});
|
|
39477
39562
|
}
|
|
39563
|
+
setupPlayerWrapper() {
|
|
39564
|
+
const player = new AlphaSynthWrapper();
|
|
39565
|
+
this._player = player;
|
|
39566
|
+
player.ready.on(() => {
|
|
39567
|
+
this.loadMidiForScore();
|
|
39568
|
+
});
|
|
39569
|
+
player.readyForPlayback.on(() => {
|
|
39570
|
+
this.onPlayerReady();
|
|
39571
|
+
if (this.tracks) {
|
|
39572
|
+
for (const track of this.tracks) {
|
|
39573
|
+
const volume = track.playbackInfo.volume / 16;
|
|
39574
|
+
player.setChannelVolume(track.playbackInfo.primaryChannel, volume);
|
|
39575
|
+
player.setChannelVolume(track.playbackInfo.secondaryChannel, volume);
|
|
39576
|
+
}
|
|
39577
|
+
}
|
|
39578
|
+
});
|
|
39579
|
+
player.soundFontLoaded.on(this.onSoundFontLoaded.bind(this));
|
|
39580
|
+
player.soundFontLoadFailed.on(e => {
|
|
39581
|
+
this.onError(e);
|
|
39582
|
+
});
|
|
39583
|
+
player.midiLoaded.on(this.onMidiLoaded.bind(this));
|
|
39584
|
+
player.midiLoadFailed.on(e => {
|
|
39585
|
+
this.onError(e);
|
|
39586
|
+
});
|
|
39587
|
+
player.stateChanged.on(this.onPlayerStateChanged.bind(this));
|
|
39588
|
+
player.positionChanged.on(this.onPlayerPositionChanged.bind(this));
|
|
39589
|
+
player.midiEventsPlayed.on(this.onMidiEventsPlayed.bind(this));
|
|
39590
|
+
player.playbackRangeChanged.on(this.onPlaybackRangeChanged.bind(this));
|
|
39591
|
+
player.finished.on(this.onPlayerFinished.bind(this));
|
|
39592
|
+
}
|
|
39478
39593
|
/**
|
|
39479
39594
|
* Destroys the alphaTab control and restores the initial state of the UI.
|
|
39480
39595
|
* @remarks
|
|
@@ -39507,11 +39622,9 @@ class AlphaTabApiBase {
|
|
|
39507
39622
|
*/
|
|
39508
39623
|
destroy() {
|
|
39509
39624
|
this._isDestroyed = true;
|
|
39510
|
-
|
|
39511
|
-
this.player.destroy();
|
|
39512
|
-
}
|
|
39625
|
+
this._player.destroy();
|
|
39513
39626
|
this.uiFacade.destroy();
|
|
39514
|
-
this.
|
|
39627
|
+
this._renderer.destroy();
|
|
39515
39628
|
}
|
|
39516
39629
|
/**
|
|
39517
39630
|
* Applies any changes that were done to the settings object.
|
|
@@ -39556,17 +39669,29 @@ class AlphaTabApiBase {
|
|
|
39556
39669
|
if (score) {
|
|
39557
39670
|
ModelUtils.applyPitchOffsets(this.settings, score);
|
|
39558
39671
|
}
|
|
39559
|
-
this.
|
|
39560
|
-
|
|
39561
|
-
|
|
39562
|
-
|
|
39563
|
-
|
|
39672
|
+
this.updateRenderer();
|
|
39673
|
+
this._renderer.updateSettings(this.settings);
|
|
39674
|
+
this.setupOrDestroyPlayer();
|
|
39675
|
+
this.onSettingsUpdated();
|
|
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();
|
|
39564
39686
|
}
|
|
39565
39687
|
}
|
|
39566
39688
|
else {
|
|
39567
|
-
|
|
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
|
+
}
|
|
39568
39694
|
}
|
|
39569
|
-
this.onSettingsUpdated();
|
|
39570
39695
|
}
|
|
39571
39696
|
/**
|
|
39572
39697
|
* Initiates a load of the score using the given data.
|
|
@@ -39762,30 +39887,33 @@ class AlphaTabApiBase {
|
|
|
39762
39887
|
}
|
|
39763
39888
|
else {
|
|
39764
39889
|
const resizeEventInfo = new ResizeEventArgs();
|
|
39765
|
-
resizeEventInfo.oldWidth = this.
|
|
39890
|
+
resizeEventInfo.oldWidth = this._renderer.width;
|
|
39766
39891
|
resizeEventInfo.newWidth = this.container.width;
|
|
39767
39892
|
resizeEventInfo.settings = this.settings;
|
|
39768
39893
|
this.onResize(resizeEventInfo);
|
|
39769
|
-
this.
|
|
39770
|
-
this.
|
|
39771
|
-
this.
|
|
39894
|
+
this._renderer.updateSettings(this.settings);
|
|
39895
|
+
this._renderer.width = this.container.width;
|
|
39896
|
+
this._renderer.resizeRender();
|
|
39772
39897
|
}
|
|
39773
39898
|
}
|
|
39774
|
-
appendRenderResult(result) {
|
|
39775
|
-
|
|
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) {
|
|
39776
39904
|
this.canvasElement.width = result.totalWidth;
|
|
39777
39905
|
this.canvasElement.height = result.totalHeight;
|
|
39778
39906
|
if (this._cursorWrapper) {
|
|
39779
39907
|
this._cursorWrapper.width = result.totalWidth;
|
|
39780
39908
|
this._cursorWrapper.height = result.totalHeight;
|
|
39781
39909
|
}
|
|
39782
|
-
if (result.width > 0 || result.height > 0) {
|
|
39783
|
-
this.uiFacade.beginAppendRenderResults(result);
|
|
39784
|
-
}
|
|
39785
39910
|
}
|
|
39786
|
-
|
|
39911
|
+
if (result.width > 0 || result.height > 0) {
|
|
39787
39912
|
this.uiFacade.beginAppendRenderResults(result);
|
|
39788
39913
|
}
|
|
39914
|
+
if (isLast) {
|
|
39915
|
+
this.uiFacade.beginAppendRenderResults(null);
|
|
39916
|
+
}
|
|
39789
39917
|
}
|
|
39790
39918
|
updateRenderResult(result) {
|
|
39791
39919
|
if (result && result.renderResult) {
|
|
@@ -39871,9 +39999,6 @@ class AlphaTabApiBase {
|
|
|
39871
39999
|
* ```
|
|
39872
40000
|
*/
|
|
39873
40001
|
loadSoundFont(data, append = false) {
|
|
39874
|
-
if (!this.player) {
|
|
39875
|
-
return false;
|
|
39876
|
-
}
|
|
39877
40002
|
return this.uiFacade.loadSoundFont(data, append);
|
|
39878
40003
|
}
|
|
39879
40004
|
/**
|
|
@@ -39919,10 +40044,7 @@ class AlphaTabApiBase {
|
|
|
39919
40044
|
* ```
|
|
39920
40045
|
*/
|
|
39921
40046
|
resetSoundFonts() {
|
|
39922
|
-
|
|
39923
|
-
return;
|
|
39924
|
-
}
|
|
39925
|
-
this.player.resetSoundFonts();
|
|
40047
|
+
this._player.resetSoundFonts();
|
|
39926
40048
|
}
|
|
39927
40049
|
/**
|
|
39928
40050
|
* Initiates a re-rendering of the current setup.
|
|
@@ -39953,13 +40075,10 @@ class AlphaTabApiBase {
|
|
|
39953
40075
|
* ```
|
|
39954
40076
|
*/
|
|
39955
40077
|
render() {
|
|
39956
|
-
if (!this.renderer) {
|
|
39957
|
-
return;
|
|
39958
|
-
}
|
|
39959
40078
|
if (this.uiFacade.canRender) {
|
|
39960
40079
|
// when font is finally loaded, start rendering
|
|
39961
|
-
this.
|
|
39962
|
-
this.
|
|
40080
|
+
this._renderer.width = this.container.width;
|
|
40081
|
+
this._renderer.renderScore(this.score, this._trackIndexes);
|
|
39963
40082
|
}
|
|
39964
40083
|
else {
|
|
39965
40084
|
this.uiFacade.canRenderChanged.on(() => this.render());
|
|
@@ -40034,7 +40153,38 @@ class AlphaTabApiBase {
|
|
|
40034
40153
|
* @since 1.5.0
|
|
40035
40154
|
*/
|
|
40036
40155
|
get boundsLookup() {
|
|
40037
|
-
return this.
|
|
40156
|
+
return this._renderer.boundsLookup;
|
|
40157
|
+
}
|
|
40158
|
+
/**
|
|
40159
|
+
* The alphaSynth player used for playback.
|
|
40160
|
+
* @remarks
|
|
40161
|
+
* This is the low-level API to the Midi synthesizer used for playback.
|
|
40162
|
+
* Gets access to the underling {@link IAlphaSynth} that is used for the audio playback.
|
|
40163
|
+
* @category Properties - Player
|
|
40164
|
+
* @since 0.9.4
|
|
40165
|
+
* @example
|
|
40166
|
+
* JavaScript
|
|
40167
|
+
* ```js
|
|
40168
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
40169
|
+
* setupPlayerEvents(api.settings);
|
|
40170
|
+
* ```
|
|
40171
|
+
*
|
|
40172
|
+
* @example
|
|
40173
|
+
* C#
|
|
40174
|
+
* ```cs
|
|
40175
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
40176
|
+
* SetupPlayerEvents(api.Player);
|
|
40177
|
+
* ```
|
|
40178
|
+
*
|
|
40179
|
+
* @example
|
|
40180
|
+
* Android
|
|
40181
|
+
* ```kotlin
|
|
40182
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
40183
|
+
* setupPlayerEvents(api.player)
|
|
40184
|
+
* ```
|
|
40185
|
+
*/
|
|
40186
|
+
get player() {
|
|
40187
|
+
return this._player.instance ? this._player : null;
|
|
40038
40188
|
}
|
|
40039
40189
|
/**
|
|
40040
40190
|
* Whether the player is ready for starting the playback.
|
|
@@ -40065,10 +40215,7 @@ class AlphaTabApiBase {
|
|
|
40065
40215
|
* ```
|
|
40066
40216
|
*/
|
|
40067
40217
|
get isReadyForPlayback() {
|
|
40068
|
-
|
|
40069
|
-
return false;
|
|
40070
|
-
}
|
|
40071
|
-
return this.player.isReadyForPlayback;
|
|
40218
|
+
return this._player.isReadyForPlayback;
|
|
40072
40219
|
}
|
|
40073
40220
|
/**
|
|
40074
40221
|
* The current player state.
|
|
@@ -40098,10 +40245,7 @@ class AlphaTabApiBase {
|
|
|
40098
40245
|
* ```
|
|
40099
40246
|
*/
|
|
40100
40247
|
get playerState() {
|
|
40101
|
-
|
|
40102
|
-
return PlayerState.Paused;
|
|
40103
|
-
}
|
|
40104
|
-
return this.player.state;
|
|
40248
|
+
return this._player.state;
|
|
40105
40249
|
}
|
|
40106
40250
|
/**
|
|
40107
40251
|
* The current master volume as percentage (0-1).
|
|
@@ -40131,15 +40275,10 @@ class AlphaTabApiBase {
|
|
|
40131
40275
|
* ```
|
|
40132
40276
|
*/
|
|
40133
40277
|
get masterVolume() {
|
|
40134
|
-
|
|
40135
|
-
return 0;
|
|
40136
|
-
}
|
|
40137
|
-
return this.player.masterVolume;
|
|
40278
|
+
return this._player.masterVolume;
|
|
40138
40279
|
}
|
|
40139
40280
|
set masterVolume(value) {
|
|
40140
|
-
|
|
40141
|
-
this.player.masterVolume = value;
|
|
40142
|
-
}
|
|
40281
|
+
this._player.masterVolume = value;
|
|
40143
40282
|
}
|
|
40144
40283
|
/**
|
|
40145
40284
|
* The metronome volume as percentage (0-1).
|
|
@@ -40170,15 +40309,10 @@ class AlphaTabApiBase {
|
|
|
40170
40309
|
* ```
|
|
40171
40310
|
*/
|
|
40172
40311
|
get metronomeVolume() {
|
|
40173
|
-
|
|
40174
|
-
return 0;
|
|
40175
|
-
}
|
|
40176
|
-
return this.player.metronomeVolume;
|
|
40312
|
+
return this._player.metronomeVolume;
|
|
40177
40313
|
}
|
|
40178
40314
|
set metronomeVolume(value) {
|
|
40179
|
-
|
|
40180
|
-
this.player.metronomeVolume = value;
|
|
40181
|
-
}
|
|
40315
|
+
this._player.metronomeVolume = value;
|
|
40182
40316
|
}
|
|
40183
40317
|
/**
|
|
40184
40318
|
* The volume of the count-in metronome ticks.
|
|
@@ -40209,15 +40343,10 @@ class AlphaTabApiBase {
|
|
|
40209
40343
|
* ```
|
|
40210
40344
|
*/
|
|
40211
40345
|
get countInVolume() {
|
|
40212
|
-
|
|
40213
|
-
return 0;
|
|
40214
|
-
}
|
|
40215
|
-
return this.player.countInVolume;
|
|
40346
|
+
return this._player.countInVolume;
|
|
40216
40347
|
}
|
|
40217
40348
|
set countInVolume(value) {
|
|
40218
|
-
|
|
40219
|
-
this.player.countInVolume = value;
|
|
40220
|
-
}
|
|
40349
|
+
this._player.countInVolume = value;
|
|
40221
40350
|
}
|
|
40222
40351
|
/**
|
|
40223
40352
|
* The midi events which will trigger the `midiEventsPlayed` event
|
|
@@ -40276,15 +40405,10 @@ class AlphaTabApiBase {
|
|
|
40276
40405
|
* ```
|
|
40277
40406
|
*/
|
|
40278
40407
|
get midiEventsPlayedFilter() {
|
|
40279
|
-
|
|
40280
|
-
return [];
|
|
40281
|
-
}
|
|
40282
|
-
return this.player.midiEventsPlayedFilter;
|
|
40408
|
+
return this._player.midiEventsPlayedFilter;
|
|
40283
40409
|
}
|
|
40284
40410
|
set midiEventsPlayedFilter(value) {
|
|
40285
|
-
|
|
40286
|
-
this.player.midiEventsPlayedFilter = value;
|
|
40287
|
-
}
|
|
40411
|
+
this._player.midiEventsPlayedFilter = value;
|
|
40288
40412
|
}
|
|
40289
40413
|
/**
|
|
40290
40414
|
* The position within the song in midi ticks.
|
|
@@ -40312,15 +40436,10 @@ class AlphaTabApiBase {
|
|
|
40312
40436
|
* ```
|
|
40313
40437
|
*/
|
|
40314
40438
|
get tickPosition() {
|
|
40315
|
-
|
|
40316
|
-
return 0;
|
|
40317
|
-
}
|
|
40318
|
-
return this.player.tickPosition;
|
|
40439
|
+
return this._player.tickPosition;
|
|
40319
40440
|
}
|
|
40320
40441
|
set tickPosition(value) {
|
|
40321
|
-
|
|
40322
|
-
this.player.tickPosition = value;
|
|
40323
|
-
}
|
|
40442
|
+
this._player.tickPosition = value;
|
|
40324
40443
|
}
|
|
40325
40444
|
/**
|
|
40326
40445
|
* The position within the song in milliseconds
|
|
@@ -40348,15 +40467,10 @@ class AlphaTabApiBase {
|
|
|
40348
40467
|
* ```
|
|
40349
40468
|
*/
|
|
40350
40469
|
get timePosition() {
|
|
40351
|
-
|
|
40352
|
-
return 0;
|
|
40353
|
-
}
|
|
40354
|
-
return this.player.timePosition;
|
|
40470
|
+
return this._player.timePosition;
|
|
40355
40471
|
}
|
|
40356
40472
|
set timePosition(value) {
|
|
40357
|
-
|
|
40358
|
-
this.player.timePosition = value;
|
|
40359
|
-
}
|
|
40473
|
+
this._player.timePosition = value;
|
|
40360
40474
|
}
|
|
40361
40475
|
/**
|
|
40362
40476
|
* The range of the song that should be played.
|
|
@@ -40390,17 +40504,12 @@ class AlphaTabApiBase {
|
|
|
40390
40504
|
* ```
|
|
40391
40505
|
*/
|
|
40392
40506
|
get playbackRange() {
|
|
40393
|
-
|
|
40394
|
-
return null;
|
|
40395
|
-
}
|
|
40396
|
-
return this.player.playbackRange;
|
|
40507
|
+
return this._player.playbackRange;
|
|
40397
40508
|
}
|
|
40398
40509
|
set playbackRange(value) {
|
|
40399
|
-
|
|
40400
|
-
|
|
40401
|
-
|
|
40402
|
-
this.updateSelectionCursor(value);
|
|
40403
|
-
}
|
|
40510
|
+
this._player.playbackRange = value;
|
|
40511
|
+
if (this.settings.player.enableCursor) {
|
|
40512
|
+
this.updateSelectionCursor(value);
|
|
40404
40513
|
}
|
|
40405
40514
|
}
|
|
40406
40515
|
/**
|
|
@@ -40432,15 +40541,10 @@ class AlphaTabApiBase {
|
|
|
40432
40541
|
* ```
|
|
40433
40542
|
*/
|
|
40434
40543
|
get playbackSpeed() {
|
|
40435
|
-
|
|
40436
|
-
return 0;
|
|
40437
|
-
}
|
|
40438
|
-
return this.player.playbackSpeed;
|
|
40544
|
+
return this._player.playbackSpeed;
|
|
40439
40545
|
}
|
|
40440
40546
|
set playbackSpeed(value) {
|
|
40441
|
-
|
|
40442
|
-
this.player.playbackSpeed = value;
|
|
40443
|
-
}
|
|
40547
|
+
this._player.playbackSpeed = value;
|
|
40444
40548
|
}
|
|
40445
40549
|
/**
|
|
40446
40550
|
* Whether the playback should automatically restart after it finished.
|
|
@@ -40471,27 +40575,21 @@ class AlphaTabApiBase {
|
|
|
40471
40575
|
* ```
|
|
40472
40576
|
*/
|
|
40473
40577
|
get isLooping() {
|
|
40474
|
-
|
|
40475
|
-
return false;
|
|
40476
|
-
}
|
|
40477
|
-
return this.player.isLooping;
|
|
40578
|
+
return this._player.isLooping;
|
|
40478
40579
|
}
|
|
40479
40580
|
set isLooping(value) {
|
|
40480
|
-
|
|
40481
|
-
this.player.isLooping = value;
|
|
40482
|
-
}
|
|
40581
|
+
this._player.isLooping = value;
|
|
40483
40582
|
}
|
|
40484
40583
|
destroyPlayer() {
|
|
40485
|
-
|
|
40486
|
-
return;
|
|
40487
|
-
}
|
|
40488
|
-
this.player.destroy();
|
|
40489
|
-
this.player = null;
|
|
40584
|
+
this._player.destroy();
|
|
40490
40585
|
this._previousTick = 0;
|
|
40491
|
-
this._playerState = PlayerState.Paused;
|
|
40492
40586
|
this.destroyCursors();
|
|
40493
40587
|
}
|
|
40494
|
-
|
|
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
|
+
*/
|
|
40592
|
+
setupOrDestroyPlayer() {
|
|
40495
40593
|
let mode = this.settings.player.playerMode;
|
|
40496
40594
|
if (mode === PlayerMode.EnabledAutomatic) {
|
|
40497
40595
|
const score = this.score;
|
|
@@ -40505,68 +40603,44 @@ class AlphaTabApiBase {
|
|
|
40505
40603
|
mode = PlayerMode.EnabledSynthesizer;
|
|
40506
40604
|
}
|
|
40507
40605
|
}
|
|
40606
|
+
let newPlayer = null;
|
|
40508
40607
|
if (mode !== this._actualPlayerMode) {
|
|
40509
40608
|
this.destroyPlayer();
|
|
40609
|
+
this.updateCursors();
|
|
40610
|
+
switch (mode) {
|
|
40611
|
+
case PlayerMode.Disabled:
|
|
40612
|
+
newPlayer = null;
|
|
40613
|
+
break;
|
|
40614
|
+
case PlayerMode.EnabledSynthesizer:
|
|
40615
|
+
newPlayer = this.uiFacade.createWorkerPlayer();
|
|
40616
|
+
break;
|
|
40617
|
+
case PlayerMode.EnabledBackingTrack:
|
|
40618
|
+
newPlayer = this.uiFacade.createBackingTrackPlayer();
|
|
40619
|
+
break;
|
|
40620
|
+
case PlayerMode.EnabledExternalMedia:
|
|
40621
|
+
newPlayer = new ExternalMediaPlayer(this.settings.player.bufferTimeInMilliseconds);
|
|
40622
|
+
break;
|
|
40623
|
+
}
|
|
40510
40624
|
}
|
|
40511
|
-
|
|
40512
|
-
|
|
40513
|
-
|
|
40514
|
-
|
|
40515
|
-
this.destroyPlayer();
|
|
40516
|
-
return false;
|
|
40517
|
-
case PlayerMode.EnabledSynthesizer:
|
|
40518
|
-
if (this.player) {
|
|
40519
|
-
return true;
|
|
40520
|
-
}
|
|
40521
|
-
// new player needed
|
|
40522
|
-
this.player = this.uiFacade.createWorkerPlayer();
|
|
40523
|
-
break;
|
|
40524
|
-
case PlayerMode.EnabledBackingTrack:
|
|
40525
|
-
if (this.player) {
|
|
40526
|
-
return true;
|
|
40527
|
-
}
|
|
40528
|
-
// new player needed
|
|
40529
|
-
this.player = this.uiFacade.createBackingTrackPlayer();
|
|
40530
|
-
break;
|
|
40531
|
-
case PlayerMode.EnabledExternalMedia:
|
|
40532
|
-
if (this.player) {
|
|
40533
|
-
return true;
|
|
40534
|
-
}
|
|
40535
|
-
this.player = new ExternalMediaPlayer(this.settings.player.bufferTimeInMilliseconds);
|
|
40536
|
-
break;
|
|
40625
|
+
else {
|
|
40626
|
+
// no change in player mode, just update song info if needed
|
|
40627
|
+
this.updateCursors();
|
|
40628
|
+
return false;
|
|
40537
40629
|
}
|
|
40538
|
-
|
|
40630
|
+
this._actualPlayerMode = mode;
|
|
40631
|
+
if (!newPlayer) {
|
|
40539
40632
|
return false;
|
|
40540
40633
|
}
|
|
40541
|
-
this.
|
|
40542
|
-
this.loadMidiForScore();
|
|
40543
|
-
});
|
|
40544
|
-
this.player.readyForPlayback.on(() => {
|
|
40545
|
-
this.onPlayerReady();
|
|
40546
|
-
if (this.tracks) {
|
|
40547
|
-
for (const track of this.tracks) {
|
|
40548
|
-
const volume = track.playbackInfo.volume / 16;
|
|
40549
|
-
this.player.setChannelVolume(track.playbackInfo.primaryChannel, volume);
|
|
40550
|
-
this.player.setChannelVolume(track.playbackInfo.secondaryChannel, volume);
|
|
40551
|
-
}
|
|
40552
|
-
}
|
|
40553
|
-
});
|
|
40554
|
-
this.player.soundFontLoaded.on(this.onSoundFontLoaded.bind(this));
|
|
40555
|
-
this.player.soundFontLoadFailed.on(e => {
|
|
40556
|
-
this.onError(e);
|
|
40557
|
-
});
|
|
40558
|
-
this.player.midiLoaded.on(this.onMidiLoaded.bind(this));
|
|
40559
|
-
this.player.midiLoadFailed.on(e => {
|
|
40560
|
-
this.onError(e);
|
|
40561
|
-
});
|
|
40562
|
-
this.player.stateChanged.on(this.onPlayerStateChanged.bind(this));
|
|
40563
|
-
this.player.positionChanged.on(this.onPlayerPositionChanged.bind(this));
|
|
40564
|
-
this.player.midiEventsPlayed.on(this.onMidiEventsPlayed.bind(this));
|
|
40565
|
-
this.player.playbackRangeChanged.on(this.onPlaybackRangeChanged.bind(this));
|
|
40566
|
-
this.player.finished.on(this.onPlayerFinished.bind(this));
|
|
40567
|
-
this.setupPlayerEvents();
|
|
40634
|
+
this._player.instance = newPlayer;
|
|
40568
40635
|
return false;
|
|
40569
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
|
+
*/
|
|
40570
40644
|
loadMidiForScore() {
|
|
40571
40645
|
if (!this.score) {
|
|
40572
40646
|
return;
|
|
@@ -40584,12 +40658,10 @@ class AlphaTabApiBase {
|
|
|
40584
40658
|
generator.generate();
|
|
40585
40659
|
this._tickCache = generator.tickLookup;
|
|
40586
40660
|
this.onMidiLoad(midiFile);
|
|
40587
|
-
const player = this.
|
|
40588
|
-
|
|
40589
|
-
|
|
40590
|
-
|
|
40591
|
-
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40592
|
-
}
|
|
40661
|
+
const player = this._player;
|
|
40662
|
+
player.loadMidiFile(midiFile);
|
|
40663
|
+
player.loadBackingTrack(score, generator.syncPoints);
|
|
40664
|
+
player.applyTranspositionPitches(generator.transpositionPitches);
|
|
40593
40665
|
}
|
|
40594
40666
|
/**
|
|
40595
40667
|
* Changes the volume of the given tracks.
|
|
@@ -40627,12 +40699,9 @@ class AlphaTabApiBase {
|
|
|
40627
40699
|
* ```
|
|
40628
40700
|
*/
|
|
40629
40701
|
changeTrackVolume(tracks, volume) {
|
|
40630
|
-
if (!this.player) {
|
|
40631
|
-
return;
|
|
40632
|
-
}
|
|
40633
40702
|
for (const track of tracks) {
|
|
40634
|
-
this.
|
|
40635
|
-
this.
|
|
40703
|
+
this._player.setChannelVolume(track.playbackInfo.primaryChannel, volume);
|
|
40704
|
+
this._player.setChannelVolume(track.playbackInfo.secondaryChannel, volume);
|
|
40636
40705
|
}
|
|
40637
40706
|
}
|
|
40638
40707
|
/**
|
|
@@ -40669,12 +40738,9 @@ class AlphaTabApiBase {
|
|
|
40669
40738
|
* ```
|
|
40670
40739
|
*/
|
|
40671
40740
|
changeTrackSolo(tracks, solo) {
|
|
40672
|
-
if (!this.player) {
|
|
40673
|
-
return;
|
|
40674
|
-
}
|
|
40675
40741
|
for (const track of tracks) {
|
|
40676
|
-
this.
|
|
40677
|
-
this.
|
|
40742
|
+
this._player.setChannelSolo(track.playbackInfo.primaryChannel, solo);
|
|
40743
|
+
this._player.setChannelSolo(track.playbackInfo.secondaryChannel, solo);
|
|
40678
40744
|
}
|
|
40679
40745
|
}
|
|
40680
40746
|
/**
|
|
@@ -40710,12 +40776,9 @@ class AlphaTabApiBase {
|
|
|
40710
40776
|
* ```
|
|
40711
40777
|
*/
|
|
40712
40778
|
changeTrackMute(tracks, mute) {
|
|
40713
|
-
if (!this.player) {
|
|
40714
|
-
return;
|
|
40715
|
-
}
|
|
40716
40779
|
for (const track of tracks) {
|
|
40717
|
-
this.
|
|
40718
|
-
this.
|
|
40780
|
+
this._player.setChannelMute(track.playbackInfo.primaryChannel, mute);
|
|
40781
|
+
this._player.setChannelMute(track.playbackInfo.secondaryChannel, mute);
|
|
40719
40782
|
}
|
|
40720
40783
|
}
|
|
40721
40784
|
/**
|
|
@@ -40753,12 +40816,9 @@ class AlphaTabApiBase {
|
|
|
40753
40816
|
* ```
|
|
40754
40817
|
*/
|
|
40755
40818
|
changeTrackTranspositionPitch(tracks, semitones) {
|
|
40756
|
-
if (!this.player) {
|
|
40757
|
-
return;
|
|
40758
|
-
}
|
|
40759
40819
|
for (const track of tracks) {
|
|
40760
|
-
this.
|
|
40761
|
-
this.
|
|
40820
|
+
this._player.setChannelTranspositionPitch(track.playbackInfo.primaryChannel, semitones);
|
|
40821
|
+
this._player.setChannelTranspositionPitch(track.playbackInfo.secondaryChannel, semitones);
|
|
40762
40822
|
}
|
|
40763
40823
|
}
|
|
40764
40824
|
/**
|
|
@@ -40789,10 +40849,7 @@ class AlphaTabApiBase {
|
|
|
40789
40849
|
* ```
|
|
40790
40850
|
*/
|
|
40791
40851
|
play() {
|
|
40792
|
-
|
|
40793
|
-
return false;
|
|
40794
|
-
}
|
|
40795
|
-
return this.player.play();
|
|
40852
|
+
return this._player.play();
|
|
40796
40853
|
}
|
|
40797
40854
|
/**
|
|
40798
40855
|
* Pauses the playback of the current song.
|
|
@@ -40821,10 +40878,7 @@ class AlphaTabApiBase {
|
|
|
40821
40878
|
* ```
|
|
40822
40879
|
*/
|
|
40823
40880
|
pause() {
|
|
40824
|
-
|
|
40825
|
-
return;
|
|
40826
|
-
}
|
|
40827
|
-
this.player.pause();
|
|
40881
|
+
this._player.pause();
|
|
40828
40882
|
}
|
|
40829
40883
|
/**
|
|
40830
40884
|
* Toggles between play/pause depending on the current player state.
|
|
@@ -40855,10 +40909,7 @@ class AlphaTabApiBase {
|
|
|
40855
40909
|
* ```
|
|
40856
40910
|
*/
|
|
40857
40911
|
playPause() {
|
|
40858
|
-
|
|
40859
|
-
return;
|
|
40860
|
-
}
|
|
40861
|
-
this.player.playPause();
|
|
40912
|
+
this._player.playPause();
|
|
40862
40913
|
}
|
|
40863
40914
|
/**
|
|
40864
40915
|
* Stops the playback of the current song, and moves the playback position back to the start.
|
|
@@ -40889,10 +40940,7 @@ class AlphaTabApiBase {
|
|
|
40889
40940
|
* ```
|
|
40890
40941
|
*/
|
|
40891
40942
|
stop() {
|
|
40892
|
-
|
|
40893
|
-
return;
|
|
40894
|
-
}
|
|
40895
|
-
this.player.stop();
|
|
40943
|
+
this._player.stop();
|
|
40896
40944
|
}
|
|
40897
40945
|
/**
|
|
40898
40946
|
* Triggers the play of the given beat.
|
|
@@ -40928,15 +40976,12 @@ class AlphaTabApiBase {
|
|
|
40928
40976
|
* ```
|
|
40929
40977
|
*/
|
|
40930
40978
|
playBeat(beat) {
|
|
40931
|
-
if (!this.player) {
|
|
40932
|
-
return;
|
|
40933
|
-
}
|
|
40934
40979
|
// we generate a new midi file containing only the beat
|
|
40935
40980
|
const midiFile = new MidiFile();
|
|
40936
40981
|
const handler = new AlphaSynthMidiFileHandler(midiFile);
|
|
40937
40982
|
const generator = new MidiFileGenerator(beat.voice.bar.staff.track.score, this.settings, handler);
|
|
40938
40983
|
generator.generateSingleBeat(beat);
|
|
40939
|
-
this.
|
|
40984
|
+
this._player.playOneTimeMidiFile(midiFile);
|
|
40940
40985
|
}
|
|
40941
40986
|
/**
|
|
40942
40987
|
* Triggers the play of the given note.
|
|
@@ -40971,15 +41016,12 @@ class AlphaTabApiBase {
|
|
|
40971
41016
|
* ```
|
|
40972
41017
|
*/
|
|
40973
41018
|
playNote(note) {
|
|
40974
|
-
if (!this.player) {
|
|
40975
|
-
return;
|
|
40976
|
-
}
|
|
40977
41019
|
// we generate a new midi file containing only the beat
|
|
40978
41020
|
const midiFile = new MidiFile();
|
|
40979
41021
|
const handler = new AlphaSynthMidiFileHandler(midiFile);
|
|
40980
41022
|
const generator = new MidiFileGenerator(note.beat.voice.bar.staff.track.score, this.settings, handler);
|
|
40981
41023
|
generator.generateSingleNote(note);
|
|
40982
|
-
this.
|
|
41024
|
+
this._player.playOneTimeMidiFile(midiFile);
|
|
40983
41025
|
}
|
|
40984
41026
|
destroyCursors() {
|
|
40985
41027
|
if (!this._cursorWrapper) {
|
|
@@ -41002,6 +41044,7 @@ class AlphaTabApiBase {
|
|
|
41002
41044
|
this._barCursor = cursors.barCursor;
|
|
41003
41045
|
this._beatCursor = cursors.beatCursor;
|
|
41004
41046
|
this._selectionWrapper = cursors.selectionWrapper;
|
|
41047
|
+
this._isInitialBeatCursorUpdate = true;
|
|
41005
41048
|
}
|
|
41006
41049
|
if (this._currentBeat !== null) {
|
|
41007
41050
|
this.cursorUpdateBeat(this._currentBeat, false, this._previousTick > 10, 1, true);
|
|
@@ -41011,36 +41054,6 @@ class AlphaTabApiBase {
|
|
|
41011
41054
|
this.destroyCursors();
|
|
41012
41055
|
}
|
|
41013
41056
|
}
|
|
41014
|
-
setupPlayerEvents() {
|
|
41015
|
-
//
|
|
41016
|
-
// Hook into events
|
|
41017
|
-
this._previousTick = 0;
|
|
41018
|
-
this._playerState = PlayerState.Paused;
|
|
41019
|
-
// we need to update our position caches if we render a tablature
|
|
41020
|
-
this.renderer.postRenderFinished.on(() => {
|
|
41021
|
-
this._currentBeat = null;
|
|
41022
|
-
this.cursorUpdateTick(this._previousTick, false, 1, this._previousTick > 10);
|
|
41023
|
-
});
|
|
41024
|
-
if (this.player) {
|
|
41025
|
-
this.player.positionChanged.on(e => {
|
|
41026
|
-
this._previousTick = e.currentTick;
|
|
41027
|
-
this.uiFacade.beginInvoke(() => {
|
|
41028
|
-
const cursorSpeed = e.modifiedTempo / e.originalTempo;
|
|
41029
|
-
this.cursorUpdateTick(e.currentTick, false, cursorSpeed, false, e.isSeek);
|
|
41030
|
-
});
|
|
41031
|
-
});
|
|
41032
|
-
this.player.stateChanged.on(e => {
|
|
41033
|
-
this._playerState = e.state;
|
|
41034
|
-
if (!e.stopped && e.state === PlayerState.Paused) {
|
|
41035
|
-
const currentBeat = this._currentBeat;
|
|
41036
|
-
const tickCache = this._tickCache;
|
|
41037
|
-
if (currentBeat && tickCache) {
|
|
41038
|
-
this.player.tickPosition = tickCache.getBeatStart(currentBeat.beat);
|
|
41039
|
-
}
|
|
41040
|
-
}
|
|
41041
|
-
});
|
|
41042
|
-
}
|
|
41043
|
-
}
|
|
41044
41057
|
/**
|
|
41045
41058
|
* updates the cursors to highlight the beat at the specified tick position
|
|
41046
41059
|
* @param tick
|
|
@@ -41071,7 +41084,7 @@ class AlphaTabApiBase {
|
|
|
41071
41084
|
if (!beat) {
|
|
41072
41085
|
return;
|
|
41073
41086
|
}
|
|
41074
|
-
const cache = this.
|
|
41087
|
+
const cache = this._renderer.boundsLookup;
|
|
41075
41088
|
if (!cache) {
|
|
41076
41089
|
return;
|
|
41077
41090
|
}
|
|
@@ -41081,7 +41094,7 @@ class AlphaTabApiBase {
|
|
|
41081
41094
|
if (!forceUpdate &&
|
|
41082
41095
|
beat === previousBeat?.beat &&
|
|
41083
41096
|
cache === previousCache &&
|
|
41084
|
-
previousState === this.
|
|
41097
|
+
previousState === this._player.state &&
|
|
41085
41098
|
previousBeat?.start === lookupResult.start) {
|
|
41086
41099
|
return;
|
|
41087
41100
|
}
|
|
@@ -41093,7 +41106,7 @@ class AlphaTabApiBase {
|
|
|
41093
41106
|
// actually show the cursor
|
|
41094
41107
|
this._currentBeat = lookupResult;
|
|
41095
41108
|
this._previousCursorCache = cache;
|
|
41096
|
-
this._previousStateForCursor = this.
|
|
41109
|
+
this._previousStateForCursor = this._player.state;
|
|
41097
41110
|
this.uiFacade.beginInvoke(() => {
|
|
41098
41111
|
this.internalCursorUpdateBeat(beat, nextBeat, duration, stop, beatsToHighlight, cache, beatBoundings, shouldScroll, lookupResult.cursorMode, cursorSpeed);
|
|
41099
41112
|
});
|
|
@@ -41104,9 +41117,9 @@ class AlphaTabApiBase {
|
|
|
41104
41117
|
* @category Methods - Player
|
|
41105
41118
|
*/
|
|
41106
41119
|
scrollToCursor() {
|
|
41107
|
-
const
|
|
41108
|
-
if (
|
|
41109
|
-
this.internalScrollToCursor(barBounds);
|
|
41120
|
+
const beatBounds = this._currentBeatBounds;
|
|
41121
|
+
if (beatBounds) {
|
|
41122
|
+
this.internalScrollToCursor(beatBounds.barBounds.masterBarBounds);
|
|
41110
41123
|
}
|
|
41111
41124
|
}
|
|
41112
41125
|
internalScrollToCursor(barBoundings) {
|
|
@@ -41165,10 +41178,12 @@ class AlphaTabApiBase {
|
|
|
41165
41178
|
const beatCursor = this._beatCursor;
|
|
41166
41179
|
const barBoundings = beatBoundings.barBounds.masterBarBounds;
|
|
41167
41180
|
const barBounds = barBoundings.visualBounds;
|
|
41168
|
-
|
|
41181
|
+
const previousBeatBounds = this._currentBeatBounds;
|
|
41182
|
+
this._currentBeatBounds = beatBoundings;
|
|
41169
41183
|
if (barCursor) {
|
|
41170
41184
|
barCursor.setBounds(barBounds.x, barBounds.y, barBounds.w, barBounds.h);
|
|
41171
41185
|
}
|
|
41186
|
+
const isPlayingUpdate = this._player.state === PlayerState.Playing && !stop;
|
|
41172
41187
|
let nextBeatX = barBoundings.visualBounds.x + barBoundings.visualBounds.w;
|
|
41173
41188
|
// get position of next beat on same system
|
|
41174
41189
|
if (nextBeat && cursorMode === MidiTickLookupFindBeatResultCursorMode.ToNextBext) {
|
|
@@ -41186,20 +41201,49 @@ class AlphaTabApiBase {
|
|
|
41186
41201
|
if (this.settings.player.enableAnimatedBeatCursor) {
|
|
41187
41202
|
const animationWidth = nextBeatX - beatBoundings.onNotesX;
|
|
41188
41203
|
const relativePosition = this._previousTick - this._currentBeat.start;
|
|
41189
|
-
const ratioPosition = relativePosition / this._currentBeat.tickDuration;
|
|
41204
|
+
const ratioPosition = this._currentBeat.tickDuration > 0 ? relativePosition / this._currentBeat.tickDuration : 0;
|
|
41190
41205
|
startBeatX = beatBoundings.onNotesX + animationWidth * ratioPosition;
|
|
41191
41206
|
duration -= duration * ratioPosition;
|
|
41207
|
+
if (isPlayingUpdate) {
|
|
41208
|
+
// we do not "reset" the cursor if we are smoothly moving from left to right.
|
|
41209
|
+
const jumpCursor = !previousBeatBounds ||
|
|
41210
|
+
this._isInitialBeatCursorUpdate ||
|
|
41211
|
+
barBounds.y !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.y ||
|
|
41212
|
+
startBeatX < previousBeatBounds.onNotesX ||
|
|
41213
|
+
barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1;
|
|
41214
|
+
if (jumpCursor) {
|
|
41215
|
+
beatCursor.transitionToX(0, startBeatX);
|
|
41216
|
+
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
41217
|
+
}
|
|
41218
|
+
// we need to put the transition to an own animation frame
|
|
41219
|
+
// otherwise the stop animation above is not applied.
|
|
41220
|
+
this.uiFacade.beginInvoke(() => {
|
|
41221
|
+
// it can happen that the cursor reaches the target position slightly too early (especially on backing tracks)
|
|
41222
|
+
// to avoid the cursor stopping, causing a wierd look, we animate the cursor to the double position in double time.
|
|
41223
|
+
// beatCursor!.transitionToX((duration / cursorSpeed), nextBeatX);
|
|
41224
|
+
const doubleEndBeatX = startBeatX + (nextBeatX - startBeatX) * 2;
|
|
41225
|
+
beatCursor.transitionToX((duration / cursorSpeed) * 2, doubleEndBeatX);
|
|
41226
|
+
});
|
|
41227
|
+
}
|
|
41228
|
+
else {
|
|
41229
|
+
beatCursor.transitionToX(0, startBeatX);
|
|
41230
|
+
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
41231
|
+
}
|
|
41232
|
+
}
|
|
41233
|
+
else {
|
|
41234
|
+
// ticking cursor
|
|
41192
41235
|
beatCursor.transitionToX(0, startBeatX);
|
|
41236
|
+
beatCursor.setBounds(startBeatX, barBounds.y, 1, barBounds.h);
|
|
41193
41237
|
}
|
|
41194
|
-
|
|
41238
|
+
this._isInitialBeatCursorUpdate = false;
|
|
41195
41239
|
}
|
|
41196
|
-
|
|
41197
|
-
|
|
41198
|
-
this.uiFacade.removeHighlights();
|
|
41240
|
+
else {
|
|
41241
|
+
this._isInitialBeatCursorUpdate = true;
|
|
41199
41242
|
}
|
|
41243
|
+
// if playing, animate the cursor to the next beat
|
|
41244
|
+
this.uiFacade.removeHighlights();
|
|
41200
41245
|
// actively playing? -> animate cursor and highlight items
|
|
41201
41246
|
let shouldNotifyBeatChange = false;
|
|
41202
|
-
const isPlayingUpdate = this._playerState === PlayerState.Playing && !stop;
|
|
41203
41247
|
if (isPlayingUpdate) {
|
|
41204
41248
|
if (this.settings.player.enableElementHighlighting) {
|
|
41205
41249
|
for (const highlight of beatsToHighlight) {
|
|
@@ -41210,15 +41254,6 @@ class AlphaTabApiBase {
|
|
|
41210
41254
|
shouldScroll = !stop;
|
|
41211
41255
|
shouldNotifyBeatChange = true;
|
|
41212
41256
|
}
|
|
41213
|
-
if (this.settings.player.enableAnimatedBeatCursor && beatCursor) {
|
|
41214
|
-
if (isPlayingUpdate) {
|
|
41215
|
-
// we need to put the transition to an own animation frame
|
|
41216
|
-
// otherwise the stop animation above is not applied.
|
|
41217
|
-
this.uiFacade.beginInvoke(() => {
|
|
41218
|
-
beatCursor.transitionToX(duration / cursorSpeed, nextBeatX);
|
|
41219
|
-
});
|
|
41220
|
-
}
|
|
41221
|
-
}
|
|
41222
41257
|
if (shouldScroll && !this._beatMouseDown && this.settings.player.scrollMode !== ScrollMode.Off) {
|
|
41223
41258
|
this.internalScrollToCursor(barBoundings);
|
|
41224
41259
|
}
|
|
@@ -41308,7 +41343,7 @@ class AlphaTabApiBase {
|
|
|
41308
41343
|
const realMasterBarStart = tickCache.getMasterBarStart(this._selectionStart.beat.voice.bar.masterBar);
|
|
41309
41344
|
// move to selection start
|
|
41310
41345
|
this._currentBeat = null; // reset current beat so it is updating the cursor
|
|
41311
|
-
if (this.
|
|
41346
|
+
if (this._player.state === PlayerState.Paused) {
|
|
41312
41347
|
this.cursorUpdateTick(this._tickCache.getBeatStart(this._selectionStart.beat), false, 1);
|
|
41313
41348
|
}
|
|
41314
41349
|
this.tickPosition = realMasterBarStart + this._selectionStart.beat.playbackStart;
|
|
@@ -41370,11 +41405,11 @@ class AlphaTabApiBase {
|
|
|
41370
41405
|
}
|
|
41371
41406
|
const relX = e.getX(this.canvasElement);
|
|
41372
41407
|
const relY = e.getY(this.canvasElement);
|
|
41373
|
-
const beat = this.
|
|
41408
|
+
const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
|
|
41374
41409
|
if (beat) {
|
|
41375
41410
|
this.onBeatMouseDown(e, beat);
|
|
41376
41411
|
if (this.settings.core.includeNoteBounds) {
|
|
41377
|
-
const note = this.
|
|
41412
|
+
const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
|
|
41378
41413
|
if (note) {
|
|
41379
41414
|
this.onNoteMouseDown(e, note);
|
|
41380
41415
|
}
|
|
@@ -41387,11 +41422,11 @@ class AlphaTabApiBase {
|
|
|
41387
41422
|
}
|
|
41388
41423
|
const relX = e.getX(this.canvasElement);
|
|
41389
41424
|
const relY = e.getY(this.canvasElement);
|
|
41390
|
-
const beat = this.
|
|
41425
|
+
const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
|
|
41391
41426
|
if (beat) {
|
|
41392
41427
|
this.onBeatMouseMove(e, beat);
|
|
41393
41428
|
if (this._noteMouseDown) {
|
|
41394
|
-
const note = this.
|
|
41429
|
+
const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY);
|
|
41395
41430
|
if (note) {
|
|
41396
41431
|
this.onNoteMouseMove(e, note);
|
|
41397
41432
|
}
|
|
@@ -41407,11 +41442,11 @@ class AlphaTabApiBase {
|
|
|
41407
41442
|
}
|
|
41408
41443
|
const relX = e.getX(this.canvasElement);
|
|
41409
41444
|
const relY = e.getY(this.canvasElement);
|
|
41410
|
-
const beat = this.
|
|
41445
|
+
const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
|
|
41411
41446
|
this.onBeatMouseUp(e, beat);
|
|
41412
41447
|
if (this._noteMouseDown) {
|
|
41413
41448
|
if (beat) {
|
|
41414
|
-
const note = this.
|
|
41449
|
+
const note = this._renderer.boundsLookup?.getNoteAtPos(beat, relX, relY) ?? null;
|
|
41415
41450
|
this.onNoteMouseUp(e, note);
|
|
41416
41451
|
}
|
|
41417
41452
|
else {
|
|
@@ -41419,7 +41454,7 @@ class AlphaTabApiBase {
|
|
|
41419
41454
|
}
|
|
41420
41455
|
}
|
|
41421
41456
|
});
|
|
41422
|
-
this.
|
|
41457
|
+
this._renderer.postRenderFinished.on(() => {
|
|
41423
41458
|
if (!this._selectionStart ||
|
|
41424
41459
|
this.settings.player.playerMode === PlayerMode.Disabled ||
|
|
41425
41460
|
!this.settings.player.enableCursor ||
|
|
@@ -41430,7 +41465,7 @@ class AlphaTabApiBase {
|
|
|
41430
41465
|
});
|
|
41431
41466
|
}
|
|
41432
41467
|
cursorSelectRange(startBeat, endBeat) {
|
|
41433
|
-
const cache = this.
|
|
41468
|
+
const cache = this._renderer.boundsLookup;
|
|
41434
41469
|
if (!cache) {
|
|
41435
41470
|
return;
|
|
41436
41471
|
}
|
|
@@ -41499,7 +41534,8 @@ class AlphaTabApiBase {
|
|
|
41499
41534
|
}
|
|
41500
41535
|
this.scoreLoaded.trigger(score);
|
|
41501
41536
|
this.uiFacade.triggerEvent(this.container, 'scoreLoaded', score);
|
|
41502
|
-
if (this.
|
|
41537
|
+
if (!this.setupOrDestroyPlayer()) {
|
|
41538
|
+
// feed midi into current player (a new player will trigger a midi generation once the player is ready)
|
|
41503
41539
|
this.loadMidiForScore();
|
|
41504
41540
|
}
|
|
41505
41541
|
}
|
|
@@ -41528,6 +41564,8 @@ class AlphaTabApiBase {
|
|
|
41528
41564
|
if (this._isDestroyed) {
|
|
41529
41565
|
return;
|
|
41530
41566
|
}
|
|
41567
|
+
this._currentBeat = null;
|
|
41568
|
+
this.cursorUpdateTick(this._previousTick, false, 1, true, true);
|
|
41531
41569
|
this.postRenderFinished.trigger();
|
|
41532
41570
|
this.uiFacade.triggerEvent(this.container, 'postRenderFinished', null);
|
|
41533
41571
|
}
|
|
@@ -41542,25 +41580,155 @@ class AlphaTabApiBase {
|
|
|
41542
41580
|
this.error.trigger(error);
|
|
41543
41581
|
this.uiFacade.triggerEvent(this.container, 'error', error);
|
|
41544
41582
|
}
|
|
41583
|
+
/**
|
|
41584
|
+
* This event is fired when all required data for playback is loaded and ready.
|
|
41585
|
+
* @remarks
|
|
41586
|
+
* This event is fired when all required data for playback is loaded and ready. The player is ready for playback when
|
|
41587
|
+
* all background workers are started, the audio output is initialized, a soundfont is loaded, and a song was loaded into the player as midi file.
|
|
41588
|
+
*
|
|
41589
|
+
* @eventProperty
|
|
41590
|
+
* @category Events - Player
|
|
41591
|
+
* @since 0.9.4
|
|
41592
|
+
*
|
|
41593
|
+
* @example
|
|
41594
|
+
* JavaScript
|
|
41595
|
+
* ```js
|
|
41596
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41597
|
+
* api.playerReady.on(() => {
|
|
41598
|
+
* enablePlayerControls();
|
|
41599
|
+
* });
|
|
41600
|
+
* ```
|
|
41601
|
+
*
|
|
41602
|
+
* @example
|
|
41603
|
+
* C#
|
|
41604
|
+
* ```cs
|
|
41605
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41606
|
+
* api.PlayerReady.On(() =>
|
|
41607
|
+
* {
|
|
41608
|
+
* EnablePlayerControls()
|
|
41609
|
+
* });
|
|
41610
|
+
* ```
|
|
41611
|
+
*
|
|
41612
|
+
* @example
|
|
41613
|
+
* Android
|
|
41614
|
+
* ```kotlin
|
|
41615
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
41616
|
+
* api.playerReady.on {
|
|
41617
|
+
* enablePlayerControls()
|
|
41618
|
+
* }
|
|
41619
|
+
* ```
|
|
41620
|
+
*/
|
|
41621
|
+
get playerReady() {
|
|
41622
|
+
return this._player.readyForPlayback;
|
|
41623
|
+
}
|
|
41545
41624
|
onPlayerReady() {
|
|
41546
41625
|
if (this._isDestroyed) {
|
|
41547
41626
|
return;
|
|
41548
41627
|
}
|
|
41549
|
-
this.playerReady.trigger();
|
|
41550
41628
|
this.uiFacade.triggerEvent(this.container, 'playerReady', null);
|
|
41551
41629
|
}
|
|
41630
|
+
/**
|
|
41631
|
+
* This event is fired when the playback of the whole song finished.
|
|
41632
|
+
* @remarks
|
|
41633
|
+
* This event is finished regardless on whether looping is enabled or not.
|
|
41634
|
+
*
|
|
41635
|
+
* @eventProperty
|
|
41636
|
+
* @category Events - Player
|
|
41637
|
+
* @since 0.9.4
|
|
41638
|
+
*
|
|
41639
|
+
* @example
|
|
41640
|
+
* JavaScript
|
|
41641
|
+
* ```js
|
|
41642
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41643
|
+
* api.playerFinished.on((args) => {
|
|
41644
|
+
* // speed trainer
|
|
41645
|
+
* api.playbackSpeed = Math.min(1.0, api.playbackSpeed + 0.1);
|
|
41646
|
+
* });
|
|
41647
|
+
* api.isLooping = true;
|
|
41648
|
+
* api.playbackSpeed = 0.5;
|
|
41649
|
+
* api.play()
|
|
41650
|
+
* ```
|
|
41651
|
+
*
|
|
41652
|
+
* @example
|
|
41653
|
+
* C#
|
|
41654
|
+
* ```cs
|
|
41655
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41656
|
+
* api.PlayerFinished.On(() =>
|
|
41657
|
+
* {
|
|
41658
|
+
* // speed trainer
|
|
41659
|
+
* api.PlaybackSpeed = Math.Min(1.0, api.PlaybackSpeed + 0.1);
|
|
41660
|
+
* });
|
|
41661
|
+
* api.IsLooping = true;
|
|
41662
|
+
* api.PlaybackSpeed = 0.5;
|
|
41663
|
+
* api.Play();
|
|
41664
|
+
* ```
|
|
41665
|
+
*
|
|
41666
|
+
* @example
|
|
41667
|
+
* Android
|
|
41668
|
+
* ```kotlin
|
|
41669
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
41670
|
+
* api.playerFinished.on {
|
|
41671
|
+
* // speed trainer
|
|
41672
|
+
* api.playbackSpeed = min(1.0, api.playbackSpeed + 0.1);
|
|
41673
|
+
* }
|
|
41674
|
+
* api.isLooping = true
|
|
41675
|
+
* api.playbackSpeed = 0.5
|
|
41676
|
+
* api.play()
|
|
41677
|
+
* ```
|
|
41678
|
+
*
|
|
41679
|
+
*/
|
|
41680
|
+
get playerFinished() {
|
|
41681
|
+
return this._player.finished;
|
|
41682
|
+
}
|
|
41552
41683
|
onPlayerFinished() {
|
|
41553
41684
|
if (this._isDestroyed) {
|
|
41554
41685
|
return;
|
|
41555
41686
|
}
|
|
41556
|
-
this.playerFinished.trigger();
|
|
41557
41687
|
this.uiFacade.triggerEvent(this.container, 'playerFinished', null);
|
|
41558
41688
|
}
|
|
41689
|
+
/**
|
|
41690
|
+
* This event is fired when the SoundFont needed for playback was loaded.
|
|
41691
|
+
*
|
|
41692
|
+
* @eventProperty
|
|
41693
|
+
* @category Events - Player
|
|
41694
|
+
* @since 0.9.4
|
|
41695
|
+
*
|
|
41696
|
+
* @example
|
|
41697
|
+
* JavaScript
|
|
41698
|
+
* ```js
|
|
41699
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41700
|
+
* api.soundFontLoaded.on(() => {
|
|
41701
|
+
* hideSoundFontLoadingIndicator();
|
|
41702
|
+
* });
|
|
41703
|
+
* ```
|
|
41704
|
+
*
|
|
41705
|
+
* @example
|
|
41706
|
+
* C#
|
|
41707
|
+
* ```cs
|
|
41708
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41709
|
+
* api.SoundFontLoaded.On(() =>
|
|
41710
|
+
* {
|
|
41711
|
+
* HideSoundFontLoadingIndicator();
|
|
41712
|
+
* });
|
|
41713
|
+
* ```
|
|
41714
|
+
*
|
|
41715
|
+
* @example
|
|
41716
|
+
* Android
|
|
41717
|
+
* ```kotlin
|
|
41718
|
+
* val api = AlphaTabApi<MyControl>(...);
|
|
41719
|
+
* api.soundFontLoaded.on {
|
|
41720
|
+
* hideSoundFontLoadingIndicator();
|
|
41721
|
+
* }
|
|
41722
|
+
* ```
|
|
41723
|
+
*
|
|
41724
|
+
*/
|
|
41725
|
+
get soundFontLoaded() {
|
|
41726
|
+
return this._player.soundFontLoaded;
|
|
41727
|
+
}
|
|
41559
41728
|
onSoundFontLoaded() {
|
|
41560
41729
|
if (this._isDestroyed) {
|
|
41561
41730
|
return;
|
|
41562
41731
|
}
|
|
41563
|
-
this.soundFontLoaded.trigger();
|
|
41564
41732
|
this.uiFacade.triggerEvent(this.container, 'soundFontLoaded', null);
|
|
41565
41733
|
}
|
|
41566
41734
|
onMidiLoad(e) {
|
|
@@ -41577,34 +41745,255 @@ class AlphaTabApiBase {
|
|
|
41577
41745
|
this.midiLoaded.trigger(e);
|
|
41578
41746
|
this.uiFacade.triggerEvent(this.container, 'midiFileLoaded', e);
|
|
41579
41747
|
}
|
|
41748
|
+
/**
|
|
41749
|
+
* This event is fired when the playback state changed.
|
|
41750
|
+
*
|
|
41751
|
+
* @eventProperty
|
|
41752
|
+
* @category Events - Player
|
|
41753
|
+
* @since 0.9.4
|
|
41754
|
+
*
|
|
41755
|
+
* @example
|
|
41756
|
+
* JavaScript
|
|
41757
|
+
* ```js
|
|
41758
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41759
|
+
* api.playerStateChanged.on((args) => {
|
|
41760
|
+
* updatePlayerControls(args.state, args.stopped);
|
|
41761
|
+
* });
|
|
41762
|
+
* ```
|
|
41763
|
+
*
|
|
41764
|
+
* @example
|
|
41765
|
+
* C#
|
|
41766
|
+
* ```cs
|
|
41767
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41768
|
+
* api.PlayerStateChanged.On(args =>
|
|
41769
|
+
* {
|
|
41770
|
+
* UpdatePlayerControls(args);
|
|
41771
|
+
* });
|
|
41772
|
+
* ```
|
|
41773
|
+
*
|
|
41774
|
+
* @example
|
|
41775
|
+
* Android
|
|
41776
|
+
* ```kotlin
|
|
41777
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
41778
|
+
* api.playerStateChanged.on { args ->
|
|
41779
|
+
* updatePlayerControls(args)
|
|
41780
|
+
* }
|
|
41781
|
+
* ```
|
|
41782
|
+
*
|
|
41783
|
+
*/
|
|
41784
|
+
get playerStateChanged() {
|
|
41785
|
+
return this._player.stateChanged;
|
|
41786
|
+
}
|
|
41580
41787
|
onPlayerStateChanged(e) {
|
|
41581
41788
|
if (this._isDestroyed) {
|
|
41582
41789
|
return;
|
|
41583
41790
|
}
|
|
41584
|
-
|
|
41791
|
+
if (!e.stopped && e.state === PlayerState.Paused) {
|
|
41792
|
+
const currentBeat = this._currentBeat;
|
|
41793
|
+
const tickCache = this._tickCache;
|
|
41794
|
+
if (currentBeat && tickCache) {
|
|
41795
|
+
this._player.tickPosition = tickCache.getBeatStart(currentBeat.beat);
|
|
41796
|
+
}
|
|
41797
|
+
}
|
|
41585
41798
|
this.uiFacade.triggerEvent(this.container, 'playerStateChanged', e);
|
|
41586
41799
|
}
|
|
41800
|
+
/**
|
|
41801
|
+
* This event is fired when the current playback position of the song changed.
|
|
41802
|
+
*
|
|
41803
|
+
* @eventProperty
|
|
41804
|
+
* @category Events - Player
|
|
41805
|
+
* @since 0.9.4
|
|
41806
|
+
*
|
|
41807
|
+
* @example
|
|
41808
|
+
* JavaScript
|
|
41809
|
+
* ```js
|
|
41810
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41811
|
+
* api.playerPositionChanged.on((args) => {
|
|
41812
|
+
* updatePlayerPosition(args);
|
|
41813
|
+
* });
|
|
41814
|
+
* ```
|
|
41815
|
+
*
|
|
41816
|
+
* @example
|
|
41817
|
+
* C#
|
|
41818
|
+
* ```cs
|
|
41819
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41820
|
+
* api.PlayerPositionChanged.On(args =>
|
|
41821
|
+
* {
|
|
41822
|
+
* UpdatePlayerPosition(args);
|
|
41823
|
+
* });
|
|
41824
|
+
* ```
|
|
41825
|
+
*
|
|
41826
|
+
* @example
|
|
41827
|
+
* Android
|
|
41828
|
+
* ```kotlin
|
|
41829
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
41830
|
+
* api.playerPositionChanged.on { args ->
|
|
41831
|
+
* updatePlayerPosition(args)
|
|
41832
|
+
* }
|
|
41833
|
+
* ```
|
|
41834
|
+
*
|
|
41835
|
+
*/
|
|
41836
|
+
get playerPositionChanged() {
|
|
41837
|
+
return this._player.positionChanged;
|
|
41838
|
+
}
|
|
41587
41839
|
onPlayerPositionChanged(e) {
|
|
41588
41840
|
if (this._isDestroyed) {
|
|
41589
41841
|
return;
|
|
41590
41842
|
}
|
|
41591
|
-
|
|
41592
|
-
|
|
41593
|
-
|
|
41594
|
-
|
|
41843
|
+
this._previousTick = e.currentTick;
|
|
41844
|
+
this.uiFacade.beginInvoke(() => {
|
|
41845
|
+
const cursorSpeed = e.modifiedTempo / e.originalTempo;
|
|
41846
|
+
this.cursorUpdateTick(e.currentTick, false, cursorSpeed, false, e.isSeek);
|
|
41847
|
+
});
|
|
41848
|
+
this.uiFacade.triggerEvent(this.container, 'playerPositionChanged', e);
|
|
41849
|
+
}
|
|
41850
|
+
/**
|
|
41851
|
+
* This event is fired when the synthesizer played certain midi events.
|
|
41852
|
+
*
|
|
41853
|
+
* @remarks
|
|
41854
|
+
* This event is fired when the synthesizer played certain midi events. This allows reacing on various low level
|
|
41855
|
+
* audio playback elements like notes/rests played or metronome ticks.
|
|
41856
|
+
*
|
|
41857
|
+
* Refer to the [related guide](https://www.alphatab.net/docs/guides/handling-midi-events) to learn more about this feature.
|
|
41858
|
+
*
|
|
41859
|
+
* Also note that the provided data models changed significantly in {@version 1.3.0}. We try to provide backwards compatibility
|
|
41860
|
+
* until some extend but highly encourage changing to the new models in case of problems.
|
|
41861
|
+
*
|
|
41862
|
+
* @eventProperty
|
|
41863
|
+
* @category Events - Player
|
|
41864
|
+
* @since 1.2.0
|
|
41865
|
+
*
|
|
41866
|
+
* @example
|
|
41867
|
+
* JavaScript
|
|
41868
|
+
* ```js
|
|
41869
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41870
|
+
* api.midiEventsPlayedFilter = [alphaTab.midi.MidiEventType.AlphaTabMetronome];
|
|
41871
|
+
* api.midiEventsPlayed.on(function(e) {
|
|
41872
|
+
* for(const midi of e.events) {
|
|
41873
|
+
* if(midi.isMetronome) {
|
|
41874
|
+
* console.log('Metronome tick ' + midi.tick);
|
|
41875
|
+
* }
|
|
41876
|
+
* }
|
|
41877
|
+
* });
|
|
41878
|
+
* ```
|
|
41879
|
+
*
|
|
41880
|
+
* @example
|
|
41881
|
+
* C#
|
|
41882
|
+
* ```cs
|
|
41883
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41884
|
+
* api.MidiEventsPlayedFilter = new MidiEventType[] { AlphaTab.Midi.MidiEventType.AlphaTabMetronome };
|
|
41885
|
+
* api.MidiEventsPlayed.On(e =>
|
|
41886
|
+
* {
|
|
41887
|
+
* foreach(var midi of e.events)
|
|
41888
|
+
* {
|
|
41889
|
+
* if(midi is AlphaTab.Midi.AlphaTabMetronomeEvent sysex && sysex.IsMetronome)
|
|
41890
|
+
* {
|
|
41891
|
+
* Console.WriteLine("Metronome tick " + midi.Tick);
|
|
41892
|
+
* }
|
|
41893
|
+
* }
|
|
41894
|
+
* });
|
|
41895
|
+
* ```
|
|
41896
|
+
*
|
|
41897
|
+
* @example
|
|
41898
|
+
* Android
|
|
41899
|
+
* ```kotlin
|
|
41900
|
+
* val api = AlphaTabApi<MyControl>(...);
|
|
41901
|
+
* api.midiEventsPlayedFilter = alphaTab.collections.List<alphaTab.midi.MidiEventType>( alphaTab.midi.MidiEventType.AlphaTabMetronome )
|
|
41902
|
+
* api.midiEventsPlayed.on { e ->
|
|
41903
|
+
* for (midi in e.events) {
|
|
41904
|
+
* if(midi instanceof alphaTab.midi.AlphaTabMetronomeEvent && midi.isMetronome) {
|
|
41905
|
+
* println("Metronome tick " + midi.tick);
|
|
41906
|
+
* }
|
|
41907
|
+
* }
|
|
41908
|
+
* }
|
|
41909
|
+
* ```
|
|
41910
|
+
* @see {@link MidiEvent}
|
|
41911
|
+
* @see {@link TimeSignatureEvent}
|
|
41912
|
+
* @see {@link AlphaTabMetronomeEvent}
|
|
41913
|
+
* @see {@link AlphaTabRestEvent}
|
|
41914
|
+
* @see {@link NoteOnEvent}
|
|
41915
|
+
* @see {@link NoteOffEvent}
|
|
41916
|
+
* @see {@link ControlChangeEvent}
|
|
41917
|
+
* @see {@link ProgramChangeEvent}
|
|
41918
|
+
* @see {@link TempoChangeEvent}
|
|
41919
|
+
* @see {@link PitchBendEvent}
|
|
41920
|
+
* @see {@link NoteBendEvent}
|
|
41921
|
+
* @see {@link EndOfTrackEvent}
|
|
41922
|
+
* @see {@link MetaEvent}
|
|
41923
|
+
* @see {@link MetaDataEvent}
|
|
41924
|
+
* @see {@link MetaNumberEvent}
|
|
41925
|
+
* @see {@link Midi20PerNotePitchBendEvent}
|
|
41926
|
+
* @see {@link SystemCommonEvent}
|
|
41927
|
+
* @see {@link SystemExclusiveEvent}
|
|
41928
|
+
*/
|
|
41929
|
+
get midiEventsPlayed() {
|
|
41930
|
+
return this._player.midiEventsPlayed;
|
|
41595
41931
|
}
|
|
41596
41932
|
onMidiEventsPlayed(e) {
|
|
41597
41933
|
if (this._isDestroyed) {
|
|
41598
41934
|
return;
|
|
41599
41935
|
}
|
|
41600
|
-
this.midiEventsPlayed.trigger(e);
|
|
41601
41936
|
this.uiFacade.triggerEvent(this.container, 'midiEventsPlayed', e);
|
|
41602
41937
|
}
|
|
41938
|
+
/**
|
|
41939
|
+
* This event is fired when the playback range changed.
|
|
41940
|
+
*
|
|
41941
|
+
* @eventProperty
|
|
41942
|
+
* @category Events - Player
|
|
41943
|
+
* @since 1.2.3
|
|
41944
|
+
*
|
|
41945
|
+
* @example
|
|
41946
|
+
* JavaScript
|
|
41947
|
+
* ```js
|
|
41948
|
+
* const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
|
|
41949
|
+
* api.playbackRangeChanged.on((args) => {
|
|
41950
|
+
* if (args.playbackRange) {
|
|
41951
|
+
* highlightRangeInProgressBar(args.playbackRange.startTick, args.playbackRange.endTick);
|
|
41952
|
+
* } else {
|
|
41953
|
+
* clearHighlightInProgressBar();
|
|
41954
|
+
* }
|
|
41955
|
+
* });
|
|
41956
|
+
* ```
|
|
41957
|
+
*
|
|
41958
|
+
* @example
|
|
41959
|
+
* C#
|
|
41960
|
+
* ```cs
|
|
41961
|
+
* var api = new AlphaTabApi<MyControl>(...);
|
|
41962
|
+
* api.PlaybackRangeChanged.On(args =>
|
|
41963
|
+
* {
|
|
41964
|
+
* if (args.PlaybackRange != null)
|
|
41965
|
+
* {
|
|
41966
|
+
* HighlightRangeInProgressBar(args.PlaybackRange.StartTick, args.PlaybackRange.EndTick);
|
|
41967
|
+
* }
|
|
41968
|
+
* else
|
|
41969
|
+
* {
|
|
41970
|
+
* ClearHighlightInProgressBar();
|
|
41971
|
+
* }
|
|
41972
|
+
* });
|
|
41973
|
+
* ```
|
|
41974
|
+
*
|
|
41975
|
+
* @example
|
|
41976
|
+
* Android
|
|
41977
|
+
* ```kotlin
|
|
41978
|
+
* val api = AlphaTabApi<MyControl>(...)
|
|
41979
|
+
* api.playbackRangeChanged.on { args ->
|
|
41980
|
+
* val playbackRange = args.playbackRange
|
|
41981
|
+
* if (playbackRange != null) {
|
|
41982
|
+
* highlightRangeInProgressBar(playbackRange.startTick, playbackRange.endTick)
|
|
41983
|
+
* } else {
|
|
41984
|
+
* clearHighlightInProgressBar()
|
|
41985
|
+
* }
|
|
41986
|
+
* }
|
|
41987
|
+
* ```
|
|
41988
|
+
*
|
|
41989
|
+
*/
|
|
41990
|
+
get playbackRangeChanged() {
|
|
41991
|
+
return this._player.playbackRangeChanged;
|
|
41992
|
+
}
|
|
41603
41993
|
onPlaybackRangeChanged(e) {
|
|
41604
41994
|
if (this._isDestroyed) {
|
|
41605
41995
|
return;
|
|
41606
41996
|
}
|
|
41607
|
-
this.playbackRangeChanged.trigger(e);
|
|
41608
41997
|
this.uiFacade.triggerEvent(this.container, 'playbackRangeChanged', e);
|
|
41609
41998
|
}
|
|
41610
41999
|
onSettingsUpdated() {
|
|
@@ -41666,10 +42055,7 @@ class AlphaTabApiBase {
|
|
|
41666
42055
|
* ```
|
|
41667
42056
|
*/
|
|
41668
42057
|
async enumerateOutputDevices() {
|
|
41669
|
-
|
|
41670
|
-
return await this.player.output.enumerateOutputDevices();
|
|
41671
|
-
}
|
|
41672
|
-
return [];
|
|
42058
|
+
return await this._player.output.enumerateOutputDevices();
|
|
41673
42059
|
}
|
|
41674
42060
|
/**
|
|
41675
42061
|
* Changes the output device which should be used for playing the audio (player must be enabled).
|
|
@@ -41720,9 +42106,7 @@ class AlphaTabApiBase {
|
|
|
41720
42106
|
* ```
|
|
41721
42107
|
*/
|
|
41722
42108
|
async setOutputDevice(device) {
|
|
41723
|
-
|
|
41724
|
-
await this.player.output.setOutputDevice(device);
|
|
41725
|
-
}
|
|
42109
|
+
await this._player.output.setOutputDevice(device);
|
|
41726
42110
|
}
|
|
41727
42111
|
/**
|
|
41728
42112
|
* The currently configured output device if changed via {@link setOutputDevice}.
|
|
@@ -41762,10 +42146,7 @@ class AlphaTabApiBase {
|
|
|
41762
42146
|
*
|
|
41763
42147
|
*/
|
|
41764
42148
|
async getOutputDevice() {
|
|
41765
|
-
|
|
41766
|
-
return await this.player.output.getOutputDevice();
|
|
41767
|
-
}
|
|
41768
|
-
return null;
|
|
42149
|
+
return await this._player.output.getOutputDevice();
|
|
41769
42150
|
}
|
|
41770
42151
|
}
|
|
41771
42152
|
|
|
@@ -41942,38 +42323,52 @@ class HtmlElementContainer {
|
|
|
41942
42323
|
this.element = element;
|
|
41943
42324
|
this.mouseDown = {
|
|
41944
42325
|
on: (value) => {
|
|
41945
|
-
|
|
42326
|
+
const nativeListener = e => {
|
|
41946
42327
|
value(new BrowserMouseEventArgs(e));
|
|
41947
|
-
}
|
|
42328
|
+
};
|
|
42329
|
+
this.element.addEventListener('mousedown', nativeListener, true);
|
|
42330
|
+
return () => {
|
|
42331
|
+
this.element.removeEventListener('mousedown', nativeListener, true);
|
|
42332
|
+
};
|
|
41948
42333
|
},
|
|
41949
42334
|
off: (value) => {
|
|
41950
42335
|
}
|
|
41951
42336
|
};
|
|
41952
42337
|
this.mouseUp = {
|
|
41953
42338
|
on: (value) => {
|
|
41954
|
-
|
|
42339
|
+
const nativeListener = e => {
|
|
41955
42340
|
value(new BrowserMouseEventArgs(e));
|
|
41956
|
-
}
|
|
42341
|
+
};
|
|
42342
|
+
this.element.addEventListener('mouseup', nativeListener, true);
|
|
42343
|
+
return () => {
|
|
42344
|
+
this.element.removeEventListener('mouseup', nativeListener, true);
|
|
42345
|
+
};
|
|
41957
42346
|
},
|
|
41958
42347
|
off: (value) => {
|
|
41959
42348
|
}
|
|
41960
42349
|
};
|
|
41961
42350
|
this.mouseMove = {
|
|
41962
42351
|
on: (value) => {
|
|
41963
|
-
|
|
42352
|
+
const nativeListener = e => {
|
|
41964
42353
|
value(new BrowserMouseEventArgs(e));
|
|
41965
|
-
}
|
|
42354
|
+
};
|
|
42355
|
+
this.element.addEventListener('mousemove', nativeListener, true);
|
|
42356
|
+
return () => {
|
|
42357
|
+
this.element.removeEventListener('mousemove', nativeListener, true);
|
|
42358
|
+
};
|
|
41966
42359
|
},
|
|
41967
42360
|
off: (_) => {
|
|
41968
42361
|
}
|
|
41969
42362
|
};
|
|
42363
|
+
const container = this;
|
|
41970
42364
|
this.resize = {
|
|
41971
|
-
on: (value)
|
|
41972
|
-
if (
|
|
41973
|
-
HtmlElementContainer.resizeObserver.value.observe(
|
|
42365
|
+
on: function (value) {
|
|
42366
|
+
if (container._resizeListeners === 0) {
|
|
42367
|
+
HtmlElementContainer.resizeObserver.value.observe(container.element);
|
|
41974
42368
|
}
|
|
41975
|
-
|
|
41976
|
-
|
|
42369
|
+
container.element.addEventListener('resize', value, true);
|
|
42370
|
+
container._resizeListeners++;
|
|
42371
|
+
return () => this.off(value);
|
|
41977
42372
|
},
|
|
41978
42373
|
off: (value) => {
|
|
41979
42374
|
this.element.removeEventListener('resize', value, true);
|
|
@@ -42554,21 +42949,6 @@ class AlphaSynthScriptProcessorOutput extends AlphaSynthWebAudioOutputBase {
|
|
|
42554
42949
|
}
|
|
42555
42950
|
}
|
|
42556
42951
|
|
|
42557
|
-
/**
|
|
42558
|
-
* Represents the progress of any data being loaded.
|
|
42559
|
-
*/
|
|
42560
|
-
class ProgressEventArgs {
|
|
42561
|
-
/**
|
|
42562
|
-
* Initializes a new instance of the {@link ProgressEventArgs} class.
|
|
42563
|
-
* @param loaded
|
|
42564
|
-
* @param total
|
|
42565
|
-
*/
|
|
42566
|
-
constructor(loaded, total) {
|
|
42567
|
-
this.loaded = loaded;
|
|
42568
|
-
this.total = total;
|
|
42569
|
-
}
|
|
42570
|
-
}
|
|
42571
|
-
|
|
42572
42952
|
/**
|
|
42573
42953
|
* a WebWorker based alphaSynth which uses the given player as output.
|
|
42574
42954
|
* @target web
|
|
@@ -42805,25 +43185,6 @@ class AlphaSynthWebWorkerApi {
|
|
|
42805
43185
|
append: append
|
|
42806
43186
|
});
|
|
42807
43187
|
}
|
|
42808
|
-
loadSoundFontFromUrl(url, append, progress) {
|
|
42809
|
-
Logger.debug('AlphaSynth', `Start loading Soundfont from url ${url}`);
|
|
42810
|
-
const request = new XMLHttpRequest();
|
|
42811
|
-
request.open('GET', url, true, null, null);
|
|
42812
|
-
request.responseType = 'arraybuffer';
|
|
42813
|
-
request.onload = _ => {
|
|
42814
|
-
const buffer = new Uint8Array(request.response);
|
|
42815
|
-
this.loadSoundFont(buffer, append);
|
|
42816
|
-
};
|
|
42817
|
-
request.onerror = e => {
|
|
42818
|
-
Logger.error('AlphaSynth', `Loading failed: ${e.message}`);
|
|
42819
|
-
this.soundFontLoadFailed.trigger(new FileLoadError(e.message, request));
|
|
42820
|
-
};
|
|
42821
|
-
request.onprogress = e => {
|
|
42822
|
-
Logger.debug('AlphaSynth', `Soundfont downloading: ${e.loaded}/${e.total} bytes`);
|
|
42823
|
-
progress(new ProgressEventArgs(e.loaded, e.total));
|
|
42824
|
-
};
|
|
42825
|
-
request.send();
|
|
42826
|
-
}
|
|
42827
43188
|
resetSoundFonts() {
|
|
42828
43189
|
this._synth.postMessage({
|
|
42829
43190
|
cmd: 'alphaSynth.resetSoundFonts'
|
|
@@ -43375,7 +43736,11 @@ class AudioElementBackingTrackSynthOutput {
|
|
|
43375
43736
|
}
|
|
43376
43737
|
this._padding = backingTrack.padding / 1000;
|
|
43377
43738
|
const blob = new Blob([backingTrack.rawAudioFile]);
|
|
43739
|
+
// https://html.spec.whatwg.org/multipage/media.html#loading-the-media-resource
|
|
43740
|
+
// Step 8. resets the playbackRate, we need to remember and restore it.
|
|
43741
|
+
const playbackRate = this.audioElement.playbackRate;
|
|
43378
43742
|
this.audioElement.src = URL.createObjectURL(blob);
|
|
43743
|
+
this.audioElement.playbackRate = playbackRate;
|
|
43379
43744
|
}
|
|
43380
43745
|
open(_bufferTimeInMilliseconds) {
|
|
43381
43746
|
const audioElement = document.createElement('audio');
|
|
@@ -44094,6 +44459,21 @@ class BrowserUiFacade {
|
|
|
44094
44459
|
}
|
|
44095
44460
|
}
|
|
44096
44461
|
|
|
44462
|
+
/**
|
|
44463
|
+
* Represents the progress of any data being loaded.
|
|
44464
|
+
*/
|
|
44465
|
+
class ProgressEventArgs {
|
|
44466
|
+
/**
|
|
44467
|
+
* Initializes a new instance of the {@link ProgressEventArgs} class.
|
|
44468
|
+
* @param loaded
|
|
44469
|
+
* @param total
|
|
44470
|
+
*/
|
|
44471
|
+
constructor(loaded, total) {
|
|
44472
|
+
this.loaded = loaded;
|
|
44473
|
+
this.total = total;
|
|
44474
|
+
}
|
|
44475
|
+
}
|
|
44476
|
+
|
|
44097
44477
|
/**
|
|
44098
44478
|
* @target web
|
|
44099
44479
|
*/
|
|
@@ -44337,13 +44717,29 @@ class AlphaTabApi extends AlphaTabApiBase {
|
|
|
44337
44717
|
* @since 0.9.4
|
|
44338
44718
|
*/
|
|
44339
44719
|
loadSoundFontFromUrl(url, append) {
|
|
44340
|
-
|
|
44720
|
+
const player = this.player;
|
|
44721
|
+
if (!player) {
|
|
44341
44722
|
return;
|
|
44342
44723
|
}
|
|
44343
|
-
|
|
44344
|
-
|
|
44345
|
-
|
|
44346
|
-
|
|
44724
|
+
Logger.debug('AlphaSynth', `Start loading Soundfont from url ${url}`);
|
|
44725
|
+
const request = new XMLHttpRequest();
|
|
44726
|
+
request.open('GET', url, true, null, null);
|
|
44727
|
+
request.responseType = 'arraybuffer';
|
|
44728
|
+
request.onload = _ => {
|
|
44729
|
+
const buffer = new Uint8Array(request.response);
|
|
44730
|
+
this.loadSoundFont(buffer, append);
|
|
44731
|
+
};
|
|
44732
|
+
request.onerror = e => {
|
|
44733
|
+
Logger.error('AlphaSynth', `Loading failed: ${e.message}`);
|
|
44734
|
+
player.soundFontLoadFailed.trigger(new FileLoadError(e.message, request));
|
|
44735
|
+
};
|
|
44736
|
+
request.onprogress = e => {
|
|
44737
|
+
Logger.debug('AlphaSynth', `Soundfont downloading: ${e.loaded}/${e.total} bytes`);
|
|
44738
|
+
const args = new ProgressEventArgs(e.loaded, e.total);
|
|
44739
|
+
this.soundFontLoad.trigger(args);
|
|
44740
|
+
this.uiFacade.triggerEvent(this.container, 'soundFontLoad', args);
|
|
44741
|
+
};
|
|
44742
|
+
request.send();
|
|
44347
44743
|
}
|
|
44348
44744
|
}
|
|
44349
44745
|
|
|
@@ -46725,7 +47121,7 @@ class StaffSystem {
|
|
|
46725
47121
|
return this.masterBarsRenderers[0].masterBar.index;
|
|
46726
47122
|
}
|
|
46727
47123
|
get lastBarIndex() {
|
|
46728
|
-
return this.masterBarsRenderers[this.masterBarsRenderers.length - 1].
|
|
47124
|
+
return this.masterBarsRenderers[this.masterBarsRenderers.length - 1].lastMasterBarIndex;
|
|
46729
47125
|
}
|
|
46730
47126
|
addMasterBarRenderers(tracks, renderers) {
|
|
46731
47127
|
if (tracks.length === 0) {
|
|
@@ -47539,6 +47935,9 @@ class ScoreLayout {
|
|
|
47539
47935
|
}
|
|
47540
47936
|
}
|
|
47541
47937
|
}
|
|
47938
|
+
else {
|
|
47939
|
+
this.tuningGlyph = null;
|
|
47940
|
+
}
|
|
47542
47941
|
}
|
|
47543
47942
|
// chord diagram glyphs
|
|
47544
47943
|
if (notation.isNotationElementVisible(NotationElement.ChordDiagrams)) {
|
|
@@ -47567,6 +47966,12 @@ class ScoreLayout {
|
|
|
47567
47966
|
}
|
|
47568
47967
|
}
|
|
47569
47968
|
}
|
|
47969
|
+
if (this.chordDiagrams.isEmpty) {
|
|
47970
|
+
this.chordDiagrams = null;
|
|
47971
|
+
}
|
|
47972
|
+
}
|
|
47973
|
+
else {
|
|
47974
|
+
this.chordDiagrams = null;
|
|
47570
47975
|
}
|
|
47571
47976
|
}
|
|
47572
47977
|
createEmptyStaffSystem() {
|
|
@@ -60310,9 +60715,9 @@ class VersionInfo {
|
|
|
60310
60715
|
print(`build date: ${VersionInfo.date}`);
|
|
60311
60716
|
}
|
|
60312
60717
|
}
|
|
60313
|
-
VersionInfo.version = '1.6.0-alpha.
|
|
60314
|
-
VersionInfo.date = '2025-05-
|
|
60315
|
-
VersionInfo.commit = '
|
|
60718
|
+
VersionInfo.version = '1.6.0-alpha.1415';
|
|
60719
|
+
VersionInfo.date = '2025-05-19T16:08:22.342Z';
|
|
60720
|
+
VersionInfo.commit = '459db69f8896a2ea8822ce5d49dcc824edd36521';
|
|
60316
60721
|
|
|
60317
60722
|
/**
|
|
60318
60723
|
* A factory for custom layout engines.
|