1+ import { AccidentalType } from '@coderline/alphatab/model/AccidentalType' ;
2+ import { KeySignature } from '@coderline/alphatab/model/KeySignature' ;
3+ import { ModelUtils } from '@coderline/alphatab/model/ModelUtils' ;
4+ import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode' ;
5+ import { expect } from 'chai' ;
6+
7+ describe ( 'AccidentalResolutionTests' , ( ) => {
8+ const degreeSemitones = [ 0 , 2 , 4 , 5 , 7 , 9 , 11 ] ;
9+
10+ function noteValueForDegree ( keySignature : KeySignature , degree : number , octave : number ) : number {
11+ const ksOffset = ModelUtils . getKeySignatureAccidentalOffset ( keySignature , degree ) ;
12+ const baseSemitone = degreeSemitones [ degree ] + ksOffset ;
13+ return ( octave + 1 ) * 12 + baseSemitone ;
14+ }
15+
16+ const allKeySignatures : KeySignature [ ] = [
17+ KeySignature . Cb ,
18+ KeySignature . Gb ,
19+ KeySignature . Db ,
20+ KeySignature . Ab ,
21+ KeySignature . Eb ,
22+ KeySignature . Bb ,
23+ KeySignature . F ,
24+ KeySignature . C ,
25+ KeySignature . G ,
26+ KeySignature . D ,
27+ KeySignature . A ,
28+ KeySignature . E ,
29+ KeySignature . B ,
30+ KeySignature . FSharp ,
31+ KeySignature . CSharp
32+ ] ;
33+
34+ it ( 'diatonic notes require no accidental in each key signature' , ( ) => {
35+ for ( const ks of allKeySignatures ) {
36+ for ( let degree = 0 ; degree < 7 ; degree ++ ) {
37+ const noteValue = noteValueForDegree ( ks , degree , 4 ) ;
38+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . Default ) ;
39+ expect ( spelling . degree , `ks=${ ks } degree=${ degree } ` ) . to . equal ( degree ) ;
40+ expect ( spelling . accidentalOffset , `ks=${ ks } degree=${ degree } ` ) . to . equal (
41+ ModelUtils . getKeySignatureAccidentalOffset ( ks , degree )
42+ ) ;
43+
44+ const accidental = ModelUtils . computeAccidentalForSpelling (
45+ ks ,
46+ NoteAccidentalMode . Default ,
47+ spelling ,
48+ false ,
49+ null
50+ ) ;
51+ expect ( accidental , `ks=${ ks } degree=${ degree } ` ) . to . equal ( AccidentalType . None ) ;
52+ }
53+ }
54+ } ) ;
55+
56+ it ( 'spells E# in F# major for pitch F natural' , ( ) => {
57+ const ks = KeySignature . FSharp ;
58+ const noteValue = 65 ; // F natural
59+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . Default ) ;
60+ expect ( spelling . degree ) . to . equal ( 2 ) ; // E
61+ expect ( spelling . accidentalOffset ) . to . equal ( 1 ) ; // E#
62+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . Default , spelling , false , null ) ;
63+ expect ( accidental ) . to . equal ( AccidentalType . None ) ;
64+ } ) ;
65+
66+ it ( 'spells Cb in Cb major for pitch B natural' , ( ) => {
67+ const ks = KeySignature . Cb ;
68+ const noteValue = 59 ; // B natural
69+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . Default ) ;
70+ expect ( spelling . degree ) . to . equal ( 0 ) ; // C
71+ expect ( spelling . accidentalOffset ) . to . equal ( - 1 ) ; // Cb
72+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . Default , spelling , false , null ) ;
73+ expect ( accidental ) . to . equal ( AccidentalType . None ) ;
74+ } ) ;
75+
76+ it ( 'forces flat spelling preference when requested' , ( ) => {
77+ const ks = KeySignature . C ;
78+ const noteValue = 61 ; // C# / Db
79+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . ForceFlat ) ;
80+ expect ( spelling . degree ) . to . equal ( 1 ) ; // D
81+ expect ( spelling . accidentalOffset ) . to . equal ( - 1 ) ; // Db
82+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . ForceFlat , spelling , false , null ) ;
83+ expect ( accidental ) . to . equal ( AccidentalType . Flat ) ;
84+ } ) ;
85+
86+ it ( 'forces sharp spelling preference when requested' , ( ) => {
87+ const ks = KeySignature . C ;
88+ const noteValue = 61 ; // C# / Db
89+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . ForceSharp ) ;
90+ expect ( spelling . degree ) . to . equal ( 0 ) ; // C
91+ expect ( spelling . accidentalOffset ) . to . equal ( 1 ) ; // C#
92+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . ForceSharp , spelling , false , null ) ;
93+ expect ( accidental ) . to . equal ( AccidentalType . Sharp ) ;
94+ } ) ;
95+
96+ it ( 'force natural displays a natural accidental when key signature would otherwise apply one' , ( ) => {
97+ const ks = KeySignature . D ; // F#, C#
98+ const noteValue = 65 ; // F natural
99+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . ForceNatural ) ;
100+ expect ( spelling . degree ) . to . equal ( 3 ) ; // F
101+ expect ( spelling . accidentalOffset ) . to . equal ( 0 ) ; // natural
102+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . ForceNatural , spelling , false , null ) ;
103+ expect ( accidental ) . to . equal ( AccidentalType . Natural ) ;
104+ } ) ;
105+
106+ it ( 'force none suppresses accidentals regardless of spelling' , ( ) => {
107+ const ks = KeySignature . C ;
108+ const noteValue = 61 ; // C#
109+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . ForceNone ) ;
110+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . ForceNone , spelling , false , null ) ;
111+ expect ( accidental ) . to . equal ( AccidentalType . None ) ;
112+ } ) ;
113+
114+ it ( 'no accidental when current accidental already matches' , ( ) => {
115+ const ks = KeySignature . C ;
116+ const noteValue = 61 ; // C#
117+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . Default ) ;
118+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . Default , spelling , false , 1 ) ;
119+ expect ( accidental ) . to . equal ( AccidentalType . None ) ;
120+ } ) ;
121+
122+ it ( 'quarter tone accidentals are chosen when quarter bend is true' , ( ) => {
123+ const ks = KeySignature . C ;
124+ const noteValue = 61 ; // C# -> requires sharp
125+ const spelling = ModelUtils . resolveSpelling ( ks , noteValue , NoteAccidentalMode . Default ) ;
126+ const accidental = ModelUtils . computeAccidentalForSpelling ( ks , NoteAccidentalMode . Default , spelling , true , null ) ;
127+ expect ( accidental ) . to . equal ( AccidentalType . SharpQuarterNoteUp ) ;
128+ } ) ;
129+ } ) ;
0 commit comments