@libraz/libcantus 1.0.1 → 1.2.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 (142) hide show
  1. package/README.md +27 -65
  2. package/README_ja.md +28 -66
  3. package/dist/analyze/index.cjs +11 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +79 -144
  6. package/dist/analyze/index.d.ts +79 -144
  7. package/dist/analyze/index.js +20 -16
  8. package/dist/chunk-35NBV2FT.cjs +145 -0
  9. package/dist/chunk-35NBV2FT.cjs.map +1 -0
  10. package/dist/{chunk-UADYAYAT.cjs → chunk-3R3QVKY5.cjs} +1353 -580
  11. package/dist/chunk-3R3QVKY5.cjs.map +1 -0
  12. package/dist/{chunk-WWURE4XT.cjs → chunk-76ZBTRG2.cjs} +624 -729
  13. package/dist/chunk-76ZBTRG2.cjs.map +1 -0
  14. package/dist/{chunk-FVSI36UV.js → chunk-7KG3RTYS.js} +197 -110
  15. package/dist/chunk-7KG3RTYS.js.map +1 -0
  16. package/dist/{chunk-VTI6FOLB.js → chunk-AGOVW7U6.js} +709 -133
  17. package/dist/chunk-AGOVW7U6.js.map +1 -0
  18. package/dist/{chunk-E3F4AHAH.cjs → chunk-CGBADIO5.cjs} +221 -134
  19. package/dist/chunk-CGBADIO5.cjs.map +1 -0
  20. package/dist/{chunk-6XH6QZYQ.cjs → chunk-DSMJRXCZ.cjs} +1455 -849
  21. package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
  22. package/dist/{chunk-PKFNUHDY.js → chunk-FBH75DAY.js} +1218 -612
  23. package/dist/chunk-FBH75DAY.js.map +1 -0
  24. package/dist/chunk-H2KJHBW4.js +3329 -0
  25. package/dist/chunk-H2KJHBW4.js.map +1 -0
  26. package/dist/chunk-HPVSH7SF.cjs +2294 -0
  27. package/dist/chunk-HPVSH7SF.cjs.map +1 -0
  28. package/dist/{chunk-MXO72PLY.cjs → chunk-HWHGOVO6.cjs} +21 -19
  29. package/dist/chunk-HWHGOVO6.cjs.map +1 -0
  30. package/dist/chunk-HYJJ2V2I.js +2294 -0
  31. package/dist/chunk-HYJJ2V2I.js.map +1 -0
  32. package/dist/{chunk-AFM2ULJH.cjs → chunk-J7FHVZII.cjs} +1567 -1067
  33. package/dist/chunk-J7FHVZII.cjs.map +1 -0
  34. package/dist/chunk-K6OZXIR6.js +1 -0
  35. package/dist/{chunk-H3VORETI.js → chunk-M5RYMAFI.js} +487 -592
  36. package/dist/chunk-M5RYMAFI.js.map +1 -0
  37. package/dist/chunk-NB25P3EH.js +363 -0
  38. package/dist/chunk-NB25P3EH.js.map +1 -0
  39. package/dist/{chunk-TTCLJX6D.js → chunk-NFJ4P6ZZ.js} +10 -8
  40. package/dist/chunk-NFJ4P6ZZ.js.map +1 -0
  41. package/dist/chunk-NM7UAMC3.cjs +457 -0
  42. package/dist/chunk-NM7UAMC3.cjs.map +1 -0
  43. package/dist/chunk-PM4XJE2Y.cjs +363 -0
  44. package/dist/chunk-PM4XJE2Y.cjs.map +1 -0
  45. package/dist/chunk-PUKJP2Z5.js +145 -0
  46. package/dist/chunk-PUKJP2Z5.js.map +1 -0
  47. package/dist/{chunk-VOHZB45S.js → chunk-Q6LFSAKV.js} +1321 -821
  48. package/dist/chunk-Q6LFSAKV.js.map +1 -0
  49. package/dist/{chunk-INHVXP6Z.js → chunk-RFIBF2RR.js} +1203 -430
  50. package/dist/chunk-RFIBF2RR.js.map +1 -0
  51. package/dist/{chunk-Q2WGKJNN.cjs → chunk-S5BKZZGM.cjs} +699 -123
  52. package/dist/chunk-S5BKZZGM.cjs.map +1 -0
  53. package/dist/chunk-SGLEQVJH.cjs +3329 -0
  54. package/dist/chunk-SGLEQVJH.cjs.map +1 -0
  55. package/dist/chunk-UGLFDG7L.cjs +1 -0
  56. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  57. package/dist/{chunk-YH3BIKHH.js → chunk-UMWZ4ZET.js} +146 -49
  58. package/dist/chunk-UMWZ4ZET.js.map +1 -0
  59. package/dist/{index-szBv3496.d.ts → coerce-DP0vYu-x.d.ts} +286 -199
  60. package/dist/{index-C_8o--VE.d.cts → coerce-DRERzdve.d.cts} +286 -199
  61. package/dist/core/index.cjs +9 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +122 -108
  64. package/dist/core/index.d.ts +122 -108
  65. package/dist/core/index.js +32 -28
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.d.cts +168 -84
  68. package/dist/generate/index.d.ts +168 -84
  69. package/dist/generate/index.js +7 -7
  70. package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
  71. package/dist/{index-CCNJRG-P.d.cts → index-B9QLziDe.d.cts} +293 -56
  72. package/dist/{index-Dzr13q3d.d.cts → index-BBWNZS0f.d.cts} +135 -58
  73. package/dist/{index-9wcMhG73.d.ts → index-BHIUyjhZ.d.ts} +293 -56
  74. package/dist/{index-BGFW-8P2.d.cts → index-BJgP97N1.d.ts} +196 -114
  75. package/dist/{index-CunYKLiu.d.ts → index-BL5N2Oyz.d.cts} +1 -1
  76. package/dist/{index-Cj49F892.d.ts → index-BMweUlm5.d.cts} +63 -23
  77. package/dist/{index-CgHqxdps.d.ts → index-BNSM-DSk.d.cts} +196 -114
  78. package/dist/{index-D57QRbVn.d.ts → index-BRMbPkHE.d.ts} +480 -254
  79. package/dist/{index-Chbst3Jx.d.ts → index-CJBBiAmU.d.ts} +135 -58
  80. package/dist/{index-BkGmlyI3.d.cts → index-CL4b4gUQ.d.ts} +63 -23
  81. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  82. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  83. package/dist/{index-BO6xE9YU.d.cts → index-DGCNKAUr.d.cts} +480 -254
  84. package/dist/{index-B4o55NDr.d.cts → index-Dw2DP7vm.d.ts} +1 -1
  85. package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
  86. package/dist/index.cjs +42 -14
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +14 -14
  89. package/dist/index.d.ts +14 -14
  90. package/dist/index.js +82 -54
  91. package/dist/{masks-DYbE485J.d.cts → masks-Cc7-MzzY.d.ts} +49 -2
  92. package/dist/{masks-DYbE485J.d.ts → masks-pADfTOiD.d.cts} +49 -2
  93. package/dist/model/index.cjs +19 -12
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +504 -182
  96. package/dist/model/index.d.ts +504 -182
  97. package/dist/model/index.js +18 -11
  98. package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
  99. package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
  100. package/dist/theory/index.cjs +35 -6
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +257 -62
  103. package/dist/theory/index.d.ts +257 -62
  104. package/dist/theory/index.js +41 -12
  105. package/package.json +7 -5
  106. package/dist/chunk-2LCPM3EG.js +0 -3224
  107. package/dist/chunk-2LCPM3EG.js.map +0 -1
  108. package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
  109. package/dist/chunk-6XPTBE7Y.cjs +0 -49
  110. package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
  111. package/dist/chunk-AFM2ULJH.cjs.map +0 -1
  112. package/dist/chunk-B6XNKWZV.cjs +0 -3224
  113. package/dist/chunk-B6XNKWZV.cjs.map +0 -1
  114. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  115. package/dist/chunk-ELCTZUJJ.js +0 -379
  116. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  117. package/dist/chunk-FVSI36UV.js.map +0 -1
  118. package/dist/chunk-H3VORETI.js.map +0 -1
  119. package/dist/chunk-H42UHPYW.js +0 -1577
  120. package/dist/chunk-H42UHPYW.js.map +0 -1
  121. package/dist/chunk-HFPS7JFD.js +0 -1
  122. package/dist/chunk-INHVXP6Z.js.map +0 -1
  123. package/dist/chunk-K4ZJUGF4.cjs +0 -379
  124. package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
  125. package/dist/chunk-KNJEKLKV.js +0 -49
  126. package/dist/chunk-KNJEKLKV.js.map +0 -1
  127. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  128. package/dist/chunk-NLEU4QSE.cjs +0 -360
  129. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  130. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  131. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  132. package/dist/chunk-PKFNUHDY.js.map +0 -1
  133. package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
  134. package/dist/chunk-RHIJ3S4I.cjs +0 -1
  135. package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
  136. package/dist/chunk-TTCLJX6D.js.map +0 -1
  137. package/dist/chunk-UADYAYAT.cjs.map +0 -1
  138. package/dist/chunk-VOHZB45S.js.map +0 -1
  139. package/dist/chunk-VTI6FOLB.js.map +0 -1
  140. package/dist/chunk-WWURE4XT.cjs.map +0 -1
  141. package/dist/chunk-YH3BIKHH.js.map +0 -1
  142. /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
