@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.
@@ -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
  *
@@ -1418,7 +1418,7 @@ class SyncPointData {
1418
1418
  */
1419
1419
  this.modifiedTempo = 0;
1420
1420
  /**
1421
- * The uadio offset marking the position within the audio track in milliseconds.
1421
+ * The audio offset marking the position within the audio track in milliseconds.
1422
1422
  * This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
1423
1423
  */
1424
1424
  this.millisecondOffset = 0;
@@ -7299,6 +7299,61 @@ class Score {
7299
7299
  this.tracks[i].finish(settings, sharedDataBag);
7300
7300
  }
7301
7301
  }
7302
+ /**
7303
+ * Applies the given list of {@link FlatSyncPoint} to this song.
7304
+ * @param syncPoints The list of sync points to apply.
7305
+ * @since 1.6.0
7306
+ */
7307
+ applyFlatSyncPoints(syncPoints) {
7308
+ for (const b of this.masterBars) {
7309
+ b.syncPoints = undefined;
7310
+ }
7311
+ for (const syncPoint of syncPoints) {
7312
+ const automation = new Automation();
7313
+ automation.ratioPosition = Math.min(1, Math.max(0, syncPoint.barPosition));
7314
+ automation.type = AutomationType.SyncPoint;
7315
+ automation.syncPointValue = new SyncPointData();
7316
+ automation.syncPointValue.modifiedTempo = syncPoint.modifiedTempo;
7317
+ automation.syncPointValue.millisecondOffset = syncPoint.millisecondOffset;
7318
+ automation.syncPointValue.barOccurence = syncPoint.barOccurence;
7319
+ if (syncPoint.barIndex < this.masterBars.length) {
7320
+ this.masterBars[syncPoint.barIndex].addSyncPoint(automation);
7321
+ }
7322
+ }
7323
+ for (const b of this.masterBars) {
7324
+ if (b.syncPoints) {
7325
+ b.syncPoints.sort((a, b) => {
7326
+ const occurence = a.syncPointValue.barOccurence - b.syncPointValue.barOccurence;
7327
+ if (occurence !== 0) {
7328
+ return occurence;
7329
+ }
7330
+ return a.ratioPosition - b.ratioPosition;
7331
+ });
7332
+ }
7333
+ }
7334
+ }
7335
+ /**
7336
+ * Exports all sync points in this song to a {@link FlatSyncPoint} list.
7337
+ * @since 1.6.0
7338
+ */
7339
+ exportFlatSyncPoints() {
7340
+ const syncPoints = [];
7341
+ for (const masterBar of this.masterBars) {
7342
+ const masterBarSyncPoints = masterBar.syncPoints;
7343
+ if (masterBarSyncPoints) {
7344
+ for (const syncPoint of masterBarSyncPoints) {
7345
+ syncPoints.push({
7346
+ barIndex: masterBar.index,
7347
+ barOccurence: syncPoint.syncPointValue.barOccurence,
7348
+ barPosition: syncPoint.ratioPosition,
7349
+ millisecondOffset: syncPoint.syncPointValue.millisecondOffset,
7350
+ modifiedTempo: syncPoint.syncPointValue.modifiedTempo
7351
+ });
7352
+ }
7353
+ }
7354
+ }
7355
+ return syncPoints;
7356
+ }
7302
7357
  }
7303
7358
 
