subsequence.cadences
Cadences — the curated formula table behind the producer cadence names.
A cadence is a two-chord tail formula plus a melodic close degree. The
producer names are primary — "strong", "soft", "open",
"fakeout" — with the theory names (authentic, plagal, half, deceptive)
accepted as aliases, per the standing rule: theory machinery under the hood,
producer words on the surface.
The table is pure data; the consumers wire it in:
Progression.cadence(name)— tail substitution on a progression value.Progression.generate(cadence=)/freeze(cadence=)— the formula becomes pins on the final bars of the constrained walk.Motif.generate(cadence=)— the close degree becomesend_on.Composition.request_cadence()/section_cadence()— the live clock steers its walk to arrive at the formula.sentence()/period()— the close degree aims the final unit.
Formula elements follow the progression-element grammar: ints are diatonic
degrees (quality inferred from key+scale at resolution time — 4 is IV
in major and iv in minor), roman strings carry their quality with them
("V" is the major dominant even in minor — the cadential convention).
1"""Cadences — the curated formula table behind the producer cadence names. 2 3A cadence is a two-chord tail formula plus a melodic close degree. The 4producer names are primary — ``"strong"``, ``"soft"``, ``"open"``, 5``"fakeout"`` — with the theory names (authentic, plagal, half, deceptive) 6accepted as aliases, per the standing rule: theory machinery under the hood, 7producer words on the surface. 8 9The table is pure data; the consumers wire it in: 10 11- ``Progression.cadence(name)`` — tail substitution on a progression value. 12- ``Progression.generate(cadence=)`` / ``freeze(cadence=)`` — the formula 13 becomes pins on the final bars of the constrained walk. 14- ``Motif.generate(cadence=)`` — the close degree becomes ``end_on``. 15- ``Composition.request_cadence()`` / ``section_cadence()`` — the live 16 clock steers its walk to arrive at the formula. 17- ``sentence()`` / ``period()`` — the close degree aims the final unit. 18 19Formula elements follow the progression-element grammar: ints are diatonic 20degrees (quality inferred from key+scale at resolution time — ``4`` is IV 21in major and iv in minor), roman strings carry their quality with them 22(``"V"`` is the major dominant even in minor — the cadential convention). 23""" 24 25import dataclasses 26import typing 27 28 29@dataclasses.dataclass(frozen=True) 30class Cadence: 31 32 """One cadence formula — a named tail plus its melodic close. 33 34 Attributes: 35 name: The producer name (the primary key in the table). 36 theory_name: The traditional name, for the curious. 37 formula: The chord tail, in progression-element grammar, ending on 38 the arrival chord. 39 close_degree: The scale degree a melody lands on at this cadence 40 (1 for full closes; 5 for the open half — and 1 for the 41 fakeout too: the melody resolves as promised while the 42 harmony swerves, which is the trick of it). 43 """ 44 45 name: str 46 theory_name: str 47 formula: typing.Tuple[typing.Any, ...] 48 close_degree: int 49 50 51# The curated table — producer names primary. Two-chord tails throughout: 52# a cadence is an arrival WITH its approach, and two chords is the smallest 53# honest spelling of that. 54CADENCES: typing.Dict[str, Cadence] = { 55 "strong": Cadence( 56 name = "strong", 57 theory_name = "authentic", 58 formula = ("V", 1), 59 close_degree = 1, 60 ), 61 "soft": Cadence( 62 name = "soft", 63 theory_name = "plagal", 64 formula = (4, 1), 65 close_degree = 1, 66 ), 67 "open": Cadence( 68 name = "open", 69 theory_name = "half", 70 formula = (4, "V"), 71 close_degree = 5, 72 ), 73 "fakeout": Cadence( 74 name = "fakeout", 75 theory_name = "deceptive", 76 formula = ("V", 6), 77 close_degree = 1, 78 ), 79} 80 81# Theory names as aliases — accuracy costs nothing here, the words name the 82# same formulas. 83_ALIASES: typing.Dict[str, str] = { 84 "authentic": "strong", 85 "perfect": "strong", 86 "plagal": "soft", 87 "half": "open", 88 "deceptive": "fakeout", 89 "interrupted": "fakeout", 90} 91 92 93def cadence_formula (name: str) -> Cadence: 94 95 """Look up a cadence by producer name or theory alias, loudly. 96 97 Raises: 98 ValueError: If the name is unknown — the error lists every valid 99 name and alias. 100 """ 101 102 if not isinstance(name, str): 103 raise TypeError(f"a cadence is named by string, got {name!r}") 104 105 key = name.strip().lower() 106 key = _ALIASES.get(key, key) 107 108 if key not in CADENCES: 109 names = ", ".join(sorted(CADENCES)) 110 aliases = ", ".join(sorted(_ALIASES)) 111 raise ValueError(f"Unknown cadence {name!r}. Cadences: {names} (aliases: {aliases}).") 112 113 return CADENCES[key]
30@dataclasses.dataclass(frozen=True) 31class Cadence: 32 33 """One cadence formula — a named tail plus its melodic close. 34 35 Attributes: 36 name: The producer name (the primary key in the table). 37 theory_name: The traditional name, for the curious. 38 formula: The chord tail, in progression-element grammar, ending on 39 the arrival chord. 40 close_degree: The scale degree a melody lands on at this cadence 41 (1 for full closes; 5 for the open half — and 1 for the 42 fakeout too: the melody resolves as promised while the 43 harmony swerves, which is the trick of it). 44 """ 45 46 name: str 47 theory_name: str 48 formula: typing.Tuple[typing.Any, ...] 49 close_degree: int
One cadence formula — a named tail plus its melodic close.
Attributes:
- name: The producer name (the primary key in the table).
- theory_name: The traditional name, for the curious.
- formula: The chord tail, in progression-element grammar, ending on the arrival chord.
- close_degree: The scale degree a melody lands on at this cadence (1 for full closes; 5 for the open half — and 1 for the fakeout too: the melody resolves as promised while the harmony swerves, which is the trick of it).
94def cadence_formula (name: str) -> Cadence: 95 96 """Look up a cadence by producer name or theory alias, loudly. 97 98 Raises: 99 ValueError: If the name is unknown — the error lists every valid 100 name and alias. 101 """ 102 103 if not isinstance(name, str): 104 raise TypeError(f"a cadence is named by string, got {name!r}") 105 106 key = name.strip().lower() 107 key = _ALIASES.get(key, key) 108 109 if key not in CADENCES: 110 names = ", ".join(sorted(CADENCES)) 111 aliases = ", ".join(sorted(_ALIASES)) 112 raise ValueError(f"Unknown cadence {name!r}. Cadences: {names} (aliases: {aliases}).") 113 114 return CADENCES[key]
Look up a cadence by producer name or theory alias, loudly.
Raises:
- ValueError: If the name is unknown — the error lists every valid name and alias.