tonus 0.7.0 → 0.9.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 (35) hide show
  1. package/CHANGELOG.md +213 -1
  2. package/README.md +1 -1
  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/api.d.ts +3 -3
  7. package/dist/engines/score/api.js +3 -3
  8. package/dist/engines/score/cadence.d.ts +10 -4
  9. package/dist/engines/score/cadence.js +10 -4
  10. package/dist/engines/score/emitters/accidentals.d.ts +9 -0
  11. package/dist/engines/score/emitters/accidentals.js +76 -13
  12. package/dist/engines/score/emitters/atramentum.js +1 -1
  13. package/dist/engines/score/emitters/breaking.d.ts +2 -2
  14. package/dist/engines/score/emitters/breaking.js +2 -2
  15. package/dist/engines/score/emitters/moderna.js +16 -6
  16. package/dist/engines/score/emitters/svg.d.ts +16 -0
  17. package/dist/engines/score/emitters/svg.js +19 -12
  18. package/dist/engines/score/emitters/tracks.js +29 -10
  19. package/dist/engines/score/ir.js +24 -4
  20. package/dist/engines/score/{prosody.d.ts → metrics.d.ts} +3 -3
  21. package/dist/engines/score/{prosody.js → metrics.js} +2 -2
  22. package/dist/engines/score/neume.d.ts +19 -0
  23. package/dist/engines/score/neume.js +35 -0
  24. package/dist/engines/score/parse.js +4 -1
  25. package/dist/engines/score/phrasing.js +1 -1
  26. package/dist/engines/score/types.d.ts +6 -0
  27. package/dist/engines/temper/data/guido.js +4 -2
  28. package/dist/engines/temper/neume.d.ts +1 -1
  29. package/dist/engines/temper/neume.js +24 -3
  30. package/dist/index.d.ts +2 -2
  31. package/docs/api/chant.md +7 -7
  32. package/docs/api/index.md +2 -2
  33. package/docs/api/score.md +159 -106
  34. package/docs/api/tuning.md +17 -9
  35. package/package.json +2 -2
package/docs/api/score.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `tonus.notatio` renders a chant into a score: the analyzed, tuned, and
4
4
  rhythm-classified reading of one GABC melody. The score is data: `phrases`,
5
- `tabula`, `prosody`, `cadences`, `modulations`, and `imprint`. The
5
+ `tabula`, `metrics`, `cadences`, `modulations`, and `imprint`. The
6
6
  standalone `tonus.inscriptio(score)` draws it to SVG.
7
7
 
