tonus 0.9.0 → 0.10.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +3 -3
  3. package/dist/census.d.ts +4 -0
  4. package/dist/census.js +23 -0
  5. package/dist/corpus.d.ts +4 -0
  6. package/dist/corpus.js +17 -0
  7. package/dist/data/am.js +10220 -3006
  8. package/dist/data/attestation.js +38640 -4568
  9. package/dist/data/attestation.json +38639 -4567
  10. package/dist/data/census.d.ts +2 -2
  11. package/dist/data/census.js +4 -4
  12. package/dist/data/corpus-overlap.js +5 -118
  13. package/dist/data/gr.d.ts +29 -2
  14. package/dist/data/gr.js +2436 -6411
  15. package/dist/data/kyriale.d.ts +1 -1
  16. package/dist/data/kyriale.js +38 -38
  17. package/dist/data/la.js +12369 -1103
  18. package/dist/data/lh.js +3139 -158
  19. package/dist/data/lu.js +17332 -2379
  20. package/dist/data/nocturnale-romanum.js +10656 -1899
  21. package/dist/data/psm.js +463 -33
  22. package/dist/data/types.d.ts +1 -0
  23. package/dist/engines/chant/chant.js +60 -13
  24. package/dist/engines/chant/data/masses.js +18 -18
  25. package/dist/engines/chant/ordinary.js +12 -5
  26. package/dist/engines/chant/psalm.js +4 -0
  27. package/dist/engines/chant/types.d.ts +32 -4
  28. package/dist/engines/chant/types.js +47 -4
  29. package/dist/engines/score/emitters/moderna.js +150 -12
  30. package/dist/engines/score/infer.js +1 -1
  31. package/dist/engines/temper/gabc.d.ts +12 -0
  32. package/dist/engines/temper/gabc.js +51 -18
  33. package/dist/index.d.ts +5 -9
  34. package/dist/inscriptio.d.ts +5 -0
  35. package/dist/inscriptio.js +23 -0
  36. package/dist/score.d.ts +6 -0
  37. package/dist/score.js +20 -0
  38. package/docs/api/calendar.md +4 -4
  39. package/docs/api/census.md +31 -22
  40. package/docs/api/chant.md +102 -82
  41. package/docs/api/heavens.md +7 -7
  42. package/docs/api/index.md +40 -6
  43. package/docs/api/score.md +44 -44
  44. package/docs/api/tuning.md +14 -14
  45. package/package.json +22 -4
  46. package/dist/data/ams.d.ts +0 -5
  47. package/dist/data/ams.js +0 -122
  48. package/dist/data/cot.d.ts +0 -5
  49. package/dist/data/cot.js +0 -172
  50. package/dist/data/cse.d.ts +0 -5
  51. package/dist/data/cse.js +0 -122
package/CHANGELOG.md CHANGED
@@ -2,6 +2,130 @@
2
2
 
3
3
  All notable changes to tonus. Newest first.
4
4
 
