tonus 0.1.8 → 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 +598 -1
- package/LICENSE +133 -29
- package/README.md +106 -83
- 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/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 +26 -8
- package/dist/index.js +37 -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 +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-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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,603 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to tonus. Newest first.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.5.0 — 2026-08-12
|
|
8
|
+
|
|
9
|
+
Rendering, mostly — a day of looking at real chant on a real page and fixing
|
|
10
|
+
what the page showed.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **A third analysis track: `"prosodia"`** — how the melody treats the word.
|
|
15
|
+
One tent per word (the hairpin's top edge), its apex over the accented
|
|
16
|
+
syllable, the landing at the peak in the liturgical red — filled for arsic
|
|
17
|
+
(struck), an open ring for thetic (deferred). Below it a fence on a rail:
|
|
18
|
+
a stem per spoken syllable, a flat dash for a syllable recited on the tenor,
|
|
19
|
+
and a block per melisma — as wide as its real extent, as tall as its note
|
|
20
|
+
count, connected melismas joining into one ridge. It rides first in the
|
|
21
|
+
stack, directly under the lyric line it reads, and the site's vestigia strip
|
|
22
|
+
and tracks key carry it. Shaped across eight lab rounds
|
|
23
|
+
(`working/review/diagram-word-track-01…08.html`).
|
|
24
|
+
- **The rubrica reservation, amended.** Ruled 2026-07-29 as the mode line's
|
|
25
|
+
alone, the liturgical red now belongs to the claims: the tonarium's mode
|
|
26
|
+
line and the prosodia's accent dots. The precedent is the score's own text
|
|
27
|
+
apparatus — the dropcap and annotations were always rubricated, and in the
|
|
28
|
+
books red is the word's colour.
|
|
29
|
+
|
|
30
|
+
### Changed — breaking
|
|
31
|
+
|
|
32
|
+
- **The layout options are `width` and `scale`.** `padding`, `noteScale`,
|
|
33
|
+
`systemGap`, and `custos` are gone. Nothing ever set them — not the docs site,
|
|
34
|
+
not the 28 lab plates, not the 13 stress pieces — so they were surface without
|
|
35
|
+
use. They are now constants chosen to look right at every scale, and the
|
|
36
|
+
custos appears whenever a system wraps, which is what a chant book does.
|
|
37
|
+
|
|
38
|
+
`scale` replaces `staffHeight`: `"small"`, `"normal"` (default), `"large"`, or
|
|
39
|
+
a staff height in px for fitting a known column. A caller decides how big the
|
|
40
|
+
chant should be, not how tall its staff is in pixels.
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
inscriptio(score, { width: 900, scale: "large" })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The page margin deliberately does NOT scale with it — a margin belongs to the
|
|
47
|
+
page, not the notation, and scaling it gave a large chant *less* usable width
|
|
48
|
+
than a small one (89% of a 900px canvas against 93%). The air between systems
|
|
49
|
+
does scale, since flat 24px held the system pitch at 135px whether the staff
|
|
50
|
+
was 30 or 56.
|
|
51
|
+
|
|
52
|
+
- **`inscriptio`'s look options are one `theme` object.** `fonts`, `noteColor`,
|
|
53
|
+
`staffLineColor`, and `rubricaColor` are replaced by
|
|
54
|
+
`theme: { fonts, colors }`. These travel
|
|
55
|
+
together — a caller setting a lyric face is usually setting a whole look — and
|
|
56
|
+
a house style is worth naming once and passing everywhere.
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// before
|
|
60
|
+
inscriptio(score, { fonts: { lyric: "Junicode" }, rubricaColor: "#801", staffHeight: 48 })
|
|
61
|
+
// after
|
|
62
|
+
inscriptio(score, { scale: 48, theme: {
|
|
63
|
+
fonts: { lyric: "Junicode" }, colors: { rubrica: "#801" },
|
|
64
|
+
} })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`fonts` keeps its four roles unchanged, `dropcap` among them: a book's
|
|
68
|
+
initial is very often not its lyric face, and the two stay separate.
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **A note knows the diphthong it belongs to.** `Context.diphthong` and
|
|
73
|
+
`ChantTabulaRow.diphthong` report the pair a sung vowel is part of — `ae` ·
|
|
74
|
+
`oe` · `au`, and `ui` in the cui/hui stems — or null. `vowel` is unchanged: it
|
|
75
|
+
stays the NUCLEUS, the single vowel a singer sustains, because that is what
|
|
76
|
+
the analysis passes key on. The pair rides beside it for anyone rendering the
|
|
77
|
+
off-glide.
|
|
78
|
+
|
|
79
|
+
It has to come from here, because **only the syllabifier can tell a diphthong
|
|
80
|
+
from a hiatus: the accent decides.** `cae` is one syllable — nucleus `a`,
|
|
81
|
+
gliding toward `e`; `sa-é` is two, the same letters with the accent on the
|
|
82
|
+
second and no glide at all. Downstream, given only the lyric, the two are
|
|
83
|
+
indistinguishable. A scan for the letter pair also misreads `quae`, where `qu`
|
|
84
|
+
is a consonantal glide: the nucleus is `u`, and the `ae` is not the sung pair.
|
|
85
|
+
Over the Graduale that is 71 distinct syllables — 1,526 sung notes — a naive
|
|
86
|
+
reading gets wrong, and 192 syllables over the whole corpus. The detection
|
|
87
|
+
reuses the syllabifier's own `DIPHTHONGS` set, so the word means the same
|
|
88
|
+
thing here as it does when a word is split.
|
|
89
|
+
|
|
90
|
+
- **The ink is themable from CSS.** Colours now reach the SVG as custom
|
|
91
|
+
properties with the render's own value as the fallback —
|
|
92
|
+
`fill="var(--tonus-note, #111)"` — so a host stylesheet can retheme a drawn
|
|
93
|
+
chant without re-rendering it, while a file opened on its own still shows the
|
|
94
|
+
ink it was drawn with. Three properties: `--tonus-note`, `--tonus-staff-line`,
|
|
95
|
+
`--tonus-rubrica`. The emitter already carried semantic classes (`note`,
|
|
96
|
+
`lyric`, `dropcap`, `custos`, `episema`, `divisio`, `clef`, `mora`, `ictus`)
|
|
97
|
+
but an inline `fill` beats any stylesheet rule, so none of them could be
|
|
98
|
+
styled. `scale` deliberately stays outside the theme for the same reason it
|
|
99
|
+
cannot be a CSS property: line breaking consumes it long before a stylesheet
|
|
100
|
+
sees the output.
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
|
|
104
|
+
- **An accidental was emitted as a sounding note.** GABC's `fx` means "F is flat
|
|
105
|
+
from here" — a mark drawn on the staff, not a pitch to sing. The parser set the
|
|
106
|
+
state correctly and then fell through and pushed a note anyway, so `A(fxfg)`
|
|
107
|
+
returned three notes for two and duplicated the pitch. 1337 markers across 426
|
|
108
|
+
Graduale chants: 0.97% of every note in the book was a phantom, at a wrong
|
|
109
|
+
pitch, inventing a unison before each one and inflating every count, interval
|
|
110
|
+
and analysis downstream.
|
|
111
|
+
|
|
112
|
+
The SIGN still draws, and now on the note that follows it — where the books
|
|
113
|
+
print it. That needed a new field: `accidentalSign` is what to draw, separate
|
|
114
|
+
from `accidental`, which is the note's own alteration. In `fe(jx)cit(ih)` the
|
|
115
|
+
flat is printed before the I while it governs J; the emitter had been reading
|
|
116
|
+
the alteration and so drew nothing once the phantom was gone.
|
|
117
|
+
|
|
118
|
+
- **A written flat could come back spelled as a sharp.** Pitch spelling was
|
|
119
|
+
derived from the pitch class alone, against a preferred-flat set that omitted
|
|
120
|
+
pc 1 and pc 6 — so a GABC `x` (a flat) landing on those degrees returned
|
|
121
|
+
`C#`/`F#` rather than `Db`/`Gb`, and reported `accidental: +1` for a source
|
|
122
|
+
that wrote a flat. The pitch was always right; only the spelling and its sign
|
|
123
|
+
were wrong. `toPitch` now takes an optional spelling preference and `notatio`
|
|
124
|
+
passes what the source wrote. The Graduale writes only the bmolle (1821 B-flats
|
|
125
|
+
and no sharps at all), so the corpus never reached the gap.
|
|
126
|
+
|
|
127
|
+
- **Moderna honoured no note colour at all.** It hardcoded `#111` in seventeen
|
|
128
|
+
places while quadrata threaded the option, so a caller theming the ink saw one
|
|
129
|
+
species change and the other not.
|
|
130
|
+
|
|
131
|
+
- **`staffHeight` means the same thing in both species.** Moderna's staff was
|
|
132
|
+
a hardcoded constant, so the option moved quadrata and did nothing here: a
|
|
133
|
+
request of 30, 40 or 60 left moderna at 7.4px every time. Every geometric
|
|
134
|
+
constant now derives from it through one metrics factory.
|
|
135
|
+
- **The canvas is the width you asked for.** It was `max(systemMaxX)` —
|
|
136
|
+
whatever the widest system happened to reach — so a requested 900 came back
|
|
137
|
+
915, 986, 1074, 1203 by chant, and a host applying `max-width` shrank each
|
|
138
|
+
differently. That is why the same page showed one chant's notation a third
|
|
139
|
+
smaller than the next's.
|
|
140
|
+
- **Lyrics count toward a system's extent.** A syllable is centred on its
|
|
141
|
+
note, so half of a final wide syllable always hung past the canvas and was
|
|
142
|
+
cut off. The layout already tracked the lyric's right edge for its collision
|
|
143
|
+
check; the width calculation never asked.
|
|
144
|
+
- **The custos is a custos.** It was drawn as a shrunken punctum on the
|
|
145
|
+
authority of a comment saying no custos glyph was baked — Bravura's have
|
|
146
|
+
been baked all along. The real glyph is a hooked note whose stem points
|
|
147
|
+
toward the pitch it announces. It also floated 41px past the last note,
|
|
148
|
+
having been placed after the divisio's trailing air; and it is now
|
|
149
|
+
suppressed after a full stop, where drawing it put two marks in one place
|
|
150
|
+
and read as a heavy double barline.
|
|
151
|
+
- **Analysis tracks scale with the staff in moderna**, and a track that SPANS
|
|
152
|
+
notes now reaches their ink rather than their anchors — the emitters had
|
|
153
|
+
measured the ink all along and the mapping dropped it.
|
|
154
|
+
- **Capitals are measured as capitals.** The lyric width estimate was a flat
|
|
155
|
+
per-character average, and chant sets its opening word in capitals; "CAn"
|
|
156
|
+
was estimated at 22.9px against a real 27.9, so opening syllables collided
|
|
157
|
+
with what followed.
|
|
158
|
+
|
|
159
|
+
### Changed
|
|
160
|
+
|
|
161
|
+
- **More air.** Syllable and word spacing widened in both species, the
|
|
162
|
+
staff-to-lyric gap from 21px to 28, and moderna's note advance retuned so a
|
|
163
|
+
chant takes about one system more than quadrata rather than twice as many.
|
|
164
|
+
|
|
165
|
+
## 0.4.5 — 2026-08-04
|
|
166
|
+
|
|
167
|
+
The rubric true-up, the era view, and a long pass of making the library say
|
|
168
|
+
what it means. The 2026-07-27 review found the Kyriale selection chain leaking
|
|
169
|
+
past its own gates; the fixes landed with regression tests, the corpus pipeline
|
|
170
|
+
was corrected and regenerated behind them, and the calendar's and the corpus's
|
|
171
|
+
`before` arguments were reconciled into one composable view. The shelf widened,
|
|
172
|
+
the office settled on one cursus, the cadence catalogue was re-mined over the
|
|
173
|
+
sung corpus, and a new verb — `census` — measures every chant against the
|
|
174
|
+
corpus that holds it.
|
|
175
|
+
|
|
176
|
+
Then the tidying, most of it prompted by things that turned out to be measured
|
|
177
|
+
rather than assumed. The salicus took Cardine's correction and, in the process,
|
|
178
|
+
stopped being confused with the Solesmes ictus — two nearly disjoint sets under
|
|
179
|
+
one name. The cadence catalogue learned its own denominator and joined itself
|
|
180
|
+
once instead of per consumer. The appendix roughly doubled and was renamed to
|
|
181
|
+
follow the register rule. The corpus ledger stopped needing three numbers to
|
|
182
|
+
say how much chant there is. And the Apel formula matcher came out: what
|
|
183
|
+
shipped could not use the catalogue it shipped with, and saying so was better
|
|
184
|
+
than leaving a public field that answered `null`.
|
|
185
|
+
|
|
186
|
+
`docs/` is a published site now, which is where 0.5 is headed.
|
|
187
|
+
|
|
188
|
+
### Removed
|
|
189
|
+
|
|
190
|
+
- **BREAKING — `Score.formulas` and the Apel centonization matcher are out.**
|
|
191
|
+
The catalogue of standard phrases, the per-phrase matcher, `FormulaMatch`,
|
|
192
|
+
`Formula`, `FormulaSlot`, and `formulaeFor` are gone from the library. What
|
|
193
|
+
shipped was one mode's alphabet (mode-5 Graduals) behind a matcher whose
|
|
194
|
+
per-phrase window structurally could not fire on 40 of its 53 formulae —
|
|
195
|
+
Apel tabulates a chant as a sequence across a whole respond, and matching at
|
|
196
|
+
that scale is a different algorithm that was never built. The honest state
|
|
197
|
+
was a public field that answered `null` for nearly every chant, and an
|
|
198
|
+
appendix of research the library could not yet use.
|
|
199
|
+
The census keeps its `formulas` field group: those four floats are baked
|
|
200
|
+
upstream in tonus-corpus and describe the corpus as measured, whatever tonus
|
|
201
|
+
does with them. The transcriptions, photographs, tooling and verification
|
|
202
|
+
reports are kept in the workshop, not deleted — the reading of Apel stands on
|
|
203
|
+
its own, and the pipeline is there if the matcher is ever built properly.
|
|
204
|
+
|
|
205
|
+
### Changed
|
|
206
|
+
|
|
207
|
+
- **BREAKING — the shelf reports ONE number, and the Kyriale leaves it.**
|
|
208
|
+
`corpus()` reported `count` (book listings, a chant in two books counted
|
|
209
|
+
twice) beside `distinct` (chants), which made the headline figure depend on
|
|
210
|
+
how many books happened to print the same melody — a reader had to know that
|
|
211
|
+
to read either. Now `count` is **how many chants tonus holds**, each counted
|
|
212
|
+
once, and `listings` is the shelf's length; `listings - count` is the
|
|
213
|
+
overlap. `genera` and `modes` sum to `count`, describing the same population
|
|
214
|
+
the headline does.
|
|
215
|
+
The Kyriale is no longer an eleventh book. There is no Kyriale in GregoBase:
|
|
216
|
+
`ky` and `gr` are the same source, partitioned by the extractor so the
|
|
217
|
+
ordinary can be routed to per-ordinary codes, so listing it beside its parent
|
|
218
|
+
counted the Graduale twice and its `total: null` read as "unmeasured" when
|
|
219
|
+
there was no separate book to measure. `ky` is not a `ChantSource` and not a
|
|
220
|
+
row in the shelf — which is 10 books, 2,187 chants, 2,767 listings.
|
|
221
|
+
Nothing became unreachable. `ordinarium({ feast })` is untouched, and the
|
|
222
|
+
ordinary is now queryable directly: **`cantus({ ordinary: "ky" })`** returns
|
|
223
|
+
all 31 Kyries, composes with the other filters, and the Kyriale's
|
|
224
|
+
bibliographic record still rides every one of its chants. A plain search does
|
|
225
|
+
not sweep the ordinary in — you ask for a Kyrie rather than stumbling onto
|
|
226
|
+
one.
|
|
227
|
+
- **BEHAVIOUR — the salicus takes Cardine's correction, and stops being
|
|
228
|
+
confused with the Solesmes ictus.** Two changes, each ruled and each
|
|
229
|
+
measured separately over the sung corpus (2,887 chants, 28,498 phrases).
|
|
230
|
+
The second — redefining what counts as a salicus at all — was ratified on
|
|
231
|
+
its own terms, since it decides what the word denotes everywhere in tonus,
|
|
232
|
+
not just how one note is weighted.
|
|
233
|
+
**Detection.** A salicus is "at least three ascending notes in which the
|
|
234
|
+
next-to-last is an oriscus" [biblio: cardine-semiology, ch. 16]. tonus had
|
|
235
|
+
been calling any ascending run with the editorial ictus a salicus — 2,795
|
|
236
|
+
groups, of which **36 (1.3%) actually carried an oriscus** — while missing
|
|
237
|
+
188 of the corpus's 226 real salici, which classified as `scandicus`. Two
|
|
238
|
+
nearly disjoint sets under one name. Now: `salicus` 2,795 → **226**, exactly
|
|
239
|
+
the chants that meet Cardine's definition; `scandicus` 498 → 2,501, and the
|
|
240
|
+
4-and-5-note ictus ascents (566) join `compound`. An ictus-marked ascent
|
|
241
|
+
with no oriscus is a scandicus that Solesmes marked for rhythm, and the mark
|
|
242
|
+
is still readable on `context.ictus`.
|
|
243
|
+
**Weighting.** The printed editions lengthen the oriscus; the manuscripts
|
|
244
|
+
show the principal note is the one immediately following it. The 1.3×
|
|
245
|
+
prolongation moves from the next-to-last note to the **summit** — which is
|
|
246
|
+
the last note at any length, since the oriscus is next-to-last by
|
|
247
|
+
definition, so the 4- and 5-note forms need no separate rule. The salicus
|
|
248
|
+
stays arsic: tension toward the summit is the arsic gesture, so Cardine
|
|
249
|
+
strengthens that rule rather than contradicting it.
|
|
250
|
+
**Downstream:** 208 of 28,498 phrases (0.73%) change `rhythmicType`,
|
|
251
|
+
overwhelmingly toward VIII. This is the one point where tonus's rhythmic
|
|
252
|
+
layer departs from Mocquereau and Suñol, and `ir.ts` now says so at its
|
|
253
|
+
header.
|
|
254
|
+
**The limit, stated plainly:** tonus sees only what the transcription marks.
|
|
255
|
+
Bevenot's own example — the mode-6 _Requiem_ introit's fa-sol-la — carries
|
|
256
|
+
the ictus and no oriscus in GregoBase, so it still reads as a scandicus. He
|
|
257
|
+
is reading the manuscripts; the corpus is a printed edition that resolved
|
|
258
|
+
the oriscus away. Recovering those wants the sources, not a looser rule.
|
|
259
|
+
- **The tonarium's cadence label says how typical a close is, not which
|
|
260
|
+
family it belongs to.** The bracket read `"2,0,-2 @0"` — the family's name,
|
|
261
|
+
which a reader could not weigh. It now reads `"×2.1"`: the family's share
|
|
262
|
+
within this chant's mode over its share of the corpus at large. Measured
|
|
263
|
+
across four books the figure spans ×0.46 to ×10.56, median ×2.22; a lift
|
|
264
|
+
below 1.0 prints too, since an atypical close is information. A mode-less
|
|
265
|
+
chant, or a family with fewer than ten occurrences in the chant's mode,
|
|
266
|
+
falls back to the plain corpus share. The key is not lost — each cadence
|
|
267
|
+
now draws inside a group carrying `data-cadentia`, the join back to
|
|
268
|
+
`CADENTIAE` and the provenance a margin gloss can print.
|
|
269
|
+
- **BREAKING — five appendix tables renamed to match the register rule.**
|
|
270
|
+
`TEMPUS_NAME` → `TEMPORA`, `GRADE_NAME` → `GRADUS`, and the internal
|
|
271
|
+
`OFFICE_LABELS` / `ORDINARY_LABELS` / `MODE_LABELS` → `OFFICIA` /
|
|
272
|
+
`ORDINARIA` / `MODI`, the last three now public. The house rule is that
|
|
273
|
+
Latin names carry Latin content and English names carry codes or English;
|
|
274
|
+
the appendix had three spellings of "code → display string" and five tables
|
|
275
|
+
whose Latin values sat under English names. Names now follow the register of
|
|
276
|
+
their values, so `SEASON_LABEL` ("Advent") and `TEMPORA` ("Tempus
|
|
277
|
+
Adventus") are distinguishable by name rather than by memory. Only the two
|
|
278
|
+
`cal` tables were public before; the rest is new surface.
|
|
279
|
+
- **`officium` throws on an unrecognised `hora`.** It returned `[]`, which
|
|
280
|
+
read as "no chants at this hour" rather than "there is no such hour" — the
|
|
281
|
+
same silent-nothing the unknown-query-key guard beside it already refused.
|
|
282
|
+
|
|
283
|
+
### Added
|
|
284
|
+
|
|
285
|
+
- **Every shelved book reports what it HOLDS.** The Nocturnale was the last
|
|
286
|
+
one answering `full: null`, which read as "not yet measured" — but the tally
|
|
287
|
+
had existed all along in its own extract. It holds **1,564** chants against
|
|
288
|
+
the 470 tonus ships, and it shares nothing: `unique` is all of them. That is
|
|
289
|
+
a measurement, not a gap. The crosswalk pairing a nocturnale chant with a
|
|
290
|
+
GregoBase twin is enrichment, a route to metadata, not a claim that two books
|
|
291
|
+
print the same chant, so counting those as shared would invent a
|
|
292
|
+
relationship. The shelf now holds 10,156 chants before the cut.
|
|
293
|
+
- **Three genera that are reported but not shipped now have names.**
|
|
294
|
+
`Improperia` (the Good Friday Reproaches), `Antiphona Mariana` (Marian
|
|
295
|
+
antiphons outside the office cycle) and `Supplicatio` (litanies) appear in a
|
|
296
|
+
book's pre-cut `full` tally, where they had been printing as bare codes —
|
|
297
|
+
`genus: "su"` — while every other row carried a Latin genus. Being outside
|
|
298
|
+
the cut is not a reason to be nameless in a table tonus publishes.
|
|
299
|
+
- **The cadence catalogue carries its own denominator, and joins itself.**
|
|
300
|
+
Every `CadentiaFamilia` gains `share` — its occurrences over ALL 28,481
|
|
301
|
+
phrase-ends, not over the 58.7% that cleared the table's floor, which would
|
|
302
|
+
have flattered every family in it. `CADENTIAE_POPULATION` ships beside it
|
|
303
|
+
with the same total per mode digit, so a family's **lift** in a mode is one
|
|
304
|
+
division: `(modes[m] / byMode[m]) / share`. The ratio itself is not baked —
|
|
305
|
+
export the vocabulary, not the arithmetic.
|
|
306
|
+
A `Cadence` now carries `finality`, the share of ITS family's corpus
|
|
307
|
+
occurrences that land at a final close, joined once in `notatio` instead of
|
|
308
|
+
by every caller rebuilding the index. It rides the cadence while `familia`
|
|
309
|
+
still does not, because the two differ in kind: the signature already IS the
|
|
310
|
+
family's name, but how often that family closes cannot be read off it — of
|
|
311
|
+
the 55 families landing on the final, 31 do not close, and their finality
|
|
312
|
+
spans 0.054 to 1.000. Detection stays pure; the corpus table meets detected
|
|
313
|
+
data in the score builder, where `MODES` already does.
|
|
314
|
+
Both cadence catalogues are now documented as what they are — _tradita_
|
|
315
|
+
(the treatises' figures, final cadences only) and _inventa_ (the corpus
|
|
316
|
+
tally, any target, and so the only account of medial closes) — with the
|
|
317
|
+
measured coverage of each and a worked lift example that was run before it
|
|
318
|
+
was printed.
|
|
319
|
+
- **The appendix widened, so callers stop transcribing the library's own
|
|
320
|
+
vocabulary.** `HORAE` (the eight canonical hours, Matins first — the order
|
|
321
|
+
is the content, and `officium`'s validation reads the same list, so the two
|
|
322
|
+
cannot drift), `OFFICIA`, `ORDINARIA`, `MODI`, `SOURCES` (the book codes
|
|
323
|
+
`cantus({ source })` takes, with their bibliographic records), and
|
|
324
|
+
`CENSUS_GROUPS` / `CENSUS_ORDER` — the census field groups and the block
|
|
325
|
+
index, so asking whether a chant is censused stops needing a `try/catch`.
|
|
326
|
+
A table is admitted when a caller would otherwise type it out, because a
|
|
327
|
+
transcribed copy drifts and fails as wrong answers rather than as an error.
|
|
328
|
+
The appendix is now grouped by engine.
|
|
329
|
+
- **The census distance rule is documented as a contract.** Similarity is
|
|
330
|
+
cosine per field group, never over the flat 225; `by: "all"` is the
|
|
331
|
+
equal-weight mean. Grouping is userland, so a caller pooling blocks is
|
|
332
|
+
computing a distance and must reproduce the rule or silently disagree with
|
|
333
|
+
`census()`. `census.md` now states it for callers with the three ways to get
|
|
334
|
+
a plausible wrong answer, and a worked example — pooling the 178 Communions
|
|
335
|
+
— that was run before it was printed, and reproduces `census()` exactly.
|
|
336
|
+
- **The analysis tracks ship with `inscriptio`.** `tracks: ["chironomia"]`
|
|
337
|
+
draws the conducting hand's wave — arsic crests, thetic
|
|
338
|
+
troughs, pick-up loops, Pierik letters. `tracks: ["tonarium"]`
|
|
339
|
+
draws the melodic lane: the four maneriae rails, the melody
|
|
340
|
+
compressed to its ambitus, the mode line in rubrica (solid inflection,
|
|
341
|
+
dashed transposition). A cadence is the melody's own ending re-inked
|
|
342
|
+
at full strength — the same curve at the same width — landing on a
|
|
343
|
+
terminal node (filled when the family's measured finality in CADENTIAE
|
|
344
|
+
closes, open when it suspends), labelled beneath by its signature on a
|
|
345
|
+
light end-ticked bracket spanning the figure. One governing ink system
|
|
346
|
+
runs through both tracks: one black graded by stratum opacity (rubrica
|
|
347
|
+
is the mode line's alone), and one nib law — velocity as stroke
|
|
348
|
+
width — for every pressure-bearing line. Either track rides either
|
|
349
|
+
species, and both may ride one score: they stack in a fixed order, the
|
|
350
|
+
chironomia above the tonarium, whichever order they are asked for. The
|
|
351
|
+
two-register principle — the rhythmic band under the square notation,
|
|
352
|
+
the melodic band under the transcription — is the house default, not a
|
|
353
|
+
rule the renderer enforces. Confidence is
|
|
354
|
+
opacity; claims under 0.45 draw nothing. The tracks consume the same
|
|
355
|
+
note anchors the geometry contract exports — the score's own ink is
|
|
356
|
+
untouched, and the geometry is unchanged with tracks on.
|
|
357
|
+
- **The duae species share one lyric setting and one opening.**
|
|
358
|
+
Quadrata's staff→lyric gap now equals moderna's (21px at the default
|
|
359
|
+
staff height, scaling with it), and both species default the lyric
|
|
360
|
+
weight to 518; an explicit `fonts.lyric.weight` still overrides.
|
|
361
|
+
Moderna now honours the front matter (`title`, `rubric`,
|
|
362
|
+
`annotation: "auto"`) as quadrata does; the official opening of a
|
|
363
|
+
tonus score is the incipit as `title` plus the auto genus/mode mark,
|
|
364
|
+
with no dropcap.
|
|
365
|
+
- **The era view composes end to end.** `festum({ date, before })` stamps
|
|
366
|
+
the view year on the returned `Feast` (`feast.before`), and every day
|
|
367
|
+
verb — `proprium`, `ordinarium`, `officium` — serves the
|
|
368
|
+
same view without being told the year twice; an own `before`
|
|
369
|
+
overrides. Previously the day verbs' types promised `before`/`century`/
|
|
370
|
+
`cursus` (they extend `CantusQuery`) while the implementations diverged
|
|
371
|
+
three ways: `proprium` threw "unknown query key," `officium` and
|
|
372
|
+
`ordinarium` silently ignored them.
|
|
373
|
+
- One admissibility rule for every door: `engines/chant/attest.ts`, a leaf
|
|
374
|
+
module shared by `cantus`, the day verbs, and the census. One spelling
|
|
375
|
+
at the door — `before`, a year.
|
|
376
|
+
- Under a view, `ordinarium` **re-picks** over the admissible pool (the
|
|
377
|
+
Kyriale offers ranked alternatives by design); `proprium` and `officium`
|
|
378
|
+
degrade to silence — the corpus cut's evidence law.
|
|
379
|
+
- **The shelf widens to eleven books.** Four more marked Solesmes office
|
|
380
|
+
books join — Antiphonale Monasticum Solesmense (`ams`), Psalterium
|
|
381
|
+
Monasticum (`psm`), Cantus selecti (`cse`), Chants of the Church
|
|
382
|
+
(`cot`) — admitted by the stated rule: Solesmes and rhythmically marked.
|
|
383
|
+
The shipped corpus is 2,887 rows over 2,187 distinct chants.
|
|
384
|
+
- **`tonus.census({ id })`** — one chant measured against the corpus that
|
|
385
|
+
holds it: a per-group profile with typicality, `balance` (distance and
|
|
386
|
+
deviant groups), and nearest neighbours by per-group cosine (`k`, `by`,
|
|
387
|
+
`before`). One block of 225 float32s per shipped chant; see
|
|
388
|
+
`docs/census.md`.
|
|
389
|
+
- **`corpus()` with no argument returns the whole shelf** — the rollup
|
|
390
|
+
plus every book's ledger — and every book carries `full`, the pre-cut
|
|
391
|
+
tally: the ledger of the cut, auditable rather than asserted.
|
|
392
|
+
- **`CADENTIAE` joins the appendix** (with `CadentiaFamilia`): the
|
|
393
|
+
corpus-grounded cadence catalogue, mined from every phrase-end in the
|
|
394
|
+
sung corpus — the same population the census counts — keyed by shape
|
|
395
|
+
and arrival.
|
|
396
|
+
- Test suites: `tests/ordinarium.test.mjs` (the selection chain end to
|
|
397
|
+
end) and `tests/era-view.test.mjs` (the view's whole contract).
|
|
398
|
+
|
|
399
|
+
### Removed
|
|
400
|
+
|
|
401
|
+
- **The `rite` option.** tonus assembles one cursus, the Benedictine: the
|
|
402
|
+
Roman office table carried no chant at all on 63.4% of its days, and its
|
|
403
|
+
little-hours psalmody had no other consumer, so `rite: "romanum"`
|
|
404
|
+
returned a cursus nobody sang. A stale `rite` key now throws
|
|
405
|
+
(`officium` rejects unknown keys) rather than being silently ignored.
|
|
406
|
+
- **`matutinum` as a separate verb.** Matins is an hour of `officium`
|
|
407
|
+
(`hora: "matutinum"`), returned flat like every other hour; the
|
|
408
|
+
three-nocturn ordo shape remains unmodelled.
|
|
409
|
+
- **`century`.** It was `before: N * 100` in different clothes; the two
|
|
410
|
+
spellings converged on the one that is a year.
|
|
411
|
+
|
|
412
|
+
### Fixed
|
|
413
|
+
|
|
414
|
+
- **"Requiescant in pace" was the dismissal on every green feria** (~20
|
|
415
|
+
days/year): `isRequiem` missed the bare "Requiescant" incipit AND the
|
|
416
|
+
last-resort appendix return was ungated. The same hole handed ferias an
|
|
417
|
+
ad libitum Gloria. The appendix now never reaches a day whose rubric
|
|
418
|
+
does not admit it; the Requiem stays reachable via
|
|
419
|
+
`ordinarium({ mass: 102 })`.
|
|
420
|
+
- **The Gloria follows the day's rank rubric, not its season**: ferias
|
|
421
|
+
print none; a I-class feast inside Advent (Immaculate Conception) keeps
|
|
422
|
+
hers. The "Benedicamus Domino as in Mass II" borrow the book directs is
|
|
423
|
+
now real — ferias and penitential Sundays sing Mass II's dismissal.
|
|
424
|
+
- **The paschal rubric is a time, not a rank**: removed from the solemn
|
|
425
|
+
set (an Eastertide Tuesday no longer leads with an appendix Kyrie;
|
|
426
|
+
Easter keeps Lux et origo every year), and BVM outranks paschal in
|
|
427
|
+
`rubricForDay`, so Cum jubilo is reachable in Eastertide.
|
|
428
|
+
- **Credo V was never sung**: the off-year rotation's parity was coupled
|
|
429
|
+
to the two-year bias (2 divides 6). The off-years now advance their own
|
|
430
|
+
cycle — all six credos are heard across twelve years.
|
|
431
|
+
- The leftover masses-1–9 preference on high feasts (precedence measuring
|
|
432
|
+
what the rubric should) — deleted; it defeated the appendix solemnity
|
|
433
|
+
boost on the class feasts it was built for and pinned high BVM feasts to
|
|
434
|
+
mass IX.
|
|
435
|
+
- `centuryOf` off-by-one at exact century multiples: `before: 1100` now
|
|
436
|
+
admits the closed 11th century, as its own doc always claimed.
|
|
437
|
+
- "Kyrie XVII C" refiled from a synthetic mass 100 to mass 17 (corpus-side
|
|
438
|
+
incipit parsing read the variant letter C as roman 100) — it now rotates
|
|
439
|
+
as Mass XVII's sibling printing.
|
|
440
|
+
|
|
441
|
+
### Data
|
|
442
|
+
|
|
443
|
+
- Regenerated from the corrected pipeline: `office-roman` (canticle
|
|
444
|
+
antiphons split positionally — the last SURVIVOR of matching is no
|
|
445
|
+
longer promoted to the Benedictus/Magnificat slot), `office-monastic`
|
|
446
|
+
(464 days; rubric-year variant files excluded from the merge; stub
|
|
447
|
+
redirects followed to their actual targets), `office-ferial` (honestly
|
|
448
|
+
monastic rows only; the tier-then-book tiebreak; Matins 94 → 98 of 128),
|
|
449
|
+
`commune-office` (rubric-variant sections no longer blended; the Introit
|
|
450
|
+
genre-cap hole closed), `kyriale`.
|
|
451
|
+
|
|
452
|
+
## 0.3.0
|
|
453
|
+
|
|
454
|
+
The vox-ectomy. The voice engine (`vox`, `chorus`, the formant tables,
|
|
455
|
+
personae, consortia, liquescentia coda tables, spectrum, accordatio) leaves
|
|
456
|
+
the public library for orreliquum, the private instrument it was always
|
|
457
|
+
being built toward. tonus returns to what it is: theory, calendar, chant,
|
|
458
|
+
notation, and the heavens — the algorithms, publishable and citable.
|
|
459
|
+
|
|
460
|
+
### Added
|
|
461
|
+
|
|
462
|
+
- **`accent` on `ChantTabulaRow`** — a boolean marking whether a note's
|
|
463
|
+
syllable bears the Latin tonic word-accent. The value already existed inside
|
|
464
|
+
the parse (`detectVowelAccent` drives articulation stress); it is now surfaced
|
|
465
|
+
as data on the tabula, so downstream analysis can read accent placement
|
|
466
|
+
without re-deriving it. Threaded `ParsedNote → Context → ChantTabulaRow`.
|
|
467
|
+
|
|
468
|
+
### Removed
|
|
469
|
+
|
|
470
|
+
- **`tonus.vox()` and `tonus.chorus()`** and every voice type
|
|
471
|
+
(`Vox`, `VoxInput`, `VoxParams`, `Vowel`, `Formant`, `Locus`, `Latinitas`,
|
|
472
|
+
`Persona`, `PersonaName`, `Coda`, `TuningLike`, `Chorus`, `ChorusOpts`,
|
|
473
|
+
`Dispersio`, `ConsortiumName`, `Voces`). The namespace holds fourteen
|
|
474
|
+
verbs again. `docs/voice.md` emigrates with the code.
|
|
475
|
+
- Voice test suites (`voice-vox`, `voice-chorus`, `voice-features`,
|
|
476
|
+
`voice-spectrum`) move with their subject.
|
|
477
|
+
|
|
478
|
+
### Unchanged
|
|
479
|
+
|
|
480
|
+
- `harmonia`'s planetary voices (`VoicedPitch`, `VoicedBody`, the Greek
|
|
481
|
+
vowel tables) are its own and remain.
|
|
482
|
+
- Every other engine, table, and contract.
|
|
483
|
+
|
|
484
|
+
## 0.2.0
|
|
485
|
+
|
|
486
|
+
Rendering becomes a standalone engine (`inscriptio`), the per-chant analysis
|
|
487
|
+
chart is completed, a voice engine is added, and the emitter surface settles on
|
|
488
|
+
one format (SVG).
|
|
489
|
+
|
|
490
|
+
### Added
|
|
491
|
+
|
|
492
|
+
- **`inscriptio(score, opts?)`** — the standalone SVG renderer. Draws a `Score`
|
|
493
|
+
and returns `{ svg, geometry }`. Two notation species, each with its own
|
|
494
|
+
spacing pass: `"quadrata"` (square-note, SMuFL glyphs baked inline) and
|
|
495
|
+
`"moderna"` (modern round-note transcription — treble-8 clef, engraved slurs,
|
|
496
|
+
after the Lomer practice). A multi-system **layout engine** (`width`,
|
|
497
|
+
`systemGap`, `custos`) and **front matter** (`title`, `rubric` /
|
|
498
|
+
`annotation: "auto"`, `dropcap`, `rubricaColor`).
|
|
499
|
+
- **The geometry contract** — `geometry: NoteGeometry[]`, one entry per note in
|
|
500
|
+
tabula order (system, x, y, systemY). A public API: downstream analysis tracks
|
|
501
|
+
build on it instead of scraping the SVG.
|
|
502
|
+
- **The intonation channel** — `accidentals: "standard" | "heji" | "cents"`, a
|
|
503
|
+
**moderna** overlay. HEJI comma accidentals are baselined on the Pythagorean
|
|
504
|
+
chain (tonus's default tuning), so a Pythagorean chant renders clean and
|
|
505
|
+
syntonic-comma arrows bloom only under a just preset; meantone is not just, so
|
|
506
|
+
`heji` throws under it. `cents` labels signed deviations against `"pythagorean"`
|
|
507
|
+
(default) or `"et"`. Square notation (`quadrata`) carries only the accidentals
|
|
508
|
+
GABC itself expresses (flat / natural / sharp) — the HEJI and cents overlays are
|
|
509
|
+
modern analytical marks, so quadrata throws when asked for them.
|
|
510
|
+
- **Score metrics** on `prosody`: interval statistics (histogram, maxLeap,
|
|
511
|
+
leapRate, step/skip/leap `motus`), `tessitura`, the melodic `arcus`
|
|
512
|
+
(initial/peak/final + arch index), `melismaCadential`; and conveniences
|
|
513
|
+
`phrase.noteCount` / `phrase.syllableCount` / `syllable.melisma`.
|
|
514
|
+
- **`vox` and `chorus`** — a singing voice modelled as formant and spectrum
|
|
515
|
+
data, and seeded ensembles. `formantes` tunes to a temperament directly:
|
|
516
|
+
`tenor.formantes("a", temper, vis?)` — `vis` weights the pull, 0 (phonetic
|
|
517
|
+
truth) to 1 (fully tuned, the default).
|
|
518
|
+
- **The appendix.** The export law is settled: verbs live on the namespace,
|
|
519
|
+
return values are plain data, and the named exports are canonical constant
|
|
520
|
+
tables only — `SEASON_LABEL`, `TEMPUS_NAME`, `GRADE_ORDER`, `GRADE_NAME`,
|
|
521
|
+
`MODES`, `TONES` (types `PsalmTone`/`Differentia` ride with their table).
|
|
522
|
+
The grade helper functions (`gradeOrder`, `compareGrade`, `ritusToGrade`)
|
|
523
|
+
retire from the surface: `gradeOrder(g)` is `GRADE_ORDER.indexOf(g)`.
|
|
524
|
+
- **`docs/` ships in the package** — the documentation renders from the
|
|
525
|
+
installed tarball, pinned to the version it describes.
|
|
526
|
+
- **GABC lyric markup decodes.** The angle-bracket text tags — excluded since
|
|
527
|
+
the MIDI-only days — are parsed at intake: `<sp>` specials become real
|
|
528
|
+
characters (℣ ℟ † ǽ œ, the raised *), style tags (`<i>`, `<b>`, `<sc>`,
|
|
529
|
+
`<c>` rubric color, `<e>` elision) survive as styled `runs` on `Syllable`
|
|
530
|
+
and the tabula row, and both notation species draw them as `<tspan>`s.
|
|
531
|
+
Layout hints (`<clear>`, `<nlba>`, centering braces) and `\pageref`
|
|
532
|
+
cross-references vanish. Before this, quadrata printed `<sp>V/</sp>` as
|
|
533
|
+
literal lyric text and syllable widths were measured tags-and-all.
|
|
534
|
+
- **Per-role text faces** — `inscriptio`'s `fonts` option assigns a face to
|
|
535
|
+
the `dropcap`, `title`, `annotation`, and `lyric` roles (family, optional
|
|
536
|
+
weight, optional size scale). By default the SVG carries references and
|
|
537
|
+
the host page supplies the face; a slot may instead `embed` the caller's
|
|
538
|
+
own font bytes (base64) into the SVG's `<style>` for a self-contained
|
|
539
|
+
file. tonus bundles no font files either way.
|
|
540
|
+
|
|
541
|
+
### Changed
|
|
542
|
+
|
|
543
|
+
- **License: PolyForm Noncommercial 1.0.0** (was MIT through 0.1.x, which
|
|
544
|
+
remain MIT). Free for any noncommercial purpose; commercial use by
|
|
545
|
+
arrangement with the author.
|
|
546
|
+
- **Corpus double-escape fixed.** Every `gabc` field stored its non-ASCII as a
|
|
547
|
+
literal `\uXXXX` escape, which had silently disabled accent detection across
|
|
548
|
+
the whole corpus — so note weights, prosody, rhythm, and imprint were computed
|
|
549
|
+
accent-blind. The extractor now decodes correctly; **computed accent weights
|
|
550
|
+
shift corpus-wide** as a result. A guard test asserts no gabc carries an escape.
|
|
551
|
+
- **`cantus({})` throws.** An empty or unknown-key chant query is a caller bug,
|
|
552
|
+
not an empty result; it throws with guidance (matching the `festum` contract).
|
|
553
|
+
- NABC pipes stripped from the corpus (`(notes|nabc)` → `(notes)`), with a
|
|
554
|
+
corpus-wide guard test.
|
|
555
|
+
|
|
556
|
+
### Fixed (pre-release review)
|
|
557
|
+
|
|
558
|
+
- **The Pythagorean chain is the medieval dodecachord (E♭–G♯).** The chain had
|
|
559
|
+
stacked twelve fifths ascending from C, spelling F as E♯ — a 521.5¢ wolf
|
|
560
|
+
ut–fa — and b molle as A♯, so `temperamentum` disagreed with `harmonia`'s own
|
|
561
|
+
pure F. The naturals now sit F–B with b molle and E♭ on the flat side, the
|
|
562
|
+
ficta sharps on the sharp side; the heji/cents baseline derives from the same
|
|
563
|
+
chain constant, so a flatted chant under the default tuning renders clean
|
|
564
|
+
(it had thrown).
|
|
565
|
+
- **Quadrata figure grouping is phrase-aware.** A single-syllable phrase
|
|
566
|
+
followed by another phrase merged figures across the divisio, silently
|
|
567
|
+
dropping the second lyric and the divisio itself. An accidental inflecting
|
|
568
|
+
any note of a ligature now prints before the whole figure (Solesmes
|
|
569
|
+
practice) instead of vanishing on non-initial notes.
|
|
570
|
+
- **Canticles by name resolve their own texts.** `magnificat` and
|
|
571
|
+
`nunc dimittis` pointed at the wrong psalter rows (one returned the Symbolum
|
|
572
|
+
Athanasium); `te deum` is withdrawn — the Te Deum is not psalmody.
|
|
573
|
+
- **Formula step-skeletons count Apel's degrees.** Octave-aware relative to the
|
|
574
|
+
final's register: the mode-5 tenor reads +4, where the old ±half-octave fold
|
|
575
|
+
read −3. (Cadence figures keep their deliberate local-contour fold.)
|
|
576
|
+
- **The register doctrine at the seams.** `Matins.nomen` / `Matins.ritus`
|
|
577
|
+
(were `name` / `rank`), `PsalmTone.nomen`, and `rubricaColor` (was `rubrica`,
|
|
578
|
+
a Latin key that took a CSS color string).
|
|
579
|
+
- **The error contract enforced across the surface.** `notatio`, `festum`,
|
|
580
|
+
`caelum`, `matutinum`, `harmonia`, `proprium`, `temperamentum`, and `vox`
|
|
581
|
+
throw with guidance on junk input instead of raw TypeErrors or silent
|
|
582
|
+
fallbacks; a bare `chorus()` sings (the schola is the default); `psalmus`
|
|
583
|
+
no longer stamps the wall-clock year into `source.year` (determinism);
|
|
584
|
+
`iter` honours the `latinitas` shift; a user-GABC `office-part` header
|
|
585
|
+
normalizes to its OfficeCode.
|
|
586
|
+
|
|
587
|
+
### Removed
|
|
588
|
+
|
|
589
|
+
- **The MusicXML and MIDI emitters** (`score.musicxml()`, `score.midi()`).
|
|
590
|
+
tonus emits one format now: SVG. Microtuning still lives on the tabula
|
|
591
|
+
(`bend`/`hz`/`offset`) for a Web-Audio player to read directly — microtonally
|
|
592
|
+
exact, which MIDI never was.
|
|
593
|
+
|
|
594
|
+
## 0.1.8
|
|
595
|
+
|
|
596
|
+
- **Roman Matins.** `matutinum({ feast })` assembles the structured Roman night
|
|
597
|
+
office — the nocturns with their great responsories — from the community
|
|
598
|
+
*Nocturnale Romanum* (new `nr` chant source, 1,564 chants). Sanctorale feasts
|
|
599
|
+
draw Matins from their commune; coverage is the sanctorale and Advent today
|
|
600
|
+
(see COVERAGE.md). A separate accessor; the flat `officium` path is unchanged.
|
|
601
|
+
|
|
5
602
|
## 0.1.7
|
|
6
603
|
|
|
7
604
|
- **`corpus(code)`** — metadata and analytics for a corpus book. Returns its
|
|
@@ -44,7 +641,7 @@ All notable changes to tonus. Newest first.
|
|
|
44
641
|
|
|
45
642
|
- **Modulation detection** (`score.modulations`) — where the tonal centre leans
|
|
46
643
|
away from the home mode, calibrated against Suñol.
|
|
47
|
-
- **
|
|
644
|
+
- **Modal affinity** — degree-, ictus-, and cadence-weighted, with ranked
|
|
48
645
|
initials after Rockstro.
|
|
49
646
|
- **`modus()`** tunes its finalis, tenor, and ambitus through the temperamentum.
|
|
50
647
|
|