MusicTheory Help

Analyzing Chord Progressions

This tutorial teaches you how to analyze, create, and work with chord progressions using the MusicTheory library. You'll learn about Roman numeral analysis, common progressions, and how to build your own.

Prerequisites

Before starting, you should understand:

Understanding Chord Progressions

Chord progressions are sequences of chords that create harmonic movement in music. The library provides tools for:

  • Roman numeral analysis

  • Diatonic chord generation

  • Common progression patterns

  • Key-based transposition

Getting Started with ChordProgression

Creating a Chord Progression Object

// Create a progression in C major var cMajorKey = new KeySignature(new Note(NoteName.C), KeyMode.Major); var progression = new ChordProgression(cMajorKey); // Create a progression in A minor var aMinorKey = new KeySignature(new Note(NoteName.A), KeyMode.Minor); var minorProgression = new ChordProgression(aMinorKey);

Parsing Roman Numerals

// Classic I-V-vi-IV progression var chords = progression.ParseProgression("I - V - vi - IV").ToList(); foreach (var chord in chords) { Console.WriteLine($"{chord.GetSymbol()} - Notes: {string.Join(", ", chord.GetNotes())}"); } // Output: // C - Notes: C4, E4, G4 // G - Notes: G4, B4, D5 // Am - Notes: A4, C5, E5 // F - Notes: F4, A4, C5

Roman Numeral Analysis

Understanding Roman Numerals

Roman numerals indicate:

  • Scale degree: I, II, III, IV, V, VI, VII

  • Chord quality: Uppercase = major, lowercase = minor

  • Extensions: 7, 9, 11, 13

  • Alterations: b5, #5, sus2, sus4

// Major key diatonic chords var majorDiatonic = progression.ParseProgression("I - ii - iii - IV - V - vi - vii°"); // C - Dm - Em - F - G - Am - B° // Minor key diatonic chords var minorDiatonic = minorProgression.ParseProgression("i - ii° - III - iv - v - VI - VII"); // Am - B° - C - Dm - Em - F - G

Extended Chords in Progressions

// Jazz progression with extensions var jazzChords = progression.ParseProgression("IMaj7 - vi7 - ii7 - V7"); // CMaj7 - Am7 - Dm7 - G7 // More complex extensions var complexChords = progression.ParseProgression("IMaj9 - iii7 - vi7 - II7 - V13 - IMaj7"); // CMaj9 - Em7 - Am7 - D7 - G13 - CMaj7

Common Chord Progressions

Pop/Rock Progressions

