| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
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
JazzRefusalvalues — 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
- data JazzToken
- data JazzChord = JazzChord {
- jcRoot :: PitchClass
- jcBass :: Maybe PitchClass
- jcQuality :: Text
- jcTones :: [PitchClass]
- jcAnchor :: PitchClass
- data JazzRefusal = JazzRefusal {}
- parseToken :: Text -> Either JazzRefusal JazzToken
- data JazzSong = JazzSong {}
- parseSong :: Text -> Text -> Either JazzRefusal JazzSong
- beatSlots :: (Int, Int) -> Int -> [Int]
- beatStream :: JazzSong -> [JazzChord]
- jazzZeroForm :: JazzChord -> [Int]
- data JazzCadence = JazzCadence {}
- jazzShow :: JazzCadence -> Text
- songCadences :: JazzSong -> [JazzCadence]
- normalizeComposer :: Text -> Text
- buildChangeEdges :: [JazzSong] -> [((JazzCadence, JazzCadence), Map Text Double)]
- qualityIntervals :: Map Text [Int]
- qualityNames :: [Text]
- qualityFrequency :: Map Text Int
- canonicalQuality :: Map [Int] Text
- jazzFunctionality :: [Int] -> Maybe Text
- jazzFunctionalityR :: [Int] -> Maybe (Text, Int)
- parseSlashName :: Text -> Maybe (Text, Int)
- data BassVocab = BassVocab {}
- bassVocabFor :: [Int] -> BassVocab
Documentation
One corpus chord token: sounding harmony or notated silence (NC).
A parsed chord symbol as pitch-class content.
Constructors
| JazzChord | |
Fields
| |
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
| Show JazzRefusal Source # | |
Defined in Harmonic.Rules.Import.Jazz Methods showsPrec :: Int -> JazzRefusal -> ShowS # show :: JazzRefusal -> String # showList :: [JazzRefusal] -> ShowS # | |
| Eq JazzRefusal Source # | |
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), ...}))
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 | |
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
| |
Instances
| Show JazzCadence Source # | |
Defined in Harmonic.Rules.Import.Jazz Methods showsPrec :: Int -> JazzCadence -> ShowS # show :: JazzCadence -> String # showList :: [JazzCadence] -> ShowS # | |
| Eq JazzCadence Source # | |
Defined in Harmonic.Rules.Import.Jazz | |
| Ord JazzCadence Source # | |
Defined in Harmonic.Rules.Import.Jazz Methods compare :: JazzCadence -> JazzCadence -> Ordering # (<) :: JazzCadence -> JazzCadence -> Bool # (<=) :: JazzCadence -> JazzCadence -> Bool # (>) :: JazzCadence -> JazzCadence -> Bool # (>=) :: JazzCadence -> JazzCadence -> Bool # max :: JazzCadence -> JazzCadence -> JazzCadence # min :: JazzCadence -> JazzCadence -> JazzCadence # | |
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):
- Written alterations and additions are always present, exactly as
notated, and displace their natural counterpart (
#5/b5replace the 5th,b9/#9replace the natural 9th where one is implied). - 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).
- Dominant 11th chords omit the 3rd (1-5-b7-9-11): at pitch-class
level
11and9sus4are deliberately the same sonority. b13stacks over an unaltered 5th (that presence is what separates it from#5at set level);b6likewise.altis rendered as the classic altered voicing 1-3-#5-b7-b9 (identical set to7#5b9).- 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. - Literal oddities stay literal:
5is the bare power-chord dyad,67is 6th plus b7,Mis an explicit major triad,susb9is 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.
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
| |
Instances
| Show BassVocab Source # | |
| Eq BassVocab Source # | |
| Ord BassVocab Source # | |
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.