@coderline/alphatab 1.9.0-alpha.1767 → 1.9.0-alpha.1768
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 +264 -202
- package/dist/alphaTab.js +264 -202
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.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.9.0-alpha.
|
|
2
|
+
* alphaTab v1.9.0-alpha.1768 (develop, build 1768)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -203,9 +203,9 @@ class AlphaTabError extends Error {
|
|
|
203
203
|
* @internal
|
|
204
204
|
*/
|
|
205
205
|
class VersionInfo {
|
|
206
|
-
static version = '1.9.0-alpha.
|
|
207
|
-
static date = '2026-04-
|
|
208
|
-
static commit = '
|
|
206
|
+
static version = '1.9.0-alpha.1768';
|
|
207
|
+
static date = '2026-04-10T03:35:57.433Z';
|
|
208
|
+
static commit = '759bd788fc094ab5cb4db380f6a7657e459c14bf';
|
|
209
209
|
static print(print) {
|
|
210
210
|
print(`alphaTab ${VersionInfo.version}`);
|
|
211
211
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -3826,7 +3826,6 @@ class Score {
|
|
|
3826
3826
|
*/
|
|
3827
3827
|
class SynthConstants {
|
|
3828
3828
|
static DefaultChannelCount = 16 + 1;
|
|
3829
|
-
static MetronomeChannel = SynthConstants.DefaultChannelCount - 1;
|
|
3830
3829
|
static MetronomeKey = 33;
|
|
3831
3830
|
static AudioChannels = 2;
|
|
3832
3831
|
static MinVolume = 0;
|
|
@@ -3850,6 +3849,10 @@ class SynthConstants {
|
|
|
3850
3849
|
static DefaultPitchWheel = SynthConstants.MaxPitchWheel / 2;
|
|
3851
3850
|
static MicroBufferCount = 32;
|
|
3852
3851
|
static MicroBufferSize = 64;
|
|
3852
|
+
/**
|
|
3853
|
+
* approximately -60 dB, which is inaudible to humans
|
|
3854
|
+
*/
|
|
3855
|
+
static AudibleLevelThreshold = 1e-3;
|
|
3853
3856
|
}
|
|
3854
3857
|
|
|
3855
3858
|
/**
|
|
@@ -23814,26 +23817,26 @@ class GpifParser {
|
|
|
23814
23817
|
case 'HarmonicType':
|
|
23815
23818
|
const htype = c.findChildElement('HType');
|
|
23816
23819
|
if (htype) {
|
|
23817
|
-
switch (htype.innerText) {
|
|
23818
|
-
case '
|
|
23820
|
+
switch (htype.innerText.toLowerCase()) {
|
|
23821
|
+
case 'noharmonic':
|
|
23819
23822
|
note.harmonicType = HarmonicType.None;
|
|
23820
23823
|
break;
|
|
23821
|
-
case '
|
|
23824
|
+
case 'natural':
|
|
23822
23825
|
note.harmonicType = HarmonicType.Natural;
|
|
23823
23826
|
break;
|
|
23824
|
-
case '
|
|
23827
|
+
case 'artificial':
|
|
23825
23828
|
note.harmonicType = HarmonicType.Artificial;
|
|
23826
23829
|
break;
|
|
23827
|
-
case '
|
|
23830
|
+
case 'pinch':
|
|
23828
23831
|
note.harmonicType = HarmonicType.Pinch;
|
|
23829
23832
|
break;
|
|
23830
|
-
case '
|
|
23833
|
+
case 'tap':
|
|
23831
23834
|
note.harmonicType = HarmonicType.Tap;
|
|
23832
23835
|
break;
|
|
23833
|
-
case '
|
|
23836
|
+
case 'semi':
|
|
23834
23837
|
note.harmonicType = HarmonicType.Semi;
|
|
23835
23838
|
break;
|
|
23836
|
-
case '
|
|
23839
|
+
case 'feedback':
|
|
23837
23840
|
note.harmonicType = HarmonicType.Feedback;
|
|
23838
23841
|
break;
|
|
23839
23842
|
}
|
|
@@ -39223,8 +39226,11 @@ class AlphaTabWorkerScoreRenderer {
|
|
|
39223
39226
|
this.renderFinished.trigger(data.result);
|
|
39224
39227
|
break;
|
|
39225
39228
|
case 'alphaTab.postRenderFinished':
|
|
39226
|
-
|
|
39227
|
-
|
|
39229
|
+
const score = this._api.score;
|
|
39230
|
+
if (score && data.boundsLookup) {
|
|
39231
|
+
this.boundsLookup = BoundsLookup.fromJson(data.boundsLookup, this._api.score);
|
|
39232
|
+
this.boundsLookup?.finish();
|
|
39233
|
+
}
|
|
39228
39234
|
this.postRenderFinished.trigger();
|
|
39229
39235
|
break;
|
|
39230
39236
|
case 'alphaTab.error':
|
|
@@ -39481,6 +39487,164 @@ class AudioElementBackingTrackSynthOutput {
|
|
|
39481
39487
|
}
|
|
39482
39488
|
}
|
|
39483
39489
|
|
|
39490
|
+
/**
|
|
39491
|
+
* @internal
|
|
39492
|
+
*/
|
|
39493
|
+
class QueueItem {
|
|
39494
|
+
value;
|
|
39495
|
+
next;
|
|
39496
|
+
constructor(value) {
|
|
39497
|
+
this.value = value;
|
|
39498
|
+
}
|
|
39499
|
+
}
|
|
39500
|
+
/**
|
|
39501
|
+
* @internal
|
|
39502
|
+
*/
|
|
39503
|
+
class Queue {
|
|
39504
|
+
_head;
|
|
39505
|
+
_tail;
|
|
39506
|
+
get isEmpty() {
|
|
39507
|
+
return this._head === undefined;
|
|
39508
|
+
}
|
|
39509
|
+
clear() {
|
|
39510
|
+
this._head = undefined;
|
|
39511
|
+
this._tail = undefined;
|
|
39512
|
+
}
|
|
39513
|
+
enqueue(item) {
|
|
39514
|
+
const queueItem = new QueueItem(item);
|
|
39515
|
+
if (this._tail) {
|
|
39516
|
+
// not empty -> add after tail
|
|
39517
|
+
this._tail.next = queueItem;
|
|
39518
|
+
this._tail = queueItem;
|
|
39519
|
+
}
|
|
39520
|
+
else {
|
|
39521
|
+
// empty -> new item takes head and tail
|
|
39522
|
+
this._head = queueItem;
|
|
39523
|
+
this._tail = queueItem;
|
|
39524
|
+
}
|
|
39525
|
+
}
|
|
39526
|
+
enqueueFront(item) {
|
|
39527
|
+
const queueItem = new QueueItem(item);
|
|
39528
|
+
queueItem.next = this._head;
|
|
39529
|
+
if (this._head) {
|
|
39530
|
+
this._head = queueItem;
|
|
39531
|
+
}
|
|
39532
|
+
else {
|
|
39533
|
+
this._head = queueItem;
|
|
39534
|
+
this._tail = queueItem;
|
|
39535
|
+
}
|
|
39536
|
+
}
|
|
39537
|
+
peek() {
|
|
39538
|
+
const head = this._head;
|
|
39539
|
+
if (!head) {
|
|
39540
|
+
return undefined;
|
|
39541
|
+
}
|
|
39542
|
+
return head.value;
|
|
39543
|
+
}
|
|
39544
|
+
dequeue() {
|
|
39545
|
+
const head = this._head;
|
|
39546
|
+
if (!head) {
|
|
39547
|
+
return undefined;
|
|
39548
|
+
}
|
|
39549
|
+
const newHead = head.next;
|
|
39550
|
+
this._head = newHead;
|
|
39551
|
+
// last item removed?
|
|
39552
|
+
if (!newHead) {
|
|
39553
|
+
this._tail = undefined;
|
|
39554
|
+
}
|
|
39555
|
+
return head.value;
|
|
39556
|
+
}
|
|
39557
|
+
}
|
|
39558
|
+
|
|
39559
|
+
/**
|
|
39560
|
+
* The options controlling how to export the audio.
|
|
39561
|
+
* @public
|
|
39562
|
+
*/
|
|
39563
|
+
class AudioExportOptions {
|
|
39564
|
+
/**
|
|
39565
|
+
* The soundfonts to load and use for generating the audio.
|
|
39566
|
+
* If not provided, the already loaded soundfonts of the synthesizer will be used.
|
|
39567
|
+
* If no existing synthesizer is initialized, the generated audio might not contain any hearable audio.
|
|
39568
|
+
*/
|
|
39569
|
+
soundFonts;
|
|
39570
|
+
/**
|
|
39571
|
+
* The output sample rate.
|
|
39572
|
+
* @default `44100`
|
|
39573
|
+
*/
|
|
39574
|
+
sampleRate = 44100;
|
|
39575
|
+
/**
|
|
39576
|
+
* Whether to respect sync point information during export.
|
|
39577
|
+
* @default `true`
|
|
39578
|
+
* @remarks
|
|
39579
|
+
* If the song contains sync point information for synchronization with an external media,
|
|
39580
|
+
* this option allows controlling whether the synthesized audio is aligned with these points.
|
|
39581
|
+
*
|
|
39582
|
+
* This is useful when mixing the exported audio together with external media, keeping the same timing.
|
|
39583
|
+
*
|
|
39584
|
+
* Disable this option if you want the original/exact timing as per music sheet in the exported audio.
|
|
39585
|
+
*/
|
|
39586
|
+
useSyncPoints = false;
|
|
39587
|
+
/**
|
|
39588
|
+
* The current master volume as percentage. (range: 0.0-3.0, default 1.0)
|
|
39589
|
+
*/
|
|
39590
|
+
masterVolume = 1;
|
|
39591
|
+
/**
|
|
39592
|
+
* The metronome volume. (range: 0.0-3.0, default 0.0)
|
|
39593
|
+
*/
|
|
39594
|
+
metronomeVolume = 0;
|
|
39595
|
+
/**
|
|
39596
|
+
* The range of the song that should be exported. Set this to null
|
|
39597
|
+
* to play the whole song.
|
|
39598
|
+
*/
|
|
39599
|
+
playbackRange;
|
|
39600
|
+
/**
|
|
39601
|
+
* The volume for individual tracks as percentage (range: 0.0-3.0).
|
|
39602
|
+
* @remarks
|
|
39603
|
+
* The key is the track index, and the value is the relative volume.
|
|
39604
|
+
* The configured volume (as per data model) still applies, this is an additional volume control.
|
|
39605
|
+
* If no custom value is set, 100% is used.
|
|
39606
|
+
* No values from the currently active synthesizer are applied.
|
|
39607
|
+
*
|
|
39608
|
+
* The meaning of the key changes when used with AlphaSynth directly, in this case the key is the midi channel .
|
|
39609
|
+
*/
|
|
39610
|
+
trackVolume = new Map();
|
|
39611
|
+
/**
|
|
39612
|
+
* The additional semitone pitch transpose to apply for individual tracks.
|
|
39613
|
+
* @remarks
|
|
39614
|
+
* The key is the track index, and the value is the number of semitones to apply.
|
|
39615
|
+
* No values from the currently active synthesizer are applied.
|
|
39616
|
+
*
|
|
39617
|
+
* The meaning of the key changes when used with AlphaSynth directly, in this case the key is the midi channel .
|
|
39618
|
+
*/
|
|
39619
|
+
trackTranspositionPitches = new Map();
|
|
39620
|
+
}
|
|
39621
|
+
/**
|
|
39622
|
+
* Represents a single chunk of audio produced.
|
|
39623
|
+
* @public
|
|
39624
|
+
*/
|
|
39625
|
+
class AudioExportChunk {
|
|
39626
|
+
/**
|
|
39627
|
+
* The generated samples for the requested chunk.
|
|
39628
|
+
*/
|
|
39629
|
+
samples;
|
|
39630
|
+
/**
|
|
39631
|
+
* The current time position within the song in milliseconds.
|
|
39632
|
+
*/
|
|
39633
|
+
currentTime = 0;
|
|
39634
|
+
/**
|
|
39635
|
+
* The total length of the song in milliseconds.
|
|
39636
|
+
*/
|
|
39637
|
+
endTime = 0;
|
|
39638
|
+
/**
|
|
39639
|
+
* The current time position within the song in midi ticks.
|
|
39640
|
+
*/
|
|
39641
|
+
currentTick = 0;
|
|
39642
|
+
/**
|
|
39643
|
+
* The total length of the song in midi ticks.
|
|
39644
|
+
*/
|
|
39645
|
+
endTick = 0;
|
|
39646
|
+
}
|
|
39647
|
+
|
|
39484
39648
|
// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT,
|
|
39485
39649
|
// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont)
|
|
39486
39650
|
// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny
|
|
@@ -39594,6 +39758,7 @@ class MidiSequencerState {
|
|
|
39594
39758
|
endTime = 0;
|
|
39595
39759
|
currentTempo = 0;
|
|
39596
39760
|
syncPointTempo = 0;
|
|
39761
|
+
metronomeChannel = SynthConstants.DefaultChannelCount - 1;
|
|
39597
39762
|
}
|
|
39598
39763
|
/**
|
|
39599
39764
|
* This sequencer dispatches midi events to the synthesizer based on the current
|
|
@@ -39606,6 +39771,9 @@ class MidiFileSequencer {
|
|
|
39606
39771
|
_mainState;
|
|
39607
39772
|
_oneTimeState = null;
|
|
39608
39773
|
_countInState = null;
|
|
39774
|
+
get metronomeChannel() {
|
|
39775
|
+
return this._mainState.metronomeChannel;
|
|
39776
|
+
}
|
|
39609
39777
|
get isPlayingMain() {
|
|
39610
39778
|
return this._currentState === this._mainState;
|
|
39611
39779
|
}
|
|
@@ -39689,7 +39857,7 @@ class MidiFileSequencer {
|
|
|
39689
39857
|
const metronomeVolume = this._synthesizer.metronomeVolume;
|
|
39690
39858
|
this._synthesizer.noteOffAll(true);
|
|
39691
39859
|
this._synthesizer.resetSoft();
|
|
39692
|
-
this._synthesizer.setupMetronomeChannel(metronomeVolume);
|
|
39860
|
+
this._synthesizer.setupMetronomeChannel(this.metronomeChannel, metronomeVolume);
|
|
39693
39861
|
}
|
|
39694
39862
|
this._mainSilentProcess(timePosition);
|
|
39695
39863
|
}
|
|
@@ -39741,6 +39909,7 @@ class MidiFileSequencer {
|
|
|
39741
39909
|
let metronomeLengthInMillis = 0;
|
|
39742
39910
|
let metronomeTick = midiFile.tickShift; // shift metronome to content
|
|
39743
39911
|
let metronomeTime = 0.0;
|
|
39912
|
+
let maxChannel = 0;
|
|
39744
39913
|
let previousTick = 0;
|
|
39745
39914
|
for (const mEvent of midiFile.events) {
|
|
39746
39915
|
const synthData = new SynthEvent(state.synthData.length, mEvent);
|
|
@@ -39782,6 +39951,9 @@ class MidiFileSequencer {
|
|
|
39782
39951
|
if (!state.firstProgramEventPerChannel.has(channel)) {
|
|
39783
39952
|
state.firstProgramEventPerChannel.set(channel, synthData);
|
|
39784
39953
|
}
|
|
39954
|
+
if (channel > maxChannel) {
|
|
39955
|
+
maxChannel = channel;
|
|
39956
|
+
}
|
|
39785
39957
|
const isPercussion = channel === SynthConstants.PercussionChannel;
|
|
39786
39958
|
if (!isPercussion) {
|
|
39787
39959
|
this.instrumentPrograms.add(programChange.program);
|
|
@@ -39793,6 +39965,9 @@ class MidiFileSequencer {
|
|
|
39793
39965
|
if (isPercussion) {
|
|
39794
39966
|
this.percussionKeys.add(noteOn.noteKey);
|
|
39795
39967
|
}
|
|
39968
|
+
if (noteOn.channel > maxChannel) {
|
|
39969
|
+
maxChannel = noteOn.channel;
|
|
39970
|
+
}
|
|
39796
39971
|
}
|
|
39797
39972
|
}
|
|
39798
39973
|
state.currentTempo = state.tempoChanges.length > 0 ? state.tempoChanges[0].bpm : bpm;
|
|
@@ -39808,6 +39983,7 @@ class MidiFileSequencer {
|
|
|
39808
39983
|
});
|
|
39809
39984
|
state.endTime = absTime;
|
|
39810
39985
|
state.endTick = absTick;
|
|
39986
|
+
state.metronomeChannel = maxChannel + 1;
|
|
39811
39987
|
return state;
|
|
39812
39988
|
}
|
|
39813
39989
|
fillMidiEventQueue() {
|
|
@@ -44013,6 +44189,11 @@ class Voice {
|
|
|
44013
44189
|
if (dynamicGain) {
|
|
44014
44190
|
noteGain = SynthHelper.decibelsToGain(this.noteGainDb + this.modLfo.level * tmpModLfoToVolume);
|
|
44015
44191
|
}
|
|
44192
|
+
// Update EG.
|
|
44193
|
+
this.ampEnv.process(blockSamples, f.outSampleRate);
|
|
44194
|
+
if (updateModEnv) {
|
|
44195
|
+
this.modEnv.process(blockSamples, f.outSampleRate);
|
|
44196
|
+
}
|
|
44016
44197
|
gainMono = noteGain * this.ampEnv.level;
|
|
44017
44198
|
if (isMuted) {
|
|
44018
44199
|
gainMono = 0;
|
|
@@ -44020,11 +44201,6 @@ class Voice {
|
|
|
44020
44201
|
else {
|
|
44021
44202
|
gainMono *= this.mixVolume;
|
|
44022
44203
|
}
|
|
44023
|
-
// Update EG.
|
|
44024
|
-
this.ampEnv.process(blockSamples, f.outSampleRate);
|
|
44025
|
-
if (updateModEnv) {
|
|
44026
|
-
this.modEnv.process(blockSamples, f.outSampleRate);
|
|
44027
|
-
}
|
|
44028
44204
|
// Update LFOs.
|
|
44029
44205
|
if (updateModLFO) {
|
|
44030
44206
|
this.modLfo.process(blockSamples);
|
|
@@ -44103,7 +44279,12 @@ class Voice {
|
|
|
44103
44279
|
}
|
|
44104
44280
|
break;
|
|
44105
44281
|
}
|
|
44106
|
-
|
|
44282
|
+
const inaudible = this.ampEnv.segment === VoiceEnvelopeSegment.Release &&
|
|
44283
|
+
Math.abs(gainMono) < SynthConstants.AudibleLevelThreshold;
|
|
44284
|
+
if (tmpSourceSamplePosition >= tmpSampleEndDbl ||
|
|
44285
|
+
this.ampEnv.segment === VoiceEnvelopeSegment.Done ||
|
|
44286
|
+
// Check if voice is inaudible during release to terminate early
|
|
44287
|
+
inaudible) {
|
|
44107
44288
|
this.kill();
|
|
44108
44289
|
return;
|
|
44109
44290
|
}
|
|
@@ -44118,75 +44299,6 @@ class Voice {
|
|
|
44118
44299
|
}
|
|
44119
44300
|
}
|
|
44120
44301
|
|
|
44121
|
-
/**
|
|
44122
|
-
* @internal
|
|
44123
|
-
*/
|
|
44124
|
-
class QueueItem {
|
|
44125
|
-
value;
|
|
44126
|
-
next;
|
|
44127
|
-
constructor(value) {
|
|
44128
|
-
this.value = value;
|
|
44129
|
-
}
|
|
44130
|
-
}
|
|
44131
|
-
/**
|
|
44132
|
-
* @internal
|
|
44133
|
-
*/
|
|
44134
|
-
class Queue {
|
|
44135
|
-
_head;
|
|
44136
|
-
_tail;
|
|
44137
|
-
get isEmpty() {
|
|
44138
|
-
return this._head === undefined;
|
|
44139
|
-
}
|
|
44140
|
-
clear() {
|
|
44141
|
-
this._head = undefined;
|
|
44142
|
-
this._tail = undefined;
|
|
44143
|
-
}
|
|
44144
|
-
enqueue(item) {
|
|
44145
|
-
const queueItem = new QueueItem(item);
|
|
44146
|
-
if (this._tail) {
|
|
44147
|
-
// not empty -> add after tail
|
|
44148
|
-
this._tail.next = queueItem;
|
|
44149
|
-
this._tail = queueItem;
|
|
44150
|
-
}
|
|
44151
|
-
else {
|
|
44152
|
-
// empty -> new item takes head and tail
|
|
44153
|
-
this._head = queueItem;
|
|
44154
|
-
this._tail = queueItem;
|
|
44155
|
-
}
|
|
44156
|
-
}
|
|
44157
|
-
enqueueFront(item) {
|
|
44158
|
-
const queueItem = new QueueItem(item);
|
|
44159
|
-
queueItem.next = this._head;
|
|
44160
|
-
if (this._head) {
|
|
44161
|
-
this._head = queueItem;
|
|
44162
|
-
}
|
|
44163
|
-
else {
|
|
44164
|
-
this._head = queueItem;
|
|
44165
|
-
this._tail = queueItem;
|
|
44166
|
-
}
|
|
44167
|
-
}
|
|
44168
|
-
peek() {
|
|
44169
|
-
const head = this._head;
|
|
44170
|
-
if (!head) {
|
|
44171
|
-
return undefined;
|
|
44172
|
-
}
|
|
44173
|
-
return head.value;
|
|
44174
|
-
}
|
|
44175
|
-
dequeue() {
|
|
44176
|
-
const head = this._head;
|
|
44177
|
-
if (!head) {
|
|
44178
|
-
return undefined;
|
|
44179
|
-
}
|
|
44180
|
-
const newHead = head.next;
|
|
44181
|
-
this._head = newHead;
|
|
44182
|
-
// last item removed?
|
|
44183
|
-
if (!newHead) {
|
|
44184
|
-
this._tail = undefined;
|
|
44185
|
-
}
|
|
44186
|
-
return head.value;
|
|
44187
|
-
}
|
|
44188
|
-
}
|
|
44189
|
-
|
|
44190
44302
|
/**
|
|
44191
44303
|
* Lists all midi controllers.
|
|
44192
44304
|
* @public
|
|
@@ -44343,6 +44455,7 @@ class TinySoundFont {
|
|
|
44343
44455
|
currentTempo = 0;
|
|
44344
44456
|
timeSignatureNumerator = 0;
|
|
44345
44457
|
timeSignatureDenominator = 0;
|
|
44458
|
+
_metronomeChannel = SynthConstants.DefaultChannelCount - 1;
|
|
44346
44459
|
constructor(sampleRate) {
|
|
44347
44460
|
this.outSampleRate = sampleRate;
|
|
44348
44461
|
}
|
|
@@ -44447,8 +44560,8 @@ class TinySoundFont {
|
|
|
44447
44560
|
while (!this._midiEventQueue.isEmpty) {
|
|
44448
44561
|
const m = this._midiEventQueue.dequeue();
|
|
44449
44562
|
if (m.isMetronome && this.metronomeVolume > 0) {
|
|
44450
|
-
this.channelNoteOff(
|
|
44451
|
-
this.channelNoteOn(
|
|
44563
|
+
this.channelNoteOff(this._metronomeChannel, SynthConstants.MetronomeKey);
|
|
44564
|
+
this.channelNoteOn(this._metronomeChannel, SynthConstants.MetronomeKey, 95 / 127);
|
|
44452
44565
|
}
|
|
44453
44566
|
else if (m.event) {
|
|
44454
44567
|
this.processMidiMessage(m.event);
|
|
@@ -44462,7 +44575,7 @@ class TinySoundFont {
|
|
|
44462
44575
|
// channel is muted if it is either explicitley muted, or another channel is set to solo but not this one.
|
|
44463
44576
|
// exception. metronome is implicitly added in solo
|
|
44464
44577
|
const isChannelMuted = this._mutedChannels.has(channel) ||
|
|
44465
|
-
(anySolo && channel !==
|
|
44578
|
+
(anySolo && channel !== this._metronomeChannel && !this._soloChannels.has(channel));
|
|
44466
44579
|
if (!buffer) {
|
|
44467
44580
|
voice.kill();
|
|
44468
44581
|
}
|
|
@@ -44516,16 +44629,17 @@ class TinySoundFont {
|
|
|
44516
44629
|
}
|
|
44517
44630
|
}
|
|
44518
44631
|
get metronomeVolume() {
|
|
44519
|
-
return this.channelGetMixVolume(
|
|
44632
|
+
return this.channelGetMixVolume(this._metronomeChannel);
|
|
44520
44633
|
}
|
|
44521
44634
|
set metronomeVolume(value) {
|
|
44522
|
-
this.setupMetronomeChannel(value);
|
|
44635
|
+
this.setupMetronomeChannel(this._metronomeChannel, value);
|
|
44523
44636
|
}
|
|
44524
|
-
setupMetronomeChannel(volume) {
|
|
44525
|
-
this.
|
|
44637
|
+
setupMetronomeChannel(channel, volume) {
|
|
44638
|
+
this._metronomeChannel = channel;
|
|
44639
|
+
this.channelSetMixVolume(channel, volume);
|
|
44526
44640
|
if (volume > 0) {
|
|
44527
|
-
this.channelSetVolume(
|
|
44528
|
-
this.channelSetPresetNumber(
|
|
44641
|
+
this.channelSetVolume(channel, 1);
|
|
44642
|
+
this.channelSetPresetNumber(channel, 0, true);
|
|
44529
44643
|
}
|
|
44530
44644
|
}
|
|
44531
44645
|
get masterVolume() {
|
|
@@ -45605,95 +45719,6 @@ class TinySoundFont {
|
|
|
45605
45719
|
}
|
|
45606
45720
|
}
|
|
45607
45721
|
|
|
45608
|
-
/**
|
|
45609
|
-
* The options controlling how to export the audio.
|
|
45610
|
-
* @public
|
|
45611
|
-
*/
|
|
45612
|
-
class AudioExportOptions {
|
|
45613
|
-
/**
|
|
45614
|
-
* The soundfonts to load and use for generating the audio.
|
|
45615
|
-
* If not provided, the already loaded soundfonts of the synthesizer will be used.
|
|
45616
|
-
* If no existing synthesizer is initialized, the generated audio might not contain any hearable audio.
|
|
45617
|
-
*/
|
|
45618
|
-
soundFonts;
|
|
45619
|
-
/**
|
|
45620
|
-
* The output sample rate.
|
|
45621
|
-
* @default `44100`
|
|
45622
|
-
*/
|
|
45623
|
-
sampleRate = 44100;
|
|
45624
|
-
/**
|
|
45625
|
-
* Whether to respect sync point information during export.
|
|
45626
|
-
* @default `true`
|
|
45627
|
-
* @remarks
|
|
45628
|
-
* If the song contains sync point information for synchronization with an external media,
|
|
45629
|
-
* this option allows controlling whether the synthesized audio is aligned with these points.
|
|
45630
|
-
*
|
|
45631
|
-
* This is useful when mixing the exported audio together with external media, keeping the same timing.
|
|
45632
|
-
*
|
|
45633
|
-
* Disable this option if you want the original/exact timing as per music sheet in the exported audio.
|
|
45634
|
-
*/
|
|
45635
|
-
useSyncPoints = false;
|
|
45636
|
-
/**
|
|
45637
|
-
* The current master volume as percentage. (range: 0.0-3.0, default 1.0)
|
|
45638
|
-
*/
|
|
45639
|
-
masterVolume = 1;
|
|
45640
|
-
/**
|
|
45641
|
-
* The metronome volume. (range: 0.0-3.0, default 0.0)
|
|
45642
|
-
*/
|
|
45643
|
-
metronomeVolume = 0;
|
|
45644
|
-
/**
|
|
45645
|
-
* The range of the song that should be exported. Set this to null
|
|
45646
|
-
* to play the whole song.
|
|
45647
|
-
*/
|
|
45648
|
-
playbackRange;
|
|
45649
|
-
/**
|
|
45650
|
-
* The volume for individual tracks as percentage (range: 0.0-3.0).
|
|
45651
|
-
* @remarks
|
|
45652
|
-
* The key is the track index, and the value is the relative volume.
|
|
45653
|
-
* The configured volume (as per data model) still applies, this is an additional volume control.
|
|
45654
|
-
* If no custom value is set, 100% is used.
|
|
45655
|
-
* No values from the currently active synthesizer are applied.
|
|
45656
|
-
*
|
|
45657
|
-
* The meaning of the key changes when used with AlphaSynth directly, in this case the key is the midi channel .
|
|
45658
|
-
*/
|
|
45659
|
-
trackVolume = new Map();
|
|
45660
|
-
/**
|
|
45661
|
-
* The additional semitone pitch transpose to apply for individual tracks.
|
|
45662
|
-
* @remarks
|
|
45663
|
-
* The key is the track index, and the value is the number of semitones to apply.
|
|
45664
|
-
* No values from the currently active synthesizer are applied.
|
|
45665
|
-
*
|
|
45666
|
-
* The meaning of the key changes when used with AlphaSynth directly, in this case the key is the midi channel .
|
|
45667
|
-
*/
|
|
45668
|
-
trackTranspositionPitches = new Map();
|
|
45669
|
-
}
|
|
45670
|
-
/**
|
|
45671
|
-
* Represents a single chunk of audio produced.
|
|
45672
|
-
* @public
|
|
45673
|
-
*/
|
|
45674
|
-
class AudioExportChunk {
|
|
45675
|
-
/**
|
|
45676
|
-
* The generated samples for the requested chunk.
|
|
45677
|
-
*/
|
|
45678
|
-
samples;
|
|
45679
|
-
/**
|
|
45680
|
-
* The current time position within the song in milliseconds.
|
|
45681
|
-
*/
|
|
45682
|
-
currentTime = 0;
|
|
45683
|
-
/**
|
|
45684
|
-
* The total length of the song in milliseconds.
|
|
45685
|
-
*/
|
|
45686
|
-
endTime = 0;
|
|
45687
|
-
/**
|
|
45688
|
-
* The current time position within the song in midi ticks.
|
|
45689
|
-
*/
|
|
45690
|
-
currentTick = 0;
|
|
45691
|
-
/**
|
|
45692
|
-
* The total length of the song in midi ticks.
|
|
45693
|
-
*/
|
|
45694
|
-
endTick = 0;
|
|
45695
|
-
}
|
|
45696
|
-
|
|
45697
45722
|
/**
|
|
45698
45723
|
* This is the base class for synthesizer components which can be used to
|
|
45699
45724
|
* play a {@link MidiFile} via a {@link ISynthOutput}.
|
|
@@ -45902,6 +45927,17 @@ class AlphaSynthBase {
|
|
|
45902
45927
|
}
|
|
45903
45928
|
this._notPlayedSamples += samples.length;
|
|
45904
45929
|
this.output.addSamples(samples);
|
|
45930
|
+
// if the sequencer finished, we instantly force a noteOff on all
|
|
45931
|
+
// voices to complete playback and stop voices fast.
|
|
45932
|
+
// Doing this in the samplePlayed callback is too late as we might
|
|
45933
|
+
// continue generating audio for long-release notes (especially percussion like cymbals)
|
|
45934
|
+
// we still have checkForFinish which takes care of the counterpart
|
|
45935
|
+
// on the sample played area to ensure we seek back.
|
|
45936
|
+
// but thanks to this code we ensure the output will complete fast as we won't
|
|
45937
|
+
// be adding more samples beside a 0.1s ramp-down
|
|
45938
|
+
if (this.sequencer.isFinished) {
|
|
45939
|
+
this.synthesizer.noteOffAll(true);
|
|
45940
|
+
}
|
|
45905
45941
|
}
|
|
45906
45942
|
else {
|
|
45907
45943
|
// Tell output that there is no data left for it.
|
|
@@ -45918,7 +45954,7 @@ class AlphaSynthBase {
|
|
|
45918
45954
|
if (this._countInVolume > 0) {
|
|
45919
45955
|
Logger.debug('AlphaSynth', 'Starting countin');
|
|
45920
45956
|
this.sequencer.startCountIn();
|
|
45921
|
-
this.synthesizer.setupMetronomeChannel(this._countInVolume);
|
|
45957
|
+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this._countInVolume);
|
|
45922
45958
|
this.updateTimePosition(0, true);
|
|
45923
45959
|
}
|
|
45924
45960
|
this.output.play();
|
|
@@ -45930,7 +45966,7 @@ class AlphaSynthBase {
|
|
|
45930
45966
|
this._stopOneTimeMidi();
|
|
45931
45967
|
}
|
|
45932
45968
|
Logger.debug('AlphaSynth', 'Starting playback');
|
|
45933
|
-
this.synthesizer.setupMetronomeChannel(this.metronomeVolume);
|
|
45969
|
+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this.metronomeVolume);
|
|
45934
45970
|
this._synthStopping = false;
|
|
45935
45971
|
this.state = PlayerState.Playing;
|
|
45936
45972
|
this.stateChanged.trigger(new PlayerStateChangedEventArgs(this.state, false));
|
|
@@ -46014,7 +46050,7 @@ class AlphaSynthBase {
|
|
|
46014
46050
|
}
|
|
46015
46051
|
_checkReadyForPlayback() {
|
|
46016
46052
|
if (this.isReadyForPlayback) {
|
|
46017
|
-
this.synthesizer.setupMetronomeChannel(this.metronomeVolume);
|
|
46053
|
+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this.metronomeVolume);
|
|
46018
46054
|
const programs = this.sequencer.instrumentPrograms;
|
|
46019
46055
|
const percussionKeys = this.sequencer.percussionKeys;
|
|
46020
46056
|
let append = false;
|
|
@@ -46355,7 +46391,7 @@ class AlphaSynthAudioExporter {
|
|
|
46355
46391
|
_generatedAudioCurrentTime = 0;
|
|
46356
46392
|
_generatedAudioEndTime = 0;
|
|
46357
46393
|
setup() {
|
|
46358
|
-
this._synth.setupMetronomeChannel(this._synth.metronomeVolume);
|
|
46394
|
+
this._synth.setupMetronomeChannel(this._sequencer.metronomeChannel, this._synth.metronomeVolume);
|
|
46359
46395
|
const syncPoints = this._sequencer.currentSyncPoints;
|
|
46360
46396
|
const alphaTabEndTime = this._sequencer.currentEndTime;
|
|
46361
46397
|
if (syncPoints.length === 0) {
|
|
@@ -46437,7 +46473,7 @@ class BackingTrackAudioSynthesizer {
|
|
|
46437
46473
|
}
|
|
46438
46474
|
loadPresets(_hydra, _instrumentPrograms, _percussionKeys, _append) {
|
|
46439
46475
|
}
|
|
46440
|
-
setupMetronomeChannel(_metronomeVolume) {
|
|
46476
|
+
setupMetronomeChannel(_metronomeChannel, _metronomeVolume) {
|
|
46441
46477
|
}
|
|
46442
46478
|
synthesizeSilent(_sampleCount) {
|
|
46443
46479
|
this.fakeSynthesize();
|
|
@@ -47871,6 +47907,7 @@ class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase {
|
|
|
47871
47907
|
_bufferTimeInMilliseconds = 0;
|
|
47872
47908
|
_settings;
|
|
47873
47909
|
_boundHandleMessage;
|
|
47910
|
+
_pendingEvents;
|
|
47874
47911
|
constructor(settings) {
|
|
47875
47912
|
super();
|
|
47876
47913
|
this._settings = settings;
|
|
@@ -47898,6 +47935,13 @@ class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase {
|
|
|
47898
47935
|
this.source.connect(this._worklet);
|
|
47899
47936
|
this.source.start(0);
|
|
47900
47937
|
this._worklet.connect(ctx.destination);
|
|
47938
|
+
const pending = this._pendingEvents;
|
|
47939
|
+
if (pending) {
|
|
47940
|
+
for (const e of pending) {
|
|
47941
|
+
this._worklet.port.postMessage(e);
|
|
47942
|
+
}
|
|
47943
|
+
this._pendingEvents = undefined;
|
|
47944
|
+
}
|
|
47901
47945
|
}, (reason) => {
|
|
47902
47946
|
Logger.error('WebAudio', `Audio Worklet creation failed: reason=${reason}`);
|
|
47903
47947
|
});
|
|
@@ -47924,15 +47968,26 @@ class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase {
|
|
|
47924
47968
|
this._worklet.disconnect();
|
|
47925
47969
|
}
|
|
47926
47970
|
this._worklet = null;
|
|
47971
|
+
this._pendingEvents = undefined;
|
|
47972
|
+
}
|
|
47973
|
+
_postWorkerMessage(message) {
|
|
47974
|
+
const worklet = this._worklet;
|
|
47975
|
+
if (worklet) {
|
|
47976
|
+
worklet.port.postMessage(message);
|
|
47977
|
+
}
|
|
47978
|
+
else {
|
|
47979
|
+
this._pendingEvents ??= [];
|
|
47980
|
+
this._pendingEvents.push(message);
|
|
47981
|
+
}
|
|
47927
47982
|
}
|
|
47928
47983
|
addSamples(f) {
|
|
47929
|
-
this.
|
|
47984
|
+
this._postWorkerMessage({
|
|
47930
47985
|
cmd: 'alphaSynth.output.addSamples',
|
|
47931
47986
|
samples: Environment.prepareForPostMessage(f)
|
|
47932
47987
|
});
|
|
47933
47988
|
}
|
|
47934
47989
|
resetSamples() {
|
|
47935
|
-
this.
|
|
47990
|
+
this._postWorkerMessage({
|
|
47936
47991
|
cmd: 'alphaSynth.output.resetSamples'
|
|
47937
47992
|
});
|
|
47938
47993
|
}
|
|
@@ -55805,6 +55860,9 @@ class AlphaTabApiBase {
|
|
|
55805
55860
|
this._beatVisibilityChecker.bounds = this.boundsLookup;
|
|
55806
55861
|
this._currentBeat = null;
|
|
55807
55862
|
this._cursorUpdateTick(this._previousTick, false, 1, true, true);
|
|
55863
|
+
if (this._selectionStart) {
|
|
55864
|
+
this.highlightPlaybackRange(this._selectionStart.beat, this._selectionEnd.beat);
|
|
55865
|
+
}
|
|
55808
55866
|
this.postRenderFinished.trigger();
|
|
55809
55867
|
this.uiFacade.triggerEvent(this.container, 'postRenderFinished', null);
|
|
55810
55868
|
}
|
|
@@ -69210,16 +69268,18 @@ class LineBarRenderer extends BarRendererBase {
|
|
|
69210
69268
|
s = [];
|
|
69211
69269
|
const zero = MusicFontSymbol.Tuplet0;
|
|
69212
69270
|
if (num > 10) {
|
|
69213
|
-
|
|
69214
|
-
s.push((zero +
|
|
69271
|
+
const tens = Math.floor(num / 10);
|
|
69272
|
+
s.push((zero + tens));
|
|
69273
|
+
s.push((zero + (num - 10 * tens)));
|
|
69215
69274
|
}
|
|
69216
69275
|
else {
|
|
69217
69276
|
s.push((zero + num));
|
|
69218
69277
|
}
|
|
69219
69278
|
s.push(MusicFontSymbol.TupletColon);
|
|
69220
69279
|
if (den > 10) {
|
|
69221
|
-
|
|
69222
|
-
s.push((zero +
|
|
69280
|
+
const tens = Math.floor(den / 10);
|
|
69281
|
+
s.push((zero + tens));
|
|
69282
|
+
s.push((zero + (den - 10 * tens)));
|
|
69223
69283
|
}
|
|
69224
69284
|
else {
|
|
69225
69285
|
s.push((zero + den));
|
|
@@ -72198,8 +72258,10 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
|
|
|
72198
72258
|
const group = new GlyphGroup(0, 0);
|
|
72199
72259
|
group.renderer = this.renderer;
|
|
72200
72260
|
for (const note of this.container.beat.notes) {
|
|
72201
|
-
|
|
72202
|
-
|
|
72261
|
+
if (note.isVisible) {
|
|
72262
|
+
const g = this._createBeatDot(sr.getNoteSteps(note), group);
|
|
72263
|
+
g.colorOverride = ElementStyleHelper.noteColor(sr.resources, NoteSubElement.StandardNotationEffects, note);
|
|
72264
|
+
}
|
|
72203
72265
|
}
|
|
72204
72266
|
this.addEffect(group);
|
|
72205
72267
|
}
|