theHarmonicAlgorithm-3.1.0: Real-time harmonic progression generation for TidalCycles live performance
Safe HaskellNone
LanguageGHC2021

Harmonic.Rules.Import.Jazz

Description

Parses leadsheet chord symbols from the Jazz-Chord-Progressions-Corpus (Bunks, Weyde, Dixon, Di Giorgi, ISMIR 2023) into absolute pitch-class sets. Every symbol has the shape root quality [/bass] (or the bare token NC for silence); the parser honours the notation exactly:

  • The written root and slash bass are never rewritten or dropped. A slash bass is unioned into the pitch-class set and becomes the chord's anchor (the reference for zero-form and movement); without a slash the root is the anchor.
  • Qualities map to curated canonical tone sets (qualityIntervals): the tones a jazz player reads from the symbol, not the full theoretical extension stack. The conventions are documented on the table itself.
  • Symbols that fail to parse are returned as JazzRefusal values — recorded, never silently skipped.

Shared surface: the ingest run parses the corpus through it, genJ names walked chords with jazzFunctionality, and leadJ cues parse through parseToken.

Synopsis

Documentation

data JazzToken Source #

One corpus chord token: sounding harmony or notated silence (NC).

Constructors

NoChord

The NC token: no harmony sounding.

Sounding JazzChord

A parsed chord symbol.

Instances

Instances details
Show JazzToken Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

Eq JazzToken Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

data JazzChord Source #

A parsed chord symbol as pitch-class content.

Constructors

JazzChord 

Fields

Instances

Instances details
Show JazzChord Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

Eq JazzChord Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

data JazzRefusal Source #

A symbol the parser refused, with the reason. Refusals are first-class data: aggregated and reported by the ingest run, never silently dropped.

Constructors

JazzRefusal 

Fields

Instances

Instances details
Show JazzRefusal Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

Eq JazzRefusal Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

parseToken :: Text -> Either JazzRefusal JazzToken Source #

Parse one whitespace-delimited corpus token.

>>> parseToken "Dm7/G"
Right (Sounding (JazzChord {jcRoot = P 2, jcBass = Just (P 7), ...}))

data JazzSong Source #

One corpus tune: identifying header fields and the bar-structured token stream. DBKeySig is deliberately not read — the graph is zero-form, so key signatures carry no information it needs.

Constructors

JazzSong 

Fields

Instances

Instances details
Show JazzSong Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

Eq JazzSong Source # 
Instance details

Defined in Harmonic.Rules.Import.Jazz

parseSong :: Text -> Text -> Either JazzRefusal JazzSong Source #

Parse one corpus song file. The first argument names the song in refusals (and stands in for a corrupt Title header); the second is the file content. Header fields are matched by prefix so the two known corrupt headers in the corpus degrade gracefully; the Bars header is ignored in favour of counting actual bar delimiters (it is wrong in three corpus files). Any token refusal refuses the whole song, with the song name attached.

beatSlots :: (Int, Int) -> Int -> [Int] Source #

Beat slots per chord for one bar: how many one-beat cadence steps each chord occupies. A bar's beats (the time-signature numerator; numerator `div` 3 for compound x/8 meters) are shared out with any remainder going to the earliest chords — so 3 chords in 4/4 hold [2,1,1] beats. More chords than beats means sub-beat harmony: every chord gets one slot. A chord holding n slots contributes n-1 self-cadences, mirroring the classical graph's sustained-harmony self-edges.

beatStream :: JazzSong -> [JazzChord] Source #

Expand a song to its beat-level chord stream. Each chord repeats once per beat slot; NC beats vanish entirely, so the chords either side of a silence become adjacent — the notated harmonic motion bridges the gap, and silence never becomes a graph node.

jazzZeroForm :: JazzChord -> [Int] Source #

Anchor-relative zero form of a parsed chord: intervals above the sounding fundamental, sorted, starting at 0. This is the jazz graph's node identity (paired with the arrival movement).

data JazzCadence Source #

One jazz cadence: how the anchor moved and what sounds above it. The jazz-graph analogue of the classical Cadence.

Constructors

JazzCadence 

Fields

jazzShow :: JazzCadence -> Text Source #

Graph node key for a jazz cadence, matching the classical key shape: ( <movement> -> <functionality> ).

songCadences :: JazzSong -> [JazzCadence] Source #

The cadence chain of one song: one cadence per beat-to-beat step (n beats yield n-1 cadences; the opening beat has no arrival). A beat sustaining the same harmony over the same anchor yields a pedal self-cadence. Chains never cross song boundaries — the caller builds edges from consecutive pairs within one song's chain only.

normalizeComposer :: Text -> Text Source #

Composer name to weight key: lower-cased, alphanumerics only, with the empty result mapped to unknown. The ONE normalisation applied at every write and lookup, so a composer can never split across variant spellings of the same name.

buildChangeEdges :: [JazzSong] -> [((JazzCadence, JazzCadence), Map Text Double)] Source #

Aggregate the whole corpus into weighted transition edges: every consecutive cadence pair within one song contributes 1.0 to that song's composer on the from -> to edge. Chains never cross songs. The result feeds writeChangeEdges.

qualityIntervals :: Map Text [Int] Source #

Curated canonical tone set per quality, as semitone intervals from the root. Covers all 122 quality strings in the corpus census.

