@coderline/alphatab 1.6.0-alpha.1426 → 1.6.0-alpha.1428

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/alphaTab.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.6.0-alpha.1426 (develop, build 1426)
2
+ * alphaTab v1.6.0-alpha.1428 (develop, build 1428)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -1424,7 +1424,7 @@
1424
1424
  */
1425
1425
  this.modifiedTempo = 0;
1426
1426
  /**
1427
- * The uadio offset marking the position within the audio track in milliseconds.
1427
+ * The audio offset marking the position within the audio track in milliseconds.
1428
1428
  * This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
1429
1429
  */
1430
1430
  this.millisecondOffset = 0;
@@ -7305,6 +7305,61 @@
7305
7305
  this.tracks[i].finish(settings, sharedDataBag);
7306
7306
  }
7307
7307
  }
7308
+ /**
7309
+ * Applies the given list of {@link FlatSyncPoint} to this song.
7310
+ * @param syncPoints The list of sync points to apply.
7311
+ * @since 1.6.0
7312
+ */
7313
+ applyFlatSyncPoints(syncPoints) {
7314
+ for (const b of this.masterBars) {
7315
+ b.syncPoints = undefined;
7316
+ }
7317
+ for (const syncPoint of syncPoints) {
7318
+ const automation = new Automation();
7319
+ automation.ratioPosition = Math.min(1, Math.max(0, syncPoint.barPosition));
7320
+ automation.type = AutomationType.SyncPoint;
7321
+ automation.syncPointValue = new SyncPointData();
7322
+ automation.syncPointValue.modifiedTempo = syncPoint.modifiedTempo;
7323
+ automation.syncPointValue.millisecondOffset = syncPoint.millisecondOffset;
7324
+ automation.syncPointValue.barOccurence = syncPoint.barOccurence;
7325
+ if (syncPoint.barIndex < this.masterBars.length) {
7326
+ this.masterBars[syncPoint.barIndex].addSyncPoint(automation);
7327
+ }
7328
+ }
7329
+ for (const b of this.masterBars) {
7330
+ if (b.syncPoints) {
7331
+ b.syncPoints.sort((a, b) => {
7332
+ const occurence = a.syncPointValue.barOccurence - b.syncPointValue.barOccurence;
7333
+ if (occurence !== 0) {
7334
+ return occurence;
7335
+ }
7336
+ return a.ratioPosition - b.ratioPosition;
7337
+ });
7338
+ }
7339
+ }
7340
+ }
7341
+ /**
7342
+ * Exports all sync points in this song to a {@link FlatSyncPoint} list.
7343
+ * @since 1.6.0
7344
+ */
7345
+ exportFlatSyncPoints() {
7346
+ const syncPoints = [];
7347
+ for (const masterBar of this.masterBars) {
7348
+ const masterBarSyncPoints = masterBar.syncPoints;
7349
+ if (masterBarSyncPoints) {
7350
+ for (const syncPoint of masterBarSyncPoints) {
7351
+ syncPoints.push({
7352
+ barIndex: masterBar.index,
7353
+ barOccurence: syncPoint.syncPointValue.barOccurence,
7354
+ barPosition: syncPoint.ratioPosition,
7355
+ millisecondOffset: syncPoint.syncPointValue.millisecondOffset,
7356
+ modifiedTempo: syncPoint.syncPointValue.modifiedTempo
7357
+ });
7358
+ }
7359
+ }
7360
+ }
7361
+ return syncPoints;
7362
+ }
7308
7363
  }
7309
7364
 
