tonus 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +275 -1
  2. package/README.md +3 -3
  3. package/dist/engines/chant/attest.js +8 -8
  4. package/dist/engines/chant/hour.js +3 -3
  5. package/dist/engines/chant/intone.js +7 -1
  6. package/dist/engines/score/cadence.d.ts +10 -4
  7. package/dist/engines/score/cadence.js +10 -4
  8. package/dist/engines/score/emitters/accidentals.d.ts +9 -0
  9. package/dist/engines/score/emitters/accidentals.js +76 -13
  10. package/dist/engines/score/emitters/atramentum.js +1 -1
  11. package/dist/engines/score/emitters/breaking.d.ts +2 -2
  12. package/dist/engines/score/emitters/breaking.js +2 -2
  13. package/dist/engines/score/emitters/moderna.js +165 -17
  14. package/dist/engines/score/emitters/svg.d.ts +16 -0
  15. package/dist/engines/score/emitters/svg.js +19 -12
  16. package/dist/engines/score/emitters/tracks.js +29 -10
  17. package/dist/engines/score/ir.js +24 -4
  18. package/dist/engines/score/neume.d.ts +19 -0
  19. package/dist/engines/score/neume.js +35 -0
  20. package/dist/engines/score/parse.js +4 -1
  21. package/dist/engines/score/types.d.ts +6 -0
  22. package/dist/engines/temper/data/guido.js +4 -2
  23. package/dist/engines/temper/gabc.d.ts +12 -0
  24. package/dist/engines/temper/gabc.js +51 -18
  25. package/dist/engines/temper/neume.d.ts +1 -1
  26. package/dist/engines/temper/neume.js +24 -3
  27. package/docs/api/calendar.md +4 -4
  28. package/docs/api/census.md +20 -20
  29. package/docs/api/chant.md +27 -27
  30. package/docs/api/heavens.md +7 -7
  31. package/docs/api/index.md +5 -5
  32. package/docs/api/score.md +191 -138
  33. package/docs/api/tuning.md +31 -23
  34. package/package.json +4 -3
package/docs/api/score.md CHANGED
@@ -42,8 +42,8 @@ const score = tonus.notatio(introit, { temperamentum: t });
42
42
  ```
43
43
 
44
44
  The structured view is `score.phrases`; the flat view, one row per note,
45
- is `score.tabula`. Phrases split at every divisio — the bars of chant
46
- notation, signs of punctuation rather than measure:
45
+ is `score.tabula`. Phrases split at every divisio, the bars of chant
46
+ notation, which are signs of punctuation rather than measure:
47
47
 
48
48
  | divisio | name |
49
49
  | ------- | ---------------------------- |
@@ -83,18 +83,27 @@ interface Syllable {
83
83
  lyric: string;
84
84
  runs?: LyricRun[]; // styled spans, present only when GABC markup styled this syllable
85
85
  notes: Note[];
86
- neume: Neume;
86
+ neume: Neume; // the syllable read as ONE figure
87
+ neumes: Neume[]; // its figures, as GABC groups them, each classified
87
88
  melisma: number; // notes on this syllable (1 = syllabic, >1 melismatic)
88
89
  }
89
90
  ```
90
91
 
92
+ A syllable carries neumes rather than being one. GABC marks the figures with
93
+ `!`, `/` and `//`, and `neumes` names each; `neume` reads the whole syllable as
94
+ a single figure, which for a melisma of several is usually `compound`. A
95
+ syllable of one figure reports the same shape both ways. The salicus is the
96
+ exception and reports at syllable scope: its rule reads the oriscus on the
97
+ next-to-last note of an ascent, and the scribe may break the figure between
98
+ that oriscus and the summit.
99
+
91
100
  GABC's lyric markup is decoded at parse, so `lyric` is always clean display
92
101
  text: the `<sp>` shortcuts arrive as real characters (`<sp>V/</sp>` → ℣,
93
102
  `<sp>R/</sp>` → ℟, `<sp>+</sp>` → the flex †, `<sp>'ae</sp>` → ǽ, the
94
103
  `\greheightstar` verbatim → the raised *), centering braces and layout tags
95
104
  (`<clear>`, `<nlba>`) vanish, above-lines text (`<alt>`) is not lyric text,
96
105
  and page cross-references (`\pageref`) to the paper books are dropped. Style
97
- tags — `<i>`, `<b>`, `<sc>`, `<c>` (rubric color), `<e>` (elision) — survive
106
+ tags (`<i>`, `<b>`, `<sc>`, `<c>` for rubric color, `<e>` for elision) survive
98
107
  as `runs`, styled spans that concatenate to `lyric`; a style opened in one