8
8
  - [Score](#score)
@@ -15,7 +15,7 @@ standalone `tonus.inscriptio(score)` draws it to SVG.
15
15
  - [theme — faces and ink](#theme--faces-and-ink)
16
16
  - [The analysis tracks](#the-analysis-tracks)
17
17
  - [The imprint](#the-imprint)
18
- - [Prosody](#prosody)
18
+ - [Metrics](#metrics)
19
19
  - [Cadences](#cadences)
20
20
  - [One spine, two annotations](#one-spine-two-annotations)
21
21
  - [`finality` — how often this family closes](#finality--how-often-this-family-closes)
@@ -54,7 +54,7 @@ notation, signs of punctuation rather than measure:
54
54
  | `::` | divisio finalis (double bar) |
55
55
 
56
56
  This hierarchy is read three ways in the engine, each weighting the bars for its
57
- own end: an analytic cadence weight (prosody), a phrasing strength (which zeroes
57
+ own end: an analytic cadence weight (metrics), a phrasing strength (which zeroes
58
58
  the virgula), and a rest duration (the divisio's pause length). Each weighting
59
59
  is documented at its table in the code.
60
60
 
@@ -64,7 +64,7 @@ interface Score {
64
64
  phrases: Phrase[];
65
65
  errors: ParseError[];
66
66
  tabula: ChantTabulaRow[];
67
- prosody: Prosody;
67
+ metrics: Metrics;
68
68
  cadences: Cadence[];
69
69
  modulations: Modulation[];
70
70
  imprint: Imprint;
@@ -83,11 +83,20 @@ 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
@@ -504,20 +513,27 @@ 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
@@ -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
 
@@ -665,16 +677,23 @@ interface ModalAffinity {
665
677
  }
666
678
  ```
667
679
 
668
- ## Prosody
680
+ ## Metrics
681
+
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
+ ```
669
691
 
670
- `score.prosody` measures the chant's shape — counts, range, melisma,
671
- melodic motion, contour, tessitura, rhythm, cadence. It is chant-specific;
672
- `Harmony` has no prosody. 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%).
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
- interface Prosody {
696
+ interface Metrics {
678
697
  noteCount: number;
679
698
  syllableCount: number;
680
699
  phraseCount: number;
@@ -691,12 +710,19 @@ interface Prosody {
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 — rise to a peak, 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 prosody
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
@@ -756,9 +809,9 @@ this before deciding which field to use:
756
809
  so it is the one of the two that speaks about **medial** cadences.
757
810
 
758
811
  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.
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
 
@@ -267,15 +267,23 @@ t.neuma(["D4", "F4", "E4"]);
267
267
 
268
268
  Shapes that match no simple figure classify as `"compound"`.
269
269
 
270
- | `shape` | figure | `shape` | figure |
271
- | ----------- | ------------------ | -------------------- | --------------------------- |
272
- | `punctum` | a single note | `torculus resupinus` | torculus, then rising |
273
- | `pes` | two notes, rising | `porrectus flexus` | porrectus, then falling |
274
- | `clivis` | two notes, falling | `scandicus flexus` | scandicus, then falling |
275
- | `torculus` | three: up, down | `climacus resupinus` | climacus, then rising |
276
- | `porrectus` | three: down, up | `pes subpunctis` | pes, then descending points |
277
- | `scandicus` | three, rising | `compound` | any figure not above |
278
- | `climacus` | three, falling | | |
270
+ | `shape` | figure | `shape` | figure |
271
+ | ----------- | ------------------ | ---------------------- | ------------------------------- |
272
+ | `punctum` | a single note | `torculus resupinus` | torculus, then rising |
273
+ | `pes` | two notes, rising | `porrectus flexus` | porrectus, then falling |
274
+ | `clivis` | two notes, falling | `scandicus flexus` | scandicus, then falling |
275
+ | `torculus` | three: up, down | `climacus resupinus` | climacus, then rising |
276
+ | `porrectus` | three: down, up | `pes subpunctis` | pes, then descending points |
277
+ | `scandicus` | three, rising | `scandicus subpunctis` | two rising, then descending |
278
+ | `climacus` | three, falling | `salicus` | rising, next-to-last an oriscus |
279
+
280
+ The repercussive figures restate a pitch rather than leave it.
281
+
282
+ | `shape` | figure | `shape` | figure |
283
+ | ------------------ | ------------------- | --------------- | ---------------------- |
284
+ | `distropha` | two on one pitch | `pressus` | restated, then falling |
285
+ | `tristropha` | three on one pitch | `pressus maior` | falling in, then out |
286
+ | `tristropha flexa` | three, then falling | `compound` | any figure not above |
279
287
 
280
288
  ```ts
281
289
  interface Neume {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tonus",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Medieval music analysis and performance: GABC plainchant exports, liturgical calendar, tuning systems, ephemeris, and the harmony of the spheres",
6
6
  "author": "Jeffrey Pierce <jeffrey@jeffreypierce.net>",
@@ -9,7 +9,7 @@
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/jeffreypierce/tonus.git"
11
11
  },
12
- "homepage": "https://jeffreypierce.github.io/tonus/",
12
+ "homepage": "https://jeffreypierce.github.io/orreliquum/",
13
13
  "bugs": "https://github.com/jeffreypierce/tonus/issues",
14
14
  "keywords": [
15
15
  "gregorian-chant",