Conventions (the tones a player reads from the symbol, kept deterministic so equal sonorities land on equal graph nodes):

  1. Written alterations and additions are always present, exactly as notated, and displace their natural counterpart (#5/b5 replace the 5th, b9/#9 replace the natural 9th where one is implied).
  2. The natural 5th is included except where altered — and except in 13th chords, which take the working-voicing form 1-3-b7-9-13 (no 5th, no 11th).
  3. Dominant 11th chords omit the 3rd (1-5-b7-9-11): at pitch-class level 11 and 9sus4 are deliberately the same sonority.
  4. b13 stacks over an unaltered 5th (that presence is what separates it from #5 at set level); b6 likewise.
  5. alt is rendered as the classic altered voicing 1-3-#5-b7-b9 (identical set to 7#5b9).
  6. Spelling variants are separate keys mapping to identical sets: M7/maj7, o/dim, h7/m7b5, 7+/7#5/+7, sus/sus4/4, 2/sus2, mi/m, and so on.
  7. Literal oddities stay literal: 5 is the bare power-chord dyad, 67 is 6th plus b7, M is an explicit major triad, susb9 is a sus4 triad with b9 and no 7th.

qualityNames :: [Text] Source #

Every quality string the table covers (the corpus census: 122 qualities; NC is handled at token level).

qualityFrequency :: Map Text Int Source #

Corpus occurrence count per quality string (census of the 2,614-tune Bunks corpus, 2026-08-25: 134,355 tokens). Drives the canonical-spelling choice in canonicalQuality: where spelling variants share a tone set, the spelling the corpus itself uses most is the one the graph shows.

canonicalQuality :: Map [Int] Text Source #

Canonical quality name per tone set: the reverse of qualityIntervals, collapsing spelling variants (and pitch-class coincidences such as 13b5/13#11) onto the corpus-preferred spelling. 122 qualities reduce to 85 distinct sets.

jazzFunctionality :: [Int] -> Maybe Text Source #

Name a zero-form pitch-class set (sorted, deduplicated, containing 0) for the jazz graph: the pure set-to-name function that keeps node identity and node label in lockstep.

A set matching a curated quality names directly (with the bare major triad displayed as maj). Anything else is read as a slash shape: some rotation of the set is a curated quality — guaranteed for every corpus-derived set, whose rotation to the notated root is its quality by construction — and the name is quality/bass-degree ([0,5,9] is a major triad over its 5th: maj/5). Candidate rotations are ranked by corpus frequency of the quality, preferring the reading whose bass is a chord member, then the smallest rotation. Nothing only for sets no rotation of which is a curated quality — impossible for corpus data, reported (never invented) if it ever happens downstream.

jazzFunctionalityR :: [Int] -> Maybe (Text, Int) Source #

jazzFunctionality with the rotation exposed: the second component is the offset of the TRUE quality root above the anchor (0 when the set names directly, the chosen rotation for slash shapes). The structural authority for anchor-independent readings — e.g. fund recovering the harmonic fundamental of a slash bar.

parseSlashName :: Text -> Maybe (Text, Int) Source #

Exact inverse of the slash names jazzFunctionalityR emits, over the CLOSED corpus vocabulary: quality/degreeLabel where the quality is a curated name and the label one of the eleven bass degrees. Returns the quality and the true root's offset ABOVE the anchor (the rotation the namer applied). Fails on anything else — classical names that merely contain a slash (sus2/4no5) fail the degree-label membership, so a successful parse is proof the name came from the jazz namer. The DISPLAY authority for chart-convention rendering: parsing the stored name back can never disagree with the name the walk stamped, where re-deriving from intervals could pick a different rotation.

data BassVocab Source #

Walking-bass understanding of a jazz tone set: what the SYMBOL means to a bass player rather than what the working voicing contains. Corpus sets omit degrees a bassist still needs (13th chords carry no 5th and no 11th) and notate colours a bassist must not land on strong beats (b9, #9, #11, b13). Root-relative intervals throughout.

Constructors

BassVocab 

Fields

  • bvTarget :: [Int]

    The triadic core — root, third (or sus 4) and THE fifth: the tones a line aims AT. Primary strong-beat targets; the rest of the chord is colour that guides passing motion and scale choice rather than serving as a destination.

  • bvStrong :: [Int]

    Strong-beat anchors: the target triad plus the seventh (or true 6th) — legal to land on, with the target tones preferred.

  • bvPassing :: [Int]

    Favourable passing tones — weak-beat preferred, modest strong-beat access: the 9 where natural, and the 11 over the minor family only (over a major third it is the classic avoid note).

  • bvAvoid :: [Int]

    Notated tones never to land on strong beats (colour alterations); still reachable as weak-beat tension through the connector pools.

  • bvFifth :: Int

    THE fifth of the quality: 7 natural (restored for the 13th family), 6 where b5 defines it, 8 where #5 replaces it.

bassVocabFor :: [Int] -> BassVocab Source #

Derive the walking-bass vocabulary from a zero-form tone set. The rules codify the hand-inferred per-quality palettes of notes/walking_bass_theory.md (maj/dom take the 9 and avoid the 11; the minor family takes 9 and 11; defining tones reserved for strong beats) plus the corpus table's own conventions (alterations displace naturals; 13th chords omit 5th and 11th). Total over any zero-form set, so hand-built and spliced material degrades gracefully.

Known limitation, deliberate: the same source supersedes per-QUALITY palettes with per-FUNCTION ones (a iii chord takes the 11 but not the 9, which is a b9 from the key; a IV takes the #11). This signature takes one bar's intervals and cannot see key or function, so it implements the quality-level rule only. The walk does infer a key centre, so the refinement is reachable later without changing callers.