@coderline/alphatab 1.9.0-alpha.1855 → 1.9.0-alpha.1861

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.9.0-alpha.1855 (develop, build 1855)
2
+ * alphaTab v1.9.0-alpha.1861 (develop, build 1861)
3
3
  *
4
4
  * Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -186,9 +186,9 @@ class AlphaTabError extends Error {
186
186
  * @internal
187
187
  */
188
188
  class VersionInfo {
189
- static version = "1.9.0-alpha.1855";
190
- static date = "2026-07-06T04:23:13.862Z";
191
- static commit = "acae7e9eafc26b0209712e3c3d0c2f8564e25148";
189
+ static version = "1.9.0-alpha.1861";
190
+ static date = "2026-07-11T03:34:14.452Z";
191
+ static commit = "18845390986e826fff8ae285ee3630080dbd6653";
192
192
  static print(print) {
193
193
  print(`alphaTab ${VersionInfo.version}`);
194
194
  print(`commit: ${VersionInfo.commit}`);
@@ -18187,18 +18187,24 @@ class Gp3To5Importer extends ScoreImporter {
18187
18187
  _lyrics = [];
18188
18188
  _barCount = 0;
18189
18189
  _trackCount = 0;
18190
- _playbackInfos = [];
18190
+ /**
18191
+ * For 4 ports, 16 channels of information
18192
+ */
18193
+ _midiChannelInfo = [];
18191
18194
  _doubleBars = /* @__PURE__ */ new Set();
18192
18195
  _clefsPerTrack = /* @__PURE__ */ new Map();
18193
18196
  _keySignatures = /* @__PURE__ */ new Map();
18194
18197
  _beatTextChunksByTrack = /* @__PURE__ */ new Map();
18195
18198
  _directionLookup = /* @__PURE__ */ new Map();
18196
18199
  _initialTempo;
18200
+ _stringEncoding = "";
18197
18201
  get name() {
18198
18202
  return "Guitar Pro 3-5";
18199
18203
  }
18200
18204
  readScore() {
18201
18205
  this._directionLookup.clear();
18206
+ if (this.settings.importer.encoding !== "utf-8") this._stringEncoding = this.settings.importer.encoding;
18207
+ else this._stringEncoding = this.settings.importer.gp3To5encoding;
18202
18208
  this.readVersion();
18203
18209
  this._score = new Score();
18204
18210
  this.readScoreInformation();
@@ -18208,7 +18214,7 @@ class Gp3To5Importer extends ScoreImporter {
18208
18214
  this._initialTempo = Automation.buildTempoAutomation(false, 0, 0, 0);
18209
18215
  if (this._versionNumber >= 500) {
18210
18216
  this.readPageSetup();
18211
- this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18217
+ this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18212
18218
  }
18213
18219
  this._initialTempo.value = IOHelper.readInt32LE(this.data);
18214
18220
  if (this._versionNumber >= 510) GpBinaryHelpers.gpReadBool(this.data);
@@ -18267,7 +18273,7 @@ class Gp3To5Importer extends ScoreImporter {
18267
18273
  directionsList.push(direction);
18268
18274
  }
18269
18275
  readVersion() {
18270
- let version = GpBinaryHelpers.gpReadStringByteLength(this.data, 30, this.settings.importer.encoding);
18276
+ let version = GpBinaryHelpers.gpReadStringByteLength(this.data, 30, this._stringEncoding);
18271
18277
  if (!version.startsWith(Gp3To5Importer._versionString)) throw new UnsupportedFormatError("Unsupported format");
18272
18278
  version = version.substr(Gp3To5Importer._versionString.length + 1);
18273
18279
  const dot = version.indexOf(String.fromCharCode(46));
@@ -18275,21 +18281,21 @@ class Gp3To5Importer extends ScoreImporter {
18275
18281
  Logger.debug(this.name, `Guitar Pro version ${version} detected`);
18276
18282
  }
18277
18283
  readScoreInformation() {
18278
- this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18279
- this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18280
- this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18281
- this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18282
- this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18283
- this._score.music = this._versionNumber >= 500 ? GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize) : this._score.words;
18284
- this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18285
- this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18286
- this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18284
+ this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18285
+ this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18286
+ this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18287
+ this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18288
+ this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18289
+ this._score.music = this._versionNumber >= 500 ? GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize) : this._score.words;
18290
+ this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18291
+ this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18292
+ this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18287
18293
  const noticeLines = IOHelper.readInt32LE(this.data);
18288
18294
  this._ensureLoopBoundary(noticeLines, Gp3To5Importer._maxNoticeLines, "notice line count");
18289
18295
  let notice = "";
18290
18296
  for (let i = 0; i < noticeLines; i++) {
18291
18297
  if (i > 0) notice += "\r\n";
18292
- notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize)?.toString();
18298
+ notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize)?.toString();
18293
18299
  }
18294
18300
  this._score.notices = notice;
18295
18301
  }
@@ -18307,7 +18313,7 @@ class Gp3To5Importer extends ScoreImporter {
18307
18313
  for (let i = 0; i < 5; i++) {
18308
18314
  const lyrics = new Lyrics();
18309
18315
  lyrics.startBar = IOHelper.readInt32LE(this.data) - 1;
18310
- lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18316
+ lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18311
18317
  this._lyrics.push(lyrics);
18312
18318
  }
18313
18319
  }
@@ -18315,37 +18321,43 @@ class Gp3To5Importer extends ScoreImporter {
18315
18321
  this.data.skip(28);
18316
18322
  const flags = IOHelper.readInt16LE(this.data);
18317
18323
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).isVisible = (flags & 1) !== 0;
18318
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18324
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18319
18325
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).isVisible = (flags & 2) !== 0;
18320
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18326
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18321
18327
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).isVisible = (flags & 4) !== 0;
18322
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18328
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18323
18329
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).isVisible = (flags & 8) !== 0;
18324
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18330
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18325
18331
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).isVisible = (flags & 16) !== 0;
18326
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18332
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18327
18333
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).isVisible = (flags & 32) !== 0;
18328
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18334
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18329
18335
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).isVisible = (flags & 64) !== 0;
18330
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18336
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18331
18337
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).isVisible = (flags & 128) !== 0;
18332
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18338
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18333
18339
  ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).isVisible = (flags & 128) !== 0;
