@libraz/libcantus 1.0.0 → 1.1.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-TTCLJX6D.js → chunk-57WB2EDF.js} +10 -8
  9. package/dist/chunk-57WB2EDF.js.map +1 -0
  10. package/dist/chunk-5JJ6EHEZ.cjs +457 -0
  11. package/dist/chunk-5JJ6EHEZ.cjs.map +1 -0
  12. package/dist/{chunk-AFM2ULJH.cjs → chunk-5KW7DNCJ.cjs} +1567 -1067
  13. package/dist/chunk-5KW7DNCJ.cjs.map +1 -0
  14. package/dist/chunk-7GOKQJTQ.js +145 -0
  15. package/dist/chunk-7GOKQJTQ.js.map +1 -0
  16. package/dist/{chunk-H3VORETI.js → chunk-AWEMD747.js} +487 -592
  17. package/dist/chunk-AWEMD747.js.map +1 -0
  18. package/dist/{chunk-4A2RMUCC.js → chunk-DKICR7AN.js} +1216 -611
  19. package/dist/chunk-DKICR7AN.js.map +1 -0
  20. package/dist/{chunk-INHVXP6Z.js → chunk-FPBZFNCD.js} +1159 -415
  21. package/dist/chunk-FPBZFNCD.js.map +1 -0
  22. package/dist/{chunk-M4CWTJWJ.cjs → chunk-GDKE6RQW.cjs} +1453 -848
  23. package/dist/chunk-GDKE6RQW.cjs.map +1 -0
  24. package/dist/chunk-H66ARFU5.js +363 -0
  25. package/dist/chunk-H66ARFU5.js.map +1 -0
  26. package/dist/{chunk-YH3BIKHH.js → chunk-HOHFYGKW.js} +146 -49
  27. package/dist/chunk-HOHFYGKW.js.map +1 -0
  28. package/dist/chunk-HSY2VQXZ.js +2272 -0
  29. package/dist/chunk-HSY2VQXZ.js.map +1 -0
  30. package/dist/{chunk-E3F4AHAH.cjs → chunk-J772DL3B.cjs} +221 -134
  31. package/dist/chunk-J772DL3B.cjs.map +1 -0
  32. package/dist/chunk-K6OZXIR6.js +1 -0
  33. package/dist/{chunk-VTI6FOLB.js → chunk-PQL7E5F6.js} +709 -133
  34. package/dist/chunk-PQL7E5F6.js.map +1 -0
  35. package/dist/chunk-Q6R3QAZD.cjs +3329 -0
  36. package/dist/chunk-Q6R3QAZD.cjs.map +1 -0
  37. package/dist/{chunk-MXO72PLY.cjs → chunk-QCXYKSZS.cjs} +21 -19
  38. package/dist/chunk-QCXYKSZS.cjs.map +1 -0
  39. package/dist/{chunk-VOHZB45S.js → chunk-R5VUXQMP.js} +1321 -821
  40. package/dist/chunk-R5VUXQMP.js.map +1 -0
  41. package/dist/chunk-R7B5N2JK.cjs +363 -0
  42. package/dist/chunk-R7B5N2JK.cjs.map +1 -0
  43. package/dist/{chunk-WWURE4XT.cjs → chunk-RVS4G4MR.cjs} +624 -729
  44. package/dist/chunk-RVS4G4MR.cjs.map +1 -0
  45. package/dist/chunk-TT6PKMPR.cjs +145 -0
  46. package/dist/chunk-TT6PKMPR.cjs.map +1 -0
  47. package/dist/chunk-UGLFDG7L.cjs +1 -0
  48. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  49. package/dist/chunk-VLE5R7PF.js +3329 -0
  50. package/dist/chunk-VLE5R7PF.js.map +1 -0
  51. package/dist/{chunk-FVSI36UV.js → chunk-X6RJAHII.js} +197 -110
  52. package/dist/chunk-X6RJAHII.js.map +1 -0
  53. package/dist/{chunk-Q2WGKJNN.cjs → chunk-XKSO2V5I.cjs} +699 -123
  54. package/dist/chunk-XKSO2V5I.cjs.map +1 -0
  55. package/dist/chunk-XVMJO3QF.cjs +2272 -0
  56. package/dist/chunk-XVMJO3QF.cjs.map +1 -0
  57. package/dist/{chunk-UADYAYAT.cjs → chunk-YPB3A3EY.cjs} +1309 -565
  58. package/dist/chunk-YPB3A3EY.cjs.map +1 -0
  59. package/dist/{index-C_8o--VE.d.cts → coerce-CJlgULrQ.d.cts} +210 -199
  60. package/dist/{index-szBv3496.d.ts → coerce-wwdELNYS.d.ts} +210 -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-Dzr13q3d.d.cts → index-B3WCj_Hm.d.cts} +135 -58
  72. package/dist/{index-BO6xE9YU.d.cts → index-BUtIf2go.d.ts} +460 -254
  73. package/dist/{index-CCNJRG-P.d.cts → index-BaoKVVhE.d.cts} +293 -56
  74. package/dist/{index-Chbst3Jx.d.ts → index-BpWOhkkE.d.ts} +135 -58
  75. package/dist/{index-BGFW-8P2.d.cts → index-BzLNkmA7.d.cts} +196 -114
  76. package/dist/{index-CunYKLiu.d.ts → index-CBCP84rv.d.cts} +1 -1
  77. package/dist/{index-BkGmlyI3.d.cts → index-Ci3xyows.d.ts} +60 -23
  78. package/dist/{index-9wcMhG73.d.ts → index-CnHQgqNh.d.ts} +293 -56
  79. package/dist/{index-B4o55NDr.d.cts → index-CnIdP4EZ.d.ts} +1 -1
  80. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  81. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  82. package/dist/{index-D57QRbVn.d.ts → index-DTsKaPaC.d.cts} +460 -254
  83. package/dist/{index-Cj49F892.d.ts → index-D_hzxD8w.d.cts} +60 -23
  84. package/dist/{index-CgHqxdps.d.ts → index-DpC-mDlV.d.ts} +196 -114
  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.ts → masks-C8TWLFIi.d.cts} +49 -2
  92. package/dist/{masks-DYbE485J.d.cts → masks-O73Rhj5d.d.ts} +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 +502 -181
  96. package/dist/model/index.d.ts +502 -181
  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 +6 -5
  106. package/dist/chunk-3JXCW6DX.cjs +0 -3220
  107. package/dist/chunk-3JXCW6DX.cjs.map +0 -1
  108. package/dist/chunk-4A2RMUCC.js.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-E3F4AHAH.cjs.map +0 -1
  113. package/dist/chunk-ELCTZUJJ.js +0 -379
  114. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  115. package/dist/chunk-FVSI36UV.js.map +0 -1
  116. package/dist/chunk-GOZG57ER.js +0 -3220
  117. package/dist/chunk-GOZG57ER.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-M4CWTJWJ.cjs.map +0 -1
  128. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  129. package/dist/chunk-NLEU4QSE.cjs +0 -360
  130. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  131. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  132. package/dist/chunk-OE6WK2Y2.cjs.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, q as SpelledKeyLike, b as Chord, n as ResolvedKey, j as KeyRelation, e as ChordSegment, V as VoiceSnapshot, g as ChordSpan } from './coerce-wwdELNYS.js';
2
+ import { b as MeterLike } from './index-Ci3xyows.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 };