Skip to content

Chord.Parse reads "CM9", "CM11" and "CM13" as dominant chords (Bb, not B), and "CmM9" as a plain Cm9 #322

Description

@matt-edmondson

What's wrong

The parser takes a capital M as the major-seventh marker only when a 7 follows it. When a bare extension follows it, as in M9, M11 or M13, the M is dropped and the chord becomes a dominant.

Trace for Chord.Parse("CM9") at 1ddaf24:

  1. The body is "M9". DetermineQuality finds no min, no - and no leading m, so the quality is Major.
  2. DetermineSeventh (Semantics.Music/Chord.cs:367-388) sets hasMaj7 only for "maj", "M7" or 'Δ'. "M9" matches none of these, and the body has no '7', so the seventh is None.
  3. ApplyExtensions (Chord.cs:390-424) takes "9". Because the seventh is None, it sets it to SeventhType.Dominant.
  4. IsQualityVocabulary("M") (Chord.cs:182-193) allows 'M', so the parse succeeds.

The result is Major quality, a Dominant seventh and a Nine tension. ChordTones() returns 0, 4, 7, 10, 14, which is C E G Bb D, and ToString() prints C9. The intended chord is C E G B D.

The same thing happens in three more cases:

  • CM11 and CM13 become C11 and C13.
  • CmM9 becomes Minor with a Dominant seventh, which is Cm9, not the minor-major ninth.

The parser is also inconsistent with itself. CM7 is read as a major seventh, and CΔ9 and Cmaj9 are read as major ninths, so only the M + extension spelling is affected.

Why it matters

CM9 is a standard lead-sheet spelling. Parsing it succeeds without any error but returns a chord with a different seventh, so ChordTones(), Roman-numeral analysis and anything else downstream are wrong for that chord.

Relationship to existing issues

This is related to #281 but separate from it. #281 covers CMaj7 and CmMaj7, where the capitalised word Maj is not recognised. Matching "Maj" would fix those, but it would not fix CM9 or CmM9, because they contain no Maj.

Suggested fix

  • In DetermineSeventh, also treat a capital M followed directly by 9, 11 or 13 as the major-seventh marker. That can be done with "M9", "M11" and "M13" checks, or by detecting a capital M that is not the start of "min" or part of "maj".
  • Add ChordTests cases that assert both the SeventhType and the pitch set for:
    • CM9 → [0, 4, 7, 11, 14]
    • CM11
    • CM13
    • CmM9 → [0, 3, 7, 11, 14]
  • Add a round-trip test to confirm the parse agrees with what ToString() prints.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions