Skip to content

Commit 770d9e5

Browse files
feat(drums/tabs): allow showTablature for percussion (drums) notation (#2591)
Co-authored-by: Danielku15 <danielku15@coderline.net>
1 parent b053b63 commit 770d9e5

32 files changed

Lines changed: 656 additions & 372 deletions

packages/alphatab/src/exporter/AlphaTexExporter.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import {
1717
type AlphaTexScoreNode,
1818
type AlphaTexStringLiteral,
1919
type AlphaTexArgumentList,
20-
type IAlphaTexAstNode
20+
type IAlphaTexAstNode,
21+
AlphaTexDotTokenNode,
22+
AlphaTexAtTokenNode
2123
} from '@coderline/alphatab/importer/alphaTex/AlphaTexAst';
2224
import type { IAlphaTexLanguageImportHandler } from '@coderline/alphatab/importer/alphaTex/IAlphaTexLanguageImportHandler';
2325
import { IOHelper } from '@coderline/alphatab/io/IOHelper';
@@ -181,7 +183,7 @@ class AlphaTexPrinter {
181183
this._writeComments(n.leadingComments);
182184

183185
this._writeValue(n.noteValue);
184-
this._writeToken(n.noteStringDot, false);
186+
this._writeToken(n.noteStringSeparator, false);
185187
this._writeValue(n.noteString);
186188

187189
if (n.noteEffects) {
@@ -378,6 +380,9 @@ class AlphaTexPrinter {
378380
case AlphaTexNodeType.Dot:
379381
this._writer.write('.');
380382
break;
383+
case AlphaTexNodeType.At:
384+
this._writer.write('@');
385+
break;
381386
case AlphaTexNodeType.Backslash:
382387
this._writer.write('\\');
383388
break;
@@ -658,6 +663,17 @@ export class AlphaTexExporter extends ScoreExporter {
658663
nodeType: AlphaTexNodeType.String,
659664
text: PercussionMapper.getArticulationName(data)
660665
} as AlphaTexStringLiteral;
666+
667+
if (!Number.isNaN(data.string)) {
668+
note.noteStringSeparator = {
669+
nodeType: AlphaTexNodeType.At
670+
} as AlphaTexAtTokenNode;
671+
const stringNumber = data.beat.voice.bar.staff.tuning.length - data.string + 1;
672+
note.noteString = {
673+
nodeType: AlphaTexNodeType.Number,
674+
value: stringNumber
675+
};
676+
}
661677
} else if (data.isPiano) {
662678
note.noteValue = {
663679
nodeType: AlphaTexNodeType.Ident,
@@ -668,9 +684,9 @@ export class AlphaTexExporter extends ScoreExporter {
668684
nodeType: AlphaTexNodeType.Number,
669685
value: data.fret
670686
} as AlphaTexNumberLiteral;
671-
note.noteStringDot = {
687+
note.noteStringSeparator = {
672688
nodeType: AlphaTexNodeType.Dot
673-
};
689+
} as AlphaTexDotTokenNode;
674690
const stringNumber = data.beat.voice.bar.staff.tuning.length - data.string + 1;
675691
note.noteString = {
676692
nodeType: AlphaTexNodeType.Number,

packages/alphatab/src/exporter/GpifWriter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ export class GpifWriter {
290290
}
291291
}
292292

293+
if (note.isPercussion) {
294+
this._writeSimplePropertyNode(properties, 'String', 'String', (note.string - 1).toString());
295+
}
296+
293297
if (note.isPiano) {
294298
this._writeSimplePropertyNode(properties, 'Octave', 'Number', note.octave.toString());
295299
this._writeSimplePropertyNode(properties, 'Tone', 'Step', note.tone.toString());

packages/alphatab/src/importer/AlphaTexImporter.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,10 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
542542
// Note value
543543
let isDead: boolean = false;
544544
let isTie: boolean = false;
545-
let numericValue: number = -1;
545+
let numericValue: number = Number.NaN;
546546
let articulationValue: string = '';
547-
let octave: number = -1;
548-
let tone: number = -1;
547+
let octave: number = Number.NaN;
548+
let tone: number = Number.NaN;
549549
let accidentalMode = NoteAccidentalMode.Default;
550550
const noteValue = node.noteValue as AlphaTexAstNode;
551551
let detectedNoteKind: AlphaTexStaffNoteKind | undefined = undefined;
@@ -570,7 +570,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
570570
isTie = str === '-';
571571
if (isTie || isDead) {
572572
numericValue = 0;
573-
if (node.noteStringDot && node.noteString) {
573+
if (node.noteStringSeparator && node.noteString) {
574574
detectedNoteKind = AlphaTexStaffNoteKind.Fretted;
575575
} else {
576576
detectedNoteKind = undefined; // don't know on those notes
@@ -668,19 +668,19 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
668668
return;
669669
}
670670

671-
const noteString: number = node.noteString!.value;
672-
if (noteString < 1 || noteString > this._state.currentStaff!.tuning.length) {
671+
const frettedNoteString: number = node.noteString!.value;
672+
if (frettedNoteString < 1 || frettedNoteString > this._state.currentStaff!.tuning.length) {
673673
this.addSemanticDiagnostic({
674674
code: AlphaTexDiagnosticCode.AT208,
675675
message: `Note string is out of range. Available range: 1-${this._state.currentStaff!.tuning.length}`,
676676
severity: AlphaTexDiagnosticsSeverity.Error,
677-
start: noteValue.end,
678-
end: noteValue.end
677+
start: node.noteString.start,
678+
end: node.noteString.end
679679
});
680680
return;
681681
}
682682

683-
note.string = this._state.currentStaff!.tuning.length - (noteString - 1);
683+
note.string = this._state.currentStaff!.tuning.length - (frettedNoteString - 1);
684684
if (!isTie) {
685685
note.fret = numericValue;
686686
}
@@ -716,6 +716,34 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
716716
this._state.articulationUniqueIdToIndex.set(articulationValue, articulationIndex);
717717
}
718718
note.percussionArticulation = articulationIndex;
719+
720+
if (node.noteString) {
721+
const percussionNoteString: number = node.noteString!.value;
722+
if (percussionNoteString < 1 || percussionNoteString > this._state.currentStaff!.tuning.length) {
723+
this.addSemanticDiagnostic({
724+
code: AlphaTexDiagnosticCode.AT208,
725+
message: `Note string is out of range. Available range: 1-${this._state.currentStaff!.tuning.length}`,
726+
severity: AlphaTexDiagnosticsSeverity.Error,
727+
start: node.noteString.start,
728+
end: node.noteString.end
729+
});
730+
return;
731+
}
732+
note.string = this._state.currentStaff!.tuning.length - (percussionNoteString - 1);
733+
} else {
734+
// find free string
735+
for (let i = 0; i < this._state.currentStaff!.tuning.length; i++) {
736+
const s = this._state.currentStaff!.tuning.length - i;
737+
if (!beat.noteStringLookup.has(s)) {
738+
note.string = s;
739+
break;
740+
}
741+
}
742+
if (Number.isNaN(note.string)) {
743+
note.string = this._state.currentStaff!.tuning.length;
744+
}
745+
}
746+
719747
break;
720748
}
721749
}
@@ -742,6 +770,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
742770
switch (staffNoteKind) {
743771
case AlphaTexStaffNoteKind.Pitched:
744772
staff.isPercussion = false;
773+
staff.showTablature = false;
745774
staff.stringTuning.reset();
746775
if (!this._state.staffHasExplicitDisplayTransposition.has(staff)) {
747776
staff.displayTranspositionPitch = 0;
@@ -755,6 +784,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
755784
case AlphaTexStaffNoteKind.Articulation:
756785
staff.isPercussion = true;
757786
staff.stringTuning.reset();
787+
staff.stringTuning.tunings = [0, 0, 0, 0, 0, 0];
758788
if (!this._state.staffHasExplicitDisplayTransposition.has(staff)) {
759789
staff.displayTranspositionPitch = 0;
760790
}
@@ -813,7 +843,9 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
813843
// reset to defaults
814844
staff.stringTuning.reset();
815845

816-
if (program === 15) {
846+
if (staff.isPercussion) {
847+
staff.stringTuning.tunings = [0, 0, 0, 0, 0, 0];
848+
} else if (program === 15) {
817849
// dulcimer E4 B3 G3 D3 A2 E2
818850
staff.stringTuning.tunings = Tuning.getDefaultTuningFor(6)!.tunings;
819851
} else if (program >= 24 && program <= 31) {
@@ -1097,7 +1129,6 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
10971129

10981130
public applyPercussionStaff(staff: Staff) {
10991131
staff.isPercussion = true;
1100-
staff.showTablature = false;
11011132
staff.track.playbackInfo.program = 0;
11021133
}
11031134

packages/alphatab/src/importer/Gp3To5Importer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,8 +1401,7 @@ export class Gp3To5Importer extends ScoreImporter {
14011401
newNote.percussionArticulation = Gp3To5Importer._gp5PercussionInstrumentMap.has(newNote.fret)
14021402
? Gp3To5Importer._gp5PercussionInstrumentMap.get(newNote.fret)!
14031403
: newNote.fret;
1404-
newNote.string = -1;
1405-
newNote.fret = -1;
1404+
newNote.fret = Number.NaN;
14061405
}
14071406
if (swapAccidentals) {
14081407
const accidental = ModelUtils.computeAccidental(

packages/alphatab/src/importer/GpifParser.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -606,8 +606,6 @@ export class GpifParser {
606606

607607
const track: Track = new Track();
608608
track.ensureStaveCount(1);
609-
const staff: Staff = track.staves[0];
610-
staff.showStandardNotation = true;
611609
const trackId: string = node.getAttribute('id');
612610

613611
for (const c of node.childElements()) {
@@ -979,10 +977,6 @@ export class GpifParser {
979977
}
980978
}
981979

982-
if (!staff.isPercussion) {
983-
staff.showTablature = true;
984-
}
985-
986980
break;
987981
case 'DiagramCollection':
988982
case 'ChordCollection':
@@ -1161,8 +1155,6 @@ export class GpifParser {
11611155
}
11621156
for (const staff of track.staves) {
11631157
staff.stringTuning.tunings = tuning;
1164-
staff.showStandardNotation = true;
1165-
staff.showTablature = true;
11661158
}
11671159
break;
11681160
case 'DiagramCollection':
@@ -2437,7 +2429,7 @@ export class GpifParser {
24372429
case 'Octave':
24382430
note.octave = GpifParser._parseIntSafe(c.findChildElement('Number')?.innerText, 0);
24392431
// when exporting GP6 from GP7 the tone might be missing
2440-
if (note.tone === -1) {
2432+
if (Number.isNaN(note.tone)) {
24412433
note.tone = 0;
24422434
}
24432435
break;
@@ -2779,12 +2771,10 @@ export class GpifParser {
27792771
for (const noteId of this._notesOfBeat.get(beatId)!) {
27802772
if (noteId !== GpifParser._invalidId) {
27812773
const note = NoteCloner.clone(this._noteById.get(noteId)!);
2782-
// reset midi value for non-percussion staves
27832774
if (staff.isPercussion) {
2784-
note.fret = -1;
2785-
note.string = -1;
2775+
note.fret = Number.NaN;
27862776
} else {
2787-
note.percussionArticulation = -1;
2777+
note.percussionArticulation = Number.NaN;
27882778
}
27892779
beat.addNote(note);
27902780
if (this._tappedNotes.has(noteId)) {

packages/alphatab/src/importer/MusicXmlImporter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1903,8 +1903,9 @@ export class MusicXmlImporter extends ScoreImporter {
19031903
break;
19041904
case 'percussion':
19051905
bar.clef = Clef.Neutral;
1906-
if(bar.index === 0){
1906+
if (bar.index === 0) {
19071907
bar.staff.isPercussion = true;
1908+
bar.staff.showTablature = false;
19081909
}
19091910
break;
19101911
case 'tab':

packages/alphatab/src/importer/PartConfiguration.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ export class PartConfiguration {
7070
if (trackIndex < score.tracks.length) {
7171
const track: Track = score.tracks[trackIndex];
7272
for (const staff of track.staves) {
73-
if(!staff.isPercussion){
74-
staff.showTablature = trackConfig.showTablature;
75-
}
73+
staff.showTablature = trackConfig.showTablature;
7674
staff.showStandardNotation = trackConfig.showStandardNotation;
7775
staff.showSlash = trackConfig.showSlash;
7876
staff.showNumbered = trackConfig.showNumbered;

0 commit comments

Comments
 (0)