tonus 0.1.8 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BIBLIOGRAPHY.md +138 -108
- package/CHANGELOG.md +664 -1
- package/LICENSE +133 -29
- package/README.md +107 -80
- package/dist/data/am.js +2666 -11196
- package/dist/data/ams.d.ts +5 -0
- package/dist/data/ams.js +122 -0
- package/dist/data/attestation.d.ts +19 -0
- package/dist/data/attestation.js +15716 -0
- package/dist/data/attestation.json +15711 -0
- package/dist/data/cadentiae.d.ts +43 -0
- package/dist/data/cadentiae.js +174 -0
- package/dist/data/cal.js +48 -0
- package/dist/data/census.d.ts +12 -0
- package/dist/data/census.js +36 -0
- package/dist/data/commune-office.d.ts +4 -0
- package/dist/data/commune-office.js +2371 -0
- package/dist/data/commune-office.json +2365 -0
- package/dist/data/commune.js +181 -11
- package/dist/data/corpus-overlap.d.ts +17 -0
- package/dist/data/corpus-overlap.js +299 -5
- package/dist/data/cot.d.ts +5 -0
- package/dist/data/cot.js +172 -0
- package/dist/data/cse.d.ts +5 -0
- package/dist/data/cse.js +122 -0
- package/dist/data/gabc-glyphs.d.ts +45 -0
- package/dist/data/gabc-glyphs.js +122 -0
- package/dist/data/gr.js +754 -6394
- package/dist/data/kyriale.js +116 -116
- package/dist/data/la.js +799 -13419
- package/dist/data/lh.js +113 -3473
- package/dist/data/lu.js +931 -17631
- package/dist/data/nocturnale-romanum.js +1659 -10411
- package/dist/data/office-ferial.d.ts +4 -0
- package/dist/data/office-ferial.js +396 -0
- package/dist/data/office-ferial.json +391 -0
- package/dist/data/office-monastic.d.ts +17 -1
- package/dist/data/office-monastic.js +1403 -466
- package/dist/data/office-psalms-monastic.d.ts +13 -1
- package/dist/data/office-psalms-monastic.js +9 -0
- package/dist/data/propers.js +1 -1
- package/dist/data/psalms.js +22919 -5
- package/dist/data/psm.d.ts +5 -0
- package/dist/data/psm.js +122 -0
- package/dist/data/seasonal-respbreve.d.ts +5 -0
- package/dist/data/seasonal-respbreve.js +41 -0
- package/dist/data/seasonal-respbreve.json +35 -0
- package/dist/data/smufl-glyphs.d.ts +17 -0
- package/dist/data/smufl-glyphs.js +1546 -0
- package/dist/data/smufl-glyphs.json +1530 -0
- package/dist/engines/cal/calendar.d.ts +3 -2
- package/dist/engines/cal/calendar.js +105 -29
- package/dist/engines/cal/data/eras.d.ts +35 -0
- package/dist/engines/cal/data/eras.js +128 -0
- package/dist/engines/cal/date.js +44 -0
- package/dist/engines/cal/types.d.ts +15 -3
- package/dist/engines/cal/types.js +5 -5
- package/dist/engines/census/census.d.ts +7 -0
- package/dist/engines/census/census.js +179 -0
- package/dist/engines/census/types.d.ts +55 -0
- package/dist/engines/census/types.js +8 -0
- package/dist/engines/chant/attest.d.ts +39 -0
- package/dist/engines/chant/attest.js +90 -0
- package/dist/engines/chant/chant.d.ts +16 -4
- package/dist/engines/chant/chant.js +220 -30
- package/dist/engines/chant/data/compline.js +2 -1
- package/dist/engines/chant/data/masses.d.ts +56 -4
- package/dist/engines/chant/data/masses.js +305 -80
- package/dist/engines/chant/data/prime.js +1 -1
- package/dist/engines/chant/hour.js +279 -58
- package/dist/engines/chant/ordinary.d.ts +2 -0
- package/dist/engines/chant/ordinary.js +336 -56
- package/dist/engines/chant/propers.js +55 -5
- package/dist/engines/chant/psalm.d.ts +4 -4
- package/dist/engines/chant/psalm.js +25 -11
- package/dist/engines/chant/syllabify.d.ts +1 -0
- package/dist/engines/chant/syllabify.js +90 -17
- package/dist/engines/chant/types.d.ts +115 -12
- package/dist/engines/chant/types.js +38 -3
- package/dist/engines/harmonia/api.js +4 -0
- package/dist/engines/harmonia/data/doctrines.js +3 -1
- package/dist/engines/harmonia/data/zodiac.d.ts +47 -0
- package/dist/engines/harmonia/data/zodiac.js +163 -0
- package/dist/engines/harmonia/tabula.d.ts +3 -0
- package/dist/engines/harmonia/tabula.js +1 -0
- package/dist/engines/harmonia/voice.d.ts +4 -0
- package/dist/engines/harmonia/voice.js +8 -4
- package/dist/engines/imprint.js +14 -1
- package/dist/engines/planet/orbital.js +4 -4
- package/dist/engines/planet/planet.d.ts +10 -0
- package/dist/engines/planet/planet.js +30 -3
- package/dist/engines/planet/position.js +13 -10
- package/dist/engines/planet/types.d.ts +1 -0
- package/dist/engines/score/api.d.ts +2 -13
- package/dist/engines/score/api.js +21 -8
- package/dist/engines/score/articulation.js +2 -2
- package/dist/engines/score/cadence.d.ts +76 -0
- package/dist/engines/score/cadence.js +96 -0
- package/dist/engines/score/emitters/accidentals.d.ts +21 -0
- package/dist/engines/score/emitters/accidentals.js +88 -0
- package/dist/engines/score/emitters/atramentum.d.ts +107 -0
- package/dist/engines/score/emitters/atramentum.js +239 -0
- package/dist/engines/score/emitters/breaking.d.ts +62 -0
- package/dist/engines/score/emitters/breaking.js +80 -0
- package/dist/engines/score/emitters/moderna.d.ts +38 -0
- package/dist/engines/score/emitters/moderna.js +612 -0
- package/dist/engines/score/emitters/svg.d.ts +143 -0
- package/dist/engines/score/emitters/svg.js +1335 -0
- package/dist/engines/score/emitters/tracks.d.ts +104 -0
- package/dist/engines/score/emitters/tracks.js +728 -0
- package/dist/engines/score/infer.d.ts +3 -3
- package/dist/engines/score/infer.js +2 -2
- package/dist/engines/score/inscriptio.d.ts +69 -0
- package/dist/engines/score/inscriptio.js +138 -0
- package/dist/engines/score/ir.d.ts +2 -2
- package/dist/engines/score/ir.js +59 -12
- package/dist/engines/score/lyric.d.ts +23 -0
- package/dist/engines/score/lyric.js +234 -0
- package/dist/engines/score/meta.d.ts +2 -2
- package/dist/engines/score/modulation.d.ts +12 -0
- package/dist/engines/score/modulation.js +49 -0
- package/dist/engines/score/neume.js +35 -4
- package/dist/engines/score/parse.js +150 -9
- package/dist/engines/score/phrasing.js +4 -3
- package/dist/engines/score/prosody.d.ts +38 -0
- package/dist/engines/score/prosody.js +70 -6
- package/dist/engines/score/tabula.d.ts +37 -5
- package/dist/engines/score/tabula.js +18 -0
- package/dist/engines/score/types.d.ts +88 -1
- package/dist/engines/temper/api.d.ts +4 -1
- package/dist/engines/temper/api.js +28 -5
- package/dist/engines/temper/data/guido.js +6 -2
- package/dist/engines/temper/data/modes.d.ts +6 -0
- package/dist/engines/temper/data/modes.js +42 -0
- package/dist/engines/temper/data/tones.d.ts +1 -1
- package/dist/engines/temper/data/tones.js +20 -11
- package/dist/engines/temper/interval.js +4 -3
- package/dist/engines/temper/modality.d.ts +11 -2
- package/dist/engines/temper/modality.js +74 -2
- package/dist/engines/temper/modes.d.ts +1 -1
- package/dist/engines/temper/pitch.d.ts +1 -1
- package/dist/engines/temper/pitch.js +12 -2
- package/dist/engines/temper/scale.d.ts +53 -0
- package/dist/engines/temper/scale.js +107 -8
- package/dist/index.d.ts +26 -8
- package/dist/index.js +40 -4
- package/docs/api/calendar.md +279 -0
- package/docs/api/census.md +288 -0
- package/docs/api/chant.md +657 -0
- package/docs/api/heavens.md +396 -0
- package/docs/api/index.md +265 -0
- package/docs/api/score.md +906 -0
- package/docs/api/tuning.md +619 -0
- package/package.json +13 -5
- package/dist/data/office-matins-roman.d.ts +0 -19
- package/dist/data/office-matins-roman.js +0 -4383
- package/dist/data/office-psalms-roman.d.ts +0 -15
- package/dist/data/office-psalms-roman.js +0 -28
- package/dist/data/office-roman.d.ts +0 -19
- package/dist/data/office-roman.js +0 -13792
- package/dist/engines/chant/matutinum.d.ts +0 -33
- package/dist/engines/chant/matutinum.js +0 -81
- package/dist/engines/score/emitters/midi.d.ts +0 -65
- package/dist/engines/score/emitters/midi.js +0 -162
- package/dist/engines/score/emitters/musicxml.d.ts +0 -18
- package/dist/engines/score/emitters/musicxml.js +0 -166
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// engines/harmonia/data/zodiac — what the twelve signs MEAN
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// `caelum` answers WHERE: a body's ecliptic longitude becomes `zodiac` (the
|
|
6
|
+
// index), `sign` and `signum`. This table answers WHAT IT MEANS — the doctrine
|
|
7
|
+
// a medieval reader brought to that position.
|
|
8
|
+
//
|
|
9
|
+
// DELIBERATELY NO DATES. When the Sun enters a sign is the ephemeris's
|
|
10
|
+
// business and changes with precession; a table that carried "March 21" would
|
|
11
|
+
// be wrong for most of the period this library models, and wrong differently
|
|
12
|
+
// every century. The index is the join, and `caelum` computes it.
|
|
13
|
+
//
|
|
14
|
+
// The join: `SIGNS[i]`, `SIGNA[i]` (engines/planet/planet.ts) and `ZODIACA[i]`
|
|
15
|
+
// are one list three ways, so `ZODIACA[body.zodiac]` is always that body's
|
|
16
|
+
// doctrine. The two name arrays live with the ephemeris because drawing the
|
|
17
|
+
// wheel needs them without any of this; the meanings live here because nothing
|
|
18
|
+
// in computing a position depends on them.
|
|
19
|
+
//
|
|
20
|
+
// Sources, per field:
|
|
21
|
+
// domicile Ptolemy, Tetrabiblos I.17 [biblio: ptolemy-tetrabiblos],
|
|
22
|
+
// transmitted whole through the medieval Latin tradition and
|
|
23
|
+
// uncontested across sources.
|
|
24
|
+
// exaltation Tetrabiblos I.19 and the standard medieval list, THE SEVEN
|
|
25
|
+
// PLANETS ONLY. The lunar nodes' exaltations (Caput Draconis in
|
|
26
|
+
// Gemini, Cauda in Sagittarius) are omitted because the nodes are
|
|
27
|
+
// not tonus bodies. The exaltation DEGREES (Sun at 19° Arietis
|
|
28
|
+
// and the rest) are omitted as more precision than any consumer
|
|
29
|
+
// here reads — five signs simply have no exaltation, and that
|
|
30
|
+
// absence is the tradition's, not an omission.
|
|
31
|
+
// element, the Ptolemaic triplicities and quadruplicities. The Latin
|
|
32
|
+
// quality quality terms are the scholastic mobile/fixum/commune, where
|
|
33
|
+
// Ptolemy writes tropic/solid/bicorporeal.
|
|
34
|
+
// humor the Galenic element→humor square — fire/cholera, earth/
|
|
35
|
+
// melancholia, air/sanguis, water/phlegma — ubiquitous in
|
|
36
|
+
// medieval medicine.
|
|
37
|
+
// melothesia the homo signorum of medieval calendars and medical
|
|
38
|
+
// manuscripts, Aries at the head down to Pisces at the feet (the
|
|
39
|
+
// Très Riches Heures anatomical man is the familiar picture).
|
|
40
|
+
// Practice, not decoration: phlebotomy was timed against it.
|
|
41
|
+
//
|
|
42
|
+
// Where the tradition wobbles, the entry says so in `variant` rather than
|
|
43
|
+
// picking a winner quietly. Uncertainty is data.
|
|
44
|
+
export const ZODIACA = [
|
|
45
|
+
{
|
|
46
|
+
index: 0, sign: "Aries", signum: "Aries", symbol: "♈",
|
|
47
|
+
element: "fire", elementum: "ignis",
|
|
48
|
+
quality: "cardinal", qualitas: "mobile",
|
|
49
|
+
nature: { heat: "calidum", moisture: "siccum" },
|
|
50
|
+
genus: "masculinum", humor: "cholera", temperament: "cholericum",
|
|
51
|
+
domicile: "Mars", exaltation: "Sun",
|
|
52
|
+
melothesia: { latin: "caput", english: "the head and face" },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
index: 1, sign: "Taurus", signum: "Taurus", symbol: "♉",
|
|
56
|
+
element: "earth", elementum: "terra",
|
|
57
|
+
quality: "fixed", qualitas: "fixum",
|
|
58
|
+
nature: { heat: "frigidum", moisture: "siccum" },
|
|
59
|
+
genus: "femininum", humor: "melancholia", temperament: "melancholicum",
|
|
60
|
+
domicile: "Venus", exaltation: "Moon",
|
|
61
|
+
melothesia: { latin: "collum", english: "the neck and throat" },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
index: 2, sign: "Gemini", signum: "Gemini", symbol: "♊",
|
|
65
|
+
element: "air", elementum: "aer",
|
|
66
|
+
quality: "mutable", qualitas: "commune",
|
|
67
|
+
nature: { heat: "calidum", moisture: "humidum" },
|
|
68
|
+
genus: "masculinum", humor: "sanguis", temperament: "sanguineum",
|
|
69
|
+
domicile: "Mercury", exaltation: null,
|
|
70
|
+
melothesia: { latin: "umeri", english: "the shoulders, arms and hands" },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
index: 3, sign: "Cancer", signum: "Cancer", symbol: "♋",
|
|
74
|
+
element: "water", elementum: "aqua",
|
|
75
|
+
quality: "cardinal", qualitas: "mobile",
|
|
76
|
+
nature: { heat: "frigidum", moisture: "humidum" },
|
|
77
|
+
genus: "femininum", humor: "phlegma", temperament: "phlegmaticum",
|
|
78
|
+
domicile: "Moon", exaltation: "Jupiter",
|
|
79
|
+
melothesia: { latin: "pectus", english: "the breast and chest" },
|
|
80
|
+
variant: "the melothesia wobbles here more than anywhere: some manuscripts "
|
|
81
|
+
+ "extend Cancer to the lungs and stomach",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
index: 4, sign: "Leo", signum: "Leo", symbol: "♌",
|
|
85
|
+
element: "fire", elementum: "ignis",
|
|
86
|
+
quality: "fixed", qualitas: "fixum",
|
|
87
|
+
nature: { heat: "calidum", moisture: "siccum" },
|
|
88
|
+
genus: "masculinum", humor: "cholera", temperament: "cholericum",
|
|
89
|
+
domicile: "Sun", exaltation: null,
|
|
90
|
+
melothesia: { latin: "cor", english: "the heart, back and sides" },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
index: 5, sign: "Virgo", signum: "Virgo", symbol: "♍",
|
|
94
|
+
element: "earth", elementum: "terra",
|
|
95
|
+
quality: "mutable", qualitas: "commune",
|
|
96
|
+
nature: { heat: "frigidum", moisture: "siccum" },
|
|
97
|
+
genus: "femininum", humor: "melancholia", temperament: "melancholicum",
|
|
98
|
+
domicile: "Mercury", exaltation: "Mercury",
|
|
99
|
+
melothesia: { latin: "venter", english: "the belly and bowels" },
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
index: 6, sign: "Libra", signum: "Libra", symbol: "♎",
|
|
103
|
+
element: "air", elementum: "aer",
|
|
104
|
+
quality: "cardinal", qualitas: "mobile",
|
|
105
|
+
nature: { heat: "calidum", moisture: "humidum" },
|
|
106
|
+
genus: "masculinum", humor: "sanguis", temperament: "sanguineum",
|
|
107
|
+
domicile: "Venus", exaltation: "Saturn",
|
|
108
|
+
melothesia: { latin: "renes", english: "the kidneys and loins" },
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
index: 7, sign: "Scorpio", signum: "Scorpius", symbol: "♏",
|
|
112
|
+
element: "water", elementum: "aqua",
|
|
113
|
+
quality: "fixed", qualitas: "fixum",
|
|
114
|
+
nature: { heat: "frigidum", moisture: "humidum" },
|
|
115
|
+
genus: "femininum", humor: "phlegma", temperament: "phlegmaticum",
|
|
116
|
+
domicile: "Mars", exaltation: null,
|
|
117
|
+
melothesia: { latin: "genitalia", english: "the genitals" },
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
index: 8, sign: "Sagittarius", signum: "Sagittarius", symbol: "♐",
|
|
121
|
+
element: "fire", elementum: "ignis",
|
|
122
|
+
quality: "mutable", qualitas: "commune",
|
|
123
|
+
nature: { heat: "calidum", moisture: "siccum" },
|
|
124
|
+
genus: "masculinum", humor: "cholera", temperament: "cholericum",
|
|
125
|
+
domicile: "Jupiter", exaltation: null,
|
|
126
|
+
melothesia: { latin: "femora", english: "the thighs" },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
index: 9, sign: "Capricorn", signum: "Capricornus", symbol: "♑",
|
|
130
|
+
element: "earth", elementum: "terra",
|
|
131
|
+
quality: "cardinal", qualitas: "mobile",
|
|
132
|
+
nature: { heat: "frigidum", moisture: "siccum" },
|
|
133
|
+
genus: "femininum", humor: "melancholia", temperament: "melancholicum",
|
|
134
|
+
domicile: "Saturn", exaltation: "Mars",
|
|
135
|
+
melothesia: { latin: "genua", english: "the knees" },
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
index: 10, sign: "Aquarius", signum: "Aquarius", symbol: "♒",
|
|
139
|
+
element: "air", elementum: "aer",
|
|
140
|
+
quality: "fixed", qualitas: "fixum",
|
|
141
|
+
nature: { heat: "calidum", moisture: "humidum" },
|
|
142
|
+
genus: "masculinum", humor: "sanguis", temperament: "sanguineum",
|
|
143
|
+
domicile: "Saturn", exaltation: null,
|
|
144
|
+
melothesia: { latin: "crura", english: "the shins and ankles" },
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
index: 11, sign: "Pisces", signum: "Pisces", symbol: "♓",
|
|
148
|
+
element: "water", elementum: "aqua",
|
|
149
|
+
quality: "mutable", qualitas: "commune",
|
|
150
|
+
nature: { heat: "frigidum", moisture: "humidum" },
|
|
151
|
+
genus: "femininum", humor: "phlegma", temperament: "phlegmaticum",
|
|
152
|
+
domicile: "Jupiter", exaltation: "Venus",
|
|
153
|
+
melothesia: { latin: "pedes", english: "the feet" },
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
// THE LUNAR PROHIBITION, recorded here rather than exported: while the Moon
|
|
157
|
+
// stands in a sign, that sign's member is not to be touched. It is the reason
|
|
158
|
+
// the zodiac man is in a physician's calendar at all — it was "dangerous, if
|
|
159
|
+
// not fatal, to treat that member if the Moon was in the sign at the time" —
|
|
160
|
+
// and it is one line of doctrine that any consumer can state for itself from
|
|
161
|
+
// `ZODIACA[moon.zodiac].melothesia`. A constant holding a sentence of English
|
|
162
|
+
// would be prose pretending to be data.
|
|
163
|
+
//# sourceMappingURL=zodiac.js.map
|
|
@@ -11,6 +11,9 @@ export interface HarmonyTabulaRow {
|
|
|
11
11
|
oct: number;
|
|
12
12
|
spn: string;
|
|
13
13
|
hz: number;
|
|
14
|
+
/** The doctrina's ratio against the mese, [num, den] — what the sphere IS
|
|
15
|
+
* in the scheme, of which spn and hz are the sounding. */
|
|
16
|
+
ratio: readonly [number, number];
|
|
14
17
|
presence: number;
|
|
15
18
|
motion: number;
|
|
16
19
|
velocity: number;
|
|
@@ -13,6 +13,10 @@ export interface VoicedBody extends Body {
|
|
|
13
13
|
presence: number;
|
|
14
14
|
motion: number;
|
|
15
15
|
greekName: string;
|
|
16
|
+
/** The doctrina's own ratio for this sphere, [num, den] against the mese.
|
|
17
|
+
* The pitch is DERIVED from it, so this is the primary datum and the note
|
|
18
|
+
* name is the reading — a caller comparing doctrinae wants the fraction. */
|
|
19
|
+
ratio: readonly [number, number];
|
|
16
20
|
vowel: PlanetVowel;
|
|
17
21
|
}
|
|
18
22
|
export declare function voiceBodies(bodies: Body[], doctrina: Doctrina, scale: Scale): VoicedBody[];
|
|
@@ -5,9 +5,8 @@ import { computePresence, computeMotion } from "./presence.js";
|
|
|
5
5
|
// reference. Ratios in the doctrina are multiplied by this anchor to produce Hz.
|
|
6
6
|
const ANCHOR_MIDI = 69;
|
|
7
7
|
function voiceOne(body, voice, vowel, scale) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const motion = pliny ? 0 : computeMotion(body);
|
|
8
|
+
const presence = computePresence(body);
|
|
9
|
+
const motion = computeMotion(body);
|
|
11
10
|
// Direct Hz from anchor (A4) and doctrina ratio — bypass scale quantization
|
|
12
11
|
// so the ratio's pure Hz relationship is preserved.
|
|
13
12
|
const ratioVal = voice.ratio[0] / voice.ratio[1];
|
|
@@ -28,6 +27,7 @@ function voiceOne(body, voice, vowel, scale) {
|
|
|
28
27
|
presence,
|
|
29
28
|
motion,
|
|
30
29
|
greekName: voice.greekName,
|
|
30
|
+
ratio: voice.ratio,
|
|
31
31
|
vowel,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
@@ -41,9 +41,13 @@ export function voiceBodies(bodies, doctrina, scale) {
|
|
|
41
41
|
const voice = voiceByBody.get(body.name);
|
|
42
42
|
if (!voice)
|
|
43
43
|
continue; // body not part of this doctrina (e.g. Earth in Boethius)
|
|
44
|
+
// Only the seven vowel-bearing planets voice. Earth stays silent even
|
|
45
|
+
// under Pliny, whose table assigns it proslambanomenos — the ratio is
|
|
46
|
+
// kept in the doctrina as documentation, but no classical vowel exists
|
|
47
|
+
// for Earth, and tonus does not invent one.
|
|
44
48
|
const vowel = PLANET_VOWELS[body.name];
|
|
45
49
|
if (!vowel)
|
|
46
|
-
continue;
|
|
50
|
+
continue;
|
|
47
51
|
result.push(voiceOne(body, voice, vowel, scale));
|
|
48
52
|
}
|
|
49
53
|
return result;
|
package/dist/engines/imprint.js
CHANGED
|
@@ -84,6 +84,10 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
84
84
|
const cadenceNotes = opts.cadenceNotes;
|
|
85
85
|
const pcCounts = new Array(12).fill(0);
|
|
86
86
|
let total = 0;
|
|
87
|
+
// For the tessitura signal: the mean MIDI of every note, and the last note.
|
|
88
|
+
let midiSum = 0;
|
|
89
|
+
let noteCount = 0;
|
|
90
|
+
let lastNote = null;
|
|
87
91
|
for (let pi = 0; pi < phrases.length; pi++) {
|
|
88
92
|
const phrase = phrases[pi];
|
|
89
93
|
for (let si = 0; si < phrase.syllables.length; si++) {
|
|
@@ -97,6 +101,9 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
97
101
|
w *= CADENCE_WEIGHT;
|
|
98
102
|
pcCounts[note.pitch.pc] += w;
|
|
99
103
|
total += w;
|
|
104
|
+
midiSum += note.pitch.midi;
|
|
105
|
+
noteCount++;
|
|
106
|
+
lastNote = { pc: note.pitch.pc, midi: note.pitch.midi };
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
}
|
|
@@ -105,11 +112,17 @@ export function computeImprint(phrases, scale, opts = {}) {
|
|
|
105
112
|
pcDistribution[pc] = total > 0 ? pcCounts[pc] / total : 0;
|
|
106
113
|
}
|
|
107
114
|
const firstNotePc = phrases[0]?.syllables[0]?.notes[0]?.pitch.pc;
|
|
115
|
+
// Tessitura = the melody's mean height above where it comes to rest.
|
|
116
|
+
const tessitura = lastNote && noteCount > 0 ? midiSum / noteCount - lastNote.midi : undefined;
|
|
108
117
|
return {
|
|
109
118
|
pcDistribution,
|
|
110
119
|
attractors: computeAttractors(pcDistribution, scale),
|
|
111
120
|
vowelAttractors: computeVowelAttractors(phrases, scale),
|
|
112
|
-
modalAffinity: computeModalAffinity(pcDistribution,
|
|
121
|
+
modalAffinity: computeModalAffinity(pcDistribution, {
|
|
122
|
+
firstNotePc,
|
|
123
|
+
lastNotePc: lastNote?.pc,
|
|
124
|
+
tessitura,
|
|
125
|
+
}),
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
/** Build an Imprint from voiced planetary bodies (presence-weighted pc counts). */
|
|
@@ -34,7 +34,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
34
34
|
datasets: [
|
|
35
35
|
[
|
|
36
36
|
[0.72333566, 0.0000039],
|
|
37
|
-
[0.
|
|
37
|
+
[0.00677672, -0.00004107],
|
|
38
38
|
[3.39467605, -0.0007889],
|
|
39
39
|
[181.9790995, 58517.81538729],
|
|
40
40
|
[131.60246718, 0.00268329],
|
|
@@ -42,7 +42,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
42
42
|
],
|
|
43
43
|
[
|
|
44
44
|
[0.72332102, -0.00000026],
|
|
45
|
-
[
|
|
45
|
+
[0.00676399, -0.00005107],
|
|
46
46
|
[3.39777545, 0.00043494],
|
|
47
47
|
[181.9797085, 58517.8156026],
|
|
48
48
|
[131.76755713, 0.05679648],
|
|
@@ -68,7 +68,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
68
68
|
[0, 0],
|
|
69
69
|
],
|
|
70
70
|
[
|
|
71
|
-
[1.00000018,
|
|
71
|
+
[1.00000018, -0.00000003],
|
|
72
72
|
[0.01673163, -0.00003661],
|
|
73
73
|
[-0.00054346, -0.01337178],
|
|
74
74
|
[100.46691572, 35999.37306329],
|
|
@@ -129,7 +129,7 @@ export const ORBITAL_ELEMENTS = new Map([
|
|
|
129
129
|
[14.27495244, 0.18199196],
|
|
130
130
|
[100.29282654, 0.13024619],
|
|
131
131
|
],
|
|
132
|
-
[0.00012452, 0.0606406, -0.35635438, 38.35125],
|
|
132
|
+
[-0.00012452, 0.0606406, -0.35635438, 38.35125],
|
|
133
133
|
],
|
|
134
134
|
radius: 69911,
|
|
135
135
|
rotation_period: 0.41354,
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { Cosmos, CosmosQuery } from "./types.js";
|
|
2
|
+
/** The twelve signs, in ecliptic order from the vernal point — the machine
|
|
3
|
+
* codes. Exported because a body's `sign` is one of these and a consumer
|
|
4
|
+
* drawing the zodiac needs the same twelve in the same order; copying them out
|
|
5
|
+
* is how a second, drifting list gets written. */
|
|
6
|
+
export declare const SIGNS: readonly ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"];
|
|
7
|
+
/** The same twelve as the books name them. Eight are already their own Latin
|
|
8
|
+
* nominative; the four that differ are the ones English clipped — Scorpius for
|
|
9
|
+
* Scorpio, and the -us/-i endings the others dropped. Parallel to a body's
|
|
10
|
+
* `name`/`nomen` pair: the code stays English, the Latin rides beside it. */
|
|
11
|
+
export declare const SIGNA: readonly ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpius", "Sagittarius", "Capricornus", "Aquarius", "Pisces"];
|
|
2
12
|
/**
|
|
3
13
|
* Ephemeris lookup (`tonus.caelum`). Computes geocentric and heliocentric
|
|
4
14
|
* positions, zodiac signs, retrogradation, and aspects for the classical
|
|
@@ -10,12 +10,25 @@ import { latinName } from "./types.js";
|
|
|
10
10
|
import { DEFAULT_EPOCH } from "../epoch.js";
|
|
11
11
|
const MS_PER_DAY = 86400000;
|
|
12
12
|
const ALL_BODIES = ["Sun", "Moon", "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn"];
|
|
13
|
-
|
|
13
|
+
/** The twelve signs, in ecliptic order from the vernal point — the machine
|
|
14
|
+
* codes. Exported because a body's `sign` is one of these and a consumer
|
|
15
|
+
* drawing the zodiac needs the same twelve in the same order; copying them out
|
|
16
|
+
* is how a second, drifting list gets written. */
|
|
17
|
+
export const SIGNS = [
|
|
14
18
|
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
|
15
19
|
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
|
|
16
20
|
];
|
|
21
|
+
/** The same twelve as the books name them. Eight are already their own Latin
|
|
22
|
+
* nominative; the four that differ are the ones English clipped — Scorpius for
|
|
23
|
+
* Scorpio, and the -us/-i endings the others dropped. Parallel to a body's
|
|
24
|
+
* `name`/`nomen` pair: the code stays English, the Latin rides beside it. */
|
|
25
|
+
export const SIGNA = [
|
|
26
|
+
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
|
27
|
+
"Libra", "Scorpius", "Sagittarius", "Capricornus", "Aquarius", "Pisces",
|
|
28
|
+
];
|
|
17
29
|
const zodiac = (lon) => Math.floor(wrapAngle(lon) / 30) % 12;
|
|
18
30
|
const sign = (lon) => SIGNS[zodiac(lon)];
|
|
31
|
+
const signum = (lon) => SIGNA[zodiac(lon)];
|
|
19
32
|
function computeSpeed(geoLon, name, ts) {
|
|
20
33
|
const nextState = getState(ts + MS_PER_DAY);
|
|
21
34
|
const nextSun = sunPos(nextState);
|
|
@@ -50,6 +63,7 @@ function buildSun(ts) {
|
|
|
50
63
|
apparentDiameter: app.apparentDiameter,
|
|
51
64
|
zodiac: zodiac(pos.geo.lon),
|
|
52
65
|
sign: sign(pos.geo.lon),
|
|
66
|
+
signum: signum(pos.geo.lon),
|
|
53
67
|
};
|
|
54
68
|
}
|
|
55
69
|
function buildMoon(ts) {
|
|
@@ -82,6 +96,7 @@ function buildMoon(ts) {
|
|
|
82
96
|
apparentDiameter: app.apparentDiameter,
|
|
83
97
|
zodiac: zodiac(pos.geo.lon),
|
|
84
98
|
sign: sign(pos.geo.lon),
|
|
99
|
+
signum: signum(pos.geo.lon),
|
|
85
100
|
distEarthRadii: pos.distEarthRadii,
|
|
86
101
|
};
|
|
87
102
|
}
|
|
@@ -115,8 +130,14 @@ function buildPlanet(name, ts) {
|
|
|
115
130
|
elongation: app.elongation,
|
|
116
131
|
phase: app.phase,
|
|
117
132
|
apparentDiameter: app.apparentDiameter,
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
// GEOCENTRIC, as the Sun's and Moon's are: a sign placement says where a
|
|
134
|
+
// body appears from here, which is the only frame in which "Mars in
|
|
135
|
+
// Sagittarius" means anything. Reading the heliocentric longitude put a
|
|
136
|
+
// body's own sign at odds with its own geo.lon — Mercury reported in Aries
|
|
137
|
+
// while appearing in Taurus.
|
|
138
|
+
zodiac: zodiac(pos.geo.lon),
|
|
139
|
+
sign: sign(pos.geo.lon),
|
|
140
|
+
signum: signum(pos.geo.lon),
|
|
120
141
|
};
|
|
121
142
|
}
|
|
122
143
|
function buildEarth(ts) {
|
|
@@ -142,6 +163,7 @@ function buildEarth(ts) {
|
|
|
142
163
|
apparentDiameter: 0,
|
|
143
164
|
zodiac: zodiac(pos.helio.lon),
|
|
144
165
|
sign: sign(pos.helio.lon),
|
|
166
|
+
signum: signum(pos.helio.lon),
|
|
145
167
|
};
|
|
146
168
|
}
|
|
147
169
|
const BODY_BUILDERS = {
|
|
@@ -192,7 +214,12 @@ export function getCosmos(query = {}) {
|
|
|
192
214
|
}
|
|
193
215
|
return frames;
|
|
194
216
|
}
|
|
217
|
+
if (query.feast != null &&
|
|
218
|
+
(typeof query.feast !== "object" || !(query.feast.date instanceof Date)))
|
|
219
|
+
throw new Error("caelum: feast must be a Feast (from tonus.festum) — its date places the sky");
|
|
195
220
|
const date = query.date ?? query.feast?.date ?? DEFAULT_EPOCH;
|
|
221
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime()))
|
|
222
|
+
throw new Error(`caelum: date must be a Date — e.g. new Date("2026-12-25") (UTC-canonical)`);
|
|
196
223
|
return snapshotAt(date, requested, query.orbLimit);
|
|
197
224
|
}
|
|
198
225
|
//# sourceMappingURL=planet.js.map
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
// So the reader should not expect the same constants or solver across bodies.
|
|
17
17
|
//
|
|
18
18
|
// A few time-scale models here are standard astronomy but not yet catalogued in
|
|
19
|
-
// BIBLIOGRAPHY.md (marked "source TBD" at each): the ΔT (TT−UT)
|
|
20
|
-
// the mean-obliquity expansion.
|
|
19
|
+
// ../../../BIBLIOGRAPHY.md (marked "source TBD" at each): the ΔT (TT−UT)
|
|
20
|
+
// polynomial and the mean-obliquity expansion.
|
|
21
21
|
import { sinDeg, cosDeg, atan2Deg, kepler, wrapAngle, toAu, toCartesian, toSpherical, toEquatorial } from "./math.js";
|
|
22
22
|
import { ORBITAL_ELEMENTS } from "./orbital.js";
|
|
23
23
|
const MS_PER_DAY = 86400000;
|
|
@@ -157,18 +157,21 @@ export function planetPos(name, state, sun) {
|
|
|
157
157
|
throw new Error(`Unknown body: ${name}`);
|
|
158
158
|
const { J, T, eps } = state;
|
|
159
159
|
const oe = body.datasets;
|
|
160
|
-
// Two Standish element sets per body (see orbital.ts): [
|
|
161
|
-
// for 1800–2050
|
|
162
|
-
// are J (days from J2000): −73048.5 ≈ 1800, 18626.5 ≈
|
|
163
|
-
// use the precise set, outside fall back to the
|
|
164
|
-
|
|
160
|
+
// Two Standish element sets per body (see orbital.ts): [0] is Table 1, fitted
|
|
161
|
+
// tightly for 1800–2050; [1] is Table 2a, trading accuracy for 3000 BC–3000 AD
|
|
162
|
+
// coverage. The bounds are J (days from J2000): −73048.5 ≈ 1800, 18626.5 ≈
|
|
163
|
+
// 2050. Inside the window use the precise set, outside fall back to the
|
|
164
|
+
// long-range one — which is the set tonus's medieval epoch runs on.
|
|
165
|
+
const inWindow = J > -73048.5 && J < 18626.5;
|
|
166
|
+
const dataset = inWindow ? oe[0] : oe[1];
|
|
165
167
|
const [a, e, I, L, wBar, Omega] = dataset.map(([x0, x1]) => x0 + x1 * T);
|
|
166
168
|
const omega = wBar - Omega; // argument of periapsis
|
|
167
169
|
let M = L - wBar; // mean anomaly
|
|
168
170
|
// Standish's great-inequality correction for the outer planets (Jupiter–Neptune):
|
|
169
|
-
// a secular b·T² plus a long-period cos/sin term at frequency f.
|
|
170
|
-
//
|
|
171
|
-
|
|
171
|
+
// a secular b·T² plus a long-period cos/sin term at frequency f. Table 2b is
|
|
172
|
+
// defined for use with the Table 2a elements only, so it applies exactly when
|
|
173
|
+
// the long-range set is in play. [biblio: standish-jpl]
|
|
174
|
+
if (!inWindow && oe[2]) {
|
|
172
175
|
const [b, c, s, f] = oe[2];
|
|
173
176
|
M += b * T * T + c * cosDeg(f * T) + s * sinDeg(f * T);
|
|
174
177
|
}
|
|
@@ -3,8 +3,6 @@ import { type Prosody } from "./prosody.js";
|
|
|
3
3
|
import { type Cadence } from "./cadence.js";
|
|
4
4
|
import { type Modulation } from "./modulation.js";
|
|
5
5
|
import { type ChantTabulaRow } from "./tabula.js";
|
|
6
|
-
import { type MidiOpts, type MidiEmitResult } from "./emitters/midi.js";
|
|
7
|
-
import { type MusicXmlOpts, type MusicXmlEmitResult } from "./emitters/musicxml.js";
|
|
8
6
|
import type { Chant } from "../chant/types.js";
|
|
9
7
|
import type { Temperamentum } from "../temper/api.js";
|
|
10
8
|
import type { ArticulationProfile, PhrasingProfile, ParseError, Phrase as IRPhrase } from "./types.js";
|
|
@@ -36,14 +34,6 @@ export interface Score {
|
|
|
36
34
|
/** Passages where the tonal centre leans away from the home mode. */
|
|
37
35
|
modulations: Modulation[];
|
|
38
36
|
imprint: Imprint;
|
|
39
|
-
/**
|
|
40
|
-
* Emit a Standard MIDI File from the score's tabula. Returns the file bytes
|
|
41
|
-
* by default; `format: "json"` returns the event structure, `"both"` an
|
|
42
|
-
* object. The score's pondus/accentus reach the output via the tabula.
|
|
43
|
-
*/
|
|
44
|
-
midi(opts?: MidiOpts): Uint8Array | MidiEmitResult;
|
|
45
|
-
/** Emit a MusicXML 4.0 partwise document from the score's tabula. */
|
|
46
|
-
musicxml(opts?: MusicXmlOpts): MusicXmlEmitResult;
|
|
47
37
|
}
|
|
48
38
|
/**
|
|
49
39
|
* Score builder (`tonus.notatio`). Parses a chant's GABC into a musical
|
|
@@ -55,8 +45,7 @@ export interface Score {
|
|
|
55
45
|
*/
|
|
56
46
|
export declare function buildScore(chant: Chant, opts?: ScoreOpts): Score;
|
|
57
47
|
export type { ParseError };
|
|
58
|
-
export type { Cadence, CadenceTarget, CadenceApproach } from "./cadence.js";
|
|
48
|
+
export type { Cadence, CadenceTarget, CadenceApproach, CadenceKeyEvent } from "./cadence.js";
|
|
49
|
+
export { cadenceKeys } from "./cadence.js";
|
|
59
50
|
export type { Modulation } from "./modulation.js";
|
|
60
|
-
export type { MidiOpts, MidiEmitResult, MidiJsonResult, MidiJsonEvent } from "./emitters/midi.js";
|
|
61
|
-
export type { MusicXmlOpts, MusicXmlEmitResult } from "./emitters/musicxml.js";
|
|
62
51
|
//# sourceMappingURL=api.d.ts.map
|
|
@@ -11,8 +11,7 @@ import { detectCadences } from "./cadence.js";
|
|
|
11
11
|
import { detectModulations } from "./modulation.js";
|
|
12
12
|
import { computeTabula } from "./tabula.js";
|
|
13
13
|
import { MODES } from "../temper/modes.js";
|
|
14
|
-
import {
|
|
15
|
-
import { toMusicXML } from "./emitters/musicxml.js";
|
|
14
|
+
import { cadentiaFamilia } from "../../data/cadentiae.js";
|
|
16
15
|
const PONDUS_TO_ARTICULATION = {
|
|
17
16
|
restrained: "restrained",
|
|
18
17
|
balanced: "balanced",
|
|
@@ -40,6 +39,8 @@ function resolveAccentus(input) {
|
|
|
40
39
|
* @throws Error on invalid Chant input or unparseable GABC.
|
|
41
40
|
*/
|
|
42
41
|
export function buildScore(chant, opts) {
|
|
42
|
+
if (!chant || typeof chant !== "object" || typeof chant.gabc !== "string")
|
|
43
|
+
throw new Error("notatio needs a Chant with a gabc string — build one with tonus.cantus({ gabc })");
|
|
43
44
|
const pondus = resolvePondus(opts?.pondus);
|
|
44
45
|
const accentus = resolveAccentus(opts?.accentus);
|
|
45
46
|
const parsed = parseGABC(chant.gabc, {
|
|
@@ -62,10 +63,24 @@ export function buildScore(chant, opts) {
|
|
|
62
63
|
// Cadence detection runs here, where the resolved mode (and its cadence
|
|
63
64
|
// figures) is in hand. Pure data — mirrors the arsis/thesis pass in ir.ts.
|
|
64
65
|
const cadences = detectCadences(ir.phrases, meta.mode != null ? MODES.get(meta.mode) : undefined);
|
|
66
|
+
// The corpus join, on the same footing as MODES above: a resolved table
|
|
67
|
+
// meeting detected data. The detector computes the signature and stops, so
|
|
68
|
+
// this is the one place a cadence learns how often its family closes.
|
|
69
|
+
// Below the catalogue's floor there is no family, and finality stays null —
|
|
70
|
+
// an uncatalogued close, not a close that never closes.
|
|
71
|
+
for (const cadence of cadences) {
|
|
72
|
+
cadence.finality = cadence.signature
|
|
73
|
+
? (cadentiaFamilia(cadence.signature)?.finality ?? null)
|
|
74
|
+
: null;
|
|
75
|
+
}
|
|
65
76
|
// Modulation: where the tonal centre leans away from the home mode.
|
|
66
77
|
const modulations = detectModulations(ir.phrases, meta.mode ?? undefined);
|
|
67
78
|
const tabula = computeTabula(ir, {
|
|
68
79
|
mode: meta.mode ?? undefined,
|
|
80
|
+
// The office gate: a chant that names its liturgical type gets phrasing
|
|
81
|
+
// even when its mode must be inferred — the gate computeTabula always had,
|
|
82
|
+
// now actually fed.
|
|
83
|
+
office: chant.office,
|
|
69
84
|
a4Hz: opts?.temperamentum?.a4,
|
|
70
85
|
transpose: opts?.temperamentum?.transpose,
|
|
71
86
|
cadences,
|
|
@@ -93,12 +108,10 @@ export function buildScore(chant, opts) {
|
|
|
93
108
|
return `${pi}:${si}:${ni}`;
|
|
94
109
|
})),
|
|
95
110
|
}),
|
|
96
|
-
midi(emitOpts) {
|
|
97
|
-
return toMidi(tabula, emitOpts);
|
|
98
|
-
},
|
|
99
|
-
musicxml(emitOpts) {
|
|
100
|
-
return toMusicXML(tabula, chant, emitOpts);
|
|
101
|
-
},
|
|
102
111
|
};
|
|
103
112
|
}
|
|
113
|
+
// THE cadence family key, exported as a FUNCTION and not only a type: the
|
|
114
|
+
// census and the CADENTIAE miner need to key a flat tabula, and re-deriving
|
|
115
|
+
// the algorithm is exactly the fork this shared export forbids.
|
|
116
|
+
export { cadenceKeys } from "./cadence.js";
|
|
104
117
|
//# sourceMappingURL=api.js.map
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// The pondus ("weight") tables. Each GABC performance mark — episema, quilisma,
|
|
5
5
|
// liquescent, strophicus, oriscus, ictus — carries a weight delta and a
|
|
6
|
-
// duration delta that the parser folds into a note's
|
|
7
|
-
// tanh compressor in parse.ts). The signs encode the semiological reading of
|
|
6
|
+
// duration delta that the parser folds into a note's weight and duration (see
|
|
7
|
+
// the tanh compressor in parse.ts). The signs encode the semiological reading of
|
|
8
8
|
// the mark [biblio: cardine-semiology], and the durational values the Solesmes
|
|
9
9
|
// rhythmic tradition [biblio: desrocquettes-values, liber-usualis]: POSITIVE
|
|
10
10
|
// lengthens/stresses, NEGATIVE lightens/shortens. So an episema lengthens
|
|
@@ -28,7 +28,83 @@ export interface Cadence {
|
|
|
28
28
|
confidence: number;
|
|
29
29
|
/** Note positions forming this cadence: [phraseIndex, syllableIndex, noteIndex]. */
|
|
30
30
|
notes: Array<[number, number, number]>;
|
|
31
|
+
/**
|
|
32
|
+
* The corpus-catalogue key, "shape @arrival" (e.g. "2,0,-2 @0" — see
|
|
33
|
+
* CADENTIAE). A single-note phrase keys with an empty shape (" @0"):
|
|
34
|
+
* a landing with no gesture is still a cadence.
|
|
35
|
+
*/
|
|
36
|
+
signature: string | null;
|
|
37
|
+
/** Interval signature of the closing tail (<=4 notes), in semitones. */
|
|
38
|
+
shape: number[];
|
|
39
|
+
/** Closing note minus the CHANT'S OWN closing note (its sounded final —
|
|
40
|
+
* not the labeled mode's final, which may disagree on a transposed or
|
|
41
|
+
* mislabeled chant), in SIGNED semitones — not octave-reduced. */
|
|
42
|
+
arrival: number;
|
|
43
|
+
/**
|
|
44
|
+
* The catalogued family's measured finality: the share of THIS FAMILY's
|
|
45
|
+
* corpus occurrences that fall at a final close. null when the signature is
|
|
46
|
+
* below the catalogue's floor (about a third of cadences), and null on a
|
|
47
|
+
* cadence taken straight from `detectCadences` — the join happens in the
|
|
48
|
+
* score builder, not the detector.
|
|
49
|
+
*
|
|
50
|
+
* A measurement, not a name, which is why it rides here while `familia`
|
|
51
|
+
* does not: the signature is already the family's name, but how often that
|
|
52
|
+
* family CLOSES cannot be read off the signature. Families landing on the
|
|
53
|
+
* final range from 0.054 to 1.000, so `arrival === 0` does not imply a close.
|
|
54
|
+
*/
|
|
55
|
+
finality: number | null;
|
|
31
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Octave-reduce a semitone offset to [-5..+6].
|
|
59
|
+
*
|
|
60
|
+
* NOT part of the family key — kept because the folded value is still worth
|
|
61
|
+
* reporting (it is the scale DEGREE, mode-theoretically real). As a key the
|
|
62
|
+
* fold made a fifth ABOVE the final share a family with a fourth BELOW:
|
|
63
|
+
* measured over 27,985 phrase ends, 3,499 landed on @-5, of which 2,427 were
|
|
64
|
+
* really +7 and 1,072 really -5. Two opposite gestures, one key. Arrival in
|
|
65
|
+
* the key is therefore the SIGNED offset; see cadenceKeys().
|
|
66
|
+
*/
|
|
67
|
+
export declare function reduceArrival(semitones: number): number;
|
|
68
|
+
/** One phrase-end event: the family key, and whether it closes the chant. */
|
|
69
|
+
export interface CadenceKeyEvent {
|
|
70
|
+
/** `"<interval,interval,…> @<signed arrival>"` — empty shape for a 1-note phrase. */
|
|
71
|
+
key: string;
|
|
72
|
+
/** Shape only: the tail's successive semitone intervals. */
|
|
73
|
+
shape: number[];
|
|
74
|
+
/** Signed semitone offset of the landing note from the chant's closing note. */
|
|
75
|
+
arrival: number;
|
|
76
|
+
/** The arrival octave-reduced to [-5..+6] — the scale degree, not the key. */
|
|
77
|
+
degree: number;
|
|
78
|
+
/** A chant end, or a full-bar "::" — as opposed to an interior phrase end. */
|
|
79
|
+
isFinal: boolean;
|
|
80
|
+
/** Index of the phrase this closes. */
|
|
81
|
+
phraseIndex: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* THE cadence family key — one implementation, shared by every consumer.
|
|
85
|
+
*
|
|
86
|
+
* This once existed three times: here (keyed off the Phrase tree), in
|
|
87
|
+
* tonus-corpus `census/_shared.mjs` (keyed off flat tabula rows), and in the
|
|
88
|
+
* CADENTIAE miner (a character-for-character copy of the census one). They
|
|
89
|
+
* agreed — measured corpus-wide, 28,051 engine cadences against 27,985 census
|
|
90
|
+
* phrase ends with ZERO key disagreements — but agreement by luck across three
|
|
91
|
+
* copies is what "no second parser, no drift" forbids; hence this one shared
|
|
92
|
+
* function.
|
|
93
|
+
*
|
|
94
|
+
* Takes the FLAT shape, because that is what the census and the miner have; the
|
|
95
|
+
* engine's own detection flattens into it. A phrase end is a `phraseIndex`
|
|
96
|
+
* transition or the last row.
|
|
97
|
+
*
|
|
98
|
+
* A ONE-NOTE PHRASE IS A CADENCE — it has a landing but no gesture, so it is
|
|
99
|
+
* emitted with an empty shape rather than skipped. (The census once dropped
|
|
100
|
+
* these — 68 corpus-wide, all real phrases carrying a real divisio, mostly
|
|
101
|
+
* "::" at the chant end — a hole this shared key closes.)
|
|
102
|
+
*/
|
|
103
|
+
export declare function cadenceKeys(rows: readonly {
|
|
104
|
+
phraseIndex: number;
|
|
105
|
+
midi: number;
|
|
106
|
+
divisio: string | null;
|
|
107
|
+
}[], finalMidi?: number): CadenceKeyEvent[];
|
|
32
108
|
/**
|
|
33
109
|
* Detect the cadence closing each phrase. One Cadence per phrase that carries a
|
|
34
110
|
* divisio. With no mode, targets/approach are still classified but no figure is
|