@@ -1,6 +1,6 @@
1
- import { c as ChordLike, K as KeyLike, j as ChordToneRole, b as Chord, k as KeyRelation, e as ChordSegment, V as VoiceSnapshot, g as ChordSpan } from './index-szBv3496.js';
2
- import { T as TimeSignature, c as MeterMap } from './index-Cj49F892.js';
3
- import { d as KeyScale, g as NoteEvent } from './index-B9MNZboQ.js';
1
+ import { c as ChordLike, K as KeyLike, i as ChordToneRole, r as SpelledKeyLike, b as Chord, n as ResolvedKey, j as KeyRelation, e as ChordSegment, V as VoiceSnapshot, g as ChordSpan } from './coerce-DP0vYu-x.js';
2
+ import { b as MeterLike } from './index-CL4b4gUQ.js';
3
+ import { h as NoteEvent, e as KeyScale } from './index-Cy15oTvh.js';
4
4
 
5
5
  /**
6
6
  * The vocabulary the analyze layer explains itself in.
@@ -222,33 +222,41 @@ type DetectCadenceOptions = {
222
222
  * Classify the cadence formed by moving from one chord to the next.
223
223
  *
224
224
  * - authentic: V (dominant a fifth above the tonic) to I
225
- * - plagal: IV (a fourth above the tonic) to I
225
+ * - plagal: IV, the chord on the key's own fourth degree, to I
226
226
  * - modal: bVII to I, the cadence of rock and modal writing
227
227
  * - deceptive: V to the key's own submediant, plus the borrowed bVI of a major
228
228
  * key
229
229
  * - phrygian: iv6 to V of a minor key, the half cadence whose bass falls a
230
230
  * semitone from b6 to 5
231
- * - half: any other chord than the dominant itself to V
232
- *
233
- * Both are read as scale degrees rather than as semitone distances, so a mode
234
- * cadences onto the submediant and the dominant it actually has: the deceptive
235
- * arrival of dorian is a major sixth above the tonic and that of phrygian a
236
- * minor sixth. A half cadence normally rests on the major third that carries
237
- * the leading tone, but where the key's own dominant has no leading tone —
238
- * aeolian and dorian, the modal `v` of pop writing — the arrival's own third
239
- * suffices as long as the key contains it. That relaxation never applies to a
240
- * key whose dominant does carry the leading tone, so a borrowed minor `v` in a
241
- * major key is no half cadence.
231
+ * - half: any other chord than the dominant itself to V, the cadential six-four
232
+ * standing on the dominant's own bass included
233
+ *
234
+ * These are read as scale degrees rather than as semitone distances, so a mode
235
+ * cadences from and onto the degrees it actually has: the deceptive arrival of
236
+ * dorian is a major sixth above the tonic and that of phrygian a minor sixth,
237
+ * and the subdominant a plagal cadence falls from is the tritone above the
238
+ * tonic in lydian. The two borrowed types are the exception, and are stated as
239
+ * the fixed offsets they name: `modal` is the flattened seventh whatever the
240
+ * key writes on that degree, and the Phrygian bass falls from the lowered
241
+ * submediant. A half cadence normally rests on the major third that carries
242
+ * the leading tone, but where the key's own dominant has no leading tone — the
243
+ * modal `v` of dorian, mixolydian and the pop writing built on them — the
244
+ * arrival's own third suffices as long as the key contains it. That relaxation
245
+ * belongs to the modes alone: a borrowed minor `v` in a major key is no half
246
+ * cadence, and neither is the `v` of a minor key, which cadences through the
247
+ * raised seventh it writes as an accidental rather than in its scale.
242
248
  *
243
249
  * A static V-to-V repeat with no root motion is not a cadence and yields a null
244
- * type, and so is the tonic six-four resolving onto the dominant it already
245
- * stands on: that pair is one harmony moving inside itself. Passing that
246
- * six-four as `approach` when the dominant is the `from` chord is what lets the
247
- * cadence be reported as the single event it is — the type and the beat are
248
- * still the dominant's resolution, while the `rationale` says the cadence began
249
- * at the six-four. A six-four the bass leaves instead (`IV-I64-IV`, or a
250
- * neighbouring one over the tonic) is an ordinary inverted tonic and is read as
251
- * one.
250
+ * type. The tonic six-four resolving onto the dominant it stands on is a half
251
+ * cadence, and the most ordinary one there is: `I - I64 - V` closes the
252
+ * antecedent of a period, and the six-four is a double appoggiatura over the
253
+ * dominant's own bass rather than a chord that keeps the motion going. Where
254
+ * that dominant goes on to resolve, the pair and the resolution are one event —
255
+ * passing the six-four as `approach` when the dominant is the `from` chord is
256
+ * what lets it be reported as one, with the type and the beat still the
257
+ * dominant's resolution while the `rationale` says the cadence began at the
258
+ * six-four. A six-four the bass leaves instead (`IV-I64-IV`, or a neighbouring
259
+ * one over the tonic) is an ordinary inverted tonic and is read as one.
252
260
  *
253
261
  * An authentic cadence is graded perfect or imperfect. Perfect takes the
254
262
  * dominant to the tonic with both chords in root position and the tonic in the
@@ -336,7 +344,7 @@ declare function detectCadence(from: ChordLike, to: ChordLike, key: KeyLike, opt
336
344
  * ```
337
345
  * @category Functional Harmony
338
346
  */