99
108
  syllable and closed several later (the common `<i>ij.</i>` and euouae
100
109
  patterns) styles every syllable it crosses. Both notation species draw the
@@ -280,7 +289,7 @@ analysis, visualization, or emission.
280
289
 
281
290
  `Harmony` exposes the same surface for voiced bodies
282
291
  ([heavens.md](heavens.md#the-tabula)). The tabula is also the rendering
283
- surface — the SVG renderer ([below](#rendering)) consumes it directly, which is
292
+ surface. The SVG renderer ([below](#rendering)) consumes it directly, which is
284
293
  why `hz`, `velocity`, `bend`, and the ornament flags live on each row.
285
294
 
286
295
  ```js
@@ -355,7 +364,7 @@ interface ChantTabulaRow {
355
364
 
356
365
  ## Rendering
357
366
 
358
- The score is drawn as **SVG** — a self-contained, square-note chant staff with
367
+ The score is drawn as **SVG**: a self-contained, square-note chant staff with
359
368
  SMuFL glyphs baked as inline paths (no external font). It consumes `score.tabula`,
360
369
  so the interpretation applied through `pondus` and `accentus` is already in the
361
370
  geometry. Microtuning lives on each tabula row's `bend`, `hz`, and `offset` for
@@ -364,7 +373,7 @@ a Web-Audio player to read directly.
364
373
  ### inscriptio — the standalone renderer
365
374
 
366
375
  `tonus.inscriptio(score, opts?)` draws a `Score` and returns `{ svg, geometry }`.
367
- Rendering is a standalone function that _takes_ a score, not a method on one — the
376
+ Rendering is a standalone function that _takes_ a score, not a method on one. The
368
377
  score analyzes, `inscriptio` inks. It throws on a non-Score or an unknown
369
378
  notation species (the builder-function contract).
370
379
 
@@ -382,14 +391,14 @@ Two notation species, each with its own spacing pass:
382
391
 
383
392
  **Layout is deterministic, and lyric widths are computed rather than measured.**
384
393
  The same score and options give byte-identical SVG on every machine, with no
385
- DOM, no canvas, and no font file — `inscriptio` runs anywhere Node does. Note
394
+ DOM, no canvas, and no font file, so `inscriptio` runs anywhere Node does. Note
386
395
  glyphs carry exact SMuFL advance widths; lyric text is computed from character
387
396
  classes, since measuring it would require the font's own metrics. Line breaks,
388
397
  system fill, and the width of the returned canvas all rest on that figure. It is
389
398
  close, not exact: a lyric set in a face far from the assumed proportions will
390
399
  break slightly early or late.
391
400
 
392
- Two consequences worth planning around. `width` is a **request, not a promise** —
401
+ Two consequences worth planning around. `width` is a **request, not a promise**:
393
402
  the canvas returned is `max(width, content)`, so a chant whose content cannot fit
394
403
  comes back wider rather than clipped. And a caller who needs typographic
395
404
  precision should render at a generous `width` and scale the result, rather than
@@ -397,22 +406,22 @@ relying on the estimate to land a tight column exactly.
397
406
 
398
407
  Options, by group (all optional):
399
408
 
400
- - **layout** — `width` wraps systems to fit (absent = a single line); `scale`
409
+ - **layout**: `width` wraps systems to fit (absent = a single line); `scale`
401
410
  sets how big the chant is drawn: `"small"`, `"normal"` (default), `"large"`,
402
411
  or a staff height in px for fitting a known column. Everything scales from it
403
- — notes, lyrics, the air between systems — and it reflows the music, so a
412
+ (notes, lyrics, the air between systems), and it reflows the music, so a
404
413
  larger scale means fewer notes per line. The page margin does not scale: it
405
414
  belongs to the page rather than the notation, and scaling it gave a large
406
415
  chant *less* usable width than a small one.
407
- - **front matter** — set as the Solesmes books open a piece: `title` centers
416
+ - **front matter**: set as the Solesmes books open a piece: `title` centers
408
417
  over the score; `rubric` (or `annotation: "auto"` to derive the genus/mode
409
418
  mark, e.g. _Introitus. 8._) sits upright at the left margin; `dropcap` draws
410
419
  the initial the printed books open with, taking the first letter out of the
411
420
  lyric and indenting the first system to hold it. Both species take the
412
- title; the margin mark and the initial are **quadrata's alone** — moderna is
421
+ title; the margin mark and the initial are **quadrata's alone**, because moderna is
413
422
  a transcription read as an edition, and carries the analysis tracks a
414
423
  reserved cap column would fight. It ignores them rather than refusing.
415
- - **theme** — the dress: `fonts` and `colors`.
424
+ - **theme**: the dress, `fonts` and `colors`.
416
425
 
417
426
  ### theme — faces and ink
418
427
 
@@ -432,14 +441,14 @@ tonus.inscriptio(score, {
432
441
  ```
433
442
 
434
443
  **`fonts`** carries four roles. A book's dropcap is very often *not* its lyric
435
- face — a Lombardic or uncial initial against a text hand, which is the pairing
444
+ face, but a Lombardic or uncial initial against a text hand, which is the pairing
436
445
  the printed books use. Each role takes a font-family string or
437
446
  `{ family, weight?, scale? }` (`scale` adjusts that role's size, for a face
438
447
  whose apparent size differs from the house serif).
439
448
 
440
449
  The SVG carries font-family *references* by default, and the page hosting it
441
450
  supplies the face (`@font-face`). A slot may instead carry
442
- `embed: { base64, format? }` — the caller's own bytes — and the face then rides
451
+ `embed: { base64, format? }` (the caller's own bytes), and the face then rides
443
452
  inside the SVG's `<style>`, making the file self-contained (at the cost of its
444
453
  size; one `@font-face` per family + weight, deduped). tonus bundles no font
445
454
  files: with `embed` it is a conduit for data the consumer supplies, so the
@@ -480,7 +489,7 @@ is 196 KB base64'd, which triples a typical chant and repeats in every file,
480
489
  where a reference is cached once.
481
490
 
482
491
  **`colors`** reach the SVG as CSS custom properties with the theme's own value
483
- as the fallback — `fill="var(--tonus-note, #111)"`. A rendered chant therefore
492
+ as the fallback, `fill="var(--tonus-note, #111)"`. A rendered chant therefore
484
493
  carries the ink it was drawn with *and* stays themable: a host stylesheet that
485
494
  sets the property rethemes the score without re-rendering it.
486
495
 
@@ -493,8 +502,8 @@ sets the property rethemes the score without re-rendering it.
493
502
  }
494
503
  ```
495
504
 
496
- The emitter's semantic classes — `note`, `lyric`, `dropcap`, `custos`,
497
- `episema`, `divisio`, `clef`, `mora`, `ictus` — are stylable from the host page.
505
+ The emitter's semantic classes (`note`, `lyric`, `dropcap`, `custos`,
506
+ `episema`, `divisio`, `clef`, `mora`, `ictus`) are stylable from the host page.
498
507
 
499
508
  **`scale` is not part of the theme**: line breaking consumes it, so a scale
500
509
  change re-renders while a colour change does not.
@@ -504,28 +513,35 @@ between systems, the notehead calibration against the staff, and the line-end
504
513
  custos are constants. The custos appears whenever a system wraps, as it does in
505
514
  a chant book.
506
515
 
507
- **The geometry contract (public API).** `geometry` is one `NoteGeometry` per note,
508
- in tabula order — the interface analysis _tracks_ build on, so they place marks
509
- by index and coordinate instead of scraping the SVG. The library's own tracks
510
- (below) consume exactly these anchors; a custom track downstream does the same:
516
+ **The geometry contract (public API).** `geometry` is one `NoteGeometry` per
517
+ note, in tabula order, so `geometry[i]` and `tabula[i]` are the same note two
518
+ ways. It says where each note landed on the drawn page, so a caller can put a
519
+ playhead, a selection, or an overlay against a note without scraping the SVG.
511
520
 
512
521
  ```ts
513
522
  interface NoteGeometry {
514
- phraseIndex: number; syllableIndex: number; neumeGroup: number; noteIndex: number;
523
+ phraseIndex: number; syllableIndex: number; neumeGroup: number;
524
+ noteIndex: number; // position within the SYLLABLE — the tabula's own index
525
+ neumeIndex: number; // position within the neume FIGURE
515
526
  system: number; // which wrapped system the note landed in
516
527
  x: number; y: number; // notehead anchor, svg user units
528
+ inkLeft: number; // the figure's measured ink extent. `x` is its LEFT
529
+ inkRight: number; // edge, so a mark that spans notes reaches for these
517
530
  systemY: number; // the system's top offset within the svg
518
531
  }
519
532
  ```
520
533
 
534
+ `y` is already absolute on the canvas; `systemY` reports which system a note
535
+ is in, not an offset to add.
536
+
521
537
  ### The analysis tracks
522
538
 
523
539
  `tracks` draws an analysis band beneath every system. Any track rides either
524
- species, and all may ride one score — the selection is independent of the
540
+ species, and all may ride one score: the selection is independent of the
525
541
  notation, as `notation` itself is. One governing ink system runs through them:
526
542
  every mark draws in the score's black, strata graded by opacity alone (the
527
- liturgical red belongs to the claims — the tonarium's mode line and the
528
- prosodia's accent dots), and every pressure-bearing line shares one nib law —
543
+ liturgical red belongs to the claims, the tonarium's mode line and the
544
+ prosodia's accent dots), and every pressure-bearing line shares one nib law:
529
545
  velocity as stroke width.
530
546
 
531
547
  ```js
@@ -536,74 +552,70 @@ tonus.inscriptio(score, { width: 680, tracks: ["chironomia", "tonarium"] });
536
552
  tonus.inscriptio(score, { width: 680, tracks: ["prosodia", "chironomia", "tonarium"] });
537
553
  ```
538
554
 
539
- The conventional pairing is the chironomia under `quadrata` and the tonarium
540
- under `moderna`; the prosodia, reading the text rather than the notation,
541
- rides either as naturally. The renderer enforces none of it.
542
-
543
- Requesting several stacks them in a fixed order — the prosodia first, directly
544
- under the lyric line it reads; the chironomia next; the tonarium below —
545
- whichever order they are asked for, and the page grows by the sum of the
546
- bands.
547
-
548
- - **`"prosodia"`** — how the melody treats the word, in two lanes. The upper
549
- lane draws one **tent per word** — the hairpin pair's top edge, dynamics'
550
- own mark for swell and release — its apex over the accented syllable, with
551
- the accent's landing at the peak in the liturgical red: a **filled dot**
552
- when the accent lands arsic (struck), an **open ring** when it lands thetic
553
- (deferred). Accented words rise past the lane's single rule; unaccented
554
- words crest on it. The lower lane is a fence on a rail, one mark per
555
- syllable by how the melody treats it: a spoken syllable stands as a stem
556
- (height, its notes), a syllable **recited on the tenor lies flat** — a
557
- short dash floating above the rail — and a **melisma of four notes or more
558
- becomes a block** as wide as its real extent and as tall as its count
559
- (counts of eight or more print inside). Connected melismas join into one
560
- ridge, each block's top sloping toward its neighbours, the line between
561
- them crossing the gaps. A divisio drops a hairline through both lanes.
562
- Accents are the book's written accents (the GABC accented vowels) — the
563
- track derives none.
564
- - **`"chironomia"`** — the conducting hand as one continuous line:
565
- arsic beats crest, thetic beats trough, single-note theses pass through
566
- shallow, and the hand picks up between close arses in a small backward loop
567
- [biblio: carroll-chironomy]. Pressure is the stroke's _weight_: each note's
568
- `velocity` (the `accentus` shaping) becomes nib width over solid ink, so the
569
- line presses where the voice does. Pierik letters (A · T · PT) name the
570
- beats — the incise's rhythmic shape is read straight off them.
571
- - **`"tonarium"`** — the melodic-analysis lane, named for the book
572
- that catalogued chants by mode. Four rails — the maneriae finals ladder, D on
573
- the bottom (categories, not pitches) — carry the **mode line** in the
574
- liturgical red: the governing mode of each phrase, its numeral above
575
- (authentic-vs-plagal lives in the numeral). A modulation of kind
576
- `"inflection"` steps the line solid; a `"transposition"` (the affinal frame
577
- read as displacement) draws dashed. Through the rails runs the melody itself,
578
- compressed to the chant's ambitus and wearing the same pressure grammar, a
579
- lighter stratum — context, not message.
580
- A **cadence is the melody's own ending re-inked black**: the same curve at
581
- the same width turns pure black across the cadential figure and lands on a
582
- terminal node — filled when the family's measured `finality` closes, open
583
- when it suspends. Beneath the node, centred on it, sits the family's
584
- **in-mode share**: `"3.9%"`, how often this close ends a chant in this
585
- chant's mode — the frequency a singer actually meets it at. Where the chant
586
- has no mode, or the family has too few occurrences in it to divide honestly,
587
- the label falls back to the plain corpus share, which is the same kind of
588
- number.
589
-
590
- **Every inked cadence carries a label.** A close that does not join
591
- [`CADENTIAE`](index.md#the-appendix) at all reads `"rara"` — not a gap but a
592
- measurement: the catalogue holds the 110 families above fifty corpus
593
- occurrences, so failing to join means rarer than anything it records. About
594
- 44% of cadences land there.
595
-
596
- `rara` is a word rather than a number, so it is not read on the percentage
597
- scale beside it.
598
-
599
- The lift rides the group as `data-lift` for a caller who wants distinctiveness
600
- rather than frequency, beside `data-cadentia` — the family key, which is the
601
- join back to [`CADENTIAE`](index.md#the-appendix) and the provenance a margin
602
- gloss can print. Crowded labels dodge to a second row.
555
+ Several stack in a fixed order whatever order they are asked for: prosodia
556
+ first, directly under the lyric line it reads; chironomia next; tonarium
557
+ below. The page grows by the sum of the bands. The conventional pairing is the
558
+ chironomia under `quadrata` and the tonarium under `moderna`; the renderer
559
+ enforces none of it.
560
+
561
+ **`"prosodia"`** draws how the melody treats the word, in two lanes.
562
+
563
+ | mark | means |
564
+ | --- | --- |
565
+ | tent, one per word | apex over the accented syllable; accented words rise past the lane's rule, unaccented crest on it |
566
+ | filled dot at the peak | the accent lands **arsic** (struck) |
567
+ | open ring at the peak | the accent lands **thetic** (deferred) |
568
+ | stem on the rail | a spoken syllable; height is its note count |
569
+ | flat dash above the rail | the syllable is **recited on the tenor** |
570
+ | block | a **melisma of 4+ notes**, as wide as its extent, as tall as its count (8+ prints the count inside) |
571
+ | hairline through both lanes | a divisio |
572
+
573
+ Connected melismas join into one ridge, blocks sloping toward their
574
+ neighbours. Accents are the book's written accents (the GABC accented
575
+ vowels); the track derives none.
576
+
577
+ **`"chironomia"`** draws the conducting hand as one continuous line. Arsic
578
+ beats crest, thetic beats trough, single-note theses pass through shallow, and
579
+ the hand picks up between close arses in a small backward loop
580
+ [biblio: carroll-chironomy]. Each note's `velocity` (the `accentus` shaping)
581
+ becomes nib width, so the line presses where the voice does. Pierik letters
582
+ (A · T · PT) name the beats.
583
+
584
+ **`"tonarium"`** is the melodic-analysis lane, named for the book that
585
+ catalogued chants by mode. Four rails carry the maneriae finals ladder, D on
586
+ the bottom (categories, not pitches). Through them runs the melody itself,
587
+ compressed to the chant's ambitus at a lighter stratum: context, not message.
588
+
589
+ | mark | means |
590
+ | --- | --- |
591
+ | red line + numeral | the governing **mode** of each phrase (authentic vs plagal lives in the numeral) |
592
+ | line steps solid | a modulation of kind `"inflection"` |
593
+ | line dashed | a `"transposition"` (the affinal frame read as displacement) |
594
+ | melody re-inked black | a **cadence**: the same curve at the same width, turned pure black across the figure |
595
+ | filled terminal node | the family's measured `finality` **closes** |
596
+ | open terminal node | it **suspends** |
597
+
598
+ A wrapped cadence draws its node and label once, in the system holding the
599
+ figure's last note.
600
+
601
+ Every inked cadence carries a label beneath its node: the family's **in-mode
602
+ share** (`"3.9%"`), how often this close ends a chant in this chant's mode.
603
+ Where the chant has no mode, or the family has too few occurrences in it to
604
+ divide, it falls back to the plain corpus share. A close that joins
605
+ no [`CADENTIAE`](index.md#the-appendix) family reads **`rara`**. That is a
606
+ measurement rather than a gap: the catalogue holds the 110 families above
607
+ fifty corpus occurrences, so failing to join means rarer than anything it
608
+ records. About 43% of the corpus's cadences join no family, though of the
609
+ labels a page prints, about a third read `rara`. It is a word, not a number, so
610
+ it is not read on the percentage scale beside it.
611
+
612
+ Each cadence group carries `data-cadentia` (the family key, which joins back
613
+ to [`CADENTIAE`](index.md#the-appendix) and carries the provenance a margin
614
+ gloss can print) and `data-lift` (distinctiveness, for a caller who wants it
615
+ rather than frequency). Crowded labels dodge to a second row.
603
616
 
604
617
  Everywhere, confidence is opacity, and a claim below confidence 0.45 draws
605
- nothing — weak claims are not inked. Every mark sits under the notation that
606
- would falsify it.
618
+ nothing. Every mark sits under the notation that would falsify it.
607
619
 
608
620
  ## The imprint
609
621
 
@@ -667,11 +679,18 @@ interface ModalAffinity {
667
679
 
668
680
  ## Metrics
669
681
 
670
- `score.metrics` measures the chant's shape — counts, range, melisma,
671
- melodic motion, contour, tessitura, rhythm, cadence. It is chant-specific;
672
- `Harmony` has no metrics. For _Puer natus est_: ambitus 10 semitones, melisma
673
- ratio 2.04 notes per syllable, tessitura ~5 semitones above the final, a near-
674
- perfect melodic arch, mostly stepwise motion (leap rate ~5%).
682
+ `score.metrics` measures the chant's shape: counts, range, melisma, melodic
683
+ motion, contour, tessitura, rhythm, cadence. It is chant-specific; `Harmony`
684
+ has no metrics.
685
+
686
+ ```js
687
+ tonus.notatio(puerNatusEst).metrics;
688
+ // { noteCount: 159, syllableCount: 78, phraseCount: 10,
689
+ // ambitus: 10, melismaRatio: 2.04, tessitura: 5.2, … }
690
+ ```
691
+
692
+ _Puer natus est_ spans ten semitones, sings a little over two notes to the
693
+ syllable, and sits about five semitones above its final.
675
694
 
676
695
  ```ts
677
696
  interface Metrics {
@@ -691,12 +710,19 @@ interface Metrics {
691
710
  cadenceWeight: number;
692
711
  cadenceDistribution: CadenceDistribution;
693
712
  }
713
+ ```
694
714
 
695
- interface IntervalStats {
696
- histogram: Record<number, number>; // signed semitone interval → count
697
- maxLeap: number; // largest absolute interval (semitones)
698
- leapRate: number; // fraction of motions that are leaps (a 4th+)
699
- motus: { step: number; skip: number; leap: number }; // 1–2 st / 3–4 / 5+
715
+ The five composite fields each answer one question about the chant.
716
+
717
+ **Where the melody sits, and the shape it traces.** `noteRange` is the plain
718
+ compass; `arcus` reads the classic chant arch, a rise to a peak and a return to the
719
+ final.
720
+
721
+ ```ts
722
+ interface NoteRange {
723
+ min: number; // lowest note MIDI
724
+ max: number; // highest note MIDI
725
+ span: number; // max − min, in semitones (this is `ambitus`)
700
726
  }
701
727
 
702
728
  interface Arcus {
@@ -705,32 +731,59 @@ interface Arcus {
705
731
  final: number; // last note MIDI
706
732
  archIndex: number; // signed: +1 rises and returns, 0 flat/monotonic
707
733
  }
734
+ ```
708
735
 
709
- interface NoteRange {
710
- min: number;
711
- max: number;
712
- span: number;
736
+ **How the melody moves.** Chant is overwhelmingly stepwise, so `motus` sorts
737
+ every adjacent within-phrase motion by size. The three names are the tradition's
738
+ own, and a chant whose skips and leaps outnumber its steps is unusual enough to
739
+ question.
740
+
741
+ ```ts
742
+ interface IntervalStats {
743
+ histogram: Record<number, number>; // signed semitone interval → count
744
+ maxLeap: number; // largest absolute interval (semitones)
745
+ leapRate: number; // fraction of motions that are leaps
746
+ motus: { step: number; skip: number; leap: number };
713
747
  }
748
+ ```
714
749
 
750
+ | motion | semitones | interval |
751
+ | ------ | --------- | ----------------- |
752
+ | `step` | 1–2 | a second |
753
+ | `skip` | 3–4 | a third |
754
+ | `leap` | 5+ | a fourth or wider |
755
+
756
+ **How it is rhythmed.** Arsis and thesis across the score, and the size of the
757
+ compound beats they group into.
758
+
759
+ ```ts
715
760
  interface RhythmicProfile {
716
- arsic: number; // count of arsic notes across the score
717
- thetic: number; // count of thetic notes across the score
761
+ arsic: number; // arsic notes across the score
762
+ thetic: number; // thetic notes across the score
718
763
  avgGroupSize: number; // mean notes per compound beat
719
764
  maxGroupSize: number; // largest compound beat observed
720
765
  }
721
-
722
- interface CadenceDistribution {
723
- comma: number; // divisio minima
724
- tick: number; // virgula
725
- semicolon: number; // divisio minor
726
- colon: number; // divisio maior
727
- doubleBar: number; // divisio finalis
728
- }
729
766
  ```
730
767
 
768
+ **How it comes to rest.** `cadenceDistribution` counts the divisiones the chant
769
+ writes, one field per rung of the bar-line hierarchy. `cadenceWeight` sums the
770
+ same bars by that rank, so a chant closing on full bars weighs more than one
771
+ broken by breaths.
772
+
773
+ | field | GABC | divisio | weight |
774
+ | ----------- | ------- | --------------- | -----: |
775
+ | `tick` | `` ` `` | virgula | 0.25 |
776
+ | `comma` | `,` | divisio minima | 0.5 |
777
+ | `semicolon` | `;` | divisio minor | 0.75 |
778
+ | `colon` | `:` | divisio maior | 1.0 |
779
+ | `doubleBar` | `::` | divisio finalis | 1.5 |
780
+
781
+ This table is canonical: `metrics.ts` cites it rather than restating the
782
+ weights.
783
+
731
784
  ## Cadences
732
785
 
733
- `score.cadences` names the melodic close of each phrase — where metrics
786
+ `score.cadences` names the melodic close of each phrase. Where metrics
734
787
  only counts the divisio bars, this identifies the figure. One `Cadence` per
735
788
  phrase-ending divisio: its resolution `target`, the melodic `approach`, and the
736
789
  `divisio` that tells medial from final (the double bar `::` is the final
@@ -742,23 +795,23 @@ the tabula.
742
795
  Two catalogues describe a cadence, and they answer different questions. Read
743
796
  this before deciding which field to use:
744
797
 
745
- > Every cadence carries a **`signature`** — always. Some are **catalogued** by
798
+ > Every cadence carries a **`signature`**, always. Some are **catalogued** by
746
799
  > the corpus (`finality`, and everything in
747
800
  > [`CADENTIAE`](index.md#the-appendix)). Some, on the final, are **named** by
748
801
  > received theory (`formula`).
749
802
 
750
803
  - **`formula`** is _tradita_: the mode's cadence figures as the treatises give
751
804
  them ([tuning.md](tuning.md#cadence-figures)), matched in solmization
752
- relative to the final — `"la-sol"`, `"mi-re"`. It fires **only on the
805
+ relative to the final (`"la-sol"`, `"mi-re"`). It fires **only on the
753
806
  finalis**, because the received catalogue holds only final figures.
754
807
  - **`signature`** is _inventa_: the tail's interval shape and where it lands,
755
808
  keyed as `"2,0,-2 @0"` and mined from the corpus. It fires on **any** target,
756
809
  so it is the one of the two that speaks about **medial** cadences.
757
810
 
758
- Measured over the cadences `notatio` reports across the shipped corpus — about
759
- 20,500 of them — roughly 43% carry a formula, 56% join the catalogue, 31% carry
760
- both, and 44% fall outside it. Neither is derivable from the other, because the
761
- signature is mode-blind and the formula is mode-relative.
811
+ Measured over the cadences `notatio` reports across the shipped corpus (about
812
+ 26,800 of them), roughly 43% carry a formula, 57% join the catalogue, and 31%
813
+ carry both, so about a third carry neither. Neither is derivable from the
814
+ other, because the signature is mode-blind and the formula is mode-relative.
762
815
 
763
816
  ### `finality` — how often this family closes
764
817
 
@@ -769,7 +822,7 @@ the final, 31 do not close, and finality across the catalogue runs the whole
769
822
  range from 0 to 1. So
770
823
  `arrival === 0` implies nothing about whether a close is final.
771
824
 
772
- It is `null` when the signature falls below the catalogue's floor — an
825
+ It is `null` when the signature falls below the catalogue's floor: an
773
826
  uncatalogued close, not a close that never closes.
774
827
 
775
828
  ```ts
@@ -790,7 +843,7 @@ interface Cadence {
790
843
  }
791
844
  ```
792
845
 
793
- A one-note phrase is a cadence — a landing with no gesture — and keys with an
846
+ A one-note phrase is a cadence (a landing with no gesture) and keys with an
794
847
  empty shape (`" @0"`), which is why `signature` is that key rather than null.
795
848
 
796
849
  `arrival` is signed and not octave-reduced: `@-5`, a fourth below the final,
@@ -799,7 +852,7 @@ and `@+7`, a fifth above, are distinct families.
799
852
  ## Modulations
800
853
 
801
854
  `score.modulations` marks where the tonal centre leans away from the home
802
- mode — the local, temporal counterpart to the imprint's global modal
855
+ mode, the local counterpart to the imprint's global modal
803
856
  affinity. Each phrase is scored against all eight modes (the imprint's
804
857
  affinity math); a run of phrases that favours a foreign mode, by a margin,
805
858
  becomes one `Modulation` span. The margin is calibrated against Suñol's
@@ -808,13 +861,13 @@ distribution-based: it finds where a passage leans, not a functional analysis.
808
861
 
809
862
  `kind` says what the span is evidence OF, which matters because the three are
810
863
  not the same phenomenon. **`inflection`** is a single phrase leaning away and
811
- back — passing colour, not a shift. **`modulation`** is a sustained internal
864
+ back: passing colour, not a shift. **`modulation`** is a sustained internal
812
865
  excursion, two phrases or more, that returns. **`transposition`** is the whole
813
866
  chant sitting in a foreign mode's frame: it does not close on its labelled
814
867
  final and one foreign mode dominates most of its phrases, meaning the melody is
815
868
  notated at a transposed position (the affinal) or the label disagrees with the
816
- notation. A transposed chant is not modulating — the displacement is global —
817
- so a caller displaying "modulations" should treat those spans as a re-reading of
869
+ notation. A transposed chant is not modulating, because the displacement is
870
+ global, so a caller displaying "modulations" should treat those spans as a re-reading of
818
871
  the whole chant rather than an event inside it.
819
872
 
820
873
  ```ts
@@ -860,35 +913,35 @@ The classifier applies Carroll's three melodic rules in priority order
860
913
 
861
914
  The first compound beat of an incise is always arsic. When every rule is inconclusive, the
862
915
  shape alternates from the previous group. Two conventional overrides
863
- precede the rules: the **salicus** is always arsic — the tension toward its
864
- summit is the arsic gesture — and the **doubly-dotted clivis** is always
916
+ precede the rules. The **salicus** is always arsic, because the tension toward
917
+ its summit is the arsic gesture, and the **doubly-dotted clivis** is always
865
918
  thetic, as a cadential figure.
866
919
 
867
920
  A salicus here is Cardine's: an ascent of at least three notes whose
868
921
  **next-to-last note is an oriscus** [biblio: cardine-semiology, ch. 16]. The
869
922
  oriscus is what makes one. An ascending group carrying only the editorial
870
- Solesmes ictus is a **scandicus** that was marked for rhythm — a distinction
871
- worth stating because conflating the two is, in Bevenot's word, a trap: over
923
+ Solesmes ictus is a **scandicus** that was marked for rhythm. The distinction
924
+ is worth stating because conflating the two is, in Bevenot's word, a trap: over
872
925
  the sung corpus tonus finds about 260 salici against about 1,900 scandici, so
873
926
  only about an eighth of that wider set carries an oriscus at all.
874
927
 
875
928
  Cardine's correction also decides WHICH note is principal. The printed
876
929
  editions lengthen the oriscus itself; the manuscripts show the principal note
877
- is the one **immediately following** it — the summit — so tonus prolongs that
930
+ is the one **immediately following** it (the summit), so tonus prolongs that
878
931
  note and takes the oriscus lightly. This is the one point where the rhythmic
879
932
  layer departs from Mocquereau and Suñol, and it does so deliberately.
880
933
 
881
934
  ### Rhythmic types
882
935
 
883
- Above the per-beat arsis/thesis, each phrase carries a `rhythmicType` — Le
936
+ Above the per-beat arsis/thesis, each phrase carries a `rhythmicType`, Le
884
937
  Guennant's taxonomy (via Carroll) of how the incise's compound beats chain, and
885
938
  the `beats` sequence it reads. The observable types are modeled: **IV** (a single
886
939
  arsis to a single thesis), **V** (several arses to one thesis), **VI** (one arsis
887
940
  to several theses), **VII** (regular A–T alternation), and **VIII** (a
888
- contraction — two simple rhythms overlapping at a shared ictus, after Suñol).
941
+ contraction of two simple rhythms overlapping at a shared ictus, after Suñol).
889
942
  Types I–III use sub-beat cells that never surface in isolation and are not
890
943
  labeled; an incise that fits no type is `null`. The classification rules live at
891
- the data — see `classifyRhythmicType` in
944
+ the data. See `classifyRhythmicType` in
892
945
  [`score/ir.ts`](https://github.com/jeffreypierce/tonus/blob/main/src/engines/score/ir.ts).
893
946
 
894
947
  ### Modeled and not