7310
7365
  /**
@@ -13613,6 +13668,7 @@
13613
13668
  XmlNodeType[XmlNodeType["CDATA"] = 3] = "CDATA";
13614
13669
  XmlNodeType[XmlNodeType["Document"] = 4] = "Document";
13615
13670
  XmlNodeType[XmlNodeType["DocumentType"] = 5] = "DocumentType";
13671
+ XmlNodeType[XmlNodeType["Comment"] = 6] = "Comment";
13616
13672
  })(XmlNodeType || (XmlNodeType = {}));
13617
13673
  class XmlNode {
13618
13674
  constructor() {
@@ -14189,7 +14245,7 @@
14189
14245
  this.indent();
14190
14246
  for (const child of xml.childNodes) {
14191
14247
  // skip text nodes in case of multiple children
14192
- if (child.nodeType === XmlNodeType.Element) {
14248
+ if (child.nodeType === XmlNodeType.Element || child.nodeType === XmlNodeType.Comment) {
14193
14249
  this.writeNode(child);
14194
14250
  }
14195
14251
  }
@@ -14220,6 +14276,9 @@
14220
14276
  case XmlNodeType.DocumentType:
14221
14277
  this.write(`<!DOCTYPE ${xml.value}>`);
14222
14278
  break;
14279
+ case XmlNodeType.Comment:
14280
+ this.write(`<!-- ${xml.value} -->`);
14281
+ break;
14223
14282
  }
14224
14283
  }
14225
14284
  unindend() {
@@ -16816,7 +16875,7 @@
16816
16875
  * Internal Range: 1 per quarter note
16817
16876
  */
16818
16877
  GpifParser.BendPointValueFactor = 1 / 25.0;
16819
- // test have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
16878
+ // tests have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
16820
16879
  // they are NOT using the sample rate of the input file.
16821
16880
  // Downsampling a 44100hz ogg to 8000hz and using it in as audio track resulted in the same frame offset when placing sync points.
16822
16881
  GpifParser.SampleRate = 44100;
@@ -22625,6 +22684,8 @@
22625
22684
  }
22626
22685
  }
22627
22686
  state.syncPointIndex = 0;
22687
+ state.modifiedTempo =
22688
+ state.syncPoints.length > 0 ? state.syncPoints[0].modifiedTempo : state.currentTempo;
22628
22689
  }
22629
22690
  currentTimePositionToTickPosition(timePosition) {
22630
22691
  const state = this._currentState;
@@ -38486,6 +38547,7 @@
38486
38547
  value.playbackSpeed = this._playbackSpeed;
38487
38548
  value.isLooping = this._isLooping;
38488
38549
  value.midiEventsPlayedFilter = this._midiEventsPlayedFilter;
38550
+ this.ready.trigger();
38489
38551
  }
