File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -72,7 +72,9 @@ public ScaleDegree DegreeOf(PitchClass pitchClass)
7272 }
7373
7474 // Chromatic: spell against the lower neighbour (raised/sharp) or the upper
75- // neighbour (lowered/flat), whichever is closer; tie prefers the flat.
75+ // neighbour (lowered/flat), whichever is closer; tie prefers the flat, except
76+ // above the last degree, where a flat would wrap to a lowered tonic and the
77+ // raised last degree (the minor-key leading tone, #7) is the conventional reading.
7678 int lowerIndex = - 1 ;
7779 for ( int i = 0 ; i < offsets . Count ; i ++ )
7880 {
@@ -98,7 +100,12 @@ public ScaleDegree DegreeOf(PitchClass pitchClass)
98100 int flatDegree = upperIndex >= 0 ? upperIndex + 1 : 1 ;
99101 int flatAlteration = semitone - ( upperIndex >= 0 ? offsets [ upperIndex ] : offsets [ 0 ] + 12 ) ;
100102
101- return Math . Abs ( flatAlteration ) <= Math . Abs ( sharpAlteration )
103+ bool flatWraps = upperIndex < 0 ;
104+ bool preferFlat = flatWraps
105+ ? Math . Abs ( flatAlteration ) < Math . Abs ( sharpAlteration )
106+ : Math . Abs ( flatAlteration ) <= Math . Abs ( sharpAlteration ) ;
107+
108+ return preferFlat
102109 ? new ScaleDegree ( flatDegree , flatAlteration )
103110 : new ScaleDegree ( sharpDegree , sharpAlteration ) ;
104111 }
Original file line number Diff line number Diff line change @@ -36,6 +36,23 @@ public void RomanNumeral_ChromaticGetsAccidental()
3636 Assert . AreEqual ( "bII" , CMajor . RomanNumeralOf ( Chord . Parse ( "Db" ) ) ) ;
3737 }
3838
39+ [ TestMethod ]
40+ public void RomanNumeral_MinorLeadingToneChordIsSharpSeven ( )
41+ {
42+ Key aMinor = Key . Parse ( "A aeolian" ) ;
43+ Assert . AreEqual ( "#vii°" , aMinor . RomanNumeralOf ( Chord . Parse ( "G#dim" ) ) ) ;
44+ Assert . AreEqual ( "#vii°" , Key . Parse ( "G mixolydian" ) . RomanNumeralOf ( Chord . Parse ( "F#dim" ) ) ) ;
45+ }
46+
47+ [ TestMethod ]
48+ public void RomanNumeral_MinorLeadingToneChordRoundTrips ( )
49+ {
50+ Key aMinor = Key . Parse ( "A aeolian" ) ;
51+ Chord leadingTone = Chord . Parse ( "G#dim" ) ;
52+ Chord roundTripped = aMinor . ChordFromRomanNumeral ( aMinor . RomanNumeralOf ( leadingTone ) ) ;
53+ Assert . AreEqual ( leadingTone , roundTripped ) ;
54+ }
55+
3956 [ TestMethod ]
4057 public void RomanNumeral_DistinctDegreesNeverShareALabel ( )
4158 {
Original file line number Diff line number Diff line change @@ -50,6 +50,17 @@ public void DegreeOf_SharpFourIsCloserToFourthRaised()
5050 Assert . AreEqual ( - 1 , tritone . Alteration ) ;
5151 }
5252
53+ [ TestMethod ]
54+ public void DegreeOf_RaisedSeventhAboveAFlatSeventhIsSharpSeven ( )
55+ {
56+ // G# in A aeolian sits between the flat seventh (G) and the octave; it is the
57+ // leading tone (#7), not a lowered tonic wrapped round the octave.
58+ Scale aMinor = Scale . Create ( PitchClass . Parse ( "A" ) , Mode . Aeolian ) ;
59+ ScaleDegree leadingTone = aMinor . DegreeOf ( PitchClass . Parse ( "G#" ) ) ;
60+ Assert . AreEqual ( 7 , leadingTone . Degree ) ;
61+ Assert . AreEqual ( 1 , leadingTone . Alteration ) ;
62+ }
63+
5364 [ TestMethod ]
5465 public void ToStringIsRootSpaceMode ( )
5566 {
You can’t perform that action at this time.
0 commit comments