Skip to content

Harmony & Chord Progressions

This document explains the harmonic system in MIDI Sketch.

Chord Progressions

MIDI Sketch includes 22 built-in chord progressions, covering common pop music patterns.

4-Chord Progressions

IDNameDegreesCharacter
0Pop4I-V-vi-IVUbiquitous pop
1Axisvi-IV-I-VMelancholic
2Komurovi-IV-V-IBright J-pop
3CanonI-V-vi-iii-IVClassic
4Emotional4vi-V-IV-VEmotional build
5MinimalI-IVSimple two-chord
6AltMinimalI-VPower pop
7Progression3I-vi-IVThree-chord
8Rock4I-bVII-IV-IRock feel

5-Chord Progressions

IDNameDegreesCharacter
9Extended5I-V-vi-iii-IVExtended canon
10Emotional5vi-IV-I-V-iiComplex emotional

Full Progression List

Total of 22 progressions including:

  • Pop variants (bright, dark)
  • Emotional variants
  • Minimal (2-3 chord) patterns
  • Rock-influenced
  • Jazz-influenced with ii-V

Degree System

Chord degrees are represented as integers:

cpp
enum Degree {
    I   = 0,   // Tonic
    ii  = 1,   // Supertonic
    iii = 2,   // Mediant
    IV  = 3,   // Subdominant
    V   = 4,   // Dominant
    vi  = 5,   // Submediant
    vii = 6,   // Leading tone
    bVII = 10  // Flat seven (borrowed)
};

Chord Quality

Quality is determined by degree in major key:

cpp
ChordQuality getQuality(Degree degree) {
    switch (degree) {
        case I: case IV: case V: case bVII:
            return Major;
        case ii: case iii: case vi:
            return Minor;
        case vii:
            return Diminished;
    }
}

Chord Extensions

Extensions add color to basic triads:

Extension Types

TypeNotes AddedExample (C)
TriadRoot, 3rd, 5thC-E-G
Sus2Root, 2nd, 5thC-D-G
Sus4Root, 4th, 5thC-F-G
7th+ 7thC-E-G-B/Bb
9th+ 7th + 9thC-E-G-B-D

Extension Application Rules

Configuration

cpp
struct ChordExtensionParams {
    bool enabled = true;
    float sus_probability = 0.3f;     // 30% chance
    float seventh_probability = 0.4f;  // 40% chance
    float ninth_probability = 0.2f;    // 20% chance
};

Voice Leading

Principles

  1. Minimize movement: Each voice moves by smallest interval
  2. Common tones: Retain shared notes between chords
  3. Avoid parallel fifths/octaves: Classical constraint
  4. Smooth bass: Stepwise or small leaps preferred

Algorithm

cpp
Voicing optimizeVoicing(Voicing prev, Chord next) {
    vector<Voicing> candidates = generateAllVoicings(next);

    return min_element(candidates, [&](auto& a, auto& b) {
        int distA = totalVoiceDistance(prev, a);
        int distB = totalVoiceDistance(prev, b);

        // Also penalize large leaps
        int leapA = maxSingleVoiceDistance(prev, a);
        int leapB = maxSingleVoiceDistance(prev, b);

        return (distA + leapA * 2) < (distB + leapB * 2);
    });
}

Voicing Types

Bass-Chord Coordination

The chord track uses bass analysis to avoid doubling:

cpp
struct BassAnalysis {
    bool hasRootOnBeat1;   // Root on downbeat
    bool hasRootOnBeat3;   // Root on beat 3
    bool hasFifth;         // Fifth present
    Tick accentTicks[];    // Strong beat positions
};

// When bass has root, chord uses rootless voicing
if (bassAnalysis.hasRootOnBeat1) {
    voicing = generateRootlessVoicing(chord);
}

Key Modulation

Modulation Points

StructureModulation PointAmount
StandardPopB → Chorus+1 semitone
RepeatChorusChorus 1 → 2+1 semitone
BalladB → Chorus+2 semitones
Full patternsVaries+1 to +2

Implementation

cpp
struct Modulation {
    Tick tick;           // When to modulate
    int8_t semitones;    // How much (+1, +2)
};

// Applied during MIDI output
void MidiWriter::writeTrack(MidiTrack& track, Modulation mod) {
    for (auto& note : track.notes) {
        if (note.tick >= mod.tick) {
            note.pitch += mod.semitones;
        }
    }
}

Chord Tone Analysis

Used for melody generation to determine note consonance:

cpp
bool isChordTone(uint8_t pitch, Chord chord) {
    uint8_t pitchClass = pitch % 12;

    // Check against chord pitch classes
    for (auto& chordPitch : chord.pitchClasses()) {
        if (pitchClass == chordPitch) return true;
    }
    return false;
}

uint8_t nearestChordTone(uint8_t pitch, Chord chord) {
    // Find closest chord tone by semitone distance
    int minDist = 12;
    uint8_t nearest = pitch;

    for (auto& target : chord.pitches()) {
        int dist = abs(pitch - target);
        if (dist < minDist) {
            minDist = dist;
            nearest = target;
        }
    }
    return nearest;
}

Tension Notes

Non-chord tones used for melodic interest:

TensionIntervalResolution
9thMajor 2ndDown to root
11thPerfect 4thDown to 3rd
13thMajor 6thDown to 5th
b9Minor 2ndDown to root
#11Aug 4thTo 5th

Usage by Vocal Attitude

AttitudeTensions Allowed
CleanNone (chord tones only)
Expressive9th, 13th on weak beats
RawAll tensions, any beat

Released under the MIT License.