Harmonic Analysis
Annotate chord sequences with inferred local keys and circle-of-fifths distances.
Functions
analyseHarmony
analyseHarmony(chords: Chord[], homeKey: string): AnnotatedChord[]
Analyses a chord sequence and annotates each chord with its home key and inferred current key based on 2-5-1 and 5-1 harmonic patterns. Pattern priority (highest first): 1. 2-5-1: minor/half-diminished ii, followed by dom7 V a 4th above, followed by tonic I a 5th below the V7. All three chords are assigned the resolved tonic key. 2. 5-1: dom7 V followed by a tonic I a 5th below. Both chords are assigned the resolved tonic key. Unmatched chords receive currentKey = homeKey.
analyseSong
analyseSong(song: Song): AnalysedSong
Apply `analyseHarmony` to every section of a song. Each section's home key is its own `key:` header when present, otherwise the sticky-inherited key from `resolveSectionKeys` (previous section's declared key, then `song.key`, then `C major`). Returns a new `AnalysedSong`; does not mutate.
circleOfFifthsDistance
circleOfFifthsDistance(keyA: string, keyB: string): number
Returns the circle-of-fifths distance between two keys. Minor keys are mapped to their relative major before computing distance, so relative major/minor pairs have distance 0 from each other.
resolveSectionKeys
resolveSectionKeys(song: Song): string | null[]
Resolve the effective declared key for every section, applying **sticky inheritance**: a section with no `key:` header inherits the previous section's *declared* header key (`section.key`). The first section falls back to `song.key`, then `null`. Tonality hints (`{A major}`, `{home}`) are deliberately ignored here — they are section-local and never propagate. This is a shared helper rather than a parse-time mutation so downstream code can still tell "declared in source" from "inherited". Returns an array parallel to `song.sections`; each entry is a canonical key string or `null`.
Types
AnalysedBar
interface
Analysed counterpart of `Bar`, with chord cells replaced by `AnnotatedChordCell`s.
cells: AnalysedBeatCell[]closeBarline: Barlineloc?: SourceRangetimeSignature?: TimeSignaturetonalityHints?: TonalityHintItem[]type: "bar"
AnalysedRow
interface
Analysed counterpart of `Row`, containing `AnalysedBar`s.
bars: AnalysedBar[]loc?: SourceRangeopenBarline: Barlinetype: "row"
AnalysedSection
interface
Analysed counterpart of `Section`, with rows containing annotated chords.
content?: AnalysedSectionItem[]key: string | nulllabel: string | nullloc?: SourceRangepreamble?: CommentLine[]rows: AnalysedRow[]type: "section"
AnalysedSong
interface
Analysed counterpart of `Song`, with every section fully annotated.
key: string | nullloc?: SourceRangemeter: string | nullsections: AnalysedSection[]title: string | nulltype: "song"
AnnotatedChord
interface
A chord augmented with harmonic context inferred by `analyseHarmony`.
chord: ChordcurrentKey: string- Inferred local key at this chord, based on ii–V–I and V–I pattern detection.
currentKeyCandidates: string[]- All candidate keys ordered by circle-of-fifths proximity.
homeKey: string- Key of the enclosing section.
loc?: SourceRangerealtimeKeyCandidates: string[]
AnnotatedChordCell
interface
A `ChordCell`-equivalent carrying an `AnnotatedChord`.
chord: AnnotatedChordloc?: SourceRangetype: "chord"
AnalysedBeatCell
type
Beat cell type used in an analysed bar — either an annotated chord or a plain dot.
type AnalysedBeatCell = AnnotatedChordCell | DotCell
AnalysedSectionItem
type
type AnalysedSectionItem = AnalysedRow | CommentLine