339
- declare function romanToChord(text: string, key: KeyLike): Chord;
347
+ declare function romanToChord(text: string, key: SpelledKeyLike): Chord;
340
348
  /**
341
349
  * Options for {@link chordToRoman}.
342
350
  *
@@ -350,10 +358,17 @@ type ChordToRomanOptions = {
350
358
  * Off by default: naming the root is always a correct spelling, whereas
351
359
  * whether a chromatic dominant is genuinely applied is a reading only the
352
360
  * caller can make. Turning it on names the chords {@link secondaryDominant}
353
- * builds as the applied numerals they are, except where the target degree
354
- * carries no perfect fifth: nothing tonicizes a diminished triad, so a chord
355
- * over the seventh degree of a major key stays `#IV7` rather than becoming a
356
- * numeral no harmony text writes.
361
+ * builds as the applied numerals they are, except in three cases, where the
362
+ * numeral names the root against the home key as it does with this off:
363
+ *
364
+ * - The target degree carries no perfect fifth: nothing tonicizes a
365
+ * diminished triad, so a chord over the seventh degree of a major key stays
366
+ * `#IV7` rather than becoming a numeral no harmony text writes.
367
+ * - The target is the tonic, which its own dominant points at: `V7` is the
368
+ * key's dominant and `V/I` is a numeral no harmony text writes either.
369
+ * - The chord is diatonic to the key, which keeps the function of its degree
370
+ * rather than tonicizing another. `G7` is `VII7` in A minor and `I7` in C
371
+ * mixolydian, not `V7/III` and `V7/IV`.
357
372
  */
358
373
  applied?: boolean;
