Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion kord/src/core/chord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ impl HasName for Chord {
name.push_str("(♭5)");
}

if self.modifiers.contains(&Modifier::Augmented5) && !known_name.contains('+') {
if self.modifiers.contains(&Modifier::Augmented5) && !known_name.contains('+') && !known_name.contains("(♯5)") {
name.push_str("(♯5)");
}

Expand Down Expand Up @@ -948,6 +948,10 @@ impl HasKnownChord for Chord {
}

if contains_dominant {
if modifiers.contains(&Modifier::Flat9) {
return KnownChord::AugmentedDominantFlat9(degree);
}

return KnownChord::AugmentedDominant(degree);
}

Expand Down Expand Up @@ -1490,6 +1494,7 @@ mod tests {
assert_eq!(Chord::new(C).seven().sharp11().known_chord(), KnownChord::DominantSharp11(Degree::Seven));
assert_eq!(Chord::new(C).seven().flat9().known_chord(), KnownChord::DominantFlat9(Degree::Seven));
assert_eq!(Chord::new(C).seven().sharp9().known_chord(), KnownChord::DominantSharp9(Degree::Seven));
assert_eq!(Chord::new(C).seven().flat9().augmented().known_chord(), KnownChord::AugmentedDominantFlat9(Degree::Seven));

assert_eq!(Chord::new(C).sus2().known_chord(), KnownChord::Major);
assert_eq!(Chord::new(C).sus4().known_chord(), KnownChord::Major);
Expand Down
56 changes: 56 additions & 0 deletions kord/src/core/known_chord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ pub enum KnownChord {
DominantFlat9(Degree),
/// A dominant sharp 9 chord.
DominantSharp9(Degree),
/// An augmented dominant flat 9 chord.
AugmentedDominantFlat9(Degree),
/// A minor dominant flat 13 chord.
MinorDominantFlat13(Degree),
/// A minor dominant flat 9 flat 13 chord.
Expand Down Expand Up @@ -215,6 +217,7 @@ impl HasDescription for KnownChord {
KnownChord::Diminished => "diminished",
KnownChord::DominantFlat9(_) => "dominant flat 9",
KnownChord::DominantSharp9(_) => "dominant sharp 9",
KnownChord::AugmentedDominantFlat9(_) => "augmented dominant flat 9",
KnownChord::MinorDominantFlat13(_) => "minor dominant flat 13",
KnownChord::MinorDominantFlat9Flat13(_) => "minor dominant flat 9 flat 13",
KnownChord::Sharp11 => "sharp 11",
Expand Down Expand Up @@ -353,6 +356,15 @@ impl HasRelativeScale for KnownChord {
Interval::MinorSixth,
Interval::MinorSeventh,
],
KnownChord::AugmentedDominantFlat9(_) => vec![
Interval::PerfectUnison,
Interval::MinorSecond,
Interval::MinorThird,
Interval::MajorThird,
Interval::AugmentedFourth,
Interval::AugmentedFifth,
Comment on lines +362 to +365

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relative scale for AugmentedDominantFlat9 contains both MinorThird and MajorThird, which is musically inconsistent. The Altered scale (7th mode of melodic minor), which is the primary scale recommendation for this chord, should have intervals: 1, b9, b3 (enharmonic #9), 3 (enharmonic b4), b5, #5 (enharmonic b13), b7. The current relative scale appears to mix intervals from different scale sources rather than consistently representing the Altered scale. Consider aligning these intervals with the Altered mode definition found in mode_kind.rs, which uses DiminishedFourth (enharmonic major 3rd) and MinorSixth (enharmonic augmented 5th) to avoid having both minor and major thirds in the same scale.

Suggested change
Interval::MinorThird,
Interval::MajorThird,
Interval::AugmentedFourth,
Interval::AugmentedFifth,
Interval::MinorThird, // b3 / #9
Interval::DiminishedFourth, // enharmonic major 3rd
Interval::DiminishedFifth, // b5
Interval::MinorSixth, // enharmonic augmented 5th

Copilot uses AI. Check for mistakes.
Interval::MinorSeventh,
],
KnownChord::MinorDominantFlat13(_) => vec![
Interval::PerfectUnison,
Interval::MajorSecond,
Expand Down Expand Up @@ -408,6 +420,7 @@ impl HasRelativeChord for KnownChord {
KnownChord::Diminished => vec![Interval::PerfectUnison, Interval::MinorThird, Interval::DiminishedFifth, Interval::DiminishedSeventh],
KnownChord::DominantFlat9(_) => vec![Interval::PerfectUnison, Interval::MajorThird, Interval::PerfectFifth, Interval::MinorSeventh, Interval::MinorNinth],
KnownChord::DominantSharp9(_) => vec![Interval::PerfectUnison, Interval::MajorThird, Interval::PerfectFifth, Interval::MinorSeventh, Interval::AugmentedNinth],
KnownChord::AugmentedDominantFlat9(_) => vec![Interval::PerfectUnison, Interval::MajorThird, Interval::AugmentedFifth, Interval::MinorSeventh, Interval::MinorNinth],
KnownChord::MinorDominantFlat13(_) => vec![Interval::PerfectUnison, Interval::MinorThird, Interval::PerfectFifth, Interval::MinorSeventh, Interval::MinorThirteenth],
KnownChord::MinorDominantFlat9Flat13(_) => vec![
Interval::PerfectUnison,
Expand Down Expand Up @@ -446,6 +459,7 @@ impl HasName for KnownChord {
KnownChord::Diminished => "dim".to_owned(),
KnownChord::DominantFlat9(d) => format!("{}(♭9)", d.static_name()),
KnownChord::DominantSharp9(d) => format!("{}(♯9)", d.static_name()),
KnownChord::AugmentedDominantFlat9(d) => format!("+{}(♭9)", d.static_name()),
KnownChord::MinorDominantFlat13(d) => format!("m{}(♭13)", d.static_name()),
KnownChord::MinorDominantFlat9Flat13(d) => format!("{}(♭9)(♭13)", d.static_name()),
KnownChord::Sharp11 => "(♯11)".to_owned(),
Expand All @@ -472,6 +486,7 @@ impl KnownChord {
KnownChord::Diminished => DIMINISHED_CANDIDATES,
KnownChord::DominantFlat9(_) => DOMINANT_FLAT9_CANDIDATES,
KnownChord::DominantSharp9(_) => DOMINANT_SHARP9_CANDIDATES,
KnownChord::AugmentedDominantFlat9(_) => AUGMENTED_DOMINANT_FLAT9_CANDIDATES,
KnownChord::MinorDominantFlat13(_) => MINOR_DOMINANT_FLAT13_CANDIDATES,
KnownChord::MinorDominantFlat9Flat13(_) => MINOR_DOMINANT_FLAT9_FLAT13_CANDIDATES,
KnownChord::Sharp11 => SHARP11_CANDIDATES,
Expand Down Expand Up @@ -772,6 +787,24 @@ static DOMINANT_SHARP9_CANDIDATES: &[IntervalCandidate] = &[
},
];

static AUGMENTED_DOMINANT_FLAT9_CANDIDATES: &[IntervalCandidate] = &[
IntervalCandidate {
kind: IntervalCollectionKind::Mode(ModeKind::Altered),
rank: 1,
reason: "Primary scale for V7(♭9)(♯5) chords; the altered dominant scale (7th mode of melodic minor) provides all necessary alterations including ♭9, ♯5/♭13, and ♭5; maximum tension with both flat 9 and sharp 5 present; creates the strongest resolution pull in modern jazz and chromatic harmony",
},
IntervalCandidate {
kind: IntervalCollectionKind::Scale(ScaleKind::DiminishedHalfWhole),
rank: 2,
reason: "Symmetrical half-whole diminished (dominant diminished) that supports V7(♭9)(♯5); provides ♭9, ♯9, ♯11, and 13 simultaneously; rich dominant tension palette; works well when you need the ♭9 with option to imply or approach the ♯5",

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DiminishedHalfWhole scale contains a natural 5th (PerfectFifth), not an augmented 5th, which means it doesn't contain the #5 chord tone of the AugmentedDominantFlat9 chord. While the description acknowledges this by saying "option to imply or approach the ♯5", this scale recommendation may be musically questionable for a chord where #5 is a defining characteristic. Consider whether this scale should be included at all, or if the reasoning should more explicitly clarify that this scale omits the #5 chord tone and is better suited for voice leading contexts where the #5 is approached chromatically.

Suggested change
reason: "Symmetrical half-whole diminished (dominant diminished) that supports V7(♭9)(♯5); provides ♭9, ♯9, ♯11, and 13 simultaneously; rich dominant tension palette; works well when you need the ♭9 with option to imply or approach the ♯5",
reason: "Symmetrical half-whole diminished (dominant diminished) providing ♭9, ♯9, ♯11, and 13; note that this scale has a natural 5th, not a ♯5, so it does not literally contain the augmented 5th chord tone. Best used when the V7(♭9)(♯5) is treated with chromatic voice leading, implying or approaching the ♯5 in phrasing rather than sitting on it as a stable scale degree; offers a dense dominant tension palette around the chord tones.",

Copilot uses AI. Check for mistakes.
},
IntervalCandidate {
kind: IntervalCollectionKind::Mode(ModeKind::PhrygianDominant),
rank: 3,
reason: "Spanish or Phrygian dominant sound (5th mode of harmonic minor) emphasizing the characteristic ♭9; exotic, Middle Eastern, or Flamenco flavor; while it has natural 5th, it can approach or bend toward ♯5 in phrasing; strong V in minor keys",

Copilot AI Jan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PhrygianDominant mode contains a natural 5th (PerfectFifth), not an augmented 5th, which means it doesn't contain the #5 chord tone of the AugmentedDominantFlat9 chord. While the description acknowledges this by mentioning "it can approach or bend toward ♯5 in phrasing", this scale recommendation may be musically questionable for a chord where #5 is a defining characteristic. The PhrygianDominant mode would be more appropriate for a Dominant(b9) chord without the #5 alteration. Consider removing this recommendation or moving it to a lower rank with a clearer caveat about the missing chord tone.

Suggested change
reason: "Spanish or Phrygian dominant sound (5th mode of harmonic minor) emphasizing the characteristic ♭9; exotic, Middle Eastern, or Flamenco flavor; while it has natural 5th, it can approach or bend toward ♯5 in phrasing; strong V in minor keys",
reason: "Spanish or Phrygian dominant sound (5th mode of harmonic minor) emphasizing the characteristic ♭9 over a natural 5th (no built‑in ♯5 in the scale); best treated as a coloristic option or as if the chord were V7(♭9) without the augmented 5th; avoid leaning on the 5th when the ♯5 is a defining chord tone; strong V sound in minor keys with an exotic, Middle Eastern, or Flamenco flavor",

Copilot uses AI. Check for mistakes.
},
];

static MINOR_DOMINANT_FLAT13_CANDIDATES: &[IntervalCandidate] = &[
IntervalCandidate {
kind: IntervalCollectionKind::Mode(ModeKind::Aeolian),
Expand Down Expand Up @@ -899,6 +932,28 @@ mod tests {
}
}

#[test]
fn test_augmented_dominant_flat9_candidates() {
let candidates = KnownChord::AugmentedDominantFlat9(Degree::Seven).scale_candidates();
assert!(!candidates.is_empty(), "G7(b9)(#5) should have scale candidates");

match &candidates[0] {
ScaleCandidate::Mode { kind, rank, .. } => {
assert_eq!(*kind, ModeKind::Altered);
assert_eq!(*rank, 1);
}
_ => panic!("First candidate for G7(b9)(#5) should be a Mode"),
}

match &candidates[1] {
ScaleCandidate::Scale { kind, rank, .. } => {
assert_eq!(*kind, ScaleKind::DiminishedHalfWhole);
assert_eq!(*rank, 2);
}
_ => panic!("Second candidate for G7(b9)(#5) should be a Scale"),
}
}

#[test]
fn test_half_diminished_candidates() {
let candidates = KnownChord::HalfDiminished(Degree::Seven).scale_candidates();
Expand Down Expand Up @@ -1036,6 +1091,7 @@ mod tests {
KnownChord::Diminished,
KnownChord::DominantFlat9(Degree::Seven),
KnownChord::DominantSharp9(Degree::Seven),
KnownChord::AugmentedDominantFlat9(Degree::Seven),
KnownChord::MinorDominantFlat13(Degree::Seven),
KnownChord::MinorDominantFlat9Flat13(Degree::Seven),
KnownChord::Sharp11,
Expand Down