diff --git a/src/pco_chordspkg/ast.nim b/src/pco_chordspkg/ast.nim index f33e3fc..f2dfdf2 100644 --- a/src/pco_chordspkg/ast.nim +++ b/src/pco_chordspkg/ast.nim @@ -87,14 +87,43 @@ func renderPitchInKey*( var scaleNames: array[(ord(high(ChordChartPitch)) + 1), string] if useSharps.isNone: + # If we aren't told to use sharps or flats, we render the diatonic pitches + # according to standard theory (C# in the key of D, Db in the key of Ab) + # but we render non-diatonic notes with flats (prefer the b6 and b7 over + # the #5 and #6). + # + # TODO: In the future, we should also remember the scale degree of the + # chord when parsing. So, for example, in the key of D we would parse Bb as + # the b6 and A# as the #5. The pitch would be the same, but the scale + # degree would differ. This would allow us to preserve intentional choices + # in the chart when transposing. scaleNames = case key - of A, B, D, E, G: - ["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"] - of Af, Bf, C, Df, Ef, F: + of C: ["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"] + of G: + ["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "F#", "G"] + of D: + ["A♭", "A", "B♭", "B", "C", "C#", "D", "E♭", "E", "F", "F#", "G"] + of A: + ["G#", "A", "B♭", "B", "C", "C#", "D", "E♭", "E", "F", "F#", "G"] + of E: + ["G#", "A", "B♭", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"] + of B: + ["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"] of Gf: + ["A♭", "B𝄫", "B♭", "C♭", "D𝄫", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "A𝄫"] + of Df: + ["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "A𝄫"] + of Af: + ["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "G"] + of Ef: + ["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "D", "E♭", "F♭", "F", "G♭", "G"] + of Bf: + ["A♭", "A", "B♭", "C♭", "C", "D♭", "D", "E♭", "F♭", "F", "G♭", "G"] + of F: ["A♭", "A", "B♭", "C♭", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"] + elif useSharps.isSome and useSharps.get: scaleNames = case key of A, B, C, D, E, G: @@ -112,7 +141,7 @@ func renderPitchInKey*( of F: ["G#", "G𝄪", "A#", "B", "B#", "C#", "C𝄪", "D#", "D𝄪", "E#", "F#", "F𝄪"] - else: # useSharps.isSome and not useSharps.get + else: # !useSharps (useSharps.isSome and not useSharps.get) scaleNames = case key of C, Af, Bf, Df, Ef, F: ["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"] @@ -348,7 +377,7 @@ let NOTE_START_PAT = re"\{\{" let NOTE_END_PAT = re"\}\}" let SPACE_PAT = re"\s" let CHORD_IN_LYRICS_PAT = re"(\w+)(\[.+)" -let CHORD_AND_LYRICS_PAT = re"^\[([^\]]+)\]([^\s\[]+)(.*)$" +let CHORD_AND_LYRICS_PAT = re"^\[([^\]]*)\]([^\s\[]+)(.*)$" let BRACED_CHORD_PAT = re"^\[([^\]]+)\]$" let NAKED_CHORDS_ONLY_PAT = re("^\\s*(" & CHORD_REGEX & "\\s*\\|*\\s*)+$")