18334
- ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18335
- GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18340
+ ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).template = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18341
+ GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18336
18342
  }
18337
18343
  readPlaybackInfos() {
18338
- this._playbackInfos = [];
18339
- let channel = 0;
18340
- for (let i = 0; i < 64; i++) {
18341
- const info = new PlaybackInformation();
18342
- info.primaryChannel = channel++;
18343
- info.secondaryChannel = channel++;
18344
- info.program = IOHelper.readInt32LE(this.data);
18345
- info.volume = this.data.readByte();
18346
- info.balance = this.data.readByte();
18347
- this.data.skip(6);
18348
- this._playbackInfos.push(info);
18344
+ this._midiChannelInfo = [];
18345
+ for (let port = 0; port < 4; port++) {
18346
+ const portInfo = [];
18347
+ this._midiChannelInfo.push(portInfo);
18348
+ for (let channel = 0; channel < 16; channel++) {
18349
+ const info = {
18350
+ program: IOHelper.readInt32LE(this.data),
18351
+ volume: this.data.readByte(),
18352
+ balance: this.data.readByte(),
18353
+ chorus: this.data.readByte(),
18354
+ reverb: this.data.readByte(),
18355
+ phase: this.data.readByte(),
18356
+ tremolo: this.data.readByte()
18357
+ };
18358
+ this.data.skip(2);
18359
+ portInfo.push(info);
18360
+ }
18349
18361
  }
18350
18362
  }
18351
18363
  readMasterBars() {
@@ -18382,7 +18394,7 @@ class Gp3To5Importer extends ScoreImporter {
18382
18394
  }
18383
18395
  if ((flags & 32) !== 0) {
18384
18396
  const section = new Section();
18385
- section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18397
+ section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18386
18398
  section.marker = "";
18387
18399
  GpBinaryHelpers.gpReadColor(this.data, false);
18388
18400
  newMasterBar.section = section;
@@ -18421,7 +18433,7 @@ class Gp3To5Importer extends ScoreImporter {
18421
18433
  this._score.addTrack(newTrack);
18422
18434
  const mainStaff = newTrack.staves[0];
18423
18435
  const flags = this.data.readByte();
18424
- newTrack.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 40, this.settings.importer.encoding);
18436
+ newTrack.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 40, this._stringEncoding);
18425
18437
  if ((flags & 1) !== 0) mainStaff.isPercussion = true;
18426
18438
  if (this._versionNumber >= 500) newTrack.isVisibleOnMultiTrack = (flags & 8) !== 0;
18427
18439
  if (this._score.stylesheet.perTrackDisplayTuning === null) this._score.stylesheet.perTrackDisplayTuning = /* @__PURE__ */ new Map();
@@ -18433,19 +18445,22 @@ class Gp3To5Importer extends ScoreImporter {
18433
18445
  if (stringCount > i) tuning.push(stringTuning);
18434
18446
  }
18435
18447
  mainStaff.stringTuning.tunings = tuning;
18436
- const port = IOHelper.readInt32LE(this.data);
18437
- const index = IOHelper.readInt32LE(this.data) - 1;
18448
+ const port = IOHelper.readInt32LE(this.data) - 1;
18449
+ const channel = IOHelper.readInt32LE(this.data) - 1;
18438
18450
  const effectChannel = IOHelper.readInt32LE(this.data) - 1;
18439
18451
  this.data.skip(4);
18440
- if (index >= 0 && index < this._playbackInfos.length) {
18441
- const info = this._playbackInfos[index];
18442
- info.port = port;
18443
- info.isSolo = (flags & 16) !== 0;
18444
- info.isMute = (flags & 32) !== 0;
18445
- info.secondaryChannel = effectChannel;
18446
- if (GeneralMidi.isGuitar(info.program)) mainStaff.displayTranspositionPitch = -12;
18447
- newTrack.playbackInfo = info;
18448
- }
18452
+ const mainMidiChannelInfo = this._midiChannelInfo[port][channel];
18453
+ const trackPlaybackInfo = new PlaybackInformation();
18454
+ trackPlaybackInfo.volume = mainMidiChannelInfo.volume;
18455
+ trackPlaybackInfo.balance = mainMidiChannelInfo.balance;
18456
+ trackPlaybackInfo.port = port;
18457
+ trackPlaybackInfo.program = mainMidiChannelInfo.program;
18458
+ trackPlaybackInfo.primaryChannel = channel;
18459
+ trackPlaybackInfo.secondaryChannel = effectChannel;
18460
+ trackPlaybackInfo.isSolo = (flags & 16) !== 0;
18461
+ trackPlaybackInfo.isMute = (flags & 32) !== 0;
18462
+ if (GeneralMidi.isGuitar(trackPlaybackInfo.program)) mainStaff.displayTranspositionPitch = -12;
18463
+ newTrack.playbackInfo = trackPlaybackInfo;
18449
18464
  mainStaff.capo = IOHelper.readInt32LE(this.data);
18450
18465
  newTrack.color = GpBinaryHelpers.gpReadColor(this.data, false);
18451
18466
  if (this._versionNumber >= 500) {
@@ -18469,8 +18484,8 @@ class Gp3To5Importer extends ScoreImporter {
18469
18484
  this._readRseBank();
18470
18485
  if (this._versionNumber >= 510) {
18471
18486
  this.data.skip(4);
18472
- GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18473
- GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18487
+ GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18488
+ GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18474
18489
  }
18475
18490
  } else if (tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) this._clefsPerTrack.set(newTrack.index, Clef.F4);
18476
18491
  else this._clefsPerTrack.set(newTrack.index, Clef.G2);
@@ -18573,7 +18588,7 @@ class Gp3To5Importer extends ScoreImporter {
18573
18588
  if ((flags & 2) !== 0) this.readChord(newBeat);
18574
18589
  const beatTextAsLyrics = this.settings.importer.beatTextAsLyrics && track.index !== this._lyricsTrack;
18575
18590
  if ((flags & 4) !== 0) {
18576
- const text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18591
+ const text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18577
18592
  if (beatTextAsLyrics) {
18578
18593
  const lyrics = new Lyrics();
18579
18594
  lyrics.text = text.trim();
@@ -18620,7 +18635,7 @@ class Gp3To5Importer extends ScoreImporter {
18620
18635
  const chordId = ModelUtils.newGuid();
18621
18636
  if (this._versionNumber >= 500) {
18622
18637
  this.data.skip(17);
18623
- chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this.settings.importer.encoding);
18638
+ chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this._stringEncoding);
18624
18639
  this.data.skip(4);
18625
18640
  chord.firstFret = IOHelper.readInt32LE(this.data);
18626
18641
  for (let i = 0; i < 7; i++) {
@@ -18634,7 +18649,7 @@ class Gp3To5Importer extends ScoreImporter {
18634
18649
  this.data.skip(26);
18635
18650
  } else if (this.data.readByte() !== 0) if (this._versionNumber >= 400) {
18636
18651
  this.data.skip(16);
18637
- chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this.settings.importer.encoding);
18652
+ chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this._stringEncoding);
18638
18653
  this.data.skip(4);
18639
18654
  chord.firstFret = IOHelper.readInt32LE(this.data);
18640
18655
  for (let i = 0; i < 7; i++) {
@@ -18648,7 +18663,7 @@ class Gp3To5Importer extends ScoreImporter {
18648
18663
  this.data.skip(26);
18649
18664
  } else {
18650
18665
  this.data.skip(25);
18651
- chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 34, this.settings.importer.encoding);
18666
+ chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 34, this._stringEncoding);
18652
18667
  chord.firstFret = IOHelper.readInt32LE(this.data);
18653
18668
  for (let i = 0; i < 6; i++) {
18654
18669
  const fret = IOHelper.readInt32LE(this.data);
@@ -18658,7 +18673,7 @@ class Gp3To5Importer extends ScoreImporter {
18658
18673
  }
18659
18674
  else {
18660
18675
  const strings = this._versionNumber >= 406 ? 7 : 6;
18661
- chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18676
+ chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18662
18677
  chord.firstFret = IOHelper.readInt32LE(this.data);
18663
18678
  if (chord.firstFret > 0) for (let i = 0; i < strings; i++) {
18664
18679
  const fret = IOHelper.readInt32LE(this.data);
@@ -18778,7 +18793,7 @@ class Gp3To5Importer extends ScoreImporter {
18778
18793
  const reverb = IOHelper.readSInt8(this.data);
18779
18794
  const phaser = IOHelper.readSInt8(this.data);
18780
18795
  const tremolo = IOHelper.readSInt8(this.data);
18781
- if (this._versionNumber >= 500) tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18796
+ if (this._versionNumber >= 500) tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18782
18797
  tableChange.tempo = IOHelper.readInt32LE(this.data);
18783
18798
  if (tableChange.volume >= 0) this.data.readByte();
18784
18799
  if (tableChange.balance >= 0) this.data.readByte();
@@ -18797,8 +18812,8 @@ class Gp3To5Importer extends ScoreImporter {
18797
18812
  else if (wahType >= 0) beat.wahPedal = WahPedal.Open;
18798
18813
  }
18799
18814
  if (this._versionNumber >= 510) {
18800
- GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18801
- GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
18815
+ GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18816
+ GpBinaryHelpers.gpReadStringIntByte(this.data, this._stringEncoding, this.settings.importer.maxDecodingBufferSize);
18802
18817
  }
18803
18818
  if (tableChange.volume >= 0) {
18804
18819
  const volumeAutomation = new Automation();
@@ -22548,6 +22563,10 @@ class InstrumentArticulationWithPlaybackInfo extends InstrumentArticulation {
22548
22563
  * The balance to use when playing the note (-1 if using the default track balance).
22549
22564
  */
22550
22565
  outputBalance = -1;
22566
+ /**
22567
+ * Whether this instrument was declared as an unpitched/percussion sound via `<midi-unpitched>`.
22568
+ */
22569
+ isUnpitched = false;
22551
22570
  }
22552
22571
  /**
22553
22572
  * @internal
@@ -22592,6 +22611,9 @@ class TrackInfo {
22592
22611
  this.track.percussionArticulations.push(newArticulation);
22593
22612
  return index;
22594
22613
  }
22614
+ isUnpitchedInstrument(instrumentId) {
22615
+ return this.instruments.has(instrumentId) && this.instruments.get(instrumentId).isUnpitched;
22616
+ }
22595
22617
  }
22596
22618
  /**
22597
22619
  * @internal
@@ -22906,6 +22928,7 @@ class MusicXmlImporter extends ScoreImporter {
22906
22928
  break;
22907
22929
  case "midi-unpitched":
22908
22930
  articulation.outputMidiNumber = Number.parseInt(c.innerText, 10) - 1;
22931
+ articulation.isUnpitched = true;
22909
22932
  break;
22910
22933
  case "volume":
22911
22934
  articulation.outputVolume = MusicXmlImporter._interpolatePercent(Number.parseFloat(c.innerText));
@@ -24324,11 +24347,15 @@ class MusicXmlImporter extends ScoreImporter {
24324
24347
  note.isVisible = noteIsVisible;
24325
24348
  if (note.percussionArticulation >= 0) return;
24326
24349
  const trackInfo = this._indexToTrackInfo.get(track.index);
24327
- if (instrumentId !== null) note.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId, note);
24328
- else if (note.beat.voice.bar.staff.isPercussion && isPitched) {
24350
+ if (!isPitched) {
24351
+ note.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId ?? "", note);
24352
+ return;
24353
+ }
24354
+ if (instrumentId !== null && trackInfo.isUnpitchedInstrument(instrumentId)) note.percussionArticulation = trackInfo.getOrCreateArticulation(instrumentId, note);
24355
+ else if (note.beat.voice.bar.staff.isPercussion) {
24329
24356
  const knownArticulation = PercussionMapper.getArticulationById(note.displayValue);
24330
24357
  if (knownArticulation) note.percussionArticulation = knownArticulation.id;
24331
- } else if (!isPitched) note.percussionArticulation = trackInfo.getOrCreateArticulation("", note);
24358
+ }
24332
24359
  }
24333
24360
  _parsePlay(element, note) {
24334
24361
  for (const c of element.childElements()) switch (c.localName) {
@@ -28832,11 +28859,21 @@ class ImporterSettings {
28832
28859
  *
28833
28860
  * * Guitar Pro 7
28834
28861
  * * Guitar Pro 6
28835
- * * Guitar Pro 3-5
28836
28862
  * * MusicXML
28837
28863
  */
28838
28864
  encoding = "utf-8";
28839
28865
  /**
28866
+ * The text encoding to use when decoding strings within GuitarPro3-5 files.
28867
+ * @since 1.9.0
28868
+ * @defaultValue `windows-1252`
28869
+ * @category Importer
28870
+ * @remarks
28871
+ * Guitar Pro 3-5 encode strings as system specific ANSI encoding, typically Windows-1252 in western system cultures.
28872
+ * This is different to the other typically used utf-8 encoding.
28873
+ * Via this setting the Guitar Pro 3-5 specific decoding can be used.
28874
+ */
28875
+ gp3To5encoding = "windows-1252";
28876
+ /**
28840
28877
  * If part-groups should be merged into a single track (MusicXML).
28841
28878
  * @since 0.9.6
28842
28879
  * @defaultValue `false`
@@ -29160,11 +29197,45 @@ class PlayerSettings {
29160
29197
  * @since 0.9.7
29161
29198
  * @defaultValue `true`
29162
29199
  * @category Player
29200
+ * @json_read_only
29163
29201
  * @remarks
29164
29202
  * This setting configures whether alphaTab provides the default user interaction features like selection of the playback range and "seek on click".
29165
29203
  * By default users can select the desired playback range with the mouse and also jump to individual beats by click. This behavior can be contolled with this setting.
29204
+ * @deprecated Use {@link enableSeekToClick} and {@link enablePlaybackRangeSelection} individually
29205
+ */
29206
+ get enableUserInteraction() {
29207
+ return this.enableSeekToClick || this.enablePlaybackRangeSelection;
29208
+ }
29209
+ /**
29210
+ * @deprecated Use {@link enableSeekToClick} and {@link enablePlaybackRangeSelection} individually
29211
+ */
29212
+ set enableUserInteraction(value) {
29213
+ this.enableSeekToClick = value;
29214
+ this.enablePlaybackRangeSelection = value;
29215
+ this.resetPlaybackRangeOnClick = value;
29216
+ }
29217
+ /**
29218
+ * Whether the a click on the music sheet triggers a player seek to the note/beat at the
29219
+ * clicked location.
29220
+ * @since 1.9.0
29221
+ * @defaultValue `true`
29222
+ * @category Player
29223
+ */
29224
+ enableSeekToClick = true;
29225
+ /**
29226
+ * Whether user click and drag results in a selection defining the playback range.
29227
+ * @since 1.9.0
29228
+ * @defaultValue `true`
29229
+ * @category Player
29230
+ */
29231
+ enablePlaybackRangeSelection = true;
29232
+ /**
29233
+ * Whether a simple click (no range drag) should reset the current playback range.
29234
+ * @since 1.9.0
29235
+ * @defaultValue `true`
29236
+ * @category Player
29166
29237
  */
29167
- enableUserInteraction = true;
29238
+ resetPlaybackRangeOnClick = true;
29168
29239
  /**
29169
29240
  * The X-offset to add when scrolling.
29170
29241
  * @since 0.9.6
@@ -30188,6 +30259,7 @@ class ImporterSettingsSerializer {
30188
30259
  if (!obj) return null;
30189
30260
  const o = /* @__PURE__ */ new Map();
30190
30261
  o.set("encoding", obj.encoding);
30262
+ o.set("gp3to5encoding", obj.gp3To5encoding);
30191
30263
  o.set("mergepartgroupsinmusicxml", obj.mergePartGroupsInMusicXml);
30192
30264
  o.set("beattextaslyrics", obj.beatTextAsLyrics);
30193
30265
  o.set("maxdecodingbuffersize", obj.maxDecodingBufferSize);
@@ -30198,6 +30270,9 @@ class ImporterSettingsSerializer {
30198
30270
  case "encoding":
30199
30271
  obj.encoding = v;
30200
30272
  return true;
30273
+ case "gp3to5encoding":
30274
+ obj.gp3To5encoding = v;
30275
+ return true;
30201
30276
  case "mergepartgroupsinmusicxml":
30202
30277
  obj.mergePartGroupsInMusicXml = v;
30203
30278
  return true;
@@ -30317,7 +30392,9 @@ class PlayerSettingsSerializer {
30317
30392
  o.set("enablecursor", obj.enableCursor);
30318
30393
  o.set("enableanimatedbeatcursor", obj.enableAnimatedBeatCursor);
30319
30394
  o.set("enableelementhighlighting", obj.enableElementHighlighting);
30320
- o.set("enableuserinteraction", obj.enableUserInteraction);
30395
+ o.set("enableseektoclick", obj.enableSeekToClick);
30396
+ o.set("enableplaybackrangeselection", obj.enablePlaybackRangeSelection);
30397
+ o.set("resetplaybackrangeonclick", obj.resetPlaybackRangeOnClick);
30321
30398
  o.set("scrolloffsetx", obj.scrollOffsetX);
30322
30399
  o.set("scrolloffsety", obj.scrollOffsetY);
30323
30400
  o.set("scrollmode", obj.scrollMode);
@@ -30360,6 +30437,15 @@ class PlayerSettingsSerializer {
30360
30437
  case "enableuserinteraction":
30361
30438
  obj.enableUserInteraction = v;
30362
30439
  return true;
30440
+ case "enableseektoclick":
30441
+ obj.enableSeekToClick = v;
30442
+ return true;
30443
+ case "enableplaybackrangeselection":
30444
+ obj.enablePlaybackRangeSelection = v;
30445
+ return true;
30446
+ case "resetplaybackrangeonclick":
30447
+ obj.resetPlaybackRangeOnClick = v;
30448
+ return true;
30363
30449
  case "scrolloffsetx":
30364
30450
  obj.scrollOffsetX = v;
30365
30451
  return true;
@@ -45358,6 +45444,16 @@ class BeatContainerGlyphBase extends Glyph {
45358
45444
  scaleToWidth(beatWidth) {
45359
45445
  this.width = beatWidth;
45360
45446
  }
45447
+ /**
45448
+ * Repositions this beat so its {@link onTimeX} anchor lands at `target`, used for the
45449
+ * centered full-bar note/rest (see {@link BarLayoutingInfo.isCenteredFullBar}). The default
45450
+ * shifts the whole container, matching the regular (non-centered) positioning formula.
45451
+ * {@link BeatContainerGlyph} overrides this to shift only its ink, keeping `x`/`width`
45452
+ * spanning the full bar so bounds lookups and skyline emission stay correct.
45453
+ */
45454
+ applyCenterOffset(target) {
45455
+ this.x = target - this.onTimeX;
45456
+ }
45361
45457
  }
45362
45458
  /**
45363
45459
  * @internal
@@ -45461,13 +45557,28 @@ class BeatContainerGlyph extends BeatContainerGlyphBase {
45461
45557
  this.preNotes.renderer = this.renderer;
45462
45558
  this.preNotes.container = this;
45463
45559
  this.preNotes.doLayout();
45464
- this.onNotes.x = this.preNotes.x + this.preNotes.width;
45560
+ this._layoutOnsetX();
45465
45561
  this.onNotes.renderer = this.renderer;
45466
45562
  this.onNotes.container = this;
45467
45563
  this.onNotes.doLayout();
45468
45564
  this.createBeatTies();
45469
45565
  this.updateWidth();
45470
45566
  }
45567
+ /**
45568
+ * Resets `preNotes.x`/`onNotes.x` to their natural (un-centered) baseline.
45569
+ * Shared by {@link doLayout} and {@link applyCenterOffset} so the latter can be called
45570
+ * repeatedly (once per `_scaleToForce` pass) without compounding a previous offset.
45571
+ */
45572
+ _layoutOnsetX() {
45573
+ this.preNotes.x = 0;
45574
+ this.onNotes.x = this.preNotes.x + this.preNotes.width;
45575
+ }
45576
+ applyCenterOffset(target) {
45577
+ this._layoutOnsetX();
45578
+ const offset = target - this.onTimeX;
45579
+ this.preNotes.x = offset;
45580
+ this.onNotes.x = this.preNotes.x + this.preNotes.width;
45581
+ }
45471
45582
  createBeatTies() {
45472
45583
  let i = this.beat.notes.length - 1;
45473
45584
  while (i >= 0) this.createTies(this.beat.notes[i--]);
@@ -48557,7 +48668,7 @@ class AlphaTabApiBase {
48557
48668
  }
48558
48669
  _onBeatMouseDown(originalEvent, beat) {
48559
48670
  if (this._isDestroyed) return;
48560
- if (this._hasCursor && this.settings.player.enableUserInteraction) {
48671
+ if (this._hasCursor && this.settings.player.enablePlaybackRangeSelection) {
48561
48672
  this._selectionStart = { beat };
48562
48673
  this._selectionEnd = void 0;
48563
48674
  }
@@ -48573,10 +48684,12 @@ class AlphaTabApiBase {
48573
48684
  }
48574
48685
  _onBeatMouseMove(originalEvent, beat) {
48575
48686
  if (this._isDestroyed) return;
48576
- if (this.settings.player.enableUserInteraction) {
48687
+ if (this.settings.player.enablePlaybackRangeSelection) {
48577
48688
  if (!this._selectionEnd || this._selectionEnd.beat !== beat) {
48578
- this._selectionEnd = { beat };
48579
- this._cursorSelectRange(this._selectionStart, this._selectionEnd);
48689
+ if (this._selectionStart?.beat !== beat) {
48690
+ this._selectionEnd = { beat };
48691
+ this._cursorSelectRange(this._selectionStart, this._selectionEnd);
48692
+ }
48580
48693
  }
48581
48694
  }
48582
48695
  this.beatMouseMove.trigger(beat);
@@ -48589,11 +48702,30 @@ class AlphaTabApiBase {
48589
48702
  }
48590
48703
  _onBeatMouseUp(originalEvent, beat) {
48591
48704
  if (this._isDestroyed) return;
48592
- if (this._hasCursor && this.settings.player.enableUserInteraction) this.applyPlaybackRangeFromHighlight();
48705
+ if (this._hasCursor) {
48706
+ let shouldSeekToBeat = beat && this.settings.player.enableSeekToClick;
48707
+ if (this.settings.player.enablePlaybackRangeSelection) {
48708
+ if (this._internalApplyPlaybackRangeFromHighlight()) shouldSeekToBeat = false;
48709
+ }
48710
+ if (shouldSeekToBeat) this._seekToBeat(beat);
48711
+ }
48593
48712
  this.beatMouseUp.trigger(beat);
48594
48713
  this.uiFacade.triggerEvent(this.container, "beatMouseUp", beat, originalEvent);
48595
48714
  this._isBeatMouseDown = false;
48596
48715
  }
48716
+ _seekToBeat(beat) {
48717
+ const tickCache = this._tickCache;
48718
+ if (!tickCache) return;
48719
+ this._currentBeat = null;
48720
+ let beatTick = tickCache.getMasterBarStart(beat.voice.bar.masterBar) + (tickCache.getRelativeBeatPlaybackRange(beat)?.startTick ?? beat.playbackStart);
48721
+ const playbackRange = this.playbackRange;
48722
+ if (playbackRange) {
48723
+ if (beatTick < playbackRange.startTick) beatTick = playbackRange.startTick;
48724
+ else if (beatTick > playbackRange.endTick) beatTick = playbackRange.endTick;
48725
+ }
48726
+ if (this._player.state === PlayerState.Paused) this._cursorUpdateTick(beatTick, false, 1);
48727
+ this.tickPosition = beatTick;
48728
+ }
48597
48729
  _onNoteMouseUp(originalEvent, note) {
48598
48730
  if (this._isDestroyed) return;
48599
48731
  this.noteMouseUp.trigger(note);
@@ -48615,7 +48747,7 @@ class AlphaTabApiBase {
48615
48747
  _setupClickHandling() {
48616
48748
  this.canvasElement.mouseDown.on((e) => {
48617
48749
  if (!e.isLeftMouseButton) return;
48618
- if (this.settings.player.enableUserInteraction) e.preventDefault();
48750
+ if (this.settings.player.enablePlaybackRangeSelection || this.settings.player.enableSeekToClick) e.preventDefault();
48619
48751
  const relX = e.getX(this.canvasElement);
48620
48752
  const relY = e.getY(this.canvasElement);
48621
48753
  const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
@@ -48642,7 +48774,7 @@ class AlphaTabApiBase {
48642
48774
  });
48643
48775
  this.canvasElement.mouseUp.on((e) => {
48644
48776
  if (!this._isBeatMouseDown) return;
48645
- if (this.settings.player.enableUserInteraction) e.preventDefault();
48777
+ if (this.settings.player.enablePlaybackRangeSelection || this.settings.player.enableSeekToClick) e.preventDefault();
48646
48778
  const relX = e.getX(this.canvasElement);
48647
48779
  const relY = e.getY(this.canvasElement);
48648
48780
  const beat = this._renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null;
@@ -48653,7 +48785,7 @@ class AlphaTabApiBase {
48653
48785
  } else this._onNoteMouseUp(e, null);
48654
48786
  });
48655
48787
  this._renderer.postRenderFinished.on(() => {
48656
- if (!this._selectionStart || !this._hasCursor || !this.settings.player.enableUserInteraction) return;
48788
+ if (!this._selectionStart || !this._hasCursor || !this.settings.player.enablePlaybackRangeSelection) return;
48657
48789
  this._cursorSelectRange(this._selectionStart, this._selectionEnd);
48658
48790
  });
48659
48791
  }
@@ -48745,6 +48877,9 @@ class AlphaTabApiBase {
48745
48877
  * ```
48746
48878
  */
48747
48879
  applyPlaybackRangeFromHighlight() {
48880
+ this._internalApplyPlaybackRangeFromHighlight();
48881
+ }
48882
+ _internalApplyPlaybackRangeFromHighlight() {
48748
48883
  if (this._selectionEnd) {
48749
48884
  const startTick = this._tickCache?.getBeatStart(this._selectionStart.beat) ?? this._selectionStart.beat.absolutePlaybackStart;
48750
48885
  if ((this._tickCache?.getBeatStart(this._selectionEnd.beat) ?? this._selectionEnd.beat.absolutePlaybackStart) < startTick) {
@@ -48752,27 +48887,32 @@ class AlphaTabApiBase {
48752
48887
  this._selectionStart = this._selectionEnd;
48753
48888
  this._selectionEnd = t;
48754
48889
  }
48755
- }
48890
+ } else if (!this.settings.player.resetPlaybackRangeOnClick) return false;
48756
48891
  if (this._selectionStart && this._tickCache) {
48757
48892
  const tickCache = this._tickCache;
48758
48893
  const realStartMasterBarStart = tickCache.getMasterBarStart(this._selectionStart.beat.voice.bar.masterBar);
48759
48894
  const startBeatPlaybackStart = tickCache.getRelativeBeatPlaybackRange(this._selectionStart.beat)?.startTick ?? this._selectionStart.beat.playbackStart;
48760
- this._currentBeat = null;
48761
- if (this._player.state === PlayerState.Paused) this._cursorUpdateTick(realStartMasterBarStart + startBeatPlaybackStart, false, 1);
48762
- this.tickPosition = realStartMasterBarStart + startBeatPlaybackStart;
48895
+ let seekToStart = this.settings.player.enableSeekToClick;
48763
48896
  if (this._selectionEnd && this._selectionStart.beat !== this._selectionEnd.beat) {
48897
+ seekToStart = true;
48764
48898
  const realEndMasterBarStart = tickCache.getMasterBarStart(this._selectionEnd.beat.voice.bar.masterBar);
48765
48899
  const endBeatPlaybackEnd = tickCache.getRelativeBeatPlaybackRange(this._selectionEnd.beat)?.endTick ?? this._selectionEnd.beat.playbackStart + this._selectionEnd.beat.playbackDuration;
48766
48900
  const range = new PlaybackRange();
48767
48901
  range.startTick = realStartMasterBarStart + startBeatPlaybackStart;
48768
48902
  range.endTick = realEndMasterBarStart + endBeatPlaybackEnd - 50;
48769
48903
  this.playbackRange = range;
48770
- } else {
48904
+ } else if (this.settings.player.resetPlaybackRangeOnClick) {
48771
48905
  this._selectionStart = void 0;
48772
48906
  this.playbackRange = null;
48773
48907
  this._cursorSelectRange(this._selectionStart, this._selectionEnd);
48774
48908
  }
48909
+ if (seekToStart) {
48910
+ this._currentBeat = null;
48911
+ if (this._player.state === PlayerState.Paused) this._cursorUpdateTick(realStartMasterBarStart + startBeatPlaybackStart, false, 1);
48912
+ this.tickPosition = realStartMasterBarStart + startBeatPlaybackStart;
48913
+ }
48775
48914
  }
48915
+ return true;
48776
48916
  }
48777
48917
  /**
48778
48918
  * Clears the highlight markers marking the currently selected playback range.
@@ -50058,7 +50198,8 @@ class AlphaTabApi extends AlphaTabApiBase {
50058
50198
  settings.player.enableCursor = false;
50059
50199
  settings.player.playerMode = PlayerMode.Disabled;
50060
50200
  settings.player.enableElementHighlighting = false;
50061
- settings.player.enableUserInteraction = false;
50201
+ settings.player.enableSeekToClick = false;
50202
+ settings.player.enablePlaybackRangeSelection = false;
50062
50203
  settings.player.soundFont = null;
50063
50204
  settings.display.scale = .8;
50064
50205
  settings.display.stretchForce = .8;
@@ -50868,7 +51009,9 @@ class AlphaSynthWebWorker {
50868
51009
  }
50869
51010
  handleMessage(e) {
50870
51011
  const data = e.data;
50871
- switch (data.cmd) {
51012
+ const cmd = data.cmd;
51013
+ if (!cmd) return;
51014
+ switch (cmd) {
50872
51015
  case "alphaSynth.initialize":
50873
51016
  AlphaSynthWorkerSynthOutput.preferredSampleRate = data.sampleRate;
50874
51017
  Logger.logLevel = data.logLevel;
@@ -50962,7 +51105,7 @@ class AlphaSynthWebWorker {
50962
51105
  this._player.applyTranspositionPitches(data.transpositionPitches);
50963
51106
  break;
50964
51107
  }
50965
- if (data.cmd.startsWith("alphaSynth.exporter")) this._handleExporterMessage(e);
51108
+ if (cmd.startsWith("alphaSynth.exporter")) this._handleExporterMessage(e);
50966
51109
  }
50967
51110
  _handleExporterMessage(ev) {
50968
51111
  const data = ev.data;
@@ -54466,9 +54609,35 @@ class MultiVoiceContainerGlyph extends Glyph {
54466
54609
  const force = this.renderer.layoutingInfo.spaceToForce(width);
54467
54610
  this._scaleToForce(force, true);
54468
54611
  }
54612
+ /**
54613
+ * `true` when every voice/track/staff contributes exactly one beat, of uniform duration,
54614
+ * spanning the bar's entire duration - i.e. a single full-bar note/rest. Common engraving
54615
+ * practice centers such notes horizontally within the bar rather than anchoring them right
54616
+ * after the pre-beat content (Behind Bars, p. 41; see #2464).
54617
+ */
54618
+ _isCenteredFullBar() {
54619
+ const masterBar = this.renderer.bar.masterBar;
54620
+ const layoutingInfo = this.renderer.layoutingInfo;
54621
+ const springs = layoutingInfo.springs;
54622
+ if (springs.size !== 1 || !springs.has(masterBar.start) || layoutingInfo.allGraceRods.size > 0) return false;
54623
+ const spring = springs.get(masterBar.start);
54624
+ return spring.allDurations.size === 1 && spring.longestDuration === masterBar.calculateDuration();
54625
+ }
54469
54626
  /** `emit=false`: positioning-only path; final skyline emission runs later via {@link scaleToWidth}. */
54470
54627
  _scaleToForce(force, emit) {
54471
54628
  this.width = this.renderer.layoutingInfo.calculateVoiceWidth(force);
54629
+ if (this._isCenteredFullBar()) {
54630
+ const barNumberWidth = this.renderer.barNumberWidth;
54631
+ const target = (this.width - barNumberWidth) / 2;
54632
+ for (const beatGlyphs of this.beatGlyphs.values()) {
54633
+ const soleBeatGlyph = beatGlyphs[0];
54634
+ soleBeatGlyph.x = 0;
54635
+ soleBeatGlyph.applyCenterOffset(target);
54636
+ soleBeatGlyph.scaleToWidth(this.width);
54637
+ if (emit) this._emitBeatContainerSkyline(soleBeatGlyph);
54638
+ }
54639
+ return;
54640
+ }
54472
54641
  const positions = this.renderer.layoutingInfo.buildOnTimePositions(force);
54473
54642
  for (const beatGlyphs of this.beatGlyphs.values()) for (let i = 0, j = beatGlyphs.length; i < j; i++) {
54474
54643
  const currentBeatGlyph = beatGlyphs[i];
@@ -60790,14 +60959,22 @@ class LineBarRenderer extends BarRendererBase {
60790
60959
  calculateBeamY(h, x) {
60791
60960
  return this.calculateBeamYWithDirection(h, x, this.getBeamDirection(h));
60792
60961
  }
60962
+ barNumberGlyph;
60963
+ get barNumberWidth() {
60964
+ return this.barNumberGlyph?.width ?? 0;
60965
+ }
60793
60966
  createPreBeatGlyphs() {
60794
60967
  super.createPreBeatGlyphs();
60968
+ this.barNumberGlyph = void 0;
60795
60969
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
60796
60970
  this.createLinePreBeatGlyphs();
60797
60971
  let hasSpaceAfterStartGlyphs = false;
60798
60972
  if (this.index === 0) hasSpaceAfterStartGlyphs = this.createStartSpacing();
60799
- if (this.shouldCreateBarNumber()) this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-.5), this.bar.index + 1));
60800
- else if (!hasSpaceAfterStartGlyphs) this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
60973
+ if (this.shouldCreateBarNumber()) {
60974
+ const barNumberGlyph = new BarNumberGlyph(0, this.getLineHeight(-.5), this.bar.index + 1);
60975
+ this.barNumberGlyph = barNumberGlyph;
60976
+ this.addPreBeatGlyph(barNumberGlyph);
60977
+ } else if (!hasSpaceAfterStartGlyphs) this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
60801
60978
  }
60802
60979
  resolveClefDisplay() {
60803
60980
  return { isVisible: false };
@@ -64389,8 +64566,11 @@ class NumberedBarRenderer extends LineBarRenderer {
64389
64566
  if (this.index === 0 || this.bar.masterBar.isRepeatStart && this.staff.isCascadePrimary) this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
64390
64567
  this.createLinePreBeatGlyphs();
64391
64568
  const hasSpaceAfterStartGlyphs = this.createStartSpacing();
64392
- if (this.shouldCreateBarNumber()) this.addPreBeatGlyph(new BarNumberGlyph(0, this.getLineHeight(-.5), this.bar.index + 1));
64393
- else if (!hasSpaceAfterStartGlyphs) this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
64569
+ if (this.shouldCreateBarNumber()) {
64570
+ const barNumberGlyph = new BarNumberGlyph(0, this.getLineHeight(-.5), this.bar.index + 1);
64571
+ this.barNumberGlyph = barNumberGlyph;
64572
+ this.addPreBeatGlyph(barNumberGlyph);
64573
+ } else if (!hasSpaceAfterStartGlyphs) this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
64394
64574
  }
64395
64575
  createLinePreBeatGlyphs() {
64396
64576
  const timeSignatureDisplay = this.resolveTimeSignatureDisplay();
@@ -70035,8 +70215,8 @@ class GpifWriter {
70035
70215
  scoreNode.addElement("FirstPageFooter").setCData("");
70036
70216
  scoreNode.addElement("PageHeader").setCData("");
70037
70217
  scoreNode.addElement("PageFooter").setCData("");
70038
- scoreNode.addElement("ScoreSystemsDefaultLayout").setCData(score.defaultSystemsLayout.toString());
70039
- scoreNode.addElement("ScoreSystemsLayout").setCData(score.systemsLayout.join(" "));
70218
+ scoreNode.addElement("ScoreSystemsDefaultLayout").innerText = score.defaultSystemsLayout.toString();
70219
+ scoreNode.addElement("ScoreSystemsLayout").innerText = score.systemsLayout.join(" ");
70040
70220
  scoreNode.addElement("ScoreZoomPolicy").innerText = "Value";
70041
70221
  scoreNode.addElement("ScoreZoom").innerText = "1";
70042
70222
  scoreNode.addElement("MultiVoice").innerText = "1>";