7304
7359
  /**
@@ -13607,6 +13662,7 @@ var XmlNodeType;
13607
13662
  XmlNodeType[XmlNodeType["CDATA"] = 3] = "CDATA";
13608
13663
  XmlNodeType[XmlNodeType["Document"] = 4] = "Document";
13609
13664
  XmlNodeType[XmlNodeType["DocumentType"] = 5] = "DocumentType";
13665
+ XmlNodeType[XmlNodeType["Comment"] = 6] = "Comment";
13610
13666
  })(XmlNodeType || (XmlNodeType = {}));
13611
13667
  class XmlNode {
13612
13668
  constructor() {
@@ -14183,7 +14239,7 @@ class XmlWriter {
14183
14239
  this.indent();
14184
14240
  for (const child of xml.childNodes) {
14185
14241
  // skip text nodes in case of multiple children
14186
- if (child.nodeType === XmlNodeType.Element) {
14242
+ if (child.nodeType === XmlNodeType.Element || child.nodeType === XmlNodeType.Comment) {
14187
14243
  this.writeNode(child);
14188
14244
  }
14189
14245
  }
@@ -14214,6 +14270,9 @@ class XmlWriter {
14214
14270
  case XmlNodeType.DocumentType:
14215
14271
  this.write(`<!DOCTYPE ${xml.value}>`);
14216
14272
  break;
14273
+ case XmlNodeType.Comment:
14274
+ this.write(`<!-- ${xml.value} -->`);
14275
+ break;
14217
14276
  }
14218
14277
  }
14219
14278
  unindend() {
@@ -16810,7 +16869,7 @@ GpifParser.BendPointPositionFactor = BendPoint.MaxPosition / 100.0;
16810
16869
  * Internal Range: 1 per quarter note
16811
16870
  */
16812
16871
  GpifParser.BendPointValueFactor = 1 / 25.0;
16813
- // test have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
16872
+ // tests have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
16814
16873
  // they are NOT using the sample rate of the input file.
16815
16874
  // Downsampling a 44100hz ogg to 8000hz and using it in as audio track resulted in the same frame offset when placing sync points.
16816
16875
  GpifParser.SampleRate = 44100;
@@ -22619,6 +22678,8 @@ class MidiFileSequencer {
22619
22678
  }
22620
22679
  }
22621
22680
  state.syncPointIndex = 0;
22681
+ state.modifiedTempo =
22682
+ state.syncPoints.length > 0 ? state.syncPoints[0].modifiedTempo : state.currentTempo;
22622
22683
  }
