tonus 0.1.6 → 0.5.0
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 +132 -108
- package/CHANGELOG.md +610 -1
- package/LICENSE +133 -29
- package/README.md +106 -83
- package/dist/data/am.js +2669 -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 +24 -0
- package/dist/data/corpus-overlap.js +347 -0
- 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 +757 -6394
- package/dist/data/kyriale.js +116 -116
- package/dist/data/la.js +802 -13419
- package/dist/data/lh.js +116 -3473
- package/dist/data/lu.js +935 -17632
- package/dist/data/nocturnale-romanum.d.ts +5 -0
- package/dist/data/nocturnale-romanum.js +3772 -0
- 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 +22 -4
- package/dist/engines/chant/chant.js +274 -14
- 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 +145 -10
- 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/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 +1328 -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 +28 -6
- package/dist/index.js +39 -3
- 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 +346 -0
- package/docs/api/index.md +263 -0
- package/docs/api/score.md +873 -0
- package/docs/api/tuning.md +619 -0
- package/package.json +11 -5
- 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/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,346 @@
|
|
|
1
|
+
# Heavens
|
|
2
|
+
|
|
3
|
+
`tonus.caelum` computes the classical bodies' places for a single moment, from
|
|
4
|
+
JPL Keplerian elements valid 3000 BC – 3000 AD. `tonus.harmonia` voices those
|
|
5
|
+
bodies through a planetary-harmony doctrine, assigning each visible planet a
|
|
6
|
+
tuned pitch and a Greek vowel. The doctrines and their derived ratios are
|
|
7
|
+
summarized in [Theory & Context](#theory--context).
|
|
8
|
+
|
|
9
|
+
- [Heavens](#heavens)
|
|
10
|
+
- [The heavens — `caelum`](#the-heavens--caelum)
|
|
11
|
+
- [Aspects](#aspects)
|
|
12
|
+
- [The voiced heavens — `harmonia`](#the-voiced-heavens--harmonia)
|
|
13
|
+
- [The planetary vowels](#the-planetary-vowels)
|
|
14
|
+
- [The tabula](#the-tabula)
|
|
15
|
+
- [Theory \& Context](#theory--context)
|
|
16
|
+
|
|
17
|
+
## The heavens — `caelum`
|
|
18
|
+
|
|
19
|
+
`caelum(query?)` returns a `Cosmos` for a single moment and `Cosmos[]` for
|
|
20
|
+
a `from`/`to` range. With `bodies` omitted, all eight are returned, and
|
|
21
|
+
aspects are computed only between requested bodies. A `feast` supplies its
|
|
22
|
+
date; an explicit `date` takes precedence.
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
tonus.caelum({ date: new Date("2026-12-25") });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// bodies[6], Jupiter that morning — retrograde in Leo:
|
|
30
|
+
{ name: "Jupiter", nomen: "Iuppiter", symbol: "♃",
|
|
31
|
+
helio: { lon: 137.9, lat: 0.79, dist: 5.33 },
|
|
32
|
+
geo: { lon: 146.6, … },
|
|
33
|
+
speed: -0.037, retrograde: true,
|
|
34
|
+
magnitude: -2.13, elongation: 125.1, phase: 0.99,
|
|
35
|
+
zodiac: 4, sign: "Leo" }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
tonus.caelum(); // the default epoch (991)
|
|
40
|
+
tonus.caelum({ feast: feasts[0] }); // the feast's date
|
|
41
|
+
tonus.caelum({ bodies: ["Sun", "Moon", "Jupiter"] });
|
|
42
|
+
tonus.caelum({ from: jan1, to: dec31, step: 7 }); // weekly snapshots
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The eight bodies:
|
|
46
|
+
|
|
47
|
+
| `name` | `nomen` | | `name` | `nomen` | |
|
|
48
|
+
| ------- | --------- | --- | ------- | -------- | --- |
|
|
49
|
+
| Sun | Sol | ☉ | Earth | Terra | ♁ |
|
|
50
|
+
| Moon | Luna | ☾ | Mars | Mars | ♂ |
|
|
51
|
+
| Mercury | Mercurius | ☿ | Jupiter | Iuppiter | ♃ |
|
|
52
|
+
| Venus | Venus | ♀ | Saturn | Saturnus | ♄ |
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
interface CosmosQuery {
|
|
56
|
+
date?: Date;
|
|
57
|
+
feast?: Feast;
|
|
58
|
+
from?: Date;
|
|
59
|
+
to?: Date;
|
|
60
|
+
step?: number; // days, default 1
|
|
61
|
+
bodies?: BodyName[];
|
|
62
|
+
orbLimit?: number; // max orb for aspect detection, degrees (default 8)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface Cosmos {
|
|
66
|
+
date: Date;
|
|
67
|
+
bodies: Body[];
|
|
68
|
+
aspects: Aspect[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface Body {
|
|
72
|
+
name: BodyName; // from the table above
|
|
73
|
+
nomen: string; // Latin name
|
|
74
|
+
symbol: string; // Unicode symbol
|
|
75
|
+
helio: HelioPos;
|
|
76
|
+
geo: GeoPos;
|
|
77
|
+
speed: number; // deg/day (negative = retrograde)
|
|
78
|
+
retrograde: boolean;
|
|
79
|
+
magnitude: number;
|
|
80
|
+
elongation: number; // deg from Sun (geocentric)
|
|
81
|
+
phase: number; // 0–1 illuminated fraction
|
|
82
|
+
apparentDiameter: number | { equ: number; pol: number }; // arcsec
|
|
83
|
+
zodiac: number; // sign 0–11 (Aries=0 … Pisces=11)
|
|
84
|
+
sign: string; // the code: "Aries", "Taurus", …
|
|
85
|
+
signum: string; // the Latin: "Scorpius", "Capricornus", …
|
|
86
|
+
distEarthRadii?: number; // Moon only
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface HelioPos {
|
|
90
|
+
lon: number; // ecliptic longitude, deg (0–360)
|
|
91
|
+
lat: number; // ecliptic latitude, deg
|
|
92
|
+
dist: number; // distance from Sun, AU
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface GeoPos {
|
|
96
|
+
lon: number;
|
|
97
|
+
lat: number;
|
|
98
|
+
dist: number;
|
|
99
|
+
equatorial: {
|
|
100
|
+
ra: number; // right ascension, deg
|
|
101
|
+
dec: number; // declination, deg
|
|
102
|
+
dist: number;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Aspects
|
|
108
|
+
|
|
109
|
+
Aspects are angular relationships between geocentric body
|
|
110
|
+
longitudes, detected within the orb limit. Strength falls linearly from 1
|
|
111
|
+
at the exact angle to 0 at the limit. On Christmas 2026 the sky holds
|
|
112
|
+
eight, led by a Mercury–Jupiter trine 1.1° from exact.
|
|
113
|
+
|
|
114
|
+
| `type` | angle |
|
|
115
|
+
| ------------- | ----- |
|
|
116
|
+
| `conjunction` | 0° |
|
|
117
|
+
| `sextile` | 60° |
|
|
118
|
+
| `square` | 90° |
|
|
119
|
+
| `trine` | 120° |
|
|
120
|
+
| `opposition` | 180° |
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
interface Aspect {
|
|
124
|
+
type: string; // from the table above
|
|
125
|
+
bodies: [string, string];
|
|
126
|
+
angle: number; // exact separation, deg
|
|
127
|
+
orb: number; // degrees from exact aspect angle
|
|
128
|
+
strength: number; // 0–1
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## The voiced heavens — `harmonia`
|
|
133
|
+
|
|
134
|
+
`harmonia(cosmos, opts?)` gives each body a pitch and a Greek vowel according to
|
|
135
|
+
a **doctrina** (a named historical scheme for who sounds what: Pythagoras,
|
|
136
|
+
Boethius, Pliny, Ptolemy). The result
|
|
137
|
+
is pure data: each visible planet becomes a `VoicedBody` with a tuned pitch and
|
|
138
|
+
a Greek vowel; each aspect gains the `Interval` its angle sounds, with its
|
|
139
|
+
consonance grade. Range input populates `frames`, one per cosmos.
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const sky = tonus.caelum({ date: new Date("2026-12-25") });
|
|
143
|
+
const h = tonus.harmonia(sky); // Boethius, pythagorean A440
|
|
144
|
+
|
|
145
|
+
h.auctor; // "Anicius Manlius Severinus Boethius"
|
|
146
|
+
|
|
147
|
+
h.bodies.find((b) => b.name === "Jupiter");
|
|
148
|
+
// { …Body, nota: { pitch: { spn: "F4", hz: 347.65, … },
|
|
149
|
+
// performance: { velocity: 0.55, … } },
|
|
150
|
+
// presence: 0.55, motion: 0.15,
|
|
151
|
+
// greekName: "parhypate meson", vowel: { name: "Upsilon", phonetic: "u", … } }
|
|
152
|
+
|
|
153
|
+
h.aspects[0];
|
|
154
|
+
// trine Mercury–Jupiter → interval: Quinta, consonance: "perfect"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The doctrinae:
|
|
158
|
+
|
|
159
|
+
| `doctrina` | Source | Span | Notable |
|
|
160
|
+
| -------------- | -------------------------- | --------- | ------------------------------------------------------------- |
|
|
161
|
+
| `"pythagoras"` | via Plato, _Republic_ X | 1 octave | Disjunct diatonic tetrachords (B durum); includes Fixed Stars |
|
|
162
|
+
| `"boethius"` | _De institutione musica_ | major 7th | Conjunct diatonic tetrachords (B molle); medieval default |
|
|
163
|
+
| `"pliny"` | _Naturalis historia_ II.xx | 1 octave | Chromatic Dorian (distance-based); Earth = proslambanomenos |
|
|
164
|
+
| `"ptolemy"` | _Harmonics_ III | 2 octaves | Fixed tones of the Greater Perfect System |
|
|
165
|
+
|
|
166
|
+
Sphere pitches are computed directly from the doctrina's pure ratios,
|
|
167
|
+
anchored at the temperamentum's A4, so historical coherence holds:
|
|
168
|
+
`temperamentum("ptolemy-intense")` with `harmonia({ doctrina: "ptolemy" })`
|
|
169
|
+
gives pure Ptolemaic intervals throughout — Sun→Jupiter a pure 3/2,
|
|
170
|
+
Sun→Saturn a pure 2/1. The temperamentum's scale governs pitch naming and
|
|
171
|
+
the imprint.
|
|
172
|
+
|
|
173
|
+
Each voiced body's `nota` carries its velocity as a 0–1 factor: the body's
|
|
174
|
+
presence (visibility and brightness), on the same scale the score engine uses.
|
|
175
|
+
An aspect's interval is graded by class:
|
|
176
|
+
|
|
177
|
+
| interval class | consonance |
|
|
178
|
+
| -------------- | ---------- |
|
|
179
|
+
| P1, P5, P8 | perfect |
|
|
180
|
+
| m3, M3, m6, M6 | imperfect |
|
|
181
|
+
| all others | dissonant |
|
|
182
|
+
|
|
183
|
+
Bodies without a classical vowel are not voiced: Earth and the Fixed
|
|
184
|
+
Stars, Pliny's Earth-as-proslambanomenos among them.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
tonus.harmonia(sky, { doctrina: "ptolemy" });
|
|
188
|
+
tonus.harmonia(sky, { temperamentum: tonus.temperamentum("ptolemy-intense") });
|
|
189
|
+
const h2 = tonus.harmonia(tonus.caelum({ from, to })); // h2.frames populated
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
interface HarmoniaOpts {
|
|
194
|
+
temperamentum?: Temperamentum; // default: pythagorean A440
|
|
195
|
+
doctrina?: string; // from the doctrinae table; default "boethius"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface Harmony {
|
|
199
|
+
doctrina: string;
|
|
200
|
+
auctor: string; // full Latin name of the doctrina's author
|
|
201
|
+
date: Date; // first cosmos's date
|
|
202
|
+
bodies: VoicedBody[];
|
|
203
|
+
aspects: VoicedAspect[];
|
|
204
|
+
frames?: Frame[]; // only when input was an array of cosmos
|
|
205
|
+
|
|
206
|
+
tabula: HarmonyTabulaRow[]; // flat iterable view
|
|
207
|
+
imprint: Imprint; // shared analytical fingerprint — score.md
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface VoicedPitch {
|
|
211
|
+
pitch: Pitch;
|
|
212
|
+
performance: Performance; // velocity 0–1, presence-scaled
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
interface VoicedBody extends Body {
|
|
216
|
+
nota: VoicedPitch;
|
|
217
|
+
presence: number; // 0–1 (visibility + brightness)
|
|
218
|
+
motion: number; // 0–1 (normalized speed)
|
|
219
|
+
greekName: string; // position in the Greek tonal system
|
|
220
|
+
vowel: PlanetVowel;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
interface VoicedAspect extends Aspect {
|
|
224
|
+
interval: Interval; // carries the consonance grade
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
interface Frame {
|
|
228
|
+
date: Date;
|
|
229
|
+
bodies: VoicedBody[];
|
|
230
|
+
aspects: VoicedAspect[];
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## The planetary vowels
|
|
235
|
+
|
|
236
|
+
Each classical planet sounds one of the seven Greek vowels; the
|
|
237
|
+
Moon→Saturn ordering follows Nicomachus (_Excerpta ex Nicomacho_ 6), the
|
|
238
|
+
attestations Godwin's _The Mystery of the Seven Vowels_ (1991).
|
|
239
|
+
|
|
240
|
+
| Body | Greek | Name | Phonetic |
|
|
241
|
+
| ------- | ----- | ------- | -------- |
|
|
242
|
+
| Moon | Α / α | Alpha | a |
|
|
243
|
+
| Mercury | Ε / ε | Epsilon | e |
|
|
244
|
+
| Venus | Η / η | Eta | e |
|
|
245
|
+
| Sun | Ι / ι | Iota | i |
|
|
246
|
+
| Mars | Ο / ο | Omicron | o |
|
|
247
|
+
| Jupiter | Υ / υ | Upsilon | u |
|
|
248
|
+
| Saturn | Ω / ω | Omega | o |
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
interface PlanetVowel {
|
|
252
|
+
greek: string; // "Α"
|
|
253
|
+
greekLower: string; // "α"
|
|
254
|
+
name: string; // "Alpha"
|
|
255
|
+
modern: string; // "A"
|
|
256
|
+
phonetic: "a" | "e" | "i" | "o" | "u";
|
|
257
|
+
ipa: string;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## The tabula
|
|
262
|
+
|
|
263
|
+
`harmony.tabula` is the flat iteration surface: one row per voiced body
|
|
264
|
+
(chant scores expose the same surface per note,
|
|
265
|
+
[score.md](score.md#the-tabula)).
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
h.tabula.find((r) => r.name === "Jupiter");
|
|
269
|
+
// { name: "Jupiter", nomen: "Iuppiter", greekName: "parhypate meson",
|
|
270
|
+
// ratio: [64, 81], spn: "F4", hz: 347.7, presence: 0.55, velocity: 0.55,
|
|
271
|
+
// vowelGreek: "Υ", sign: "Leo", aspectCount: 3, … }
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`ratio` is the doctrina's own fraction against the mese, the primary datum of
|
|
275
|
+
the whole scheme — `spn` and `hz` are that ratio sounded against A4, not
|
|
276
|
+
independent claims. Comparing doctrinae means comparing these: the table
|
|
277
|
+
under [Theory & Context](#theory--context) is what the field returns.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
interface HarmonyTabulaRow {
|
|
281
|
+
bodyIndex: number;
|
|
282
|
+
name: BodyName;
|
|
283
|
+
nomen: string;
|
|
284
|
+
greekName: string;
|
|
285
|
+
|
|
286
|
+
ratio: readonly [number, number]; // the doctrina's fraction against the mese
|
|
287
|
+
midi: number;
|
|
288
|
+
pc: number;
|
|
289
|
+
oct: number;
|
|
290
|
+
spn: string;
|
|
291
|
+
hz: number;
|
|
292
|
+
|
|
293
|
+
presence: number; // 0–1
|
|
294
|
+
motion: number; // 0–1
|
|
295
|
+
velocity: number; // 0–1, presence-scaled
|
|
296
|
+
|
|
297
|
+
vowelGreek: string;
|
|
298
|
+
vowelPhonetic: string;
|
|
299
|
+
vowelName: string;
|
|
300
|
+
|
|
301
|
+
zodiac: number; // 0–11
|
|
302
|
+
sign: string;
|
|
303
|
+
retrograde: boolean;
|
|
304
|
+
elongation: number; // deg from Sun
|
|
305
|
+
magnitude: number;
|
|
306
|
+
|
|
307
|
+
aspectCount: number; // aspects this body participates in
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Theory & Context
|
|
312
|
+
|
|
313
|
+
The doctrina ratios are reconstructed from the primary texts through
|
|
314
|
+
Joscelyn Godwin's syntheses, mapping each body to a Greek tone-name and
|
|
315
|
+
deriving its ratio by Pythagorean interval arithmetic normalized to the
|
|
316
|
+
mese (Sun = 1/1). The full method, the taxonomy, and the decisions taken
|
|
317
|
+
along the way are documented at the data — see `DOCTRINAE` in
|
|
318
|
+
[`harmonia/data/doctrines.ts`](https://github.com/jeffreypierce/tonus/blob/main/src/engines/harmonia/data/doctrines.ts).
|
|
319
|
+
The same arithmetic is laid out from the tuning side in
|
|
320
|
+
[tuning.md](tuning.md#theory--context).
|
|
321
|
+
|
|
322
|
+
The resulting ratios, by sphere from the outermost:
|
|
323
|
+
|
|
324
|
+
| body | `pythagoras` | `boethius` | `pliny` | `ptolemy` |
|
|
325
|
+
| ----------- | ------------ | ---------- | ----------- | --------- |
|
|
326
|
+
| Fixed Stars | 3/2 | — | — | — |
|
|
327
|
+
| Saturn | 3/4 | 3/4 | 4/3 | 2/1 |
|
|
328
|
+
| Jupiter | 64/81 | 64/81 | 65536/59049 | 3/2 |
|
|
329
|
+
| Mars | 8/9 | 8/9 | 256/243 | 9/8 |
|
|
330
|
+
| Sun | 1/1 | 1/1 | 1/1 | 1/1 |
|
|
331
|
+
| Venus | 9/8 | 256/243 | 16384/19683 | 3/4 |
|
|
332
|
+
| Mercury | 32/27 | 32/27 | 64/81 | 9/16 |
|
|
333
|
+
| Moon | 4/3 | 4/3 | 3/4 | 1/2 |
|
|
334
|
+
| Earth | — | — | 2/3 | — |
|
|
335
|
+
|
|
336
|
+
The single pitch separating Pythagoras from Boethius is Venus: a whole
|
|
337
|
+
tone above the Sun in the disjunct system (9/8, B durum), a semitone in
|
|
338
|
+
the conjunct (256/243, B molle) — the origin of the durum/molle
|
|
339
|
+
distinction that runs through all of medieval music theory.
|
|
340
|
+
|
|
341
|
+
## Sources
|
|
342
|
+
|
|
343
|
+
Sources for this page are in the central [bibliography](https://github.com/jeffreypierce/tonus/blob/main/BIBLIOGRAPHY.md):
|
|
344
|
+
`godwin-harmonies`, `godwin-spheres`, `godwin-vowels`, `boethius-institutione`,
|
|
345
|
+
`doctrina-primaries` (Nicomachus, Plato, Pliny, Ptolemy, and the vowel
|
|
346
|
+
attestations), `standish-jpl`, `schlyter-positions`.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# tonus API
|
|
2
|
+
|
|
3
|
+
The technical center of tonus: the full public API, the conventions every method
|
|
4
|
+
obeys, and the error contract. The API is **fourteen methods on the `tonus`
|
|
5
|
+
namespace**, no sub-namespaces.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import tonus from "tonus";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- [The methods](#the-methods) — by engine
|
|
12
|
+
- [The appendix](#the-appendix) — the canonical constant tables
|
|
13
|
+
- [Full contents](#full-contents) — every method and section
|
|
14
|
+
- [Conventions](#conventions) — Latin/English, dates, determinism, error contracts, bibliography
|
|
15
|
+
|
|
16
|
+
## The methods
|
|
17
|
+
|
|
18
|
+
| Method | Page | Returns |
|
|
19
|
+
| ---------------------------------- | ----------------------- | ------------------------------------------------------- |
|
|
20
|
+
| `tonus.festum(query?)` | [calendar](calendar.md) | `Feast[]` — liturgical calendar lookup |
|
|
21
|
+
| `tonus.pascha(year)` | [calendar](calendar.md) | `Pascha` — the movable anchors of a liturgical year |
|
|
22
|
+
| `tonus.cantus(query?)` | [chant](chant.md) | `Chant[]` — cross-corpus chant retrieval / GABC parsing |
|
|
23
|
+
| `tonus.corpus(code?)` | [chant](chant.md) | `Corpus` — a book's ledger; no arg → the whole shelf |
|
|
24
|
+
| `tonus.proprium(query?)` | [chant](chant.md) | `Chant[]` — Mass propers |
|
|
25
|
+
| `tonus.ordinarium(query?)` | [chant](chant.md) | `OrdinaryChant[]` — Kyriale |
|
|
26
|
+
| `tonus.officium(query?)` | [chant](chant.md) | `Chant[]` — Divine Office hours |
|
|
27
|
+
| `tonus.psalmus(query?)` | [chant](chant.md) | `Chant[]` — intoned psalm verses |
|
|
28
|
+
| `tonus.temperamentum(input?)` | [tuning](tuning.md) | `Temperamentum` — tuning context |
|
|
29
|
+
| `tonus.notatio(chant, opts?)` | [score](score.md) | `Score` — GABC → musical score |
|
|
30
|
+
| `tonus.inscriptio(score, opts?)` | [score](score.md) | `Inscriptio` — `{ svg, geometry }`, the score drawn |
|
|
31
|
+
| `tonus.caelum(query?)` | [heavens](heavens.md) | `Cosmos \| Cosmos[]` — ephemeris |
|
|
32
|
+
| `tonus.harmonia(cosmos, opts?)` | [heavens](heavens.md) | `Harmony` — musica universalis |
|
|
33
|
+
| `tonus.census(query)` | [census](census.md) | `Census` — a chant against the corpus |
|
|
34
|
+
|
|
35
|
+
### Query and builder functions
|
|
36
|
+
|
|
37
|
+
**Query functions** (`festum`, `pascha`, `cantus`,
|
|
38
|
+
`corpus`, `proprium`, `ordinarium`, `officium`, `psalmus`, `caelum`) name what you want and return arrays. Empty matches
|
|
39
|
+
return `[]`, never `null`. Calendar results sort `day asc, dignity desc`;
|
|
40
|
+
chant results sort by rank then incipit.
|
|
41
|
+
|
|
42
|
+
**Builder functions** (`temperamentum`,
|
|
43
|
+
`notatio`, `harmonia`, and `inscriptio`) construct and return context objects, and throw
|
|
44
|
+
`Error` on invalid input. Only `Temperamentum` carries methods; `Score` is a
|
|
45
|
+
plain data record, and rendering is the standalone `inscriptio`.
|
|
46
|
+
|
|
47
|
+
`census` takes the query form but answers for exactly one chant: an id with no
|
|
48
|
+
block throws rather than returning `[]`.
|
|
49
|
+
|
|
50
|
+
Context objects can be passed back into query functions as filters:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const feasts = tonus.festum({ season: "pasc" });
|
|
54
|
+
tonus.proprium({ feast: feasts, office: "an" });
|
|
55
|
+
|
|
56
|
+
const t = tonus.temperamentum({ tuning: "pythagorean" });
|
|
57
|
+
t.nota("D4");
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## The appendix
|
|
61
|
+
|
|
62
|
+
The canonical constant tables ship as named exports beside the namespace.
|
|
63
|
+
Return values are plain data, and the appendix carries tables only, never
|
|
64
|
+
functions. It holds the tables a caller would otherwise transcribe: the mode
|
|
65
|
+
list, the hour list, the valid `by:` values.
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import tonus, { SEASON_LABEL, HORAE, MODES } from "tonus";
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Names follow the register rule: a table of Latin values takes a Latin name, a
|
|
72
|
+
table of codes or English keeps English.
|
|
73
|
+
|
|
74
|
+
**Calendar**
|
|
75
|
+
|
|
76
|
+
| Export | What it holds |
|
|
77
|
+
| -------------- | --------------------------------------------------------------- |
|
|
78
|
+
| `SEASON_LABEL` | season code → English display label (`adv` → "Advent") |
|
|
79
|
+
| `TEMPORA` | season code → the Latin tempus name (`adv` → "Tempus Adventus") |
|
|
80
|
+
| `GRADE_ORDER` | the fourteen grades in precedence order (sort by `indexOf`) |
|
|
81
|
+
| `GRADUS` | grade code → the Latin rank name |
|
|
82
|
+
|
|
83
|
+
**Chant**
|
|
84
|
+
|
|
85
|
+
| Export | What it holds |
|
|
86
|
+
| ----------- | -------------------------------------------------------------------------- |
|
|
87
|
+
| `HORAE` | the eight canonical hours, Matins first — the order is the content |
|
|
88
|
+
| `OFFICIA` | office code → the Latin genus (`an` → "Antiphona") |
|
|
89
|
+
| `ORDINARIA` | ordinary code → the Latin name (`ky` → "Kyrie eleison") |
|
|
90
|
+
| `MODI` | mode number → the Latin name (`"1"` → "Modus I") |
|
|
91
|
+
| `SOURCES` | book code → its bibliographic record; the codes `cantus({ source })` takes |
|
|
92
|
+
|
|
93
|
+
**Tuning**
|
|
94
|
+
|
|
95
|
+
| Export | What it holds |
|
|
96
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
97
|
+
| `MODES` | the eight modes' doctrine: final, tenor, ambitus, and the **received** cadence figures |
|
|
98
|
+
| `TONES` | the psalm tones (Graduale Romanum appendix), with their differentiae |
|
|
99
|
+
| `CADENTIAE` | the **mined** cadence families (`CadentiaFamilia`) — shape, arrival, share, finality, per-mode counts |
|
|
100
|
+
| `CADENTIAE_POPULATION` | the denominator behind every `share`: all phrase-ends, and the same total per mode |
|
|
101
|
+
|
|
102
|
+
The two cadence tables answer different questions. `MODES.cadences` is what the
|
|
103
|
+
treatises say a mode closes on (final cadences only); `CADENTIAE` is what the
|
|
104
|
+
corpus was measured doing (any target, so it is the one of the two that
|
|
105
|
+
accounts for medial closes). See [one spine, two
|
|
106
|
+
annotations](score.md#one-spine-two-annotations), and
|
|
107
|
+
[lift](tuning.md#lift--how-mode-bound-a-close-is) for what `CADENTIAE_POPULATION`
|
|
108
|
+
is for.
|
|
109
|
+
|
|
110
|
+
**Heavens**
|
|
111
|
+
|
|
112
|
+
| Export | What it holds |
|
|
113
|
+
| ------- | ---------------------------------------------------- |
|
|
114
|
+
| `SIGNS` | the twelve zodiac signs, English, ecliptic order |
|
|
115
|
+
| `SIGNA` | the same twelve in Latin (`Scorpius`, `Capricornus`) |
|
|
116
|
+
|
|
117
|
+
**Census**
|
|
118
|
+
|
|
119
|
+
| Export | What it holds |
|
|
120
|
+
| --------------- | ------------------------------------------------------------------------------------------------------ |
|
|
121
|
+
| `CENSUS_GROUPS` | the field groups → `{ offset, count }`; the keys are the valid `by:` values **and** the `profile` keys |
|
|
122
|
+
| `CENSUS_ORDER` | every censused chant id, in block order — so membership is a lookup, not a `try/catch` |
|
|
123
|
+
|
|
124
|
+
Use these to pool blocks without reproducing the distance rule — see [the census
|
|
125
|
+
contract](census.md#distance-is-cosine-per-field-group).
|
|
126
|
+
|
|
127
|
+
## Full contents
|
|
128
|
+
|
|
129
|
+
Every method and every section, page by page, in dependency order. Pages later in
|
|
130
|
+
the list resolve their pitches through the ones before.
|
|
131
|
+
|
|
132
|
+
**[Tuning](tuning.md)** — the medieval pitch system; depends on nothing else.
|
|
133
|
+
|
|
134
|
+
- [The context — `temperamentum`](tuning.md#the-context--temperamentum)
|
|
135
|
+
- [Pitch input](tuning.md#pitch-input)
|
|
136
|
+
- [Pitches — `nota`](tuning.md#pitches--nota)
|
|
137
|
+
- [Steps — `gradus`](tuning.md#steps--gradus)
|
|
138
|
+
- [Intervals — `intervallum`](tuning.md#intervals--intervallum)
|
|
139
|
+
- [Neumes — `neuma`](tuning.md#neumes--neuma)
|
|
140
|
+
- [Ratios — `ratio`](tuning.md#ratios--ratio)
|
|
141
|
+
- [The gamut — `gamut`](tuning.md#the-gamut--gamut)
|
|
142
|
+
- [Modes — `modus`](tuning.md#modes--modus) · [Cadence figures](tuning.md#cadence-figures) · [The corpus catalogue](tuning.md#the-corpus-catalogue--cadentiae)
|
|
143
|
+
- [Psalm tones — `tonus`](tuning.md#psalm-tones--tonus)
|
|
144
|
+
- [Theory & Context](tuning.md#theory--context) · [Sources](tuning.md#sources)
|
|
145
|
+
|
|
146
|
+
**[Calendar](calendar.md)** — the Tridentine calendar against Julian/Gregorian-computus Easter.
|
|
147
|
+
|
|
148
|
+
- [The day's feasts — `festum`](calendar.md#the-days-feasts--festum)
|
|
149
|
+
- [Rank — `ritus` and `grade`](calendar.md#rank--ritus-and-grade)
|
|
150
|
+
- [Seasons — the temporale](calendar.md#seasons--the-temporale)
|
|
151
|
+
- [The year's anchors — `pascha`](calendar.md#the-years-anchors--pascha)
|
|
152
|
+
- [Theory & Context](calendar.md#theory--context) · [Sources](calendar.md#sources)
|
|
153
|
+
|
|
154
|
+
**[Chant](chant.md)** — the Solesmes books in GABC.
|
|
155
|
+
|
|
156
|
+
- [The corpora](chant.md#the-corpora)
|
|
157
|
+
- [The books — `corpus`](chant.md#the-books--corpus)
|
|
158
|
+
- [Retrieval — `cantus`](chant.md#retrieval--cantus) · [Reaching the ordinary](chant.md#reaching-the-ordinary--ordinary)
|
|
159
|
+
- [The repertoire as of a date — the era view](chant.md#the-repertoire-as-of-a-date--the-era-view)
|
|
160
|
+
- [The Mass propers — `proprium`](chant.md#the-mass-propers--proprium)
|
|
161
|
+
- [The ordinary — `ordinarium`](chant.md#the-ordinary--ordinarium)
|
|
162
|
+
- [The Office — `officium`](chant.md#the-office--officium)
|
|
163
|
+
- [Psalms — `psalmus`](chant.md#psalms--psalmus)
|
|
164
|
+
- [Theory & Context](chant.md#theory--context) · [Sources](chant.md#sources)
|
|
165
|
+
|
|
166
|
+
**[Score](score.md)** — GABC parsed into tuned, rhythmicized, drawable notes.
|
|
167
|
+
|
|
168
|
+
- [The score — `notatio`](score.md#the-score--notatio)
|
|
169
|
+
- [Interpretation — `pondus` and `accentus`](score.md#interpretation--pondus-and-accentus)
|
|
170
|
+
- [The note](score.md#the-note)
|
|
171
|
+
- [The tabula](score.md#the-tabula)
|
|
172
|
+
- [Rendering — `inscriptio`](score.md#rendering) · [theme](score.md#theme--faces-and-ink) · [The analysis tracks](score.md#the-analysis-tracks)
|
|
173
|
+
- [The imprint](score.md#the-imprint)
|
|
174
|
+
- [Prosody](score.md#prosody)
|
|
175
|
+
- [Cadences](score.md#cadences) · [One spine, two annotations](score.md#one-spine-two-annotations)
|
|
176
|
+
- [Modulations](score.md#modulations)
|
|
177
|
+
- [Theory & Context](score.md#theory--context) · [Sources](score.md#sources)
|
|
178
|
+
|
|
179
|
+
**[Heavens](heavens.md)** — an ephemeris voiced through the planetary doctrines.
|
|
180
|
+
|
|
181
|
+
- [The heavens — `caelum`](heavens.md#the-heavens--caelum)
|
|
182
|
+
- [Aspects](heavens.md#aspects)
|
|
183
|
+
- [The voiced heavens — `harmonia`](heavens.md#the-voiced-heavens--harmonia)
|
|
184
|
+
- [The planetary vowels](heavens.md#the-planetary-vowels)
|
|
185
|
+
- [The tabula](heavens.md#the-tabula)
|
|
186
|
+
- [Theory & Context](heavens.md#theory--context) · [Sources](heavens.md#sources)
|
|
187
|
+
|
|
188
|
+
**[Census](census.md)** — one chant measured against the corpus that holds it.
|
|
189
|
+
|
|
190
|
+
- [The method](census.md#the-method)
|
|
191
|
+
- [What a block holds](census.md#what-a-block-holds)
|
|
192
|
+
- [How the measurement works](census.md#how-the-measurement-works)
|
|
193
|
+
- [Distance is cosine per field group](census.md#distance-is-cosine-per-field-group)
|
|
194
|
+
- [Profile and typicality](census.md#profile-and-typicality)
|
|
195
|
+
- [Balance — distance and deviance](census.md#balance--distance-and-deviance)
|
|
196
|
+
- [Neighbors, and `by`](census.md#neighbors-and-by)
|
|
197
|
+
- [The era view](census.md#the-era-view)
|
|
198
|
+
- [What the census is not](census.md#what-the-census-is-not)
|
|
199
|
+
|
|
200
|
+
## Conventions
|
|
201
|
+
|
|
202
|
+
### Latin and English
|
|
203
|
+
|
|
204
|
+
The language of a key tells you the register of its value. A Latin key
|
|
205
|
+
returns authentic Latin content; an English key returns a machine code or
|
|
206
|
+
datum. Where a concept has both registers, they form a pair:
|
|
207
|
+
|
|
208
|
+
| English | Latin |
|
|
209
|
+
| -------- | -------- |
|
|
210
|
+
| `season` | `tempus` |
|
|
211
|
+
| `grade` | `ritus` |
|
|
212
|
+
| `mode` | `modus` |
|
|
213
|
+
| `name` | `nomen` |
|
|
214
|
+
|
|
215
|
+
Other fields carry only one register. Latin-only, _e.g._ `genus`, `ordinarium`,
|
|
216
|
+
`incipit`, `differentia`, `accentus`. English-only, _e.g._ `date`, `velocity`, `hz`.
|
|
217
|
+
|
|
218
|
+
Display strings live in exported maps (_e.g._ `SEASON_LABEL`),
|
|
219
|
+
never as label fields on objects — the maps are [the appendix](#the-appendix).
|
|
220
|
+
|
|
221
|
+
### Dates
|
|
222
|
+
|
|
223
|
+
Dates are UTC-canonical. Local-time constructions like
|
|
224
|
+
`new Date(2026, 0, 6)` resolve to different days depending on the
|
|
225
|
+
machine's timezone. Prefer `new Date("2026-01-06")` instead.
|
|
226
|
+
|
|
227
|
+
`tonus.festum()` and `tonus.caelum()` default to **1 June 991**, the
|
|
228
|
+
symbolic birthday of Guido d'Arezzo. Pass an explicit `date` for any other
|
|
229
|
+
day.
|
|
230
|
+
|
|
231
|
+
### Determinism
|
|
232
|
+
|
|
233
|
+
All pure transforms are deterministic for identical inputs and options.
|
|
234
|
+
No runtime network requests are made. Where variation is wanted (the scatter of
|
|
235
|
+
an ensemble) it is seeded, so the same seed yields byte-identical output.
|
|
236
|
+
|
|
237
|
+
### Error contract
|
|
238
|
+
|
|
239
|
+
- Query functions return `[]` on no match, never throw — but an **empty or
|
|
240
|
+
unknown-key query** throws (a mistyped filter is a bug, not an empty result):
|
|
241
|
+
`festum({ month: 12 })` and `cantus({})` throw rather than silently resolving a
|
|
242
|
+
plausible-looking answer.
|
|
243
|
+
- Builder functions throw `Error` with a descriptive message on invalid input.
|
|
244
|
+
- `notatio` throws on invalid `Chant` input.
|
|
245
|
+
- `inscriptio` throws on a non-`Score` argument or an unknown notation species.
|
|
246
|
+
- `temperamentum.tonus()` throws if `mode` is `"auto"` — mode must be set
|
|
247
|
+
explicitly.
|
|
248
|
+
- Malformed `comma`, ratio, or Scala input throws `RangeError`; custom scales
|
|
249
|
+
must supply 7 or 12 steps, beginning at `1/1` (a degree list) or ending at
|
|
250
|
+
`2/1` (Scala convention).
|
|
251
|
+
- `pascha` throws `RangeError` on a non-finite year.
|
|
252
|
+
- `caelum` in range mode (`{ from, to, step }`) throws `RangeError` on a missing
|
|
253
|
+
bound, `to` before `from`, a non-positive step, or a range exceeding the frame cap.
|
|
254
|
+
- `harmonia` throws `RangeError` on an unknown doctrina.
|
|
255
|
+
- `census` throws on an id with no block (the census covers only the chants
|
|
256
|
+
tonus ships) and on an unknown query key or field group.
|
|
257
|
+
|
|
258
|
+
### The bibliography — [`BIBLIOGRAPHY.md`](https://github.com/jeffreypierce/tonus/blob/main/BIBLIOGRAPHY.md)
|
|
259
|
+
|
|
260
|
+
The single source of truth for citations, each with a stable key. Code
|
|
261
|
+
cites by bracketed key (`[biblio: key]`); each page keeps a short `## Sources`
|
|
262
|
+
line pointing to the keys it draws on. Nothing outside `BIBLIOGRAPHY.md` restates
|
|
263
|
+
a full reference.
|