38490
38552
  else {
38491
38553
  newUnregister.push(value.ready.on(() => {
@@ -43846,6 +43908,9 @@
43846
43908
  const audioElement = document.createElement('audio');
43847
43909
  audioElement.style.display = 'none';
43848
43910
  document.body.appendChild(audioElement);
43911
+ audioElement.addEventListener('seeked', () => {
43912
+ this.updatePosition();
43913
+ });
43849
43914
  audioElement.addEventListener('timeupdate', () => {
43850
43915
  this.updatePosition();
43851
43916
  });
@@ -60941,9 +61006,9 @@
60941
61006
  print(`build date: ${VersionInfo.date}`);
60942
61007
  }
60943
61008
  }
60944
- VersionInfo.version = '1.6.0-alpha.1426';
60945
- VersionInfo.date = '2025-05-27T02:07:22.361Z';
60946
- VersionInfo.commit = '6f8b50015eb3eaebd605045bc9f5aaf6fbf2882d';
61009
+ VersionInfo.version = '1.6.0-alpha.1428';
61010
+ VersionInfo.date = '2025-05-29T00:44:50.954Z';
61011
+ VersionInfo.commit = 'fa81a14da248f229511867324cee663ea70a72b3';
60947
61012
 
60948
61013
  /**
60949
61014
  * A factory for custom layout engines.
@@ -61856,18 +61921,24 @@
61856
61921
  writeDom(parent, score) {
61857
61922
  const gpif = parent.addElement('GPIF');
61858
61923
  // just some values at the time this was implemented,
61859
- gpif.addElement('GPVersion').innerText = '7';
61924
+ gpif.addElement('GPVersion').innerText = '8.1.3';
61860
61925
  const gpRevision = gpif.addElement('GPRevision');
61861
- gpRevision.innerText = '7';
61862
- gpRevision.attributes.set('required', '12021');
61863
- gpRevision.attributes.set('recommended', '12023');
61864
- gpRevision.innerText = '12025';
61865
- gpif.addElement('Encoding').addElement('EncodingDescription').innerText = 'GP7';
61926
+ gpRevision.attributes.set('required', '12024');
61927
+ gpRevision.attributes.set('recommended', '13000');
61928
+ gpRevision.innerText = '13007';
61929
+ const encoding = gpif.addElement('Encoding');
61930
+ encoding.addElement('EncodingDescription').innerText = 'GP8';
61931
+ const alphaTabComment = new XmlNode();
61932
+ alphaTabComment.nodeType = XmlNodeType.Comment;
61933
+ alphaTabComment.value = `Written by alphaTab ${VersionInfo.version} (${VersionInfo.commit})`;
61934
+ encoding.addChild(alphaTabComment);
61866
61935
  this.writeScoreNode(gpif, score);
61867
61936
  this.writeMasterTrackNode(gpif, score);
61937
+ this.writeBackingTrackNode(gpif, score);
61868
61938
  this.writeAudioTracksNode(gpif, score);
61869
61939
  this.writeTracksNode(gpif, score);
61870
61940
  this.writeMasterBarsNode(gpif, score);
61941
+ this.writeAssets(gpif, score);
61871
61942
  const bars = gpif.addElement('Bars');
61872
61943
  const voices = gpif.addElement('Voices');
61873
61944
  const beats = gpif.addElement('Beats');
@@ -61890,6 +61961,42 @@
61890
61961
  }
61891
61962
  }
61892
61963
  }
61964
+ writeAssets(parent, score) {
61965
+ if (!score.backingTrack?.rawAudioFile) {
61966
+ return;
61967
+ }
61968
+ const assets = parent.addElement('Assets');
61969
+ const asset = assets.addElement('Asset');
61970
+ asset.attributes.set('id', this.backingTrackAssetId);
61971
+ this.backingTrackAssetFileName = 'Content/Assets/backing-track';
61972
+ asset.addElement('EmbeddedFilePath').setCData(this.backingTrackAssetFileName);
61973
+ }
61974
+ writeBackingTrackNode(parent, score) {
61975
+ if (!score.backingTrack?.rawAudioFile) {
61976
+ return;
61977
+ }
61978
+ const backingTrackNode = parent.addElement('BackingTrack');
61979
+ const backingTrackAssetId = '0';
61980
+ this.backingTrackAssetId = backingTrackAssetId;
61981
+ backingTrackNode.addElement('IconId').innerText = '21';
61982
+ backingTrackNode.addElement('Color').innerText = '0 0 0';
61983
+ backingTrackNode.addElement('Name').setCData('Audio Track');
61984
+ backingTrackNode.addElement('ShortName').setCData('a.track');
61985
+ backingTrackNode.addElement('PlaybackState').innerText = 'Default';
61986
+ backingTrackNode.addElement('Enabled').innerText = 'true';
61987
+ backingTrackNode.addElement('Source').innerText = 'Local';
61988
+ backingTrackNode.addElement('AssetId').innerText = backingTrackAssetId;
61989
+ const channelStrip = backingTrackNode.addElement('ChannelStrip');
61990
+ channelStrip.addElement('Parameters').innerText =
61991
+ '0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.500000 0.000000 0.500000 0.500000 0.800000 0.500000 0.500000 0.500000';
61992
+ channelStrip.addElement('YouTubeVideoUrl').innerText = '';
61993
+ channelStrip.addElement('Filter').innerText = '6';
61994
+ channelStrip.addElement('FramesPerPixel').innerText = '400';
61995
+ const framePadding = this.backingTrackFramePadding !== undefined ? this.backingTrackFramePadding : 0;
61996
+ backingTrackNode.addElement('FramePadding').innerText = `${framePadding}`;
61997
+ backingTrackNode.addElement('Semitones').innerText = '0';
61998
+ backingTrackNode.addElement('Cents').innerText = '0';
61999
+ }
61893
62000
  writeNoteNode(parent, note) {
61894
62001
  const noteNode = parent.addElement('Note');
61895
62002
  noteNode.attributes.set('id', note.id.toString());
@@ -62546,6 +62653,11 @@
62546
62653
  initialTempoAutomation.addElement('Text').innerText = score.tempoLabel;
62547
62654
  }
62548
62655
  }
62656
+ const initialSyncPoint = score.masterBars[0].syncPoints
62657
+ ? score.masterBars[0].syncPoints.find(p => p.ratioPosition === 0 && p.syncPointValue.barOccurence === 0)
62658
+ : undefined;
62659
+ const millisecondPadding = initialSyncPoint ? initialSyncPoint.syncPointValue.millisecondOffset : 0;
62660
+ this.backingTrackFramePadding = (-1 * ((millisecondPadding / 1000) * GpifWriter.SampleRate)) | 0;
62549
62661
  for (const mb of score.masterBars) {
62550
62662
  for (const automation of mb.tempoAutomations) {
62551
62663
  const tempoAutomation = automations.addElement('Automation');
@@ -62559,6 +62671,25 @@
62559
62671
  tempoAutomation.addElement('Text').innerText = automation.text;
62560
62672
  }
62561
62673
  }
62674
+ if (mb.syncPoints) {
62675
+ for (const syncPoint of mb.syncPoints) {
62676
+ const syncPointAutomation = automations.addElement('Automation');
62677
+ syncPointAutomation.addElement('Type').innerText = 'SyncPoint';
62678
+ syncPointAutomation.addElement('Linear').innerText = 'false';
62679
+ syncPointAutomation.addElement('Bar').innerText = mb.index.toString();
62680
+ syncPointAutomation.addElement('Position').innerText = syncPoint.ratioPosition.toString();
62681
+ syncPointAutomation.addElement('Visible').innerText = 'true';
62682
+ const value = syncPointAutomation.addElement('Value');
62683
+ value.addElement('BarIndex').innerText = mb.index.toString();
62684
+ value.addElement('BarOccurrence').innerText = syncPoint.syncPointValue.barOccurence.toString();
62685
+ value.addElement('ModifiedTempo').innerText = syncPoint.syncPointValue.modifiedTempo.toString();
62686
+ value.addElement('OriginalTempo').innerText = score.tempo.toString();
62687
+ const frameOffset = (((syncPoint.syncPointValue.millisecondOffset - millisecondPadding) / 1000) *
62688
+ GpifWriter.SampleRate) |
62689
+ 0;
62690
+ value.addElement('FrameOffset').innerText = frameOffset.toString();
62691
+ }
62692
+ }
62562
62693
  }
62563
62694
  }
62564
62695
  writeAudioTracksNode(parent, score) {
@@ -63209,6 +63340,10 @@
63209
63340
  voiceNode.addElement('Beats').innerText = voice.beats.map(v => v.id).join(' ');
63210
63341
  }
63211
63342
  }
63343
+ // tests have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
63344
+ // they are NOT using the sample rate of the input file.
63345
+ // Downsampling a 44100hz ogg to 8000hz and using it in as audio track resulted in the same frame offset when placing sync points.
63346
+ GpifWriter.SampleRate = 44100;
63212
63347
  GpifWriter.MidiProgramInfoLookup = new Map([
63213
63348
  [0, new GpifMidiProgramInfo(GpifIconIds.Piano, 'Acoustic Piano')],
63214
63349
  [1, new GpifMidiProgramInfo(GpifIconIds.Piano, 'Acoustic Piano')],
@@ -64946,11 +65081,11 @@
64946
65081
  }
64947
65082
 
64948
65083
  /**
64949
- * This ScoreExporter can write Guitar Pro 7 (gp) files.
65084
+ * This ScoreExporter can write Guitar Pro 7+ (gp) files.
64950
65085
  */
64951
65086
  class Gp7Exporter extends ScoreExporter {
64952
65087
  get name() {
64953
- return 'Guitar Pro 7';
65088
+ return 'Guitar Pro 7-8';
64954
65089
  }
64955
65090
  writeScore(score) {
64956
65091
  Logger.debug(this.name, 'Writing data entries');
@@ -64967,6 +65102,9 @@
64967
65102
  fileSystem.writeEntry(new ZipEntry('Content/PartConfiguration', partConfiguration));
64968
65103
  fileSystem.writeEntry(new ZipEntry('Content/LayoutConfiguration', layoutConfiguration));
64969
65104
  fileSystem.writeEntry(new ZipEntry('Content/score.gpif', IOHelper.stringToBytes(gpifXml)));
65105
+ if (gpifWriter.backingTrackAssetFileName) {
65106
+ fileSystem.writeEntry(new ZipEntry(gpifWriter.backingTrackAssetFileName, score.backingTrack.rawAudioFile));
65107
+ }
64970
65108
  fileSystem.end();
64971
65109
  }
64972
65110
  }