public static class CommonProgressions { // I-V-vi-IV ("Four Chord Song") public static string PopProgression = "I - V - vi - IV"; // I-vi-IV-V ("50s Progression") public static string FiftiesProgression = "I - vi - IV - V"; // vi-IV-I-V public static string SixFourOneProgression = "vi - IV - I - V"; // I-IV-I-V ("12 Bar Blues" start) public static string BluesStart = "I - I - I - I - IV - IV - I - I"; } // Use common progressions var popChords = progression.ParseProgression(CommonProgressions.PopProgression); var songChords = popChords.Select(c => c.GetSymbol()).ToList(); Console.WriteLine($"Pop progression in C: {string.Join(" - ", songChords)}"); // Output: C - G - Am - F

Jazz Progressions

public static class JazzProgressions { // ii-V-I public static string TwoFiveOne = "ii7 - V7 - IMaj7"; // I-vi-ii-V ("Rhythm Changes" A section) public static string RhythmChanges = "IMaj7 - vi7 - ii7 - V7"; // iii-vi-ii-V-I public static string ThreeSixTwoFive = "iii7 - vi7 - ii7 - V7 - IMaj7"; // I-II7-ii7-V7 (with secondary dominant) public static string SecondaryDominant = "IMaj7 - II7 - ii7 - V7"; } // Create a ii-V-I in different keys var keys = new[] { new KeySignature(new Note(NoteName.C), KeyMode.Major), new KeySignature(new Note(NoteName.F), KeyMode.Major), new KeySignature(new Note(NoteName.Bb, Alteration.Flat), KeyMode.Major) }; foreach (var key in keys) { var prog = new ChordProgression(key); var twoFiveOne = prog.ParseProgression(JazzProgressions.TwoFiveOne); var symbols = twoFiveOne.Select(c => c.GetSymbol()); Console.WriteLine($"{key.Tonic} major: {string.Join(" - ", symbols)}"); } // Output: // C major: Dm7 - G7 - CMaj7 // F major: Gm7 - C7 - FMaj7 // Bb major: Cm7 - F7 - BbMaj7

Analyzing Existing Progressions

Finding the Key

public class ProgressionAnalyzer { public static KeySignature FindBestKey(List<Chord> chords) { var possibleKeys = new List<(KeySignature key, int matches)>(); // Test major keys foreach (NoteName noteName in Enum.GetValues(typeof(NoteName))) { var majorKey = new KeySignature(new Note(noteName), KeyMode.Major); var minorKey = new KeySignature(new Note(noteName), KeyMode.Minor); var majorMatches = CountDiatonicChords(chords, majorKey); var minorMatches = CountDiatonicChords(chords, minorKey); possibleKeys.Add((majorKey, majorMatches)); possibleKeys.Add((minorKey, minorMatches)); } // Return the key with most diatonic chords return possibleKeys.OrderByDescending(k => k.matches).First().key; } private static int CountDiatonicChords(List<Chord> chords, KeySignature key) { var progression = new ChordProgression(key); var diatonicChords = new List<Chord>(); // Get all diatonic triads and 7th chords for (int degree = 1; degree <= 7; degree++) { diatonicChords.Add(progression.GetChordByDegree(degree)); diatonicChords.Add(progression.GetChordByDegree(degree, true)); // 7th } // Count how many input chords match diatonic chords return chords.Count(chord => diatonicChords.Any(diatonic => diatonic.Root.Name == chord.Root.Name && diatonic.Type == chord.Type ) ); } }

Implementing Roman Numeral Analysis

public static List<string> AnalyzeProgression(List<Chord> chords, KeySignature key) { var progression = new ChordProgression(key); var analysis = new List<string>(); foreach (var chord in chords) { // Find scale degree of chord root var scaleDegree = GetScaleDegree(chord.Root, key); if (scaleDegree == 0) { analysis.Add("?"); // Non-diatonic continue; } // Determine Roman numeral string roman = GetRomanNumeral(scaleDegree); // Check chord quality if (chord.Type == ChordType.Major || chord.Type == ChordType.Major7) roman = roman.ToUpper(); else if (chord.Type == ChordType.Minor || chord.Type == ChordType.Minor7) roman = roman.ToLower(); else if (chord.Type == ChordType.Diminished) roman = roman.ToLower() + "°"; // Add extensions if (chord.Type.ToString().Contains("7")) roman += "7"; else if (chord.Type.ToString().Contains("9")) roman += "9"; analysis.Add(roman); } return analysis; } private static int GetScaleDegree(Note note, KeySignature key) { var scale = new Scale(key.Tonic, key.Mode == KeyMode.Major ? ScaleType.Major : ScaleType.NaturalMinor); var scaleNotes = scale.GetNotes().Take(7).ToList(); for (int i = 0; i < scaleNotes.Count; i++) { if (scaleNotes[i].Name == note.Name) return i + 1; } return 0; // Not in scale } private static string GetRomanNumeral(int degree) { return degree switch { 1 => "I", 2 => "II", 3 => "III", 4 => "IV", 5 => "V", 6 => "VI", 7 => "VII", _ => "?" }; }

Building Custom Progressions

Chord Substitution

public class ChordSubstitution { // Tritone substitution for dominant chords public static Chord TritoneSubstitute(Chord dominant7) { if (!dominant7.Type.ToString().Contains("Dominant")) throw new ArgumentException("Chord must be a dominant 7th"); // Substitute with dominant 7th a tritone away var newRoot = dominant7.Root.TransposeBySemitones(6); return new Chord(newRoot, ChordType.Dominant7); } // Relative minor/major substitution public static Chord RelativeSubstitute(Chord chord) { if (chord.Type == ChordType.Major || chord.Type == ChordType.Major7) { // Major to relative minor (down minor 3rd) var relativeRoot = chord.Root.Transpose( new Interval(IntervalQuality.Minor, 3), Direction.Down ); var newType = chord.Type == ChordType.Major ? ChordType.Minor : ChordType.Minor7; return new Chord(relativeRoot, newType); } else if (chord.Type == ChordType.Minor || chord.Type == ChordType.Minor7) { // Minor to relative major (up minor 3rd) var relativeRoot = chord.Root.Transpose( new Interval(IntervalQuality.Minor, 3), Direction.Up ); var newType = chord.Type == ChordType.Minor ? ChordType.Major : ChordType.Major7; return new Chord(relativeRoot, newType); } return chord; // No substitution } } // Example: Substitute chords in ii-V-I var originalProg = progression.ParseProgression("ii7 - V7 - IMaj7").ToList(); var substituted = new List<Chord> { originalProg[0], // Keep ii7 ChordSubstitution.TritoneSubstitute(originalProg[1]), // Tritone sub for V7 originalProg[2] // Keep IMaj7 }; Console.WriteLine("Original: " + string.Join(" - ", originalProg.Select(c => c.GetSymbol()))); Console.WriteLine("Substituted: " + string.Join(" - ", substituted.Select(c => c.GetSymbol()))); // Original: Dm7 - G7 - CMaj7 // Substituted: Dm7 - Db7 - CMaj7

Voice Leading

public class VoiceLeading { public static List<Note> OptimizeVoiceLeading(Chord from, Chord to) { var fromNotes = from.GetNotes().ToList(); var toNotes = to.GetNotes().ToList(); var optimized = new List<Note>(); foreach (var toNote in toNotes) { // Find the closest note in the 'from' chord var closest = fromNotes .OrderBy(fn => Math.Abs(fn.MidiNumber - toNote.MidiNumber)) .First(); // Choose the octave that minimizes movement var candidates = new[] { new Note(toNote.Name, toNote.Alteration, toNote.Octave - 1), new Note(toNote.Name, toNote.Alteration, toNote.Octave), new Note(toNote.Name, toNote.Alteration, toNote.Octave + 1) }; var best = candidates .Where(n => n.MidiNumber >= 0 && n.MidiNumber <= 127) .OrderBy(n => Math.Abs(n.MidiNumber - closest.MidiNumber)) .First(); optimized.Add(best); } return optimized; } } // Optimize voice leading in a progression var voiceOptimized = new List<List<Note>>(); var progressionChords = progression.ParseProgression("I - vi - ii - V").ToList(); for (int i = 0; i < progressionChords.Count; i++) { if (i == 0) { voiceOptimized.Add(progressionChords[i].GetNotes().ToList()); } else { var optimizedVoicing = VoiceLeading.OptimizeVoiceLeading( new Chord(progressionChords[i-1].Root, progressionChords[i-1].Type), progressionChords[i] ); voiceOptimized.Add(optimizedVoicing); } }

Advanced Progression Techniques

public class ModalInterchange { public static List<Chord> GetBorrowedChords(KeySignature key) { var borrowedChords = new List<Chord>(); // If major key, borrow from parallel minor if (key.Mode == KeyMode.Major) { var parallelMinor = new KeySignature(key.Tonic, KeyMode.Minor); var minorProg = new ChordProgression(parallelMinor); // Common borrowed chords: bIII, bVI, bVII, iv borrowedChords.Add(minorProg.GetChordByDegree(3)); // bIII borrowedChords.Add(minorProg.GetChordByDegree(6)); // bVI borrowedChords.Add(minorProg.GetChordByDegree(7)); // bVII borrowedChords.Add(minorProg.GetChordByDegree(4)); // iv (minor) } // If minor key, borrow from parallel major else { var parallelMajor = new KeySignature(key.Tonic, KeyMode.Major); var majorProg = new ChordProgression(parallelMajor); // Common borrowed: IV (major), V (major) borrowedChords.Add(majorProg.GetChordByDegree(4)); // IV borrowedChords.Add(majorProg.GetChordByDegree(5)); // V } return borrowedChords; } } // Use modal interchange in C major var borrowedChords = ModalInterchange.GetBorrowedChords(cMajorKey); Console.WriteLine("Borrowed chords in C major:"); foreach (var chord in borrowedChords) { Console.WriteLine($"- {chord.GetSymbol()}"); } // Output: Eb, Ab, Bb, Fm

Secondary Dominants

public static Chord GetSecondaryDominant(Chord targetChord) { // Secondary dominant is V7 of the target chord var fifthAbove = targetChord.Root.Transpose( new Interval(IntervalQuality.Perfect, 5) ); return new Chord(fifthAbove, ChordType.Dominant7); } // Add secondary dominants to progression var basicProg = progression.ParseProgression("I - vi - ii - V").ToList(); var withSecondaries = new List<Chord>(); for (int i = 0; i < basicProg.Count; i++) { // Add secondary dominant before non-tonic chords if (i > 0 && basicProg[i].Root.Name != progression.Key.Tonic.Name) { withSecondaries.Add(GetSecondaryDominant(basicProg[i])); } withSecondaries.Add(basicProg[i]); } Console.WriteLine("With secondary dominants: " + string.Join(" - ", withSecondaries.Select(c => c.GetSymbol()))); // Output: C - E7 - Am - A7 - Dm - D7 - G

Practical Examples

Song Structure Builder

public class SongStructure { public Dictionary<string, List<Chord>> Sections { get; } = new(); public void AddSection(string name, ChordProgression progression, string romanNumerals) { Sections[name] = progression.ParseProgression(romanNumerals).ToList(); } public List<Chord> BuildSong(params string[] sectionOrder) { var song = new List<Chord>(); foreach (var section in sectionOrder) { if (Sections.ContainsKey(section)) { song.AddRange(Sections[section]); } } return song; } } // Build a pop song structure var song = new SongStructure(); var prog = new ChordProgression(cMajorKey); song.AddSection("Verse", prog, "I - V - vi - IV"); song.AddSection("Chorus", prog, "vi - IV - I - V"); song.AddSection("Bridge", prog, "IV - V - iii - vi - IV - V - I"); var fullSong = song.BuildSong("Verse", "Verse", "Chorus", "Verse", "Chorus", "Bridge", "Chorus");

Progression Generator

public class ProgressionGenerator { private Random random = new Random(); public List<Chord> GenerateProgression(ChordProgression progression, int length) { var chords = new List<Chord>(); var lastDegree = 1; // Start on tonic for (int i = 0; i < length; i++) { int nextDegree; if (i == length - 1) { // End on tonic nextDegree = 1; } else { // Choose next chord based on common movements nextDegree = GetNextChord(lastDegree); } chords.Add(progression.GetChordByDegree(nextDegree, random.Next(2) == 0)); lastDegree = nextDegree; } return chords; } private int GetNextChord(int currentDegree) { // Common chord movements var movements = currentDegree switch { 1 => new[] { 2, 4, 5, 6 }, 2 => new[] { 5, 7 }, 3 => new[] { 4, 6 }, 4 => new[] { 1, 2, 5 }, 5 => new[] { 1, 6 }, 6 => new[] { 2, 4, 5 }, 7 => new[] { 1, 3 }, _ => new[] { 1 } }; return movements[random.Next(movements.Length)]; } } // Generate random progressions var generator = new ProgressionGenerator(); var randomProg = generator.GenerateProgression(progression, 8); Console.WriteLine("Generated: " + string.Join(" - ", randomProg.Select(c => c.GetSymbol())));

Exercises

Exercise 1: Progression Transposer

Create a method that transposes a progression to all 12 keys:

public static Dictionary<string, List<Chord>> TransposeToAllKeys(string romanNumerals) { // Your implementation here // Should return a dictionary with key names and their chord progressions }

Exercise 2: Progression Matcher

Find songs that use a specific progression:

public static bool ProgressionMatches(List<Chord> song, string targetProgression) { // Your implementation here // Should detect if the target progression appears in the song }

Exercise 3: Complexity Analyzer

Rate the complexity of a progression:

public static int AnalyzeComplexity(List<Chord> progression) { // Your implementation here // Consider: number of different chords, non-diatonic chords, extensions }

Best Practices

  • Start simple: Master basic triads before adding extensions

  • Consider the genre: Different styles favor different progressions

  • Think functionally: Understand tonic, subdominant, and dominant functions

  • Listen actively: Train your ear to recognize common progressions

  • Experiment: Try substitutions and variations on classic progressions

Next Steps

See Also

13 June 2025