diff --git a/Semantics.Music/Chord.cs b/Semantics.Music/Chord.cs index 3c7e49d..274be80 100644 --- a/Semantics.Music/Chord.cs +++ b/Semantics.Music/Chord.cs @@ -367,7 +367,10 @@ private static bool IsMinor(string body) => private static SeventhType DetermineSeventh(string body, ChordQuality quality) { bool hasSeven = body.Contains('7'); - bool hasMaj7 = body.Contains("maj", StringComparison.Ordinal) || body.Contains("M7", StringComparison.Ordinal) || body.Contains('Δ'); + bool hasMaj7 = body.Contains("maj", StringComparison.Ordinal) + || body.Contains("Maj", StringComparison.Ordinal) + || body.Contains("M7", StringComparison.Ordinal) + || body.Contains('Δ'); // The major seventh is tested first because one body can carry both spellings: "dimmaj7" // is the diminished-major seventh, a chord distinct from "dim7", and it is what the @@ -379,7 +382,9 @@ private static SeventhType DetermineSeventh(string body, ChordQuality quality) return SeventhType.Major; } - if (quality == ChordQuality.Diminished && body.Contains("dim", StringComparison.Ordinal) && hasSeven) + // '°' is accepted here as it is for the triad, so "C°7" is the diminished seventh rather than + // falling through to a dominant seventh on a diminished triad, which is the half-diminished chord. + if (quality == ChordQuality.Diminished && (body.Contains("dim", StringComparison.Ordinal) || body.Contains('°')) && hasSeven) { return SeventhType.Diminished; } diff --git a/Semantics.Test/Music/ChordTests.cs b/Semantics.Test/Music/ChordTests.cs index f7d142f..b243300 100644 --- a/Semantics.Test/Music/ChordTests.cs +++ b/Semantics.Test/Music/ChordTests.cs @@ -69,6 +69,20 @@ public void ChordTones_DiminishedMajorSeventh_HasTheMajorSeventhNotTheDoubleFlat Assert.AreSequenceEqual([0, 3, 6, 11], actual, "Cdimmaj7 should be a diminished triad with a major seventh."); } + [TestMethod] + [DataRow("CmMaj7", ChordQuality.Minor, SeventhType.Major, new[] { 0, 3, 7, 11 })] + [DataRow("CMaj7", ChordQuality.Major, SeventhType.Major, new[] { 0, 4, 7, 11 })] + [DataRow("C°7", ChordQuality.Diminished, SeventhType.Diminished, new[] { 0, 3, 6, 9 })] + public void Parse_AlternativeSeventhSpellings_GiveTheSeventhTheyName(string symbol, ChordQuality quality, SeventhType seventh, int[] tones) + { + // A round-trip alone cannot catch these: the misparsed chord formats and re-parses + // consistently, so the seventh type and the pitches have to be asserted directly. + Chord c = Chord.Parse(symbol); + Assert.AreEqual(quality, c.Quality, symbol); + Assert.AreEqual(seventh, c.Seventh, symbol); + Assert.AreSequenceEqual(tones, [.. c.ChordTones()], symbol); + } + [TestMethod] public void Parse_HalfDiminished_IsDiminishedTriadWithDominantSeventh() {