359
374
  /**
@@ -392,6 +407,11 @@ type ChordToRomanOptions = {
392
407
  * A chord sounding one of the augmented sixths over an explicit bass on the
393
408
  * lowered submediant renders as `It6`, `Fr6` or `Ger6`, since no numeral names
394
409
  * those chords; see {@link augmentedSixthKind} for exactly when that applies.
410
+ * That reading is made against the key as written, because what separates an
411
+ * augmented sixth from the seventh chord it sounds like is its letters: the
412
+ * German sixth of Ab minor is named from the Fb Ab Cb D it is written with, not
413
+ * from the letters those pitch classes take once the key has been respelled as
414
+ * a G# minor.
395
415
  *
396
416
  * @param chord The chord to name, as a chord symbol, chord data, or a `Chord`.
397
417
  * @param key The prevailing key, as a key name, a key/scale, or a `Key`.
@@ -502,11 +522,14 @@ declare function isMinorKey(key: KeyLike): boolean;
502
522
  * an ambiguity the root offset cannot:
503
523
  *
504
524
  * - A chord that *sounds* like a dominant — a major third with a minor seventh,
505
- * or a diminished-family chord — and is not diatonic to the key has dominant
506
- * function when its root resolves down a fifth or by a semitone onto a
507
- * diatonic degree. This is what makes an applied dominant (`A7` in C, which
508
- * tonicizes ii) and its tritone substitute (`Db7`) read as dominant rather
509
- * than inheriting the function of the degree they happen to sit on.
525
+ * or a diminished-family chord — and is neither diatonic to the key nor
526
+ * borrowed from its parallel mode has dominant function when its root
527
+ * resolves onto a degree that can be made a local tonic. This is what makes an
528
+ * applied dominant (`A7` in C, which tonicizes ii) and its tritone substitute
529
+ * (`Db7`) read as dominant rather than inheriting the function of the degree
530
+ * they happen to sit on. A bare major triad carries no tritone, so it only
531
+ * tonicizes by falling a fifth onto a degree other than the tonic: the Picardy
532
+ * third of a minor key and its borrowed major IV sound their own degrees.
510
533
  * - The Neapolitan is subdominant.
511
534
  * - An augmented sixth is subdominant. All three are altered predominants that
512
535
  * resolve outward onto the dominant, so they take subdominant function even
@@ -646,8 +669,8 @@ declare function secondaryDominant(targetDegree: number, key: KeyLike): Chord;
646
669
  * @example
647
670
  * ```ts
648
671
  * import { makeChord, secondaryDominantOf } from '@libraz/libcantus';
649
- * secondaryDominantOf(makeChord(8, 'maj'));
650
- * // { rootPc: 3, quality: 'dom7' } — Eb7 tonicizes the borrowed bVI of C major
672
+ * secondaryDominantOf(makeChord(8, 'maj')).rootPc; // 3 — Eb7 tonicizes the
673
+ * // borrowed bVI of C major
651
674
  * ```
652
675
  * @category Functional Harmony
653
676
  */
