@coderline/alphatab 1.9.0-alpha.1883 → 1.9.0-alpha.1891
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 +464 -102
- package/dist/alphaTab.d.ts +6 -0
- package/dist/alphaTab.js +464 -102
- 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 +2 -2
package/dist/alphaTab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.9.0-alpha.
|
|
2
|
+
* alphaTab v1.9.0-alpha.1891 (develop, build 1891)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -190,9 +190,9 @@
|
|
|
190
190
|
* @internal
|
|
191
191
|
*/
|
|
192
192
|
var VersionInfo = class VersionInfo {
|
|
193
|
-
static version = "1.9.0-alpha.
|
|
194
|
-
static date = "2026-08-
|
|
195
|
-
static commit = "
|
|
193
|
+
static version = "1.9.0-alpha.1891";
|
|
194
|
+
static date = "2026-08-10T02:28:09.977Z";
|
|
195
|
+
static commit = "2a460d7c4b6d879465fe23382adc67bf6fd3196d";
|
|
196
196
|
static print(print) {
|
|
197
197
|
print(`alphaTab ${VersionInfo.version}`);
|
|
198
198
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -3138,7 +3138,7 @@
|
|
|
3138
3138
|
this.beats.push(beat);
|
|
3139
3139
|
}
|
|
3140
3140
|
finish() {
|
|
3141
|
-
if (this.beats.length > 0) this.id = `${this.beats[0].
|
|
3141
|
+
if (this.beats.length > 0) this.id = `${this.beats[0].voice.bar.id}_${this.beats[0].voice.index}_${this.beats[0].absoluteDisplayStart}`;
|
|
3142
3142
|
}
|
|
3143
3143
|
};
|
|
3144
3144
|
//#endregion
|
|
@@ -4143,6 +4143,20 @@
|
|
|
4143
4143
|
}
|
|
4144
4144
|
return headerFooterStyle;
|
|
4145
4145
|
}
|
|
4146
|
+
static backfillStaffVoices(staff, targetVoiceCount) {
|
|
4147
|
+
for (let bi = 0; bi < staff.bars.length - 1; bi++) {
|
|
4148
|
+
const priorBar = staff.bars[bi];
|
|
4149
|
+
while (priorBar.voices.length < targetVoiceCount) ModelUtils.appendPlaceholderVoice(priorBar);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
static appendPlaceholderVoice(bar) {
|
|
4153
|
+
const voice = new Voice$1();
|
|
4154
|
+
bar.addVoice(voice);
|
|
4155
|
+
const beat = new Beat();
|
|
4156
|
+
beat.isEmpty = true;
|
|
4157
|
+
beat.duration = Duration.Quarter;
|
|
4158
|
+
voice.addBeat(beat);
|
|
4159
|
+
}
|
|
4146
4160
|
/**
|
|
4147
4161
|
* Performs some general consolidations of inconsistencies on the given score like
|
|
4148
4162
|
* missing bars, beats, duplicated midi channels etc
|
|
@@ -4193,13 +4207,7 @@
|
|
|
4193
4207
|
bar.keySignature = bar.previousBar.keySignature;
|
|
4194
4208
|
bar.keySignatureType = bar.previousBar.keySignatureType;
|
|
4195
4209
|
}
|
|
4196
|
-
for (let i = 0; i < voiceCount; i++)
|
|
4197
|
-
const v = new Voice$1();
|
|
4198
|
-
bar.addVoice(v);
|
|
4199
|
-
const emptyBeat = new Beat();
|
|
4200
|
-
emptyBeat.isEmpty = true;
|
|
4201
|
-
v.addBeat(emptyBeat);
|
|
4202
|
-
}
|
|
4210
|
+
for (let i = 0; i < voiceCount; i++) ModelUtils.appendPlaceholderVoice(bar);
|
|
4203
4211
|
}
|
|
4204
4212
|
}
|
|
4205
4213
|
}
|
|
@@ -4946,6 +4954,11 @@
|
|
|
4946
4954
|
const ksi = keySignature + 7;
|
|
4947
4955
|
return keySignatureType === KeySignatureType.Minor ? ModelUtils._minorKeySignatureTonicDegrees[ksi] : ModelUtils._majorKeySignatureTonicDegrees[ksi];
|
|
4948
4956
|
}
|
|
4957
|
+
/** True iff the staff's first note isn't stringed. Empty staves return false. */
|
|
4958
|
+
static staffNotesAreNotStringed(staff) {
|
|
4959
|
+
for (const bar of staff.bars) for (const voice of bar.voices) for (const beat of voice.beats) for (const note of beat.notes) return !note.isStringed;
|
|
4960
|
+
return false;
|
|
4961
|
+
}
|
|
4949
4962
|
};
|
|
4950
4963
|
//#endregion
|
|
4951
4964
|
//#region src/model/PickStroke.ts
|
|
@@ -6336,7 +6349,10 @@
|
|
|
6336
6349
|
else realValue += this.harmonicPitch;
|
|
6337
6350
|
return realValue;
|
|
6338
6351
|
}
|
|
6339
|
-
if (this.isPercussion)
|
|
6352
|
+
if (this.isPercussion) {
|
|
6353
|
+
const art = PercussionMapper.getArticulation(this);
|
|
6354
|
+
return art !== null ? art.outputMidiNumber : this.percussionArticulation;
|
|
6355
|
+
}
|
|
6340
6356
|
if (this.isStringed) return this.fret + this.stringTuning - transpositionPitch;
|
|
6341
6357
|
if (this.isPiano) return this.octave * 12 + this.tone - transpositionPitch;
|
|
6342
6358
|
return 0;
|
|
@@ -12859,6 +12875,18 @@
|
|
|
12859
12875
|
staff.track = this;
|
|
12860
12876
|
this.staves.push(staff);
|
|
12861
12877
|
}
|
|
12878
|
+
/**
|
|
12879
|
+
* Returns the index of {@link articulation} in {@link percussionArticulations},
|
|
12880
|
+
* appending it (deduplicated by `uniqueId`) when not yet present. Callers store the
|
|
12881
|
+
* returned index in {@link Note.percussionArticulation}.
|
|
12882
|
+
*/
|
|
12883
|
+
getOrRegisterPercussionArticulation(articulation) {
|
|
12884
|
+
const uniqueId = articulation.uniqueId;
|
|
12885
|
+
for (let i = 0; i < this.percussionArticulations.length; i++) if (this.percussionArticulations[i].uniqueId === uniqueId) return i;
|
|
12886
|
+
const index = this.percussionArticulations.length;
|
|
12887
|
+
this.percussionArticulations.push(articulation);
|
|
12888
|
+
return index;
|
|
12889
|
+
}
|
|
12862
12890
|
finish(settings, sharedDataBag = null) {
|
|
12863
12891
|
if (!this.shortName) {
|
|
12864
12892
|
this.shortName = this.name;
|
|
@@ -18354,11 +18382,38 @@
|
|
|
18354
18382
|
readVoice(track, bar) {
|
|
18355
18383
|
const beatCount = IOHelper.readInt32LE(this.data);
|
|
18356
18384
|
if (beatCount === 0) return;
|
|
18385
|
+
const currentVoiceCount = bar.index === 0 ? 1 : bar.previousBar.voices.length;
|
|
18386
|
+
if (bar.voices.length === 0 || currentVoiceCount === 2) {
|
|
18387
|
+
this._readAndChainVoice(track, bar, beatCount);
|
|
18388
|
+
return;
|
|
18389
|
+
}
|
|
18390
|
+
if (beatCount === 1 && this._skipEmptyVoice()) return;
|
|
18391
|
+
ModelUtils.backfillStaffVoices(bar.staff, 2);
|
|
18392
|
+
this._readAndChainVoice(track, bar, beatCount);
|
|
18393
|
+
}
|
|
18394
|
+
_readAndChainVoice(track, bar, beatCount) {
|
|
18357
18395
|
const newVoice = new Voice$1();
|
|
18358
18396
|
bar.addVoice(newVoice);
|
|
18359
18397
|
this._ensureLoopBoundary(beatCount, Gp3To5Importer._maxBeatCount, "beat count");
|
|
18360
18398
|
for (let i = 0; i < beatCount; i++) this.readBeat(track, bar, newVoice);
|
|
18361
18399
|
}
|
|
18400
|
+
/**
|
|
18401
|
+
* Attempts to skip a fully empty voice.
|
|
18402
|
+
* @returns true if we detected an empty voice, false if the beat was not empty and a full voice has to be read.
|
|
18403
|
+
*/
|
|
18404
|
+
_skipEmptyVoice() {
|
|
18405
|
+
const startOfVoice = this.data.position;
|
|
18406
|
+
const flags = this.data.readByte();
|
|
18407
|
+
let isEmpty = false;
|
|
18408
|
+
if ((flags & 64) !== 0) isEmpty = (this.data.readByte() & 2) === 0;
|
|
18409
|
+
if (!isEmpty) {
|
|
18410
|
+
this.data.position = startOfVoice;
|
|
18411
|
+
return false;
|
|
18412
|
+
}
|
|
18413
|
+
this.data.skip(2);
|
|
18414
|
+
if (this._versionNumber >= 500) this.data.skip(2);
|
|
18415
|
+
return true;
|
|
18416
|
+
}
|
|
18362
18417
|
readBeat(track, bar, voice) {
|
|
18363
18418
|
const newBeat = new Beat();
|
|
18364
18419
|
const flags = this.data.readByte();
|
|
@@ -18706,7 +18761,9 @@
|
|
|
18706
18761
|
beat.addNote(newNote);
|
|
18707
18762
|
if ((flags & 8) !== 0) this.readNoteEffects(track, voice, beat, newNote);
|
|
18708
18763
|
if (bar.staff.isPercussion) {
|
|
18709
|
-
|
|
18764
|
+
const midi = Gp3To5Importer._gp5PercussionInstrumentMap.has(newNote.fret) ? Gp3To5Importer._gp5PercussionInstrumentMap.get(newNote.fret) : newNote.fret;
|
|
18765
|
+
const knownArticulation = PercussionMapper.getArticulationById(midi);
|
|
18766
|
+
if (knownArticulation !== null) newNote.percussionArticulation = bar.staff.track.getOrRegisterPercussionArticulation(knownArticulation);
|
|
18710
18767
|
newNote.fret = NaN;
|
|
18711
18768
|
}
|
|
18712
18769
|
if (swapAccidentals) {
|
|
@@ -19474,6 +19531,140 @@
|
|
|
19474
19531
|
rawAudioFile;
|
|
19475
19532
|
};
|
|
19476
19533
|
//#endregion
|
|
19534
|
+
//#region src/model/FingeringAssigner.ts
|
|
19535
|
+
/**
|
|
19536
|
+
* Cost-function weights for {@link FingeringAssigner}. Defaults tuned for
|
|
19537
|
+
* six-string guitar.
|
|
19538
|
+
* @internal
|
|
19539
|
+
*/
|
|
19540
|
+
var FingeringOptions = class {
|
|
19541
|
+
preferredHandPosition = 5;
|
|
19542
|
+
/** Negative = prefer open strings. */
|
|
19543
|
+
openStringBonus = -1;
|
|
19544
|
+
highFretPenaltyWeight = .5;
|
|
19545
|
+
negativeFretPenaltyWeight = 3;
|
|
19546
|
+
/** Soft; heavy weight prefers distinct strings but permits collisions
|
|
19547
|
+
* for chords with more notes than strings. */
|
|
19548
|
+
collisionPenalty = 100;
|
|
19549
|
+
/** Negative = cluster chord notes on neighbouring strings. */
|
|
19550
|
+
adjacentStringBonus = -1.5;
|
|
19551
|
+
/** Negative = repeated pitches stay on the same string across beats. */
|
|
19552
|
+
stringContinuityBonus = -.75;
|
|
19553
|
+
/** EWMA weight for the hand-position anchor:
|
|
19554
|
+
* `hand = α·hand + (1−α)·newHand`. */
|
|
19555
|
+
handPositionMomentum = .7;
|
|
19556
|
+
};
|
|
19557
|
+
/**
|
|
19558
|
+
* Assigns (string, fret) to a stream of beats via greedy hand-position
|
|
19559
|
+
* hysteresis. One instance per (staff, voice); mutates notes in place.
|
|
19560
|
+
* Not thread-safe.
|
|
19561
|
+
* @internal
|
|
19562
|
+
*/
|
|
19563
|
+
var FingeringAssigner = class FingeringAssigner {
|
|
19564
|
+
static _maxStrings = 30;
|
|
19565
|
+
_tuning;
|
|
19566
|
+
_capo;
|
|
19567
|
+
_transpositionPitch;
|
|
19568
|
+
_options;
|
|
19569
|
+
_handPosition;
|
|
19570
|
+
_lastStringByMidi;
|
|
19571
|
+
_sortedIdx;
|
|
19572
|
+
/**
|
|
19573
|
+
* @param tuning High-to-low MIDI pitches (matches {@link Staff.tuning}). 1..30 entries.
|
|
19574
|
+
*/
|
|
19575
|
+
constructor(tuning, capo, transpositionPitch, options) {
|
|
19576
|
+
if (tuning.length < 1 || tuning.length > FingeringAssigner._maxStrings) throw new Error(`FingeringAssigner requires 1..${FingeringAssigner._maxStrings} strings, got tuning.length=${tuning.length}`);
|
|
19577
|
+
this._tuning = tuning;
|
|
19578
|
+
this._capo = capo;
|
|
19579
|
+
this._transpositionPitch = transpositionPitch;
|
|
19580
|
+
this._options = options ?? new FingeringOptions();
|
|
19581
|
+
this._handPosition = this._options.preferredHandPosition;
|
|
19582
|
+
this._lastStringByMidi = /* @__PURE__ */ new Uint8Array(128);
|
|
19583
|
+
this._sortedIdx = /* @__PURE__ */ new Int32Array(16);
|
|
19584
|
+
}
|
|
19585
|
+
/** Reset the hand-position anchor and per-pitch continuity memory. */
|
|
19586
|
+
reset() {
|
|
19587
|
+
this._handPosition = this._options.preferredHandPosition;
|
|
19588
|
+
this._lastStringByMidi.fill(0);
|
|
19589
|
+
}
|
|
19590
|
+
/** Assigns `(string, fret)` to notes that don't already carry both. */
|
|
19591
|
+
assign(beat) {
|
|
19592
|
+
const notes = beat.notes;
|
|
19593
|
+
const K = notes.length;
|
|
19594
|
+
if (K === 0) return;
|
|
19595
|
+
if (this._sortedIdx.length < K) this._sortedIdx = new Int32Array(K);
|
|
19596
|
+
const sortedIdx = this._sortedIdx;
|
|
19597
|
+
let n = 0;
|
|
19598
|
+
for (let i = 0; i < K; i++) {
|
|
19599
|
+
const note = notes[i];
|
|
19600
|
+
if (note.isStringed) continue;
|
|
19601
|
+
if (note.isPercussion) {
|
|
19602
|
+
const art = PercussionMapper.getArticulation(note);
|
|
19603
|
+
if (art !== null) {
|
|
19604
|
+
if (Number.isNaN(note.string)) note.string = Math.max(1, Math.min(6, 7 - art.staffLine));
|
|
19605
|
+
if (Number.isNaN(note.fret)) note.fret = art.outputMidiNumber;
|
|
19606
|
+
}
|
|
19607
|
+
continue;
|
|
19608
|
+
}
|
|
19609
|
+
const tieOrigin = note.tieOrigin;
|
|
19610
|
+
if (note.isTieDestination && tieOrigin !== null && tieOrigin.isStringed) {
|
|
19611
|
+
note.string = tieOrigin.string;
|
|
19612
|
+
note.fret = tieOrigin.fret;
|
|
19613
|
+
continue;
|
|
19614
|
+
}
|
|
19615
|
+
let j = n;
|
|
19616
|
+
const noteValue = note.realValue;
|
|
19617
|
+
while (j > 0 && notes[sortedIdx[j - 1]].realValue > noteValue) {
|
|
19618
|
+
sortedIdx[j] = sortedIdx[j - 1];
|
|
19619
|
+
j--;
|
|
19620
|
+
}
|
|
19621
|
+
sortedIdx[j] = i;
|
|
19622
|
+
n++;
|
|
19623
|
+
}
|
|
19624
|
+
if (n === 0) return;
|
|
19625
|
+
const N = this._tuning.length;
|
|
19626
|
+
const opts = this._options;
|
|
19627
|
+
let usedStrings = 0;
|
|
19628
|
+
let newHand = -1;
|
|
19629
|
+
for (let k = 0; k < n; k++) {
|
|
19630
|
+
const note = notes[sortedIdx[k]];
|
|
19631
|
+
const realValue = note.realValue;
|
|
19632
|
+
const target = realValue + this._transpositionPitch;
|
|
19633
|
+
const continuityString = realValue >= 0 && realValue < 128 ? this._lastStringByMidi[realValue] : 0;
|
|
19634
|
+
let bestString = 1;
|
|
19635
|
+
let bestFret = 0;
|
|
19636
|
+
let bestCost = Number.POSITIVE_INFINITY;
|
|
19637
|
+
for (let s = 1; s <= N; s++) {
|
|
19638
|
+
const fret = target - (this._capo + this._tuning[N - s]);
|
|
19639
|
+
const distanceCost = Math.abs(fret - this._handPosition);
|
|
19640
|
+
const openBonus = fret === 0 ? opts.openStringBonus : 0;
|
|
19641
|
+
const negFretPenalty = fret < 0 ? -fret * opts.negativeFretPenaltyWeight : 0;
|
|
19642
|
+
const highFretPenalty = fret > 12 ? (fret - 12) * opts.highFretPenaltyWeight : 0;
|
|
19643
|
+
const collisionCost = (usedStrings & 1 << s - 1) !== 0 ? opts.collisionPenalty : 0;
|
|
19644
|
+
const leftUsed = s > 1 && (usedStrings & 1 << s - 2) !== 0;
|
|
19645
|
+
const rightUsed = s < N && (usedStrings & 1 << s) !== 0;
|
|
19646
|
+
const adjacencyBonus = leftUsed || rightUsed ? opts.adjacentStringBonus : 0;
|
|
19647
|
+
const continuityBonus = s === continuityString ? opts.stringContinuityBonus : 0;
|
|
19648
|
+
const cost = distanceCost + openBonus + negFretPenalty + highFretPenalty + collisionCost + adjacencyBonus + continuityBonus;
|
|
19649
|
+
if (cost < bestCost) {
|
|
19650
|
+
bestCost = cost;
|
|
19651
|
+
bestString = s;
|
|
19652
|
+
bestFret = fret;
|
|
19653
|
+
}
|
|
19654
|
+
}
|
|
19655
|
+
note.string = bestString;
|
|
19656
|
+
note.fret = bestFret;
|
|
19657
|
+
usedStrings |= 1 << bestString - 1;
|
|
19658
|
+
if (realValue >= 0 && realValue < 128) this._lastStringByMidi[realValue] = bestString;
|
|
19659
|
+
if (bestFret > 0 && (newHand < 0 || bestFret < newHand)) newHand = bestFret;
|
|
19660
|
+
}
|
|
19661
|
+
if (newHand >= 0) {
|
|
19662
|
+
const alpha = opts.handPositionMomentum;
|
|
19663
|
+
this._handPosition = alpha * this._handPosition + (1 - alpha) * newHand;
|
|
19664
|
+
}
|
|
19665
|
+
}
|
|
19666
|
+
};
|
|
19667
|
+
//#endregion
|
|
19477
19668
|
//#region src/importer/GpifParser.ts
|
|
19478
19669
|
/**
|
|
19479
19670
|
* This structure represents a duration within a gpif
|
|
@@ -19541,6 +19732,8 @@
|
|
|
19541
19732
|
_articulationByName;
|
|
19542
19733
|
_skipApplyLyrics = false;
|
|
19543
19734
|
_backingTrackPadding = 0;
|
|
19735
|
+
/** Marks the input as a Guitar Pro 6 file. Also auto-detected from the GPIF header. */
|
|
19736
|
+
isGp6 = false;
|
|
19544
19737
|
_doubleBars = /* @__PURE__ */ new Set();
|
|
19545
19738
|
_keySignatures = /* @__PURE__ */ new Map();
|
|
19546
19739
|
loadAsset;
|
|
@@ -19561,7 +19754,7 @@
|
|
|
19561
19754
|
this._rhythmById = /* @__PURE__ */ new Map();
|
|
19562
19755
|
this._notesOfBeat = /* @__PURE__ */ new Map();
|
|
19563
19756
|
this._noteById = /* @__PURE__ */ new Map();
|
|
19564
|
-
this._tappedNotes = /* @__PURE__ */ new
|
|
19757
|
+
this._tappedNotes = /* @__PURE__ */ new Set();
|
|
19565
19758
|
this._lyricsByTrack = /* @__PURE__ */ new Map();
|
|
19566
19759
|
this._soundsByTrack = /* @__PURE__ */ new Map();
|
|
19567
19760
|
this._skipApplyLyrics = false;
|
|
@@ -19574,6 +19767,7 @@
|
|
|
19574
19767
|
this._parseDom(dom);
|
|
19575
19768
|
this._buildModel();
|
|
19576
19769
|
ModelUtils.consolidate(this.score);
|
|
19770
|
+
if (this.isGp6) this._assignFingeringForGp6();
|
|
19577
19771
|
this.score.finish(settings);
|
|
19578
19772
|
if (!this._skipApplyLyrics && this._lyricsByTrack.size > 0) for (const [t, lyrics] of this._lyricsByTrack) this._tracksById.get(t).applyLyrics(lyrics);
|
|
19579
19773
|
}
|
|
@@ -19583,6 +19777,12 @@
|
|
|
19583
19777
|
if (root.localName === "GPIF") {
|
|
19584
19778
|
this.score = new Score();
|
|
19585
19779
|
for (const n of root.childElements()) switch (n.localName) {
|
|
19780
|
+
case "GPVersion":
|
|
19781
|
+
if (n.innerText === "6") this.isGp6 = true;
|
|
19782
|
+
break;
|
|
19783
|
+
case "Encoding":
|
|
19784
|
+
if (n.findChildElement("EncodingDescription")?.innerText === "GP6") this.isGp6 = true;
|
|
19785
|
+
break;
|
|
19586
19786
|
case "Score":
|
|
19587
19787
|
this._parseScoreNode(n);
|
|
19588
19788
|
break;
|
|
@@ -21077,7 +21277,7 @@
|
|
|
21077
21277
|
variation = GpifParser._parseIntSafe(c.findChildElement("Variation")?.innerText, 0);
|
|
21078
21278
|
break;
|
|
21079
21279
|
case "Tapped":
|
|
21080
|
-
this._tappedNotes.
|
|
21280
|
+
this._tappedNotes.add(noteId);
|
|
21081
21281
|
break;
|
|
21082
21282
|
case "HarmonicType":
|
|
21083
21283
|
const htype = c.findChildElement("HType");
|
|
@@ -21265,6 +21465,84 @@
|
|
|
21265
21465
|
}
|
|
21266
21466
|
this._rhythmById.set(rhythmId, rhythm);
|
|
21267
21467
|
}
|
|
21468
|
+
_keyFor(trackId, base) {
|
|
21469
|
+
if (!this._transposeKeySignaturePerTrack.has(trackId)) return base;
|
|
21470
|
+
return [ModelUtils.transposeKey(base[0], this._transposeKeySignaturePerTrack.get(trackId)), base[1]];
|
|
21471
|
+
}
|
|
21472
|
+
_equalizeVoiceCount(staff, bar, staffVoiceLimits) {
|
|
21473
|
+
const previousMax = staffVoiceLimits.get(staff) ?? 0;
|
|
21474
|
+
const target = Math.max(previousMax, bar.voices.length, 1);
|
|
21475
|
+
if (target > previousMax) {
|
|
21476
|
+
staffVoiceLimits.set(staff, target);
|
|
21477
|
+
ModelUtils.backfillStaffVoices(staff, target);
|
|
21478
|
+
}
|
|
21479
|
+
while (bar.voices.length < target) ModelUtils.appendPlaceholderVoice(bar);
|
|
21480
|
+
}
|
|
21481
|
+
_attachVoiceToBar(bar, voiceId, staff) {
|
|
21482
|
+
const voice = this._voiceById.get(voiceId);
|
|
21483
|
+
bar.addVoice(voice);
|
|
21484
|
+
const beatIds = this._beatsOfVoice.get(voiceId);
|
|
21485
|
+
if (!beatIds) return;
|
|
21486
|
+
for (const beatId of beatIds) if (beatId !== GpifParser._invalidId) this._attachBeatToVoice(voice, beatId, staff);
|
|
21487
|
+
}
|
|
21488
|
+
_attachBeatToVoice(voice, beatId, staff) {
|
|
21489
|
+
const beat = BeatCloner.clone(this._beatById.get(beatId));
|
|
21490
|
+
voice.addBeat(beat);
|
|
21491
|
+
const rhythmId = this._rhythmOfBeat.get(beatId);
|
|
21492
|
+
const rhythm = this._rhythmById.get(rhythmId);
|
|
21493
|
+
beat.duration = rhythm.value;
|
|
21494
|
+
beat.dots = rhythm.dots;
|
|
21495
|
+
beat.tupletNumerator = rhythm.tupletNumerator;
|
|
21496
|
+
beat.tupletDenominator = rhythm.tupletDenominator;
|
|
21497
|
+
const noteIds = this._notesOfBeat.get(beatId);
|
|
21498
|
+
if (!noteIds) return;
|
|
21499
|
+
for (const noteId of noteIds) if (noteId !== GpifParser._invalidId) this._attachNoteToBeat(beat, noteId, staff);
|
|
21500
|
+
}
|
|
21501
|
+
_attachNoteToBeat(beat, noteId, staff) {
|
|
21502
|
+
const note = NoteCloner.clone(this._noteById.get(noteId));
|
|
21503
|
+
if (staff.isPercussion) note.fret = NaN;
|
|
21504
|
+
else note.percussionArticulation = NaN;
|
|
21505
|
+
beat.addNote(note);
|
|
21506
|
+
if (this._tappedNotes.has(noteId)) beat.tap = true;
|
|
21507
|
+
if (staff.isPercussion && note.percussionArticulation >= 0) {
|
|
21508
|
+
const trackArticulations = staff.track.percussionArticulations;
|
|
21509
|
+
let known = null;
|
|
21510
|
+
if (note.percussionArticulation < trackArticulations.length) known = trackArticulations[note.percussionArticulation];
|
|
21511
|
+
else known = PercussionMapper.getArticulationById(note.percussionArticulation);
|
|
21512
|
+
if (known !== null) note.percussionArticulation = staff.track.getOrRegisterPercussionArticulation(known);
|
|
21513
|
+
}
|
|
21514
|
+
}
|
|
21515
|
+
_assignFingeringForGp6() {
|
|
21516
|
+
for (const track of this.score.tracks) for (const staff of track.staves) {
|
|
21517
|
+
const isPercussion = staff.isPercussion;
|
|
21518
|
+
const isPitchedOnly = !isPercussion && staff.stringTuning.tunings.length === 0 && ModelUtils.staffNotesAreNotStringed(staff);
|
|
21519
|
+
if (!isPercussion && !isPitchedOnly) continue;
|
|
21520
|
+
if (isPercussion) staff.stringTuning.tunings = [
|
|
21521
|
+
0,
|
|
21522
|
+
0,
|
|
21523
|
+
0,
|
|
21524
|
+
0,
|
|
21525
|
+
0,
|
|
21526
|
+
0
|
|
21527
|
+
];
|
|
21528
|
+
else {
|
|
21529
|
+
const fallback = staff.index === 0 ? Tuning.getDefaultTuningFor(6) : Tuning.getDefaultTuningFor(5);
|
|
21530
|
+
if (fallback !== null) {
|
|
21531
|
+
staff.stringTuning.tunings = fallback.tunings.slice();
|
|
21532
|
+
staff.stringTuning.name = fallback.name;
|
|
21533
|
+
}
|
|
21534
|
+
}
|
|
21535
|
+
const assignersByVoiceIndex = /* @__PURE__ */ new Map();
|
|
21536
|
+
for (const bar of staff.bars) for (const voice of bar.voices) {
|
|
21537
|
+
let assigner = assignersByVoiceIndex.get(voice.index);
|
|
21538
|
+
if (assigner === void 0) {
|
|
21539
|
+
assigner = new FingeringAssigner(staff.stringTuning.tunings, staff.capo, staff.transpositionPitch);
|
|
21540
|
+
assignersByVoiceIndex.set(voice.index, assigner);
|
|
21541
|
+
}
|
|
21542
|
+
for (const beat of voice.beats) assigner.assign(beat);
|
|
21543
|
+
}
|
|
21544
|
+
}
|
|
21545
|
+
}
|
|
21268
21546
|
_buildModel() {
|
|
21269
21547
|
for (let i = 0, j = this._masterBars.length; i < j; i++) {
|
|
21270
21548
|
const masterBar = this._masterBars[i];
|
|
@@ -21282,66 +21560,46 @@
|
|
|
21282
21560
|
this.score.addTrack(track);
|
|
21283
21561
|
trackIndexToTrackId.push(trackId);
|
|
21284
21562
|
}
|
|
21563
|
+
const staffVoiceLimits = /* @__PURE__ */ new Map();
|
|
21285
21564
|
let keySignature;
|
|
21286
21565
|
for (const barIds of this._barsOfMasterBar) {
|
|
21287
21566
|
let staffIndex = 0;
|
|
21288
21567
|
let trackIndex = 0;
|
|
21289
|
-
keySignature = [KeySignature.C, KeySignatureType.Major];
|
|
21290
|
-
if (this._transposeKeySignaturePerTrack.has(trackIndexToTrackId[0])) keySignature = [ModelUtils.transposeKey(keySignature[0], this._transposeKeySignaturePerTrack.get(trackIndexToTrackId[0])), keySignature[1]];
|
|
21568
|
+
keySignature = this._keyFor(trackIndexToTrackId[0], [KeySignature.C, KeySignatureType.Major]);
|
|
21291
21569
|
for (let barIndex = 0; barIndex < barIds.length && trackIndex < this.score.tracks.length; barIndex++) {
|
|
21292
21570
|
const barId = barIds[barIndex];
|
|
21293
|
-
if (barId
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21297
|
-
|
|
21298
|
-
|
|
21299
|
-
|
|
21300
|
-
|
|
21301
|
-
|
|
21302
|
-
|
|
21303
|
-
|
|
21304
|
-
|
|
21305
|
-
|
|
21306
|
-
|
|
21307
|
-
|
|
21308
|
-
|
|
21309
|
-
|
|
21310
|
-
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
|
|
21314
|
-
const rhythm = this._rhythmById.get(rhythmId);
|
|
21315
|
-
beat.duration = rhythm.value;
|
|
21316
|
-
beat.dots = rhythm.dots;
|
|
21317
|
-
beat.tupletNumerator = rhythm.tupletNumerator;
|
|
21318
|
-
beat.tupletDenominator = rhythm.tupletDenominator;
|
|
21319
|
-
if (this._notesOfBeat.has(beatId)) {
|
|
21320
|
-
for (const noteId of this._notesOfBeat.get(beatId)) if (noteId !== GpifParser._invalidId) {
|
|
21321
|
-
const note = NoteCloner.clone(this._noteById.get(noteId));
|
|
21322
|
-
if (staff.isPercussion) note.fret = NaN;
|
|
21323
|
-
else note.percussionArticulation = NaN;
|
|
21324
|
-
beat.addNote(note);
|
|
21325
|
-
if (this._tappedNotes.has(noteId)) beat.tap = true;
|
|
21326
|
-
}
|
|
21327
|
-
}
|
|
21328
|
-
}
|
|
21571
|
+
if (barId === GpifParser._invalidId) {
|
|
21572
|
+
trackIndex++;
|
|
21573
|
+
continue;
|
|
21574
|
+
}
|
|
21575
|
+
const bar = this._barsById.get(barId);
|
|
21576
|
+
const track = this.score.tracks[trackIndex];
|
|
21577
|
+
const staff = track.staves[staffIndex];
|
|
21578
|
+
staff.addBar(bar);
|
|
21579
|
+
const masterBarIndex = staff.bars.length - 1;
|
|
21580
|
+
if (this._keySignatures.has(masterBarIndex)) keySignature = this._keyFor(trackIndexToTrackId[trackIndex], this._keySignatures.get(masterBarIndex));
|
|
21581
|
+
bar.keySignature = keySignature[0];
|
|
21582
|
+
bar.keySignatureType = keySignature[1];
|
|
21583
|
+
if (this._doubleBars.has(bar.masterBar)) bar.barLineRight = BarLineStyle.LightLight;
|
|
21584
|
+
const voiceIds = this._voicesOfBar.get(barId);
|
|
21585
|
+
if (voiceIds) {
|
|
21586
|
+
let pendingPlaceholders = 0;
|
|
21587
|
+
for (const voiceId of voiceIds) if (voiceId === GpifParser._invalidId) pendingPlaceholders++;
|
|
21588
|
+
else {
|
|
21589
|
+
while (pendingPlaceholders > 0) {
|
|
21590
|
+
ModelUtils.appendPlaceholderVoice(bar);
|
|
21591
|
+
pendingPlaceholders--;
|
|
21329
21592
|
}
|
|
21330
|
-
|
|
21331
|
-
const voice = new Voice$1();
|
|
21332
|
-
bar.addVoice(voice);
|
|
21333
|
-
const beat = new Beat();
|
|
21334
|
-
beat.isEmpty = true;
|
|
21335
|
-
beat.duration = Duration.Quarter;
|
|
21336
|
-
voice.addBeat(beat);
|
|
21593
|
+
this._attachVoiceToBar(bar, voiceId, staff);
|
|
21337
21594
|
}
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
}
|
|
21595
|
+
}
|
|
21596
|
+
this._equalizeVoiceCount(staff, bar, staffVoiceLimits);
|
|
21597
|
+
staffIndex++;
|
|
21598
|
+
if (staffIndex >= track.staves.length) {
|
|
21599
|
+
trackIndex++;
|
|
21600
|
+
staffIndex = 0;
|
|
21601
|
+
}
|
|
21602
|
+
keySignature = trackIndex < trackIndexToTrackId.length ? this._keyFor(trackIndexToTrackId[trackIndex], [KeySignature.C, KeySignatureType.Major]) : [KeySignature.C, KeySignatureType.Major];
|
|
21345
21603
|
}
|
|
21346
21604
|
}
|
|
21347
21605
|
for (const trackId of this._tracksMapping) {
|
|
@@ -21874,6 +22132,7 @@
|
|
|
21874
22132
|
if (!xml) throw new UnsupportedFormatError("No score.gpif found in GPX");
|
|
21875
22133
|
Logger.debug(this.name, "Start Parsing score.gpif");
|
|
21876
22134
|
const gpifParser = new GpifParser();
|
|
22135
|
+
gpifParser.isGp6 = true;
|
|
21877
22136
|
gpifParser.parseXml(xml, this.settings);
|
|
21878
22137
|
Logger.debug(this.name, "score.gpif parsed");
|
|
21879
22138
|
const score = gpifParser.score;
|
|
@@ -22211,6 +22470,16 @@
|
|
|
22211
22470
|
}
|
|
22212
22471
|
};
|
|
22213
22472
|
/**
|
|
22473
|
+
* Tracks how raw MusicXML voice numbers on a staff map into alphaTab's local
|
|
22474
|
+
* (dense, 0-based) voice slots. Collapses MuseScore's sparse `staff*4+local`
|
|
22475
|
+
* convention (staff 2 → voices 5..8) into dense per-staff indices.
|
|
22476
|
+
* @internal
|
|
22477
|
+
*/
|
|
22478
|
+
var StaffVoicePacking = class {
|
|
22479
|
+
mapping = /* @__PURE__ */ new Map();
|
|
22480
|
+
sortedRawVoices = [];
|
|
22481
|
+
};
|
|
22482
|
+
/**
|
|
22214
22483
|
* @internal
|
|
22215
22484
|
*/
|
|
22216
22485
|
var MusicXmlImporter = class MusicXmlImporter extends ScoreImporter {
|
|
@@ -22218,6 +22487,7 @@
|
|
|
22218
22487
|
_idToTrackInfo = /* @__PURE__ */ new Map();
|
|
22219
22488
|
_indexToTrackInfo = /* @__PURE__ */ new Map();
|
|
22220
22489
|
_staffToContext = /* @__PURE__ */ new Map();
|
|
22490
|
+
_staffVoicePacking = /* @__PURE__ */ new Map();
|
|
22221
22491
|
_currentBarNumberDisplayPart;
|
|
22222
22492
|
_currentBarNumberDisplayBar;
|
|
22223
22493
|
_divisionsPerQuarterNote = 1;
|
|
@@ -23594,14 +23864,38 @@
|
|
|
23594
23864
|
}
|
|
23595
23865
|
return staff.bars[masterBar.index];
|
|
23596
23866
|
}
|
|
23597
|
-
|
|
23598
|
-
let
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
|
|
23867
|
+
_resolveAndPlaceVoice(staff, rawVoice, bar) {
|
|
23868
|
+
let packing;
|
|
23869
|
+
if (this._staffVoicePacking.has(staff)) packing = this._staffVoicePacking.get(staff);
|
|
23870
|
+
else {
|
|
23871
|
+
packing = new StaffVoicePacking();
|
|
23872
|
+
this._staffVoicePacking.set(staff, packing);
|
|
23602
23873
|
}
|
|
23603
|
-
if (
|
|
23604
|
-
|
|
23874
|
+
if (packing.mapping.has(rawVoice)) return bar.voices[packing.mapping.get(rawVoice)];
|
|
23875
|
+
let newVoiceNumber = Number.parseInt(rawVoice, 10);
|
|
23876
|
+
if (Number.isNaN(newVoiceNumber)) {
|
|
23877
|
+
Logger.warning("MusicXML", "Voices need to be specified as numbers");
|
|
23878
|
+
newVoiceNumber = 0;
|
|
23879
|
+
}
|
|
23880
|
+
let insertPos = packing.sortedRawVoices.length;
|
|
23881
|
+
for (let i = 0; i < packing.sortedRawVoices.length; i++) {
|
|
23882
|
+
const existing = Number.parseInt(packing.sortedRawVoices[i], 10);
|
|
23883
|
+
if ((Number.isNaN(existing) ? 0 : existing) > newVoiceNumber) {
|
|
23884
|
+
insertPos = i;
|
|
23885
|
+
break;
|
|
23886
|
+
}
|
|
23887
|
+
}
|
|
23888
|
+
packing.sortedRawVoices.splice(insertPos, 0, rawVoice);
|
|
23889
|
+
for (const b of staff.bars) {
|
|
23890
|
+
b.voices.splice(insertPos, 0, new Voice$1());
|
|
23891
|
+
for (let i = insertPos; i < b.voices.length; i++) {
|
|
23892
|
+
b.voices[i].index = i;
|
|
23893
|
+
b.voices[i].bar = b;
|
|
23894
|
+
}
|
|
23895
|
+
}
|
|
23896
|
+
packing.mapping.clear();
|
|
23897
|
+
for (let i = 0; i < packing.sortedRawVoices.length; i++) packing.mapping.set(packing.sortedRawVoices[i], i);
|
|
23898
|
+
return bar.voices[insertPos];
|
|
23605
23899
|
}
|
|
23606
23900
|
_parseNote(element, masterBar, track) {
|
|
23607
23901
|
let beat = null;
|
|
@@ -23610,7 +23904,7 @@
|
|
|
23610
23904
|
let beamMode = null;
|
|
23611
23905
|
let isChord = false;
|
|
23612
23906
|
let staffIndex = 0;
|
|
23613
|
-
let
|
|
23907
|
+
let voiceRaw = "1";
|
|
23614
23908
|
let durationInTicks = -1;
|
|
23615
23909
|
let beatDuration = null;
|
|
23616
23910
|
let dots = 0;
|
|
@@ -23638,7 +23932,7 @@
|
|
|
23638
23932
|
return;
|
|
23639
23933
|
}
|
|
23640
23934
|
const bar = this._getOrCreateBar(staff, masterBar);
|
|
23641
|
-
const voice = this.
|
|
23935
|
+
const voice = this._resolveAndPlaceVoice(staff, voiceRaw, bar);
|
|
23642
23936
|
const actualMusicalPosition = voice.beats.length === 0 ? 0 : voice.beats[voice.beats.length - 1].displayEnd;
|
|
23643
23937
|
let gap = this._musicalPosition - actualMusicalPosition;
|
|
23644
23938
|
if (gap > 0) {
|
|
@@ -23740,13 +24034,11 @@
|
|
|
23740
24034
|
case "instrument":
|
|
23741
24035
|
instrumentId = c.getAttribute("id", "");
|
|
23742
24036
|
break;
|
|
23743
|
-
case "voice":
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
Logger.warning("MusicXML", "Voices need to be specified as numbers");
|
|
23747
|
-
voiceIndex = 0;
|
|
23748
|
-
} else voiceIndex = voiceIndex - 1;
|
|
24037
|
+
case "voice": {
|
|
24038
|
+
const trimmed = c.innerText.trim();
|
|
24039
|
+
voiceRaw = trimmed.length > 0 ? trimmed : "1";
|
|
23749
24040
|
break;
|
|
24041
|
+
}
|
|
23750
24042
|
case "type":
|
|
23751
24043
|
beatDuration = this._parseBeatDuration(c);
|
|
23752
24044
|
break;
|
|
@@ -23832,7 +24124,7 @@
|
|
|
23832
24124
|
if (instrumentId !== null && trackInfo.isUnpitchedInstrument(instrumentId)) note.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId, note);
|
|
23833
24125
|
else if (note.beat.voice.bar.staff.isPercussion) {
|
|
23834
24126
|
const knownArticulation = PercussionMapper.getArticulationById(note.displayValue);
|
|
23835
|
-
if (knownArticulation) note.percussionArticulation = knownArticulation
|
|
24127
|
+
if (knownArticulation) note.percussionArticulation = track.getOrRegisterPercussionArticulation(knownArticulation);
|
|
23836
24128
|
}
|
|
23837
24129
|
}
|
|
23838
24130
|
_parsePlay(element, note) {
|
|
@@ -68847,9 +69139,11 @@
|
|
|
68847
69139
|
var GpifWriter = class GpifWriter {
|
|
68848
69140
|
static _sampleRate = 44100;
|
|
68849
69141
|
_rhythmIdLookup = /* @__PURE__ */ new Map();
|
|
69142
|
+
_tuningByStaff = /* @__PURE__ */ new Map();
|
|
68850
69143
|
writeXml(score) {
|
|
68851
69144
|
const xmlDocument = new XmlDocument();
|
|
68852
69145
|
this._rhythmIdLookup = /* @__PURE__ */ new Map();
|
|
69146
|
+
this._tuningByStaff = /* @__PURE__ */ new Map();
|
|
68853
69147
|
this._writeDom(xmlDocument, score);
|
|
68854
69148
|
return xmlDocument.toFormattedString("", true);
|
|
68855
69149
|
}
|
|
@@ -68878,15 +69172,30 @@
|
|
|
68878
69172
|
const beats = gpif.addElement("Beats");
|
|
68879
69173
|
const notes = gpif.addElement("Notes");
|
|
68880
69174
|
const rhythms = gpif.addElement("Rhythms");
|
|
68881
|
-
for (const tracks of score.tracks) for (const staff of tracks.staves)
|
|
68882
|
-
|
|
68883
|
-
|
|
68884
|
-
|
|
68885
|
-
|
|
68886
|
-
|
|
68887
|
-
|
|
69175
|
+
for (const tracks of score.tracks) for (const staff of tracks.staves) {
|
|
69176
|
+
const needsFingering = ModelUtils.staffNotesAreNotStringed(staff);
|
|
69177
|
+
const assignersByVoiceIndex = needsFingering ? /* @__PURE__ */ new Map() : null;
|
|
69178
|
+
const stringedTuning = needsFingering ? this._tuningByStaff.get(staff) : null;
|
|
69179
|
+
const savedTunings = staff.tuning;
|
|
69180
|
+
if (needsFingering && stringedTuning !== null && savedTunings.length === 0) staff.stringTuning.tunings = stringedTuning.slice();
|
|
69181
|
+
for (const bar of staff.bars) {
|
|
69182
|
+
const activeVoices = this._writeBarNode(bars, bar);
|
|
69183
|
+
for (const voice of activeVoices) {
|
|
69184
|
+
let assigner = null;
|
|
69185
|
+
if (assignersByVoiceIndex !== null && stringedTuning !== null) if (assignersByVoiceIndex.has(voice.index)) assigner = assignersByVoiceIndex.get(voice.index);
|
|
69186
|
+
else {
|
|
69187
|
+
assigner = new FingeringAssigner(stringedTuning, staff.capo, staff.transpositionPitch);
|
|
69188
|
+
assignersByVoiceIndex.set(voice.index, assigner);
|
|
69189
|
+
}
|
|
69190
|
+
this._writeVoiceNode(voices, voice);
|
|
69191
|
+
for (const beat of voice.beats) {
|
|
69192
|
+
if (assigner !== null) assigner.assign(beat);
|
|
69193
|
+
this._writeBeatNode(beats, beat, rhythms);
|
|
69194
|
+
for (const note of beat.notes) this._writeNoteNode(notes, note);
|
|
69195
|
+
}
|
|
68888
69196
|
}
|
|
68889
69197
|
}
|
|
69198
|
+
staff.stringTuning.tunings = savedTunings;
|
|
68890
69199
|
}
|
|
68891
69200
|
}
|
|
68892
69201
|
_writeAssets(parent, score) {
|
|
@@ -68992,13 +69301,18 @@
|
|
|
68992
69301
|
const properties = parent.addElement("Properties");
|
|
68993
69302
|
this._writeConcertPitch(properties, note);
|
|
68994
69303
|
this._writeTransposedPitch(properties, note);
|
|
68995
|
-
if (note.
|
|
69304
|
+
if (note.isPercussion) {
|
|
69305
|
+
const art = PercussionMapper.getArticulation(note);
|
|
69306
|
+
const midi = art !== null ? art.outputMidiNumber : 0;
|
|
69307
|
+
this._writeSimplePropertyNode(properties, "String", "String", (note.string - 1).toString());
|
|
69308
|
+
this._writeSimplePropertyNode(properties, "Fret", "Fret", midi.toString());
|
|
69309
|
+
this._writeSimplePropertyNode(properties, "Midi", "Number", midi.toString());
|
|
69310
|
+
} else if (note.isStringed) {
|
|
68996
69311
|
this._writeSimplePropertyNode(properties, "String", "String", (note.string - 1).toString());
|
|
68997
69312
|
this._writeSimplePropertyNode(properties, "Fret", "Fret", note.fret.toString());
|
|
68998
69313
|
this._writeSimplePropertyNode(properties, "Midi", "Number", note.realValue.toString());
|
|
68999
69314
|
if (note.showStringNumber) this._writeSimplePropertyNode(properties, "ShowStringNumber", "Enable", null);
|
|
69000
69315
|
}
|
|
69001
|
-
if (note.isPercussion) this._writeSimplePropertyNode(properties, "String", "String", (note.string - 1).toString());
|
|
69002
69316
|
if (note.isPiano) {
|
|
69003
69317
|
this._writeSimplePropertyNode(properties, "Octave", "Number", note.octave.toString());
|
|
69004
69318
|
this._writeSimplePropertyNode(properties, "Tone", "Step", note.tone.toString());
|
|
@@ -69060,7 +69374,7 @@
|
|
|
69060
69374
|
if (slideFlags > 0) this._writeSimplePropertyNode(properties, "Slide", "Flags", slideFlags.toString());
|
|
69061
69375
|
}
|
|
69062
69376
|
_writeTransposedPitch(properties, note) {
|
|
69063
|
-
if (note.isPercussion) this._writePitch(properties, "
|
|
69377
|
+
if (note.isPercussion) this._writePitch(properties, "TransposedPitch", "C", "-1", "");
|
|
69064
69378
|
else this._writePitchForValue(properties, "TransposedPitch", note.displayValueWithoutBend, note.accidentalMode, note.beat.voice.bar.keySignature);
|
|
69065
69379
|
}
|
|
69066
69380
|
_writeConcertPitch(properties, note) {
|
|
@@ -69465,7 +69779,7 @@
|
|
|
69465
69779
|
initialTempoAutomation.addElement("Bar").innerText = "0";
|
|
69466
69780
|
initialTempoAutomation.addElement("Position").innerText = "0";
|
|
69467
69781
|
initialTempoAutomation.addElement("Visible").innerText = "true";
|
|
69468
|
-
initialTempoAutomation.addElement("Value").innerText = `${score.tempo} 2`;
|
|
69782
|
+
initialTempoAutomation.addElement("Value").innerText = `${score.tempo | 0} 2`;
|
|
69469
69783
|
if (score.tempoLabel) initialTempoAutomation.addElement("Text").innerText = score.tempoLabel;
|
|
69470
69784
|
}
|
|
69471
69785
|
const initialSyncPoint = score.masterBars[0].syncPoints ? score.masterBars[0].syncPoints.find((p) => p.ratioPosition === 0 && p.syncPointValue.barOccurence === 0) : void 0;
|
|
@@ -69480,7 +69794,7 @@
|
|
|
69480
69794
|
tempoAutomation.addElement("Bar").innerText = mb.index.toString();
|
|
69481
69795
|
tempoAutomation.addElement("Position").innerText = automation.ratioPosition.toString();
|
|
69482
69796
|
tempoAutomation.addElement("Visible").innerText = automation.isVisible ? "true" : "false";
|
|
69483
|
-
tempoAutomation.addElement("Value").innerText = `${automation.value} 2`;
|
|
69797
|
+
tempoAutomation.addElement("Value").innerText = `${automation.value | 0} 2`;
|
|
69484
69798
|
if (automation.text) tempoAutomation.addElement("Text").innerText = automation.text;
|
|
69485
69799
|
}
|
|
69486
69800
|
if (mb.syncPoints) for (const syncPoint of mb.syncPoints) {
|
|
@@ -69616,14 +69930,35 @@
|
|
|
69616
69930
|
const properties = parent.addElement("Staff").addElement("Properties");
|
|
69617
69931
|
this._writeSimplePropertyNode(properties, "CapoFret", "Fret", staff.capo.toString());
|
|
69618
69932
|
this._writeSimplePropertyNode(properties, "FretCount", "Fret", "24");
|
|
69619
|
-
|
|
69933
|
+
let tuning = staff.tuning;
|
|
69934
|
+
let tuningName = staff.tuningName;
|
|
69935
|
+
if (tuning.length === 0) {
|
|
69936
|
+
if (staff.isPercussion) {
|
|
69937
|
+
tuning = [
|
|
69938
|
+
0,
|
|
69939
|
+
0,
|
|
69940
|
+
0,
|
|
69941
|
+
0,
|
|
69942
|
+
0,
|
|
69943
|
+
0
|
|
69944
|
+
];
|
|
69945
|
+
tuningName = "";
|
|
69946
|
+
} else if (ModelUtils.staffNotesAreNotStringed(staff)) {
|
|
69947
|
+
const staffTuning = staff.index === 0 ? Tuning.getDefaultTuningFor(6) : Tuning.getDefaultTuningFor(5);
|
|
69948
|
+
tuning = staffTuning.tunings;
|
|
69949
|
+
tuningName = staffTuning.name;
|
|
69950
|
+
}
|
|
69951
|
+
}
|
|
69952
|
+
this._tuningByStaff.set(staff, tuning);
|
|
69953
|
+
if (tuning.length > 0) {
|
|
69620
69954
|
const tuningProperty = properties.addElement("Property");
|
|
69621
69955
|
tuningProperty.attributes.set("name", "Tuning");
|
|
69622
|
-
tuningProperty.addElement("Pitches").innerText =
|
|
69623
|
-
tuningProperty.addElement("Label").setCData(
|
|
69624
|
-
tuningProperty.addElement("LabelVisible").innerText =
|
|
69956
|
+
tuningProperty.addElement("Pitches").innerText = tuning.slice().reverse().join(" ");
|
|
69957
|
+
tuningProperty.addElement("Label").setCData(tuningName);
|
|
69958
|
+
tuningProperty.addElement("LabelVisible").innerText = tuningName ? "true" : "false";
|
|
69625
69959
|
tuningProperty.addElement("Flat");
|
|
69626
|
-
|
|
69960
|
+
if (staff.isPercussion) tuningProperty.addElement("Instrument").innerText = "Undefined";
|
|
69961
|
+
else switch (tuning.length) {
|
|
69627
69962
|
case 3:
|
|
69628
69963
|
tuningProperty.addElement("Instrument").innerText = "Shamisen";
|
|
69629
69964
|
break;
|
|
@@ -69996,10 +70331,37 @@
|
|
|
69996
70331
|
_writeBarNode(parent, bar) {
|
|
69997
70332
|
const barNode = parent.addElement("Bar");
|
|
69998
70333
|
barNode.attributes.set("id", bar.id.toString());
|
|
69999
|
-
|
|
70334
|
+
const activeVoices = [];
|
|
70335
|
+
const slots = [
|
|
70336
|
+
"-1",
|
|
70337
|
+
"-1",
|
|
70338
|
+
"-1",
|
|
70339
|
+
"-1"
|
|
70340
|
+
];
|
|
70341
|
+
const overflowVoices = [];
|
|
70342
|
+
for (let i = 0; i < bar.voices.length; i++) {
|
|
70343
|
+
const v = bar.voices[i];
|
|
70344
|
+
if (i < 4) {
|
|
70345
|
+
if (!v.isEmpty) {
|
|
70346
|
+
slots[i] = v.id.toString();
|
|
70347
|
+
activeVoices.push(v);
|
|
70348
|
+
}
|
|
70349
|
+
} else if (!v.isEmpty) overflowVoices.push(v);
|
|
70350
|
+
}
|
|
70351
|
+
let dropped = 0;
|
|
70352
|
+
for (const v of overflowVoices) {
|
|
70353
|
+
const freeSlot = slots.indexOf("-1");
|
|
70354
|
+
if (freeSlot >= 0) {
|
|
70355
|
+
slots[freeSlot] = v.id.toString();
|
|
70356
|
+
activeVoices.push(v);
|
|
70357
|
+
} else dropped++;
|
|
70358
|
+
}
|
|
70359
|
+
if (dropped > 0) Logger.warning("GpifWriter", `Bar ${bar.id} has ${activeVoices.length + dropped} non-empty voices; Guitar Pro supports max 4. Dropping ${dropped}.`);
|
|
70360
|
+
barNode.addElement("Voices").innerText = slots.join(" ");
|
|
70000
70361
|
barNode.addElement("Clef").innerText = Clef[bar.clef];
|
|
70001
70362
|
if (bar.clefOttava !== Ottavia.Regular) barNode.addElement("Ottavia").innerText = Ottavia[bar.clefOttava].substr(1);
|
|
70002
70363
|
if (bar.simileMark !== SimileMark.None) barNode.addElement("SimileMark").innerText = SimileMark[bar.simileMark];
|
|
70364
|
+
return activeVoices;
|
|
70003
70365
|
}
|
|
70004
70366
|
_writeVoiceNode(parent, voice) {
|
|
70005
70367
|
if (voice.isEmpty) return;
|