Skip to content

Commit 628e226

Browse files
committed
refactor: eliminate separate score tempo
1 parent 05be924 commit 628e226

File tree

14 files changed

+153
-48
lines changed

14 files changed

+153
-48
lines changed

packages/alphatab/src/generated/model/ScoreSerializer.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ export class ScoreSerializer {
3636
o.set("title", obj.title);
3737
o.set("words", obj.words);
3838
o.set("tab", obj.tab);
39-
o.set("tempo", obj.tempo);
40-
o.set("tempolabel", obj.tempoLabel);
4139
o.set("masterbars", obj.masterBars.map(i => MasterBarSerializer.toJson(i)));
4240
o.set("tracks", obj.tracks.map(i => TrackSerializer.toJson(i)));
4341
o.set("defaultsystemslayout", obj.defaultSystemsLayout);
@@ -83,12 +81,6 @@ export class ScoreSerializer {
8381
case "tab":
8482
obj.tab = v! as string;
8583
return true;
86-
case "tempo":
87-
obj.tempo = v! as number;
88-
return true;
89-
case "tempolabel":
90-
obj.tempoLabel = v! as string;
91-
return true;
9284
case "masterbars":
9385
obj.masterBars = [];
9486
for (const o of (v as (Map<string, unknown> | null)[])) {

packages/alphatab/src/importer/AlphaTexImporter.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ export class AlphaTexImporter extends ScoreImporter {
556556
private _currentStaff!: Staff;
557557
private _barIndex: number = 0;
558558
private _voiceIndex: number = 0;
559+
private _initialTempo = Automation.buildTempoAutomation(false, 0, 120, 0);
559560

560561
// Last known position that had valid syntax/symbols
561562
private _currentDuration: Duration = Duration.QuadrupleWhole;
@@ -768,8 +769,6 @@ export class AlphaTexImporter extends ScoreImporter {
768769
*/
769770
private createDefaultScore(): void {
770771
this._score = new Score();
771-
this._score.tempo = 120;
772-
this._score.tempoLabel = '';
773772
this.newTrack();
774773
}
775774

@@ -1073,13 +1072,13 @@ export class AlphaTexImporter extends ScoreImporter {
10731072
case 'tempo':
10741073
this.sy = this.newSy(true);
10751074
if (this.sy === AlphaTexSymbols.Number) {
1076-
this._score.tempo = this.syData as number;
1075+
this._initialTempo.value = this.syData as number;
10771076
} else {
10781077
this.error('tempo', AlphaTexSymbols.Number, true);
10791078
}
10801079
this.sy = this.newSy();
10811080
if (this.sy === AlphaTexSymbols.String) {
1082-
this._score.tempoLabel = this.syData as string;
1081+
this._initialTempo.text = this.syData as string;
10831082
this.sy = this.newSy();
10841083
}
10851084
anyTopLevelMeta = true;
@@ -1878,6 +1877,8 @@ export class AlphaTexImporter extends ScoreImporter {
18781877
master.timeSignatureDenominator = master.previousMasterBar!.timeSignatureDenominator;
18791878
master.timeSignatureNumerator = master.previousMasterBar!.timeSignatureNumerator;
18801879
master.tripletFeel = master.previousMasterBar!.tripletFeel;
1880+
} else {
1881+
master.tempoAutomations.push(this._initialTempo);
18811882
}
18821883
}
18831884
const anyBarMeta = this.barMeta(bar);
@@ -2367,8 +2368,19 @@ export class AlphaTexImporter extends ScoreImporter {
23672368
} else if (syData === 'tempo') {
23682369
// NOTE: playbackRatio is calculated on score finish when playback positions are known
23692370
const tempoAutomation = this.readTempoAutomation(false);
2371+
2372+
if (beat.index === 0) {
2373+
const existing = beat.voice.bar.masterBar.tempoAutomations.find(a => a.ratioPosition === 0);
2374+
if (existing) {
2375+
existing.value = tempoAutomation.value;
2376+
existing.text = tempoAutomation.text;
2377+
beat.automations.push(existing);
2378+
return true;
2379+
}
2380+
}
23702381
beat.automations.push(tempoAutomation);
23712382
beat.voice.bar.masterBar.tempoAutomations.push(tempoAutomation);
2383+
23722384
return true;
23732385
} else if (syData === 'volume') {
23742386
// NOTE: playbackRatio is calculated on score finish when playback positions are known
@@ -3326,7 +3338,14 @@ export class AlphaTexImporter extends ScoreImporter {
33263338
} else if (syData === 'tempo') {
33273339
const tempoAutomation = this.readTempoAutomation(true);
33283340

3329-
master.tempoAutomations.push(tempoAutomation);
3341+
const existing = master.tempoAutomations.find(a => a.ratioPosition === tempoAutomation.ratioPosition);
3342+
if (existing) {
3343+
existing.value = tempoAutomation.value;
3344+
existing.text = tempoAutomation.text;
3345+
existing.isVisible = tempoAutomation.isVisible;
3346+
} else {
3347+
master.tempoAutomations.push(tempoAutomation);
3348+
}
33303349
} else if (syData === 'section') {
33313350
this.sy = this.newSy();
33323351
if (this.sy !== AlphaTexSymbols.String) {
@@ -3644,7 +3663,7 @@ export class AlphaTexImporter extends ScoreImporter {
36443663
tempoAutomation.ratioPosition = this.syData as number;
36453664
this.sy = this.newSy();
36463665

3647-
if (this.sy === AlphaTexSymbols.String && this.syData === 'hide') {
3666+
if (this.sy === AlphaTexSymbols.String && (this.syData as string) === 'hide') {
36483667
tempoAutomation.isVisible = false;
36493668
this.sy = this.newSy();
36503669
}

packages/alphatab/src/importer/CapellaParser.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export class CapellaParser {
112112

113113
private _voiceCounts!: Map<number /*track*/, number /*count*/>;
114114
private _isFirstSystem: boolean = true;
115+
private _initialTempo: number = -1;
115116

116117
public parseXml(xml: string, settings: Settings): void {
117118
this._galleryObjects = new Map<string, DrawObject>();
@@ -177,7 +178,6 @@ export class CapellaParser {
177178
}
178179
if (root.localName === 'score') {
179180
this.score = new Score();
180-
this.score.tempo = 120;
181181
// parse all children
182182
for (const n of root.childElements()) {
183183
switch (n.localName) {
@@ -374,7 +374,7 @@ export class CapellaParser {
374374
private parseSystem(element: XmlNode) {
375375
if (element.attributes.has('tempo')) {
376376
if (this.score.masterBars.length === 0) {
377-
this.score.tempo = Number.parseInt(element.attributes.get('tempo')!, 10);
377+
this._initialTempo = Number.parseInt(element.attributes.get('tempo')!, 10);
378378
}
379379
}
380380

@@ -488,7 +488,6 @@ export class CapellaParser {
488488
currentBar.clefOttava = staff.bars[staff.bars.length - 1].clefOttava;
489489
currentBar.keySignature = staff.bars[staff.bars.length - 1].keySignature;
490490
currentBar.keySignatureType = staff.bars[staff.bars.length - 1].keySignatureType;
491-
492491
} else {
493492
currentBar.clef = this._currentStaffLayout.defaultClef;
494493
}
@@ -500,6 +499,8 @@ export class CapellaParser {
500499
this.score.addMasterBar(master);
501500
if (master.index > 0) {
502501
master.tripletFeel = master.previousMasterBar!.tripletFeel;
502+
} else if (this._initialTempo > 0) {
503+
master.tempoAutomations.push(Automation.buildTempoAutomation(false, 0, this._initialTempo, 0));
503504
}
504505

505506
master.timeSignatureDenominator = this._timeSignature.timeSignatureDenominator;
@@ -588,9 +589,7 @@ export class CapellaParser {
588589
this._currentBar.clefOttava = this.parseClefOttava(c.getAttribute('clef'));
589590
break;
590591
case 'keySign':
591-
this._currentBar.keySignature = Number.parseInt(
592-
c.getAttribute('fifths'), 10
593-
) as KeySignature;
592+
this._currentBar.keySignature = Number.parseInt(c.getAttribute('fifths'), 10) as KeySignature;
594593
break;
595594
case 'timeSign':
596595
this.parseTime(c.getAttribute('time'));

packages/alphatab/src/importer/Gp3To5Importer.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export class Gp3To5Importer extends ScoreImporter {
6767
private _beatTextChunksByTrack: Map<number, string[]> = new Map<number, string[]>();
6868

6969
private _directionLookup: Map<number, Direction[]> = new Map<number, Direction[]>();
70+
private _initialTempo: Automation | undefined;
7071

7172
public get name(): string {
7273
return 'Guitar Pro 3-5';
@@ -98,12 +99,13 @@ export class Gp3To5Importer extends ScoreImporter {
9899
this.data.skip(19);
99100
}
100101
// page setup since GP5
102+
this._initialTempo = Automation.buildTempoAutomation(false, 0, 0, 0);
101103
if (this._versionNumber >= 500) {
102104
this.readPageSetup();
103-
this._score.tempoLabel = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
105+
this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
104106
}
105107
// tempo stuff
106-
this._score.tempo = IOHelper.readInt32LE(this.data);
108+
this._initialTempo.value = IOHelper.readInt32LE(this.data);
107109
if (this._versionNumber >= 510) {
108110
GpBinaryHelpers.gpReadBool(this.data); // hide tempo?
109111
}
@@ -192,7 +194,8 @@ export class Gp3To5Importer extends ScoreImporter {
192194
}
193195
version = version.substr(Gp3To5Importer.VersionString.length + 1);
194196
const dot: number = version.indexOf(String.fromCharCode(46));
195-
this._versionNumber = 100 * Number.parseInt(version.substr(0, dot), 10) + Number.parseInt(version.substr(dot + 1), 10);
197+
this._versionNumber =
198+
100 * Number.parseInt(version.substr(0, dot), 10) + Number.parseInt(version.substr(dot + 1), 10);
196199
Logger.debug(this.name, `Guitar Pro version ${version} detected`);
197200
}
198201

@@ -317,6 +320,10 @@ export class Gp3To5Importer extends ScoreImporter {
317320
previousMasterBar = this._score.masterBars[this._score.masterBars.length - 1];
318321
}
319322
const newMasterBar: MasterBar = new MasterBar();
323+
if (!previousMasterBar && this._initialTempo!.value > 0) {
324+
newMasterBar.tempoAutomations.push(this._initialTempo!);
325+
}
326+
320327
const flags: number = this.data.readByte();
321328
// time signature
322329
if ((flags & 0x01) !== 0) {

packages/alphatab/src/importer/GpifParser.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,12 +2829,6 @@ export class GpifParser {
28292829
const automation: Automation = automations[i];
28302830
switch (automation.type) {
28312831
case AutomationType.Tempo:
2832-
if (barNumber === 0) {
2833-
this.score.tempo = automation.value | 0;
2834-
if (automation.text) {
2835-
this.score.tempoLabel = automation.text;
2836-
}
2837-
}
28382832
masterBar.tempoAutomations.push(automation);
28392833
break;
28402834
case AutomationType.SyncPoint:

packages/alphatab/src/importer/MusicXmlImporter.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ export class MusicXmlImporter extends ScoreImporter {
198198
throw new UnsupportedFormatError('Unsupported format', e as Error);
199199
}
200200
this._score = new Score();
201-
this._score.tempo = 120;
202201
this._score.stylesheet.hideDynamics = true;
203202

204203
this.parseDom(dom);
@@ -1995,9 +1994,6 @@ export class MusicXmlImporter extends ScoreImporter {
19951994

19961995
if (!this.hasSameTempo(masterBar, tempoAutomation)) {
19971996
masterBar.tempoAutomations.push(tempoAutomation);
1998-
if (masterBar.index === 0) {
1999-
masterBar.score.tempo = tempoAutomation.value;
2000-
}
20011997
}
20021998
}
20031999

@@ -2153,9 +2149,6 @@ export class MusicXmlImporter extends ScoreImporter {
21532149

21542150
if (!this.hasSameTempo(masterBar, tempoAutomation)) {
21552151
masterBar.tempoAutomations.push(tempoAutomation);
2156-
if (masterBar.index === 0) {
2157-
masterBar.score.tempo = tempoAutomation.value;
2158-
}
21592152
}
21602153
}
21612154
}

packages/alphatab/src/model/ModelUtils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,22 @@ export class ModelUtils {
569569
}
570570
}
571571
}
572+
573+
// ensure first masterbar has a tempo automation for score tempo
574+
if (score.masterBars.length > 0) {
575+
const firstTempoAutomation = score.masterBars[0].tempoAutomations.find(
576+
a => a.type === AutomationType.Tempo && a.ratioPosition === 0
577+
);
578+
if (!firstTempoAutomation) {
579+
const tempoAutomation = new Automation();
580+
tempoAutomation.isLinear = false;
581+
tempoAutomation.type = AutomationType.Tempo;
582+
tempoAutomation.value = score.tempo;
583+
tempoAutomation.text = score.tempoLabel;
584+
tempoAutomation.isVisible = false;
585+
score.masterBars[0].tempoAutomations.push(tempoAutomation);
586+
}
587+
}
572588
}
573589

574590
/**

packages/alphatab/src/model/Score.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,22 @@ export class Score {
268268
public tab: string = '';
269269

270270
/**
271-
* Gets or sets the global tempo of the song in BPM. The tempo might change via {@link MasterBar.tempoAutomations}.
271+
* The initial tempo of the song in BPM. The tempo might change via {@link MasterBar.tempoAutomations}.
272272
*/
273-
public tempo: number = 120;
273+
public get tempo(): number {
274+
return this.masterBars.length && this.masterBars[0].tempoAutomations.length > 0
275+
? this.masterBars[0].tempoAutomations[0].value
276+
: 120;
277+
}
274278

275279
/**
276-
* Gets or sets the name/label of the tempo.
280+
* The name/label of the initial tempo.
277281
*/
278-
public tempoLabel: string = '';
282+
public get tempoLabel(): string {
283+
return this.masterBars.length && this.masterBars[0].tempoAutomations.length > 0
284+
? this.masterBars[0].tempoAutomations[0].text
285+
: '';
286+
}
279287

280288
/**
281289
* Gets or sets a list of all masterbars contained in this song.

packages/alphatab/src/rendering/effects/TempoEffectInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class TempoEffectInfo extends EffectBarRendererInfo {
2929
beat.voice.bar.staff.index === 0 &&
3030
beat.voice.index === 0 &&
3131
beat.index === 0 &&
32-
beat.voice.bar.masterBar.tempoAutomations.filter(a => a.isVisible).length > 0
32+
beat.voice.bar.masterBar.tempoAutomations.some(t => t.isVisible)
3333
);
3434
}
3535

packages/alphatab/test-data/exporter/notation-legend-formatted.atex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
\chord {firstfret 1 showdiagram true showfingering false showname true } "G(no5)/B" -1 -1 -1 5 2 -1
3333
// Masterbar 1 Metadata
3434
\ts 4 4
35-
\tempo { 60 0 }
35+
\tempo ( 60 0 )
3636

3737
// Bar 1
3838
// Bar 1 Metadata
@@ -565,7 +565,7 @@
565565
7.5.8{dec beam Up}
566566
|
567567
// Masterbar 82 Metadata
568-
\tempo { 164 0 }
568+
\tempo ( 164 0 )
569569

570570
// Bar 82
571571
// Bar 82 / Voice 1 contents
@@ -590,7 +590,7 @@
590590
|
591591
// Masterbar 84 Metadata
592592
\ts 4 4
593-
\tempo { 60 0 }
593+
\tempo ( 60 0 )
594594

595595
// Bar 84
596596
// Bar 84 / Voice 1 contents

0 commit comments

Comments
 (0)