@@ -727,8 +750,20 @@ type KeyRegion = {
727
750
  startBeat: number;
728
751
  /** End of the region, exclusive. */
729
752
  endBeat: number;
730
- /** The key in force across the region. */
731
- key: KeyScale;
753
+ /**
754
+ * The key in force across the region, owned by it: every entry point hands
755
+ * back a key of the region's own, so writing to one changes neither the other
756
+ * regions of the same run nor what a later call reports.
757
+ *
758
+ * Whole, whichever way the region was built. A caller who named the key hands
759
+ * its spelling and scale form down, and a key the analysis found is spelled
760
+ * and formed the way {@link resolveKey} reads a bare scale — the reading every
761
+ * other layer would have made of it. There is no third state to guess at: a
762
+ * key/scale alone says nothing about how it is written, so a region carrying
763
+ * one would report whichever spelling those pitch classes read best from, and
764
+ * an Ab minor handed in would come back out a G# minor.
765
+ */
766
+ key: ResolvedKey;
732
767
  /**
733
768
  * How well the region's music fits the key, in [0, 1]: the correlation
734
769
  * between the span's pitch-class distribution and the key's profile, with
@@ -770,7 +805,7 @@ type KeyTimelineOptions = {
770
805
  *
771
806
  * @defaultValue `4/4`
772
807
  */
773
- ts?: TimeSignature;
808
+ ts?: MeterLike;
774
809
  /**
775
810
  * The meter as it changes over the span. Bar lines, downbeats and the metric
776
811
  * accents the search discounts a modulation by all follow the signature in
@@ -779,7 +814,7 @@ type KeyTimelineOptions = {
779
814
  *
780
815
  * @defaultValue 4/4 throughout
781
816
  */
782
- meters?: MeterMap;
817
+ meters?: MeterLike;
783
818
  /**
784
819
  * Expected length of one key area in beats; defaults to four bars of the
785
820
  * opening meter.
@@ -881,6 +916,12 @@ declare function keyTimelineFromNotes(notes: readonly NoteEvent[], opts?: KeyTim
881
916
  * confidence, as it does in the note path, but takes no part in choosing which
882
917
  * keys are reported: that is settled by how the chords read.
883
918
  *
919
+ * A region's evidence is the run of segments it was grouped from, which is the
920
+ * same reading the search itself makes of them. Where the segments given
921
+ * overlap each other — a chart is a sequence, so a chord timeline's are not —
922
+ * a chord held past the segment after it argues for its own segment's key
923
+ * rather than for both.
924
+ *
884
925
  * @param chords The chord segments to analyze, in time order; pass a chord
885
926
  * timeline's `segments`.
886
927
  * @param opts Analysis options; see {@link KeyTimelineOptions}.
@@ -916,7 +957,7 @@ declare function detectModulations(chords: readonly ChordSegment[], opts?: KeyTi
916
957
  * ```
917
958
  * @category Arrangement & Analysis
918
959
  */
919
- declare function prevailingKeyOf(regions: readonly KeyRegion[]): KeyScale | null;
960
+ declare function prevailingKeyOf(regions: readonly KeyRegion[]): ResolvedKey | null;
920
961
 
921
962
  /**
922
963
  * A suspension figure, named by the interval above the bass and the interval it
@@ -1033,7 +1074,7 @@ type IdentifiedVoiceNote = VoiceNote & {
1033
1074
  * ```
1034
1075
  * @category Arrangement & Analysis
1035
1076
  */
1036
- type KeyContext = KeyScale | ((beat: number) => KeyScale);
1077
+ type KeyContext = KeyLike | ((beat: number) => KeyScale);
1037
1078
  /**
1038
1079
  * Give plain note events the ids {@link analyzeVoice} reports back.
1039
1080
  *
@@ -1055,8 +1096,10 @@ declare function toVoiceNotes(events: readonly NoteEvent[]): IdentifiedVoiceNote
1055
1096
  * resolving by step the other way), and escape tones, then fall back to
1056
1097
  * tension, avoid, or an unresolved-dissonance label. The figures are named as
1057
1098
  * {@link classifyMelodyTones} names them, so a caller reading a melody through
1058
- * both layers gets one vocabulary; that classifier weighs the metre as well,
1059
- * which this one is not given, so it is the stricter of the two. Leading-tone resolutions are noted
1099
+ * both layers gets one vocabulary. The two answer different questions and
1100
+ * neither refines the other: that classifier weighs the metre, which this one is
1101
+ * not given, so it can call ornamental a note this one reads as a chord tone,
1102
+ * and the reverse. Leading-tone resolutions are noted
1060
1103
  * additionally, judged against the key in force at the beat the resolution
1061
1104
  * lands on, so a modulation is heard from its new tonic.
1062
1105
  *
@@ -1103,6 +1146,32 @@ type ChordTimeline = {
1103
1146
  at: (beat: number) => Chord | null;
1104
1147
  segments: ChordSegment[];
1105
1148
  };
1149
+ /**
1150
+ * Reject a value that is not a chord timeline.
1151
+ *
1152
+ * A timeline is the one argument a host is most likely to hold from somewhere
1153
+ * else — an in-memory project value, a previous run restored to a timeline, or
1154
+ * another library's output — and
1155
+ * it is read field by field deep inside an analysis. Checking it once at the
1156
+ * entrance names the argument that is wrong instead of reporting the library's
1157
+ * own `TypeError` from wherever the first `segments` lookup happened to be.
1158
+ *
1159
+ * The segments are checked too: every reader of a timeline compares beats, and
1160
+ * a segment whose bounds are missing compares as neither before nor after, so
1161
+ * it disappears from the analysis rather than being reported.
1162
+ *
1163
+ * @param timeline The timeline as the caller passed it.
1164
+ * @param name What the argument is called in an error message.
1165
+ * @returns The same timeline.
1166
+ * @throws If the value is not a timeline of chord segments.
1167
+ * @example
1168
+ * ```ts
1169
+ * import { assertChordTimeline, chordTimelineFromChords } from '@libraz/libcantus';
1170
+ * assertChordTimeline(chordTimelineFromChords([], 4)); // the timeline, unchanged
1171
+ * ```
1172
+ * @category Arrangement & Analysis
1173
+ */
1174
+ declare function assertChordTimeline(timeline: ChordTimeline, name?: string): ChordTimeline;
1106
1175
  /**
1107
1176
  * Build a chord timeline from placed chords.
1108
1177
  *
@@ -1139,15 +1208,19 @@ type ChordTimelineOptions = {
1139
1208
  * Key context held across the whole span. Omit it to have the key searched
1140
1209
  * for over time with {@link keyTimelineFromNotes}, which is what lets a piece
1141
1210
  * that modulates be analysed against the key actually in force.
1211
+ *
1212
+ * Taken in whatever form the caller holds a key, and kept whole: the region
1213
+ * built from it carries the spelling and the scale form it was named with, so
1214
+ * an Ab minor stated here is an Ab minor in the result.
1142
1215
  */
1143
- key?: KeyScale;
1216
+ key?: KeyLike;
1144
1217
  /**
1145
1218
  * A single time signature held across the whole span, as sugar for a
1146
1219
  * one-element `meters`; defaults to 4/4. Giving both is an input error.
1147
1220
  *
1148
1221
  * @defaultValue `4/4`
1149
1222
  */
1150
- ts?: TimeSignature;
1223
+ ts?: MeterLike;
1151
1224
  /**
1152
1225
  * The meter as it changes over the span. Bar lines, downbeats and metric
1153
1226
  * weight all follow the signature in force at the beat in question, so a
@@ -1155,7 +1228,7 @@ type ChordTimelineOptions = {
1155
1228
  *
1156
1229
  * @defaultValue 4/4 throughout
1157
1230
  */
1158
- meters?: MeterMap;
1231
+ meters?: MeterLike;
1159
1232
  /**
1160
1233
  * Length of the pickup in beats, when the piece starts with one.
1161
1234
  *
@@ -1184,18 +1257,21 @@ type ChordTimelineOptions = {
1184
1257
  */
1185
1258
  harmonicRhythm?: number;
1186
1259
  /**
1187
- * Shortest chord the `'dynamic'` search may report, in beats; defaults to one
1188
- * beat. Lower it to catch changes on off-beats, at the cost of proportionally
1189
- * more work. Values above `harmonicRhythm` are clamped to it, and the option
1190
- * is ignored under `'grid'` segmentation.
1260
+ * Resolution the `'dynamic'` search looks for changes at, in beats; defaults
1261
+ * to one main pulse of the meter — a quarter in 4/4, a dotted quarter in 6/8.
1262
+ * Lower it to catch changes on off-beats, at the cost of proportionally more
1263
+ * work. Values above `harmonicRhythm` are clamped to it, and the option is
1264
+ * ignored under `'grid'` segmentation.
1191
1265
  *
1192
1266
  * The search reads it as a resolution rather than as an exact slot length: it
1193
- * rounds down to a whole division of `harmonicRhythm`, so `0.6` against a
1194
- * four-beat chord is read in sevenths of it. That keeps the beats one
1195
- * expected chord gives way to the next on the grid whatever resolution is
1196
- * asked for, and with them every change a coarser setting found.
1267
+ * rounds down to a whole division of the meter's pulse, so `0.6` in 4/4 is
1268
+ * read in half-beats. That keeps every pulse of the meter, and the bar lines
1269
+ * with them, on the grid whatever resolution is asked for, and with them every
1270
+ * change a coarser setting found. A resolution coarser than one pulse is
1271
+ * therefore read as one pulse rather than stepping over the beats a change is
1272
+ * heard on.
1197
1273
  *
1198
- * @defaultValue `1`
1274
+ * @defaultValue one main pulse of the meter
1199
1275
  */
1200
1276
  minChordBeats?: number;
1201
1277
  /**
@@ -1237,7 +1313,7 @@ type ChordTimelineResult = {
1237
1313
  * The key held longest across {@link ChordTimelineResult.keys} — the one to
1238
1314
  * print on a key signature or hand to a generator that takes a single key.
1239
1315
  */
1240
- prevailingKey: KeyScale;
1316
+ prevailingKey: ResolvedKey;
1241
1317
  /** One confidence value in [0, 1] per segment, in segment order. */
1242
1318
  segmentConfidence: number[];
1243
1319
  };