22623
22684
  currentTimePositionToTickPosition(timePosition) {
22624
22685
  const state = this._currentState;
@@ -38480,6 +38541,7 @@ class AlphaSynthWrapper {
38480
38541
  value.playbackSpeed = this._playbackSpeed;
38481
38542
  value.isLooping = this._isLooping;
38482
38543
  value.midiEventsPlayedFilter = this._midiEventsPlayedFilter;
38544
+ this.ready.trigger();
38483
38545
  }
38484
38546
  else {
38485
38547
  newUnregister.push(value.ready.on(() => {
@@ -43840,6 +43902,9 @@ class AudioElementBackingTrackSynthOutput {
43840
43902
  const audioElement = document.createElement('audio');
43841
43903
  audioElement.style.display = 'none';
43842
43904
  document.body.appendChild(audioElement);
43905
+ audioElement.addEventListener('seeked', () => {
43906
+ this.updatePosition();
43907
+ });
43843
43908
  audioElement.addEventListener('timeupdate', () => {
43844
43909
  this.updatePosition();
43845
43910
  });
@@ -60935,9 +61000,9 @@ class VersionInfo {
60935
61000
  print(`build date: ${VersionInfo.date}`);
60936
61001
  }
60937
61002
  }
60938
- VersionInfo.version = '1.6.0-alpha.1426';
60939
- VersionInfo.date = '2025-05-27T02:07:22.361Z';
60940
- VersionInfo.commit = '6f8b50015eb3eaebd605045bc9f5aaf6fbf2882d';
61003
+ VersionInfo.version = '1.6.0-alpha.1428';
61004
+ VersionInfo.date = '2025-05-29T00:44:50.954Z';
61005
+ VersionInfo.commit = 'fa81a14da248f229511867324cee663ea70a72b3';
60941
61006
 
60942
61007
  /**
60943
61008
  * A factory for custom layout engines.
@@ -61850,18 +61915,24 @@ class GpifWriter {
61850
61915
  writeDom(parent, score) {
61851
61916
  const gpif = parent.addElement('GPIF');
61852
61917
  // just some values at the time this was implemented,
61853
- gpif.addElement('GPVersion').innerText = '7';
61918
+ gpif.addElement('GPVersion').innerText = '8.1.3';
61854
61919
  const gpRevision = gpif.addElement('GPRevision');
61855
- gpRevision.innerText = '7';
61856
- gpRevision.attributes.set('required', '12021');
61857
- gpRevision.attributes.set('recommended', '12023');
61858
- gpRevision.innerText = '12025';
61859
- gpif.addElement('Encoding').addElement('EncodingDescription').innerText = 'GP7';
61920
+ gpRevision.attributes.set('required', '12024');
61921
+ gpRevision.attributes.set('recommended', '13000');
61922
+ gpRevision.innerText = '13007';
61923
+ const encoding = gpif.addElement('Encoding');
61924
+ encoding.addElement('EncodingDescription').innerText = 'GP8';
61925
+ const alphaTabComment = new XmlNode();
61926
+ alphaTabComment.nodeType = XmlNodeType.Comment;
61927
+ alphaTabComment.value = `Written by alphaTab ${VersionInfo.version} (${VersionInfo.commit})`;
61928
+ encoding.addChild(alphaTabComment);
61860
61929
  this.writeScoreNode(gpif, score);
61861
61930
  this.writeMasterTrackNode(gpif, score);
61931
+ this.writeBackingTrackNode(gpif, score);
61862
61932
  this.writeAudioTracksNode(gpif, score);
61863
61933
  this.writeTracksNode(gpif, score);
61864
61934
  this.writeMasterBarsNode(gpif, score);
61935
+ this.writeAssets(gpif, score);
61865
61936
  const bars = gpif.addElement('Bars');
61866
61937
  const voices = gpif.addElement('Voices');
61867
61938
  const beats = gpif.addElement('Beats');
@@ -61884,6 +61955,42 @@ class GpifWriter {
61884
61955
  }
61885
61956
  }
61886
61957
  }
61958
+ writeAssets(parent, score) {
61959
+ if (!score.backingTrack?.rawAudioFile) {
61960
+ return;
61961
+ }
61962
+ const assets = parent.addElement('Assets');
61963
+ const asset = assets.addElement('Asset');
61964
+ asset.attributes.set('id', this.backingTrackAssetId);
61965
+ this.backingTrackAssetFileName = 'Content/Assets/backing-track';
61966
+ asset.addElement('EmbeddedFilePath').setCData(this.backingTrackAssetFileName);
61967
+ }
61968
+ writeBackingTrackNode(parent, score) {
61969
+ if (!score.backingTrack?.rawAudioFile) {
61970
+ return;
61971
+ }
61972
+ const backingTrackNode = parent.addElement('BackingTrack');
61973
+ const backingTrackAssetId = '0';
61974
+ this.backingTrackAssetId = backingTrackAssetId;
61975
+ backingTrackNode.addElement('IconId').innerText = '21';
61976
+ backingTrackNode.addElement('Color').innerText = '0 0 0';
61977
+ backingTrackNode.addElement('Name').setCData('Audio Track');
61978
+ backingTrackNode.addElement('ShortName').setCData('a.track');
61979
+ backingTrackNode.addElement('PlaybackState').innerText = 'Default';
61980
+ backingTrackNode.addElement('Enabled').innerText = 'true';
61981
+ backingTrackNode.addElement('Source').innerText = 'Local';
61982
+ backingTrackNode.addElement('AssetId').innerText = backingTrackAssetId;
61983
+ const channelStrip = backingTrackNode.addElement('ChannelStrip');
61984
+ channelStrip.addElement('Parameters').innerText =
61985
+ '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';
61986
+ channelStrip.addElement('YouTubeVideoUrl').innerText = '';
61987
+ channelStrip.addElement('Filter').innerText = '6';
61988
+ channelStrip.addElement('FramesPerPixel').innerText = '400';
61989
+ const framePadding = this.backingTrackFramePadding !== undefined ? this.backingTrackFramePadding : 0;
61990
+ backingTrackNode.addElement('FramePadding').innerText = `${framePadding}`;
61991
+ backingTrackNode.addElement('Semitones').innerText = '0';
61992
+ backingTrackNode.addElement('Cents').innerText = '0';
61993
+ }
61887
61994
  writeNoteNode(parent, note) {
61888
61995
  const noteNode = parent.addElement('Note');
61889
61996
  noteNode.attributes.set('id', note.id.toString());
@@ -62540,6 +62647,11 @@ class GpifWriter {
62540
62647
  initialTempoAutomation.addElement('Text').innerText = score.tempoLabel;
62541
62648
  }
62542
62649
  }
62650
+ const initialSyncPoint = score.masterBars[0].syncPoints
62651
+ ? score.masterBars[0].syncPoints.find(p => p.ratioPosition === 0 && p.syncPointValue.barOccurence === 0)
62652
+ : undefined;
62653
+ const millisecondPadding = initialSyncPoint ? initialSyncPoint.syncPointValue.millisecondOffset : 0;
62654
+ this.backingTrackFramePadding = (-1 * ((millisecondPadding / 1000) * GpifWriter.SampleRate)) | 0;
62543
62655
  for (const mb of score.masterBars) {
62544
62656
  for (const automation of mb.tempoAutomations) {
62545
62657
  const tempoAutomation = automations.addElement('Automation');
@@ -62553,6 +62665,25 @@ class GpifWriter {
62553
62665
  tempoAutomation.addElement('Text').innerText = automation.text;
62554
62666
  }
62555
62667
  }
62668
+ if (mb.syncPoints) {
62669
+ for (const syncPoint of mb.syncPoints) {
62670
+ const syncPointAutomation = automations.addElement('Automation');
62671
+ syncPointAutomation.addElement('Type').innerText = 'SyncPoint';
62672
+ syncPointAutomation.addElement('Linear').innerText = 'false';
62673
+ syncPointAutomation.addElement('Bar').innerText = mb.index.toString();
62674
+ syncPointAutomation.addElement('Position').innerText = syncPoint.ratioPosition.toString();
62675
+ syncPointAutomation.addElement('Visible').innerText = 'true';
62676
+ const value = syncPointAutomation.addElement('Value');
62677
+ value.addElement('BarIndex').innerText = mb.index.toString();
62678
+ value.addElement('BarOccurrence').innerText = syncPoint.syncPointValue.barOccurence.toString();
62679
+ value.addElement('ModifiedTempo').innerText = syncPoint.syncPointValue.modifiedTempo.toString();
62680
+ value.addElement('OriginalTempo').innerText = score.tempo.toString();
62681
+ const frameOffset = (((syncPoint.syncPointValue.millisecondOffset - millisecondPadding) / 1000) *
62682
+ GpifWriter.SampleRate) |
62683
+ 0;
62684
+ value.addElement('FrameOffset').innerText = frameOffset.toString();
62685
+ }
62686
+ }
62556
62687
  }
62557
62688
  }
62558
62689
  writeAudioTracksNode(parent, score) {
@@ -63203,6 +63334,10 @@ class GpifWriter {
63203
63334
  voiceNode.addElement('Beats').innerText = voice.beats.map(v => v.id).join(' ');
63204
63335
  }
63205
63336
  }
63337
+ // tests have shown that Guitar Pro seem to always work with 44100hz for the frame offsets,
63338
+ // they are NOT using the sample rate of the input file.
63339
+ // Downsampling a 44100hz ogg to 8000hz and using it in as audio track resulted in the same frame offset when placing sync points.
63340
+ GpifWriter.SampleRate = 44100;
63206
63341
  GpifWriter.MidiProgramInfoLookup = new Map([
63207
63342
  [0, new GpifMidiProgramInfo(GpifIconIds.Piano, 'Acoustic Piano')],
63208
63343
  [1, new GpifMidiProgramInfo(GpifIconIds.Piano, 'Acoustic Piano')],
@@ -64940,11 +65075,11 @@ class ZipWriter {
64940
65075
  }
64941
65076
 
64942
65077
  /**
64943
- * This ScoreExporter can write Guitar Pro 7 (gp) files.
65078
+ * This ScoreExporter can write Guitar Pro 7+ (gp) files.
64944
65079
  */
64945
65080
  class Gp7Exporter extends ScoreExporter {
64946
65081
  get name() {
64947
- return 'Guitar Pro 7';
65082
+ return 'Guitar Pro 7-8';
64948
65083
  }
64949
65084
  writeScore(score) {
64950
65085
  Logger.debug(this.name, 'Writing data entries');
@@ -64961,6 +65096,9 @@ class Gp7Exporter extends ScoreExporter {
64961
65096
  fileSystem.writeEntry(new ZipEntry('Content/PartConfiguration', partConfiguration));
64962
65097
  fileSystem.writeEntry(new ZipEntry('Content/LayoutConfiguration', layoutConfiguration));
64963
65098
  fileSystem.writeEntry(new ZipEntry('Content/score.gpif', IOHelper.stringToBytes(gpifXml)));
65099
+ if (gpifWriter.backingTrackAssetFileName) {
65100
+ fileSystem.writeEntry(new ZipEntry(gpifWriter.backingTrackAssetFileName, score.backingTrack.rawAudioFile));
65101
+ }
64964
65102
  fileSystem.end();
64965
65103
  }
64966
65104
  }
@@ -6474,6 +6474,36 @@ declare enum Fingers {
6474
6474
  LittleFinger = 4
6475
6475
  }
6476
6476
 
6477
+ /**
6478
+ * A simple flat sync point for easy persistence separate to the main data model.
6479
+ * @record
6480
+ */
6481
+ declare interface FlatSyncPoint {
6482
+ /**
6483
+ * Indicates index of the masterbar for which this sync point is valid.
6484
+ */
6485
+ barIndex: number;
6486
+ /**
6487
+ * Indicates relative position (0-1) of the sync point in within the masterbar.
6488
+ */
6489
+ barPosition: number;
6490
+ /**
6491
+ * Indicates for which repeat occurence this sync point is valid (e.g. 0 on the first time played, 1 on the second time played)
6492
+ */
6493
+ barOccurence: number;
6494
+ /**
6495
+ * The modified tempo at which the cursor should move (aka. the tempo played within the external audio track).
6496
+ * This information is used together with normal tempo changes to calculate how much faster/slower the
6497
+ * cursor playback is performed to align with the audio track.
6498
+ */
6499
+ modifiedTempo: number;
6500
+ /**
6501
+ * The uadio offset marking the position within the audio track in milliseconds.
6502
+ * This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
6503
+ */
6504
+ millisecondOffset: number;
6505
+ }
6506
+
6477
6507
  /**
6478
6508
  * @json_immutable
6479
6509
  */
@@ -6706,7 +6736,7 @@ declare enum GolpeType {
6706
6736
  }
6707
6737
 
6708
6738
  /**
6709
- * This ScoreExporter can write Guitar Pro 7 (gp) files.
6739
+ * This ScoreExporter can write Guitar Pro 7+ (gp) files.
6710
6740
  */
6711
6741
  declare class Gp7Exporter extends ScoreExporter {
6712
6742
  get name(): string;
@@ -7453,6 +7483,52 @@ export declare interface IEventEmitterOfT<T> {
7453
7483
  off(value: (arg: T) => void): void;
7454
7484
  }
7455
7485
 
7486
+ /**
7487
+ * A custom handler for integrating alphaTab with an external media source.
7488
+ */
7489
+ declare interface IExternalMediaHandler {
7490
+ /**
7491
+ * The total duration of the backing track in milliseconds.
7492
+ */
7493
+ readonly backingTrackDuration: number;
7494
+ /**
7495
+ * The playback rate at which the output should playback.
7496
+ */
7497
+ playbackRate: number;
7498
+ /**
7499
+ * The volume at which the output should play (0-1)
7500
+ */
7501
+ masterVolume: number;
7502
+ /**
7503
+ * Instructs the output to seek to the given time position.
7504
+ * @param time The absolute time in milliseconds.
7505
+ */
7506
+ seekTo(time: number): void;
7507
+ /**
7508
+ * Instructs the external media to start the playback.
7509
+ */
7510
+ play(): void;
7511
+ /**
7512
+ * Instructs the external media to pause the playback.
7513
+ */
7514
+ pause(): void;
7515
+ }
7516
+
7517
+ /**
7518
+ * A output handling the playback via an external media.
7519
+ */
7520
+ declare interface IExternalMediaSynthOutput extends IBackingTrackSynthOutput {
7521
+ /**
7522
+ * The handler to which the media control will be delegated.
7523
+ */
7524
+ handler: IExternalMediaHandler | undefined;
7525
+ /**
7526
+ * Updates the playback position from the external media source.
7527
+ * @param currentTime The current time in the external media.
7528
+ */
7529
+ updatePosition(currentTime: number): void;
7530
+ }
7531
+
7456
7532
  export declare interface ILogger {
7457
7533
  debug(category: string, msg: string, ...details: unknown[]): void;
7458
7534
  warning(category: string, msg: string, ...details: unknown[]): void;
@@ -9539,6 +9615,7 @@ export declare namespace model {
9539
9615
  AutomationType,
9540
9616
  Automation,
9541
9617
  SyncPointData,
9618
+ FlatSyncPoint,
9542
9619
  Bar,
9543
9620
  SustainPedalMarkerType,
9544
9621
  SustainPedalMarker,
@@ -12740,6 +12817,17 @@ declare class Score {
12740
12817
  private addMasterBarToRepeatGroups;
12741
12818
  addTrack(track: Track): void;
12742
12819
  finish(settings: Settings): void;
12820
+ /**
12821
+ * Applies the given list of {@link FlatSyncPoint} to this song.
12822
+ * @param syncPoints The list of sync points to apply.
12823
+ * @since 1.6.0
12824
+ */
12825
+ applyFlatSyncPoints(syncPoints: FlatSyncPoint[]): void;
12826
+ /**
12827
+ * Exports all sync points in this song to a {@link FlatSyncPoint} list.
12828
+ * @since 1.6.0
12829
+ */
12830
+ exportFlatSyncPoints(): FlatSyncPoint[];
12743
12831
  }
12744
12832
 
12745
12833
  /**
@@ -13575,7 +13663,7 @@ declare class SyncPointData {
13575
13663
  */
13576
13664
  modifiedTempo: number;
13577
13665
  /**
13578
- * The uadio offset marking the position within the audio track in milliseconds.
13666
+ * The audio offset marking the position within the audio track in milliseconds.
13579
13667
  * This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis.
13580
13668
  */
13581
13669
  millisecondOffset: number;
@@ -13602,7 +13690,9 @@ export declare namespace synth {
13602
13690
  AlphaSynthWebAudioOutputBase,
13603
13691
  AlphaSynthScriptProcessorOutput,
13604
13692
  AlphaSynthAudioWorkletOutput,
13605
- IAudioElementBackingTrackSynthOutput
13693
+ IAudioElementBackingTrackSynthOutput,
13694
+ IExternalMediaHandler,
13695
+ IExternalMediaSynthOutput
13606
13696
  }
13607
13697
  }
13608
13698