5
+ ## 0.10.0 — 2026-08-31
6
+
7
+ The corpus stores a melody once instead of once per book, the genus labels say
8
+ what GregoBase says, and the root index stops carrying the anatomy of its own
9
+ return values.
10
+
11
+ ### Changed
12
+
13
+ - **BREAKING: the Kyrie's ordinary code is `ke`, not `ky`.** GregoBase files
14
+ every ordinary chant under office-part `ky` = Kyriale, which is the roll-up
15
+ genus, so the Kyrie itself needed a code of its own. `cantus({ ordinary })`
16
+ and `ordinarium({ ordinary })` both take `ke` now, and `ORDINARIA` is keyed
17
+ by it. **A stale `"ky"` throws** rather than returning `[]` — an unknown
18
+ ordinary code is a malformed query, and this is the one most likely to
19
+ arrive stale, so the error names the replacement.
20
+ - **BREAKING: three office genus labels were wrong and are corrected.** They
21
+ were read off the two-letter codes rather than off the source; GregoBase's
22
+ own vocabulary (`include/txt.php`, `$txt['usage']`) is now followed. `or` is
23
+ **Toni Communes** (it read "Ordinarium"), `tp` is **Tropa** (it read "Tonus
24
+ Peregrinus"), and `pa` is **Prosa**. `or` was the costly one: tonus stamped
25
+ an invented genus on a code the database already used for something else,
26
+ and 132 real Toni Communes chants were dropped at the extractor for want of
27
+ a mapping. Anything reading `chant.genus`, or filtering `office: "or"`
28
+ expecting the Mass ordinary, is affected — the ordinary is `ky` (genus) or
29
+ `ordinary` (part).
30
+ - **Two office codes are new: `ky` and `va`.** `ky` is the Mass ordinary's
31
+ roll-up genus; `va` ("Varia") is the catch-all a chant falls to when its
32
+ office-part is unrecognised. It used to fall to `or`, which was harmless
33
+ while `or` was an invented label and is not now that it means a recitation
34
+ formula the census excludes.
35
+ - **BREAKING: type-only names moved off the root entry.** Four subpath entries
36
+ now hold the grain the index was carrying — `tonus/corpus` (the shelf and its
37
+ vocabulary), `tonus/score` (what a `Score` is made of), `tonus/inscriptio`
38
+ (the drawing surface), `tonus/census` (one chant against the corpus). Verbs
39
+ stay on the root namespace, and the types a root signature names stay on the
40
+ root too. **Every value export the root carried is still exported from it**,
41
+ so nothing moved out of a JavaScript consumer's reach — though the code
42
+ renames above do reach one.
43
+ - **A melody printed in several books is stored once.** It was stored once per
44
+ book. `books` now lists every printing and `pages` is keyed by book code, so
45
+ `cantus({ source })` presents a shared chant as the book asked for: that
46
+ book's source record and page numbers, not those of the file the record
47
+ happens to live in. 7840 chants, 9758 listings.
48
+
49
+ ### Added
50
+
51
+ - **Four subpath entries surface types that were reachable but unnameable.**
52
+ `ChantSource`, `OfficeCode`, `OrdinaryCode`, `CanonicalHour`, `Theme`,
53
+ `ThemeColors`, `TrackName`, `TrackData`, and `CENSUS_BLOCK_FLOATS` — each a
54
+ value callers had to spell by hand against a query or an option bag.
55
+ - **The two boundaries are gated instead of described.** `npm run gate` holds
56
+ the rendering boundary (only `inscriptio.ts` and the emitters may import from
57
+ `emitters/`) and the entry map (every `exports` subpath has a source module,
58
+ and every entry module is exported). Prose had already failed to hold the
59
+ rendering boundary once. It runs as part of `npm test`.
60
+
61
+ ### Removed
62
+
63
+ - **`ams`, `cot`, and `cse` are no longer separate data files.** Their contents
64
+ were listings on records the other books already own, and are now reachable
65
+ through `books`/`pages` on those records.
66
+
67
+ ## 0.9.1 — 2026-08-24
68
+
69
+ Moderna reads the pitch it was given: the whole page had been sitting a sixth
70
+ too high, and a transposed chant said nothing about its own accidentals.
71
+
72
+ ### Fixed
73
+
74
+ - **Moderna drew every note a sixth above where it belonged.** `writtenY`
75
+ measured the note against a reference term that had not been normalised the
76
+ same way (`4 * 7` against a note term carrying `+2`), so sounding E4 computed
77
+ as 14 staff steps instead of 0. Every note of every moderna score sat above
78
+ the top line. The port was faithful — the same expression is in the locked
79
+ reference generator, which was fed pitches an octave below today's corpus, so
80
+ two errors cancelled; when the corpus moved to true octaves only one of them
81
+ moved. The clef's octave lift is now stated once, explicitly.
82
+ - **A transposed chromatic rendered as a silent wrong pitch.** `temperamentum`'s
83
+ `transpose` moves what a chant sounds, and moderna reads sounding pitch — but
84
+ the accidental was parsed and discarded, so `Ab3` and `A3` landed on one slot
85
+ with no sign drawn. The page read a semitone off with nothing to say so.
86
+ Spelling now follows `spn`: flats stay flats, sharps stay sharps. Signs
87
+ written in the GABC are unaffected — `computeAccidentals` remains
88
+ authoritative wherever it has an opinion, keyed on the degree a sign alters
89
+ rather than the row that carries it.
90
+ - **Moderna had no ledger lines at all.** A note outside the five rendered as a
91
+ head floating in blank space. One line per staff line the note has passed,
92
+ drawn behind the head: the spaces immediately outside (steps 9 and −1) get
93
+ none, and a line-note and the space above it share one.
94
+ - **`midiToGabc` threw on every B-flat** — the one accidental chant sings, and
95
+ the whole reason `parse.ts` carries a flat state machine. It now spells it
96
+ `ix`, and `gabcToMidi` reads that back, so the two stay inverses. Other
97
+ chromatic pitch classes still throw. Verified over 222 round-trips.
98
+ - **The GABC clef tables disagreed about which clefs exist.** `temper/gabc.ts`
99
+ knew six (`c1`–`c4`, `f3`, `f4`) while `parse.ts` accepted sixteen, so a
100
+ `cb2` the parser reads happily raised "Unknown clef". `CLEFS` is now derived
101
+ rather than hand-listed; all six original entries are reproduced exactly.
102
+
103
+ ### Changed
104
+
105
+ - **A low moderna chant is written an octave up.** One clef throughout — what
106
+ floats is the written octave, the move the hand already makes when it lifts a
107
+ chant onto the gamut's fingers. Chant sounds below this staff (corpus median
108
+ 50–62 against a window of 52–65), and transposing down pushes it further: at
109
+ −4 an unlifted score put 43.6% of all notes under the bottom line, now 9.8%.
110
+ Untransposed moderna output moves for most chants as a result, always toward
111
+ the staff (7.8% off-staff to 5.8%). Whole octaves only, and one lift for the
112
+ whole chant — a page that changed register partway would read as two pieces.
113
+ The limit is honest: an ambitus straddling the window cannot be aligned by
114
+ any multiple of 12, and 59 of 200 measured chants are wider than the staff.
115
+ `notatio`'s `spn` remains the authority on what actually sounds.
116
+ - **Quadrata is untouched by all of this**, by construction: it draws from
117
+ staff position, which transposition does not move. Verified byte-identical.
118
+
119
+ ### Packaging
120
+
121
+ - `.npmignore` removed — it contradicted the `files` allowlist, and two
122
+ mechanisms deciding what ships is one more than can be reasoned about.
123
+ Measured before and after: the tarball is byte-identical at 216 files.
124
+ - `"sideEffects": false` declared, and verified true — nothing in `dist/` runs
125
+ at module scope and no data file is read at import time.
126
+ - The description no longer claims "and performance"; the voice lives in
127
+ tonus-sonus.
128
+
5
129
  ## 0.9.0 — 2026-08-19
6
130
 
7
131
  The tracks stop promising a contract they never kept, and the geometry says
package/README.md CHANGED
@@ -8,7 +8,7 @@ belong to which day and returns analysis and notation, and calculates the
8
8
  _music of the spheres_ proposed by Pythagoras, Boethius, and more as real
9
9
  intervals.
10
10
 
11
- **[Orreliquum — the library at work →](https://jeffreypierce.github.io/orreliquum/)**
11
+ **[Interactive demo](https://orreliquum.com/)**
12
12
 
13
13
  Everything is computed locally and deterministically: the same question always
14
14
  returns the same answer, from data that ships inside the package, with no network
@@ -72,8 +72,8 @@ its final, dominant, ambitus, and ethos, with its psalm tones.
72
72
  **The calendar** is Tridentine, resolved against Easter by
73
73
  the Julian or the Gregorian computus.
74
74
 
75
- **The repertoire** is 2,187 chants across ten books nine Solesmes editions and
76
- the _Nocturnale Romanum_ night office in
75
+ **The repertoire** is 7,840 chants across seven books, six Solesmes editions and
76
+ the _Nocturnale Romanum_ night office in
77
77
  [GABC](https://gregorio-project.github.io/) notation: Mass propers, the Kyriale,
78
78
  the Office hours, the psalter, and the Matins nocturns.
79
79
 
@@ -0,0 +1,4 @@
1
+ export { getCensus as census } from "./engines/census/census.js";
2
+ export type { Census, CensusQuery, CensusBy, CensusGroup, CensusGroupProfile, CensusNeighbor, } from "./engines/census/types.js";
3
+ export { CENSUS_GROUPS, CENSUS_ORDER, CENSUS_BLOCK_FLOATS, } from "./data/census.js";
4
+ //# sourceMappingURL=census.d.ts.map
package/dist/census.js ADDED
@@ -0,0 +1,23 @@
1
+ // ---------------------------------------------------------------------------
2
+ // tonus/census — one chant measured against the corpus that holds it
3
+ // ---------------------------------------------------------------------------
4
+ // The analysis boundary, made addressable. The library never RUNS a census; it
5
+ // CITES one. Everything here reads a baked table — the blocks the corpus
6
+ // pipeline emits — and does arithmetic over it. No mining lives behind this
7
+ // entry and none may be added: a threshold calibrated across many chants is the
8
+ // corpus repo's business, and re-enters as generated data with provenance.
9
+ //
10
+ // Year-shaped questions ("this season," "this manuscript," a window of the
11
+ // calendar) are not here and do not belong here. `data/census.ts` states the
12
+ // same rule from the data side: this is the only census artifact in the
13
+ // package, and the corpus repo's other outputs stay there. Pooling blocks is
14
+ // the caller's to do, under the rule census.md fixes — cosine per field group,
15
+ // never over the flat 221.
16
+ //
17
+ // CENSUS_BLOCK_FLOATS joins the two constants the root already exported. A
18
+ // caller decoding a block had to know the stride and could get it only by
19
+ // counting CENSUS_GROUPS, which is the transcription the appendix rule exists
20
+ // to prevent.
21
+ export { getCensus as census } from "./engines/census/census.js";
22
+ export { CENSUS_GROUPS, CENSUS_ORDER, CENSUS_BLOCK_FLOATS, } from "./data/census.js";
23
+ //# sourceMappingURL=census.js.map
@@ -0,0 +1,4 @@
1
+ export { getChants as cantus, getCorpus as corpus, SOURCES } from "./engines/chant/chant.js";
2
+ export { HORAE, OFFICIA, ORDINARIA, MODI } from "./engines/chant/types.js";
3
+ export type { Chant, OrdinaryChant, CantusQuery, CorpusQuery, Corpus, CorpusLedger, CorpusFullCount, GenusCount, ModeCount, SharedCount, ChantSource, OfficeCode, OrdinaryCode, CanonicalHour, } from "./engines/chant/types.js";
4
+ //# sourceMappingURL=corpus.d.ts.map
package/dist/corpus.js ADDED
@@ -0,0 +1,17 @@
1
+ // ---------------------------------------------------------------------------
2
+ // tonus/corpus — the shelf: what tonus holds, and how to ask for it
3
+ // ---------------------------------------------------------------------------
4
+ // The repertoire and its vocabulary, separated from the calendar that resolves
5
+ // a DAY. `cantus` and `corpus` ask what is on the shelf; `festum`, `proprium`
6
+ // and `officium` ask what a day calls for. The two questions have been one
7
+ // import since the beginning, which is part of why the shelf reads as though it
8
+ // were a property of the calendar.
9
+ //
10
+ // This entry surfaces four types the root index never exported, each of them a
11
+ // value a caller otherwise had to TYPE OUT against a query: ChantSource (the
12
+ // book codes `cantus({ source })` accepts), OfficeCode and OrdinaryCode (the
13
+ // genus and ordinary codes), and CanonicalHour (what HORAE holds). The appendix
14
+ // rule already admits the tables; the types they range over belong beside them.
15
+ export { getChants as cantus, getCorpus as corpus, SOURCES } from "./engines/chant/chant.js";
16
+ export { HORAE, OFFICIA, ORDINARIA, MODI } from "./engines/chant/types.js";
17
+ //# sourceMappingURL=corpus.js.map