@@ -1245,7 +1321,8 @@ type ChordTimelineResult = {
1245
1321
  * Infer a chord timeline from raw multi-track notes.
1246
1322
  *
1247
1323
  * Chord boundaries are searched for rather than assumed: the span is examined
1248
- * in `minChordBeats` slots, and the change points that best explain the notes
1324
+ * in slots of one main pulse, subdivided as finely as `minChordBeats` asks for,
1325
+ * and the change points that best explain the notes
1249
1326
  * are chosen, trading each slot's harmonic fit against a cost per chord change
1250
1327
  * that a strong beat discounts. A bar holding two chords therefore yields two
1251
1328
  * segments and a bar holding one yields one, without the caller having to know
@@ -1340,4 +1417,4 @@ type CadenceHit = {
1340
1417
  */
1341
1418
  declare function detectCadences(timeline: ChordTimeline, key: KeyContext): CadenceHit[];
1342
1419
 
1343
- export { type AnalyzeChordOptions as A, type BorrowedSource as B, type CadenceHit as C, type DetectCadenceOptions as D, type ExplainRomanOptions as E, isMinorKey as F, keyTimelineFromNotes as G, type HarmonicFunction as H, type IdentifiedVoiceNote as I, pivotChords as J, type KeyContext as K, prevailingKeyOf as L, romanToChord as M, secondaryDominant as N, secondaryDominantOf as O, type PivotChord as P, toVoiceNotes as Q, type RejectedCandidate as R, type SuspensionFigure as S, type TheoryLabel as T, type VoiceNote as V, type AnalyzedNote as a, type CadenceResult as b, type ChordAnalysis as c, type ChordSegmentation as d, type ChordTimeline as e, type ChordTimelineOptions as f, type ChordTimelineResult as g, type ChordToRomanOptions as h, type KeyProfileName as i, type KeyProfilePair as j, type KeyRegion as k, type KeyTimelineOptions as l, type RomanExplanation as m, analyzeChord as n, analyzeVoice as o, borrowedSource as p, chordTimelineFromChords as q, chordTimelineFromNotes as r, chordToRoman as s, detectCadence as t, detectCadences as u, detectModulations as v, explainRoman as w, functionOf as x, isBorrowedChord as y, isDiatonic as z };
1420
+ export { type AnalyzeChordOptions as A, type BorrowedSource as B, type CadenceHit as C, type DetectCadenceOptions as D, type ExplainRomanOptions as E, isDiatonic as F, isMinorKey as G, type HarmonicFunction as H, type IdentifiedVoiceNote as I, keyTimelineFromNotes as J, type KeyContext as K, pivotChords as L, prevailingKeyOf as M, romanToChord as N, secondaryDominant as O, type PivotChord as P, secondaryDominantOf as Q, type RejectedCandidate as R, type SuspensionFigure as S, type TheoryLabel as T, toVoiceNotes as U, type VoiceNote as V, type AnalyzedNote as a, type CadenceResult as b, type ChordAnalysis as c, type ChordSegmentation as d, type ChordTimeline as e, type ChordTimelineOptions as f, type ChordTimelineResult as g, type ChordToRomanOptions as h, type KeyProfileName as i, type KeyProfilePair as j, type KeyRegion as k, type KeyTimelineOptions as l, type RomanExplanation as m, analyzeChord as n, analyzeVoice as o, assertChordTimeline as p, borrowedSource as q, chordTimelineFromChords as r, chordTimelineFromNotes as s, chordToRoman as t, detectCadence as u, detectCadences as v, detectModulations as w, explainRoman as x, functionOf as y, isBorrowedChord as z };
@@ -1,4 +1,4 @@
1
- import { P as ParseResult } from './index-B9MNZboQ.cjs';
1
+ import { P as ParseResult } from './index-Cy15oTvh.js';
2
2
 
3
3
  /**
4
4
  * Meter arithmetic shared by the meter module and the validation guards.
@@ -83,6 +83,14 @@ type MeterChange = {
83
83
  * The first entry also governs everything before it, which is what reads a
84
84
  * pickup written at negative beats in the signature the piece opens in.
85
85
  *
86
+ * Beat 0 is the first downbeat here as everywhere else: the opening signature
87
+ * lays its bar lines from beat 0 whatever beat the first entry is written at, so
88
+ * a map opening at the pickup it covers numbers its bars exactly as the bare
89
+ * signature does. Every later entry starts a bar at the beat it takes effect.
90
+ *
91
+ * Pass the whole piece with its map rather than one call per meter span: a
92
+ * split call cannot see a voice move or a key region that crosses the change.
93
+ *
86
94
  * @category Rhythm & Meter
87
95
  */
88
96
  type MeterMap = MeterChange[];
@@ -123,7 +131,10 @@ type MeterLike = string | TimeSignature | MeterMap | {
123
131
  * value whose `toJSON` returns one of those.
124
132
  * @param name What the meter is called in an error message, so a caller that
125
133
  * holds several of them hears which one was malformed.
126
- * @returns The validated signature, or the validated map.
134
+ * @returns The validated signature, or the validated map, freshly allocated
135
+ * down to every grouping array — so a caller may keep and edit what it gets
136
+ * back without editing the meter it passed in, and without any two calls
137
+ * handing out the same object.
127
138
  * @throws If the value names no meter, or the meter it names is malformed.
128
139
  */
129
140
  declare function toMeterData(value: MeterLike, name?: string): MeterData;
@@ -186,17 +197,23 @@ declare function tryParseTimeSignature(text: string): ParseResult<TimeSignature>
186
197
  *
187
198
  * The additive form is what {@link parseTimeSignature} reads back, and it adds
188
199
  * its terms to get the numerator — so only a grouping written in denominator
189
- * units can be rendered that way. A grouping counted in main pulses (9/8 as
190
- * `[1, 1, 1]`, 12/8 as `[2, 2]`) would come back as a different bar, so it
191
- * falls back to the plain form, which those groupings cost nothing: they select
192
- * the same reading the bare signature does.
200
+ * units can be rendered that way. A grouping counted in main pulses whose groups
201
+ * are all the same length (9/8 as `[1, 1, 1]`, 12/8 as `[2, 2]`) states the
202
+ * division the bare signature already has, so it falls back to the plain form at
203
+ * no cost. One whose groups differ (12/8 as `[1, 1, 2]`) states an accent no
204
+ * signature text can carry, and asking for the additive form of it is refused
205
+ * rather than answered with a bar that weighs its pulses differently.
193
206
  *
194
207
  * The plain form drops the grouping, so a round trip through it reads 7/8 as
195
208
  * flat rather than as 2+2+3; ask for the additive form to keep it.
196
209
  *
197
- * @param ts The time signature.
210
+ * @param ts The time signature, in any form that names one; a meter map is
211
+ * rendered as the signature it opens in.
198
212
  * @param opts Set `grouping: true` to render an additive grouping.
199
213
  * @returns The formatted signature.
214
+ * @throws If `grouping: true` is asked of a grouping that no signature text can
215
+ * spell: one counted in main pulses, on a compound numerator, whose groups are
216
+ * not all the same length.
200
217
  * @example
201
218
  * ```ts
202
219
  * import { formatTimeSignature } from '@libraz/libcantus';
@@ -206,7 +223,7 @@ declare function tryParseTimeSignature(text: string): ParseResult<TimeSignature>
206
223
  * ```
207
224
  * @category Rhythm & Meter
208
225
  */
209
- declare function formatTimeSignature(ts: TimeSignature, opts?: {
226
+ declare function formatTimeSignature(ts: MeterLike, opts?: {
210
227
  grouping?: boolean;
211
228
  }): string;
212
229
  /**
@@ -217,19 +234,27 @@ declare function formatTimeSignature(ts: TimeSignature, opts?: {
217
234
  * is a signature whose {@link TimeSignature.grouping} selects the additive
218
235
  * reading (9/8 as 2+2+2+3).
219
236
  *
220
- * @param ts The time signature.
237
+ * @param ts The time signature, in any form that names one; a meter map is read
238
+ * as the signature it opens in.
221
239
  * @returns True for compound meters.
222
240
  * @category Rhythm & Meter
223
241
  */
224
- declare function isCompound(ts: TimeSignature): boolean;
242
+ declare function isCompound(ts: MeterLike): boolean;
225
243
  /**
226
244
  * Length of a bar in quarter-note beats.
227
245
  *
228
- * @param ts The time signature.
246
+ * @param ts The time signature, in any form that names one; a meter map has no
247
+ * single bar length, so it is read as the signature it opens in. Ask
248
+ * {@link beatsPerBarAt} for the bar a given beat falls in.
229
249
  * @returns The bar length in quarter notes.
250
+ * @example
251
+ * ```ts
252
+ * import { beatsPerBar } from '@libraz/libcantus';
253
+ * beatsPerBar('6/8'); // 3
254
+ * ```
230
255
  * @category Rhythm & Meter
231
256
  */
232
- declare function beatsPerBar(ts: TimeSignature): number;
257
+ declare function beatsPerBar(ts: MeterLike): number;
233
258
  /**
234
259
  * The time signature in force at a beat.
235
260
  *
@@ -284,6 +309,12 @@ declare function barIndexAt(beatInQuarters: number, meter: MeterLike): number;
284
309
  * The meter-map counterpart of {@link beatsPerBar}, which answers for one
285
310
  * signature and so cannot be asked about a piece that changes meter.
286
311
  *
312
+ * The bar the beat is in, not the bar its signature nominally has: a change of
313
+ * meter starts a new bar, so a bar interrupted by one is shorter than its own
314
+ * signature says. Reading the signature's own length there would put the end of
315
+ * the bar past where the next one begins, and every span measured in bars from
316
+ * that point on would be counted against a bar length the music never had.
317
+ *
287
318
  * @param beatInQuarters Absolute position in quarter-note beats.
288
319
  * @param meter A single signature, or the piece's meter map.
289
320
  * @returns The bar length in quarter notes.
@@ -300,7 +331,9 @@ declare function beatsPerBarAt(beatInQuarters: number, meter: MeterLike): number
300
331
  *
301
332
  * @param opts The options carrying `ts`, `meters`, or neither.
302
333
  * @param name What the options belong to, for the error message.
303
- * @returns The meter map to analyse against; 4/4 throughout when neither is given.
334
+ * @returns The meter map to analyse against, freshly allocated down to every
335
+ * signature and grouping array, so writing to it changes nothing another
336
+ * caller reads; 4/4 throughout when neither `ts` nor `meters` is given.
304
337
  * @throws If both `ts` and `meters` are given, or the map is malformed.
305
338
  * @example
306
339
  * ```ts
@@ -311,8 +344,8 @@ declare function beatsPerBarAt(beatInQuarters: number, meter: MeterLike): number
311
344
  * @category Rhythm & Meter
312
345
  */
313
346
  declare function resolveMeters(opts: {
314
- ts?: TimeSignature;
315
- meters?: MeterMap;
347
+ ts?: MeterLike;
348
+ meters?: MeterLike;
316
349
  }, name?: string): MeterMap;
317
350
  /**
318
351
  * Number of main pulses (felt beats) per bar: the numerator for simple meters,
@@ -324,11 +357,12 @@ declare function resolveMeters(opts: {
324
357
  * {@link TimeSignature.grouping}; {@link metricWeight} then accents each
325
358
  * group's head pulse.
326
359
  *
327
- * @param ts The time signature.
360
+ * @param ts The time signature, in any form that names one; a meter map is read
361
+ * as the signature it opens in.
328
362
  * @returns The pulse count per bar.
329
363
  * @category Rhythm & Meter
330
364
  */
331
- declare function pulsesPerBar(ts: TimeSignature): number;
365
+ declare function pulsesPerBar(ts: MeterLike): number;
332
366
  /**
333
367
  * Convert an absolute quarter-note position to a bar index and in-bar offset.
334
368
  *
@@ -351,16 +385,17 @@ declare function beatToBarPosition(beatInQuarters: number, meter: MeterLike): Ba
351
385
  * denominator unit in an additive metre. Position displays, click tracks, and
352
386
  * score export are all written against this length.
353
387
  *
354
- * @param ts The time signature.
388
+ * @param ts The time signature, in any form that names one; a meter map is read
389
+ * as the signature it opens in.
355
390
  * @returns The pulse length in quarter-note beats.
356
391
  * @example
357
392
  * ```ts
358
- * import { parseTimeSignature, pulseBeats } from '@libraz/libcantus';
359
- * pulseBeats(parseTimeSignature('6/8')); // 1.5
393
+ * import { pulseBeats } from '@libraz/libcantus';
394
+ * pulseBeats('6/8'); // 1.5
360
395
  * ```
361
396
  * @category Rhythm & Meter
362
397
  */
363
- declare function pulseBeats(ts: TimeSignature): number;
398
+ declare function pulseBeats(ts: MeterLike): number;
364
399
  /**
365
400
  * The felt-beat number of a bar position, 1-based.
366
401
  *
@@ -391,11 +426,16 @@ declare function barPositionToPulse(pos: BarPosition, meter: MeterLike): number;
391
426
  * @param beatInQuarters Absolute position in quarter-note beats.
392
427
  * @param meter A single signature, or the piece's meter map.
393
428
  * @param decimals Digits of the fractional beat to keep.
394
- * @returns The formatted position, e.g. `'3.2'` for bar 3, felt beat 2.
429
+ * @returns The formatted position in one of two forms: `bar.beat` on a felt
430
+ * beat, e.g. `'3.2'` for bar 3, felt beat 2; and `bar.beat+fraction` between
431
+ * felt beats, e.g. `'3.2+0.5'` halfway from the second felt beat to the third.
432
+ * A reader of these strings has to take both, since most onsets of an ordinary
433
+ * piece fall between pulses.
395
434
  * @example
396
435
  * ```ts
397
436
  * import { formatBarPosition, parseTimeSignature } from '@libraz/libcantus';
398
437
  * formatBarPosition(7.5, parseTimeSignature('6/8')); // '3.2'
438
+ * formatBarPosition(8.25, parseTimeSignature('6/8')); // '3.2+0.5'
399
439
  * ```
400
440
  * @category Rhythm & Meter
401
441
  */
@@ -425,7 +465,7 @@ declare function barPositionToBeat(pos: BarPosition, meter: MeterLike): number;
425
465
  * an ungrouped bar rather than accenting every group head.
426
466
  *
427
467
  * Given a meter map, the weight follows the signature in force at that beat, so
428
- * beat 5 of a piece is a mid-bar accent in 4/4 and a plain main pulse in 3/4.
468
+ * beat 2 of a piece is a mid-bar accent in 4/4 and a plain main pulse in 3/4.
429
469
  * The downbeat is beat 0 however much pickup precedes it, so a note in the
430
470
  * pickup weighs as the upbeat it is rather than as a downbeat.
431
471
  *