diff --git a/DryWetMidi.Tests/MusicTheory/ChordProgression/ChordProgressionTests.cs b/DryWetMidi.Tests/MusicTheory/ChordProgression/ChordProgressionTests.cs index f3249c63a..0a60accf8 100644 --- a/DryWetMidi.Tests/MusicTheory/ChordProgression/ChordProgressionTests.cs +++ b/DryWetMidi.Tests/MusicTheory/ChordProgression/ChordProgressionTests.cs @@ -12,6 +12,7 @@ public sealed class ChordProgressionTests [TestCase("I-II-IV", "C major", new[] { "C", "D", "F" })] [TestCase("I-II-iv", "C major", new[] { "C", "D", "F" })] [TestCase("Im-ii7-v", "C major", new[] { "Cm", "D7", "G" })] + [TestCase("i - bVI - III - bVII", "C major", new[] { "C", "G#", "E", "A#" })] public void Parse(string input, string scaleString, string[] expectedChords) { var chordProgression = ChordProgression.Parse(input, Scale.Parse(scaleString)); diff --git a/DryWetMidi/MusicTheory/ChordProgression/ChordProgressionParser.cs b/DryWetMidi/MusicTheory/ChordProgression/ChordProgressionParser.cs index 9c31d45c2..df1dd7404 100644 --- a/DryWetMidi/MusicTheory/ChordProgression/ChordProgressionParser.cs +++ b/DryWetMidi/MusicTheory/ChordProgression/ChordProgressionParser.cs @@ -10,12 +10,14 @@ internal static class ChordProgressionParser private const char PartsDelimiter = '-'; private const string ScaleDegreeGroupName = "sd"; + private const string AccidentalGroupName = "ac"; + private static readonly string AccidentalGroup = $"(?<{AccidentalGroupName}>b)"; private static readonly string ScaleDegreeGroup = $"(?<{ScaleDegreeGroupName}>(?i:M{{0,4}}(CM|CD|D?C{{0,3}})(XC|XL|L?X{{0,3}})(IX|IV|V?I{{0,3}})))"; private static readonly string[] Patterns = new[] { - $@"{ScaleDegreeGroup}\s*{ChordParser.ChordCharacteristicsGroup}" + $@"{AccidentalGroup}?\s*{ScaleDegreeGroup}\s*{ChordParser.ChordCharacteristicsGroup}" }; private static readonly Dictionary RomanMap = new Dictionary @@ -57,11 +59,19 @@ internal static ParsingResult TryParse(string input, Scale scale, out ChordProgr var degree = RomanToInteger(degreeRoman); var rootNoteName = scale.GetStep(degree - 1); + var accidentalGroup = match.Groups[AccidentalGroupName]; + if (accidentalGroup.Success) + { + var accidental = accidentalGroup.Value; + if (accidental == "b") + rootNoteName = (NoteName)(((int)rootNoteName + Octave.OctaveSize - 1) % Octave.OctaveSize); + } + var fullString = match.Value; var matchIndex = match.Index; var degreeGroupIndex = degreeGroup.Index; var chordString = - fullString.Substring(0, degreeGroupIndex - matchIndex) + + fullString.Substring(0, degreeGroupIndex - matchIndex - (accidentalGroup.Success ? accidentalGroup.Length : 0)) + rootNoteName + fullString.Substring(degreeGroupIndex - matchIndex + degreeGroup.Length);