From ebefd8b566ff59e962ea5c830587b2ff3a4093db Mon Sep 17 00:00:00 2001 From: danielku15 Date: Thu, 8 Jan 2026 18:49:00 +0100 Subject: [PATCH 1/2] feat: detect bass cleff on Guitar Pro 3-5 files --- .../alphatab/src/importer/Gp3To5Importer.ts | 31 ++++++++++-------- .../test-data/guitarpro5/bass-tuning.gp5 | Bin 0 -> 2047 bytes .../test/importer/Gp5Importer.test.ts | 9 +++++ 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 packages/alphatab/test-data/guitarpro5/bass-tuning.gp5 diff --git a/packages/alphatab/src/importer/Gp3To5Importer.ts b/packages/alphatab/src/importer/Gp3To5Importer.ts index a42714081..dbe90ebf8 100644 --- a/packages/alphatab/src/importer/Gp3To5Importer.ts +++ b/packages/alphatab/src/importer/Gp3To5Importer.ts @@ -37,17 +37,17 @@ import { TripletFeel } from '@coderline/alphatab/model/TripletFeel'; import { VibratoType } from '@coderline/alphatab/model/VibratoType'; import { Voice } from '@coderline/alphatab/model/Voice'; -import { Logger } from '@coderline/alphatab/Logger'; -import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import type { IWriteable } from '@coderline/alphatab/io/IWriteable'; -import { FadeType } from '@coderline/alphatab/model/FadeType'; -import { Rasgueado } from '@coderline/alphatab/model/Rasgueado'; +import { Logger } from '@coderline/alphatab/Logger'; +import { AccidentalType } from '@coderline/alphatab/model/AccidentalType'; import { Direction } from '@coderline/alphatab/model/Direction'; -import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; +import { FadeType } from '@coderline/alphatab/model/FadeType'; +import { ModelUtils } from '@coderline/alphatab/model/ModelUtils'; import { Ottavia } from '@coderline/alphatab/model/Ottavia'; -import { WahPedal } from '@coderline/alphatab/model/WahPedal'; -import { AccidentalType } from '@coderline/alphatab/model/AccidentalType'; +import { Rasgueado } from '@coderline/alphatab/model/Rasgueado'; import { TremoloPickingEffect } from '@coderline/alphatab/model/TremoloPickingEffect'; +import { WahPedal } from '@coderline/alphatab/model/WahPedal'; +import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection'; /** * @internal @@ -434,6 +434,11 @@ export class Gp3To5Importer extends ScoreImporter { } } + /** + * Guitar Pro 3-6 changes to a bass clef if any string tuning is below B2; + */ + private static readonly _bassClefTuningThreshold = ModelUtils.parseTuning('B2')!.realValue; + public readTrack(): void { const newTrack: Track = new Track(); newTrack.ensureStaveCount(1); @@ -527,10 +532,10 @@ export class Gp3To5Importer extends ScoreImporter { // `12` for all tunings which have bass clefs const clefMode = IOHelper.readInt32LE(this.data); - if (clefMode === 12) { - this._clefsPerTrack.set(index, Clef.F4); + if (clefMode === 12 || tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) { + this._clefsPerTrack.set(newTrack.index, Clef.F4); } else { - this._clefsPerTrack.set(index, Clef.G2); + this._clefsPerTrack.set(newTrack.index, Clef.G2); } // Unknown, no UI setting seem to affect this @@ -570,10 +575,10 @@ export class Gp3To5Importer extends ScoreImporter { GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); } } else { - if (GeneralMidi.isBass(newTrack.playbackInfo.program)) { - this._clefsPerTrack.set(index, Clef.F4); + if (tuning[tuning.length - 1] < Gp3To5Importer._bassClefTuningThreshold) { + this._clefsPerTrack.set(newTrack.index, Clef.F4); } else { - this._clefsPerTrack.set(index, Clef.G2); + this._clefsPerTrack.set(newTrack.index, Clef.G2); } } } diff --git a/packages/alphatab/test-data/guitarpro5/bass-tuning.gp5 b/packages/alphatab/test-data/guitarpro5/bass-tuning.gp5 new file mode 100644 index 0000000000000000000000000000000000000000..700c1c1379e58e866a3f21748b24774a7540e0f0 GIT binary patch literal 2047 zcmeHI+iuf95KWr2NlR01q7(_LEz(vX6mDwE1247R(pXAJWIL*OH1QH585?=+mi}R% z`Jr%TeF+#+6;TNu80qXxX3p%M-Pr4mH>S}s4=r{yG#$-i1FO$YcAvg@UQt%1@v=kO zlJWDt3%r*%%=}k`kFnkgJmGrgi8`$|F*dkkI^9EljVjz8>gmsA{A_Z~a!lLdDNpO_ z!ydnh!j0oF@+5nI#`tmHI)estdLH;!>lzyfUqowG&VA)o|WY;`Sga`aw%R zUExU_-ONvbZD0$y58MUFXu29e+y6=2Dw2ecUki zCk%w_q~)DVw4| { it('score-info', async () => { @@ -544,4 +545,12 @@ describe('Gp5ImporterTest', () => { expect(score.tracks[1].playbackInfo.program).to.equal(25); expect(score.tracks[1].playbackInfo.bank).to.equal(77); }); + + it('tuning-bass-clef', async () => { + const score = (await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/bass-tuning.gp5')).readScore(); + expect(score.tracks[0].staves[0].bars[0].clef).to.equal(Clef.F4); + expect(score.tracks[1].staves[0].bars[0].clef).to.equal(Clef.F4); + expect(score.tracks[2].staves[0].bars[0].clef).to.equal(Clef.F4); + expect(score.tracks[3].staves[0].bars[0].clef).to.equal(Clef.F4); + }); }); From 806a1e703e0a302b3bf2b84ca53e538fd03a0bac Mon Sep 17 00:00:00 2001 From: danielku15 Date: Thu, 8 Jan 2026 18:57:37 +0100 Subject: [PATCH 2/2] build: fix constant detection --- packages/transpiler/src/AstPrinterBase.ts | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/transpiler/src/AstPrinterBase.ts b/packages/transpiler/src/AstPrinterBase.ts index b095b2128..34764cd0e 100644 --- a/packages/transpiler/src/AstPrinterBase.ts +++ b/packages/transpiler/src/AstPrinterBase.ts @@ -152,10 +152,41 @@ export default abstract class AstPrinterBase { d.isStatic && !d.setAccessor && cs.isPrimitiveTypeNode(d.type) && - !!d.initializer && + this._isConstantExpression(d.initializer) && (!d.getAccessor || !d.getAccessor.body) ); } + private _isConstantExpression(expression: cs.Expression | undefined): boolean { + if (expression === undefined) { + return false; + } + + switch (expression.nodeType) { + case cs.SyntaxKind.PrefixUnaryExpression: + return this._isConstantExpression((expression as cs.PrefixUnaryExpression).operand); + case cs.SyntaxKind.PostfixUnaryExpression: + return this._isConstantExpression((expression as cs.PostfixUnaryExpression).operand); + case cs.SyntaxKind.NullLiteral: + case cs.SyntaxKind.TrueLiteral: + case cs.SyntaxKind.FalseLiteral: + case cs.SyntaxKind.StringLiteral: + case cs.SyntaxKind.NumericLiteral: + case cs.SyntaxKind.DefaultExpression: + return true; + case cs.SyntaxKind.BinaryExpression: + return ( + this._isConstantExpression((expression as cs.BinaryExpression).left) && + this._isConstantExpression((expression as cs.BinaryExpression).right) + ); + case cs.SyntaxKind.ParenthesizedExpression: + return this._isConstantExpression((expression as cs.ParenthesizedExpression).expression); + // case cs.SyntaxKind.MemberAccessExpression: + // case cs.SyntaxKind.Identifier: + // maybe detect enums and other constant declarations? + } + + return false; + } protected writePropertyAsField(d: cs.PropertyDeclaration) { if (