@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,15 +1,156 @@
1
- import { d as KeyScale, f as Note, h as NoteLike, i as NoteNameOptions, P as ParseResult } from './index-B9MNZboQ.js';
1
+ import { e as KeyScale, g as Note, i as NoteLike } from './index-Cy15oTvh.js';
2
2
 
3
3
  /**
4
- * Anything that names a key: a key name, a plain {@link KeyScale}, or a value
5
- * that serializes to one such as the `Key` class.
4
+ * The vocabulary a key is described in.
5
+ *
6
+ * Kept in a module of its own, below the masks that define the scale forms, the
7
+ * name reader that produces a key from text, and the coercion that accepts one
8
+ * from a caller. All three name these types and none of them can be the others'
9
+ * home without importing one back.
10
+ */
11
+ /**
12
+ * Which scale form a key stands in.
13
+ *
14
+ * `'modal'` is the honest answer for a key standing in none of the four, and it
15
+ * is an answer rather than an absence.
16
+ *
17
+ * @category Scales
18
+ */
19
+ type KeyVariant = 'major' | 'natural' | 'harmonic' | 'melodic' | 'modal';
20
+ /**
21
+ * The complete identity of a key: what it sounds, how it is written, and which
22
+ * scale form it stands in.
23
+ *
24
+ * What {@link resolveKey} hands back, and the shape a key is passed on in. Every
25
+ * field is present: a key that reaches here has been read, so there is nothing
26
+ * left undecided about it, and a reader never has to ask whether a missing
27
+ * field means `'modal'` or means nobody looked.
6
28
  *
7
29
  * @category Scales
8
30
  */
9
- type KeyLike = string | KeyScale | {
10
- /** The key data this value stands for, carrying the scale it denotes. */
31
+ /**
32
+ * A key/scale with the spelling it is conventionally written in beside it.
33
+ *
34
+ * What the key builders hand back. It is a {@link KeyScale} — every reader that
35
+ * wants only the pitch classes takes it unchanged — and it carries the tonic
36
+ * those pitch classes are conventionally written on, so it also satisfies the
37
+ * entry points whose answer depends on how the key is written.
38
+ *
39
+ * Carrying the spelling here is what lets those entry points refuse a bare
40
+ * `KeyScale` without refusing the library's own builders. A bare scale reaching
41
+ * one of them has had a spelling and lost it, which is the defect; a built key
42
+ * never lost one.
43
+ *
44
+ * @category Scales
45
+ */
46
+ type SpelledKeyScale = KeyScale & {
47
+ /** The tonic the key is conventionally written on. */
48
+ tonic: Note;
49
+ /** The scale form the mask stands in. */
50
+ variant: KeyVariant;
51
+ };
52
+ /**
53
+ * A key read into the three things every reader of one needs: the pitch
54
+ * classes it is built from, the spelled tonic its letter names are anchored on,
55
+ * and the scale form it stands in.
56
+ *
57
+ * What a key-shaped argument becomes once it has been read, whatever form the
58
+ * caller held it in — a name, a scale, a `Key`. Carrying the tonic beside the
59
+ * scale is what keeps an A flat minor an A flat minor rather than the G sharp
60
+ * minor its pitch classes alone would be spelled back as.
61
+ *
62
+ * @category Scales
63
+ */
64
+ type ResolvedKey = {
65
+ /** The pitch classes the key is built from. */
66
+ readonly scale: KeyScale;
67
+ /** The spelled tonic every letter name derived from this key is anchored on. */
68
+ readonly tonic: Note;
69
+ /** The scale form the key stands in. */
70
+ readonly variant: KeyVariant;
71
+ };
72
+
73
+ /**
74
+ * Anything that names a key: a key name, a plain {@link KeyScale}, the plain
75
+ * data a key serializes to, or a value that serializes to one such as the `Key`
76
+ * class.
77
+ *
78
+ * The plain-data form is here because a project file holds a key that way, and
79
+ * reading one back has to be the same call as passing the class it came from.
80
+ *
81
+ * Deliberately wider than what a resolver hands back: a caller may hand in a
82
+ * bare scale, or data written before the spelling travelled with it, and the
83
+ * resolver fills in what is missing rather than refusing it. Narrowing this to
84
+ * the resolved shape would make the acceptance of every entry point shrink the
85
+ * day the resolved shape gained a field.
86
+ *
87
+ * A scale is accepted with the spelling and the form written beside it as well
88
+ * as without them, because a key region carries its key that way — asked
89
+ * positional questions wherever a plain scale would be. The resolver reads them
90
+ * when they are there, and this says so: a shape a resolver reads and a type
91
+ * denies is one refactoring away from being dropped silently.
92
+ *
93
+ * @category Scales
94
+ */
95
+ type KeyLike = string | (KeyScale & {
96
+ /** The spelled tonic the key is written with, when it travels beside it. */
97
+ tonic?: Note;
98
+ /** The scale form the key stands in, when it travels beside it. */
99
+ variant?: KeyVariant;
100
+ }) | {
101
+ /** The scale this key data denotes. */
102
+ scale: KeyScale;
103
+ /** The spelled tonic the key is written with, when the caller named one. */
104
+ tonic?: Note;
105
+ /** The scale form the key was read under, when it was read under one. */
106
+ variant?: KeyVariant;
107
+ } | {
108
+ /** The key data this value stands for, carrying everything it knows. */
109
+ toJSON(): {
110
+ scale: KeyScale;
111
+ tonic?: Note;
112
+ variant?: KeyVariant;
113
+ };
114
+ };
115
+ /**
116
+ * A key that says how it is written, for the entry points whose answer depends
117
+ * on it.
118
+ *
119
+ * The shapes {@link KeyLike} accepts, minus the one that cannot carry a
120
+ * spelling: a bare {@link KeyScale}. An A flat minor and a G sharp minor are one
121
+ * set of pitch classes and two keys, so an entry point whose answer differs
122
+ * between them cannot be handed the shape that does not distinguish them and
123
+ * still be right. Declaring this makes that a compile error rather than a
124
+ * defect found later — and `Key`, `ResolvedKey`, a key name and stored key data
125
+ * all satisfy it, so the narrowing costs a caller nothing it was doing
126
+ * correctly.
127
+ *
128
+ * Which entry points declare this is measured rather than chosen: an entry point
129
+ * belongs here exactly when varying its key argument alone — between the two
130
+ * spellings of one sound — changes its answer. A scale a caller genuinely wants
131
+ * to hand in is spelled first, at the place it happens, which is the same rule
132
+ * the reduction in the other direction already follows.
133
+ *
134
+ * @category Scales
135
+ */
136
+ type SpelledKeyLike = string | (KeyScale & {
137
+ /** The spelled tonic the key is written with. Required: it is the point. */
138
+ tonic: Note;
139
+ /** The scale form the key stands in, when it travels beside it. */
140
+ variant?: KeyVariant;
141
+ }) | {
142
+ /** The scale this key data denotes. */
143
+ scale: KeyScale;
144
+ /** The spelled tonic the key is written with, when the caller named one. */
145
+ tonic?: Note;
146
+ /** The scale form the key was read under, when it was read under one. */
147
+ variant?: KeyVariant;
148
+ } | {
149
+ /** The key data this value stands for, carrying everything it knows. */
11
150
  toJSON(): {
12
151
  scale: KeyScale;
152
+ tonic?: Note;
153
+ variant?: KeyVariant;
13
154
  };
14
155
  };
15
156
  /**
@@ -21,12 +162,12 @@ type KeyLike = string | KeyScale | {
21
162
  * instance is accepted through its `toJSON` method rather than by its type, so
22
163
  * the layers below the model can read a class without importing it.
23
164
  *
24
- * A name is read by {@link parseKeyName} and nothing else, so every entry point
25
- * that takes a key accepts exactly the names a key field already accepts, in
26
- * every notation system it already reads. The mode word decides the scale: a
27
- * major name gives the major scale, a minor name the natural minor. The
28
- * harmonic and melodic forms have no name of their own, so a caller who needs
29
- * one builds it with {@link scaleByName} and passes the scale.
165
+ * A name is read by the one reader {@link resolveKey} and `Key.parse` also use,
166
+ * so every entry point that takes a key accepts exactly the same names: a mode
167
+ * word in every notation system that reader knows, and a built-in scale's own
168
+ * word — `'D dorian'`, `'D harmonic minor'` — in English.
169
+ * The spelling the name carries is dropped here along with the scale form,
170
+ * which is what {@link resolveKey} is for.
30
171
  *
31
172
  * @param value A key name, a plain key/scale, or a value whose `toJSON`
32
173
  * returns data carrying one.
@@ -74,16 +215,13 @@ declare function toKeyScale(value: KeyLike): KeyScale;
74
215
  *
75
216
  * @example
76
217
  * ```ts
77
- * import type { SpelledKey } from '@libraz/libcantus';
78
- * import { majorKey, parseNote } from '@libraz/libcantus';
79
- * const dbMajor: SpelledKey = { tonic: parseNote('Db'), key: majorKey(1) };
218
+ * import type { ResolvedKey } from '@libraz/libcantus';
219
+ * import { formatNote, majorKey, spelledKeyOf } from '@libraz/libcantus';
220
+ * const dbMajor: ResolvedKey = spelledKeyOf(majorKey(1));
221
+ * formatNote(dbMajor.tonic); // 'Db'
80
222
  * ```
81
223
  * @category Scales
82
224
  */
83
- type SpelledKey = {
84
- tonic: Note;
85
- key: KeyScale;
86
- };
87
225
  /**
88
226
  * How one key stands to another, as reported by {@link keyRelationBetween} and
89
227
  * tagged onto every entry of {@link relatedKeysOf}.
@@ -144,21 +282,29 @@ type KeyRelation = 'same' | 'enharmonic' | 'relative' | 'parallel' | 'dominant'
144
282
  * ```
145
283
  * @category Scales
146
284
  */
147
- declare function spelledKeyOf(key: KeyScale): SpelledKey;
285
+ declare function spelledKeyOf(key: KeyScale): ResolvedKey;
148
286
  /**
149
- * The relative key: the same key signature read in the other mode.
287
+ * The relative key: for a plain major or minor key, the same key signature read
288
+ * in the other mode.
150
289
  *
151
290
  * C major and A minor share a signature, and so are each other's relative; the
152
291
  * relation is its own inverse for a plain major or minor key. Because the new
153
292
  * tonic is read off the circle of fifths rather than transposed by semitones,
154
293
  * the relative of Db major is Bb minor and not its enharmonic A# minor.
155
294
  *
295
+ * A key that is not a plain major or minor is read through the mode its third
296
+ * names and stepped from where its tonic stands on the circle, the origin every
297
+ * relation here shares: the relative of D dorian is F major, the relative of the
298
+ * D minor its third names. The shared-signature reading does not carry over to
299
+ * those — D dorian is written with no accidentals and F major with one — and the
300
+ * modes have no relative of their own for it to be measured against.
301
+ *
156
302
  * @param tonic The spelled tonic of the key, as a note name, note data, or a
157
303
  * `Note`.
158
304
  * @param key The key/scale, as a key name, a key/scale, or a `Key`; its mask
159
305
  * decides which mode the relative is in.
160
306
  * @returns The relative key and the tonic spelling it is written with.
161
- * @throws If the tonic or the mask is malformed, or if the key's signature
307
+ * @throws If the tonic or the mask is malformed, or if the resulting signature
162
308
  * falls outside [-12, 12] fifths.
163
309
  * @example
164
310
  * ```ts
@@ -168,7 +314,7 @@ declare function spelledKeyOf(key: KeyScale): SpelledKey;
168
314
  * ```
169
315
  * @category Scales
170
316
  */
171
- declare function relativeKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
317
+ declare function relativeKeyOf(tonic: NoteLike, key: KeyLike): ResolvedKey;
172
318
  /**
173
319
  * The parallel key: the other mode on the same tonic.
174
320
  *
@@ -190,7 +336,7 @@ declare function relativeKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
190
336
  * ```
191
337
  * @category Scales
192
338
  */
193
- declare function parallelKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
339
+ declare function parallelKeyOf(tonic: NoteLike, key: KeyLike): ResolvedKey;
194
340
  /**
195
341
  * The dominant key: the key a fifth above, in the mode this key's third names.
196
342
  *
@@ -212,7 +358,7 @@ declare function parallelKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
212
358
  * ```
213
359
  * @category Scales
214
360
  */
215
- declare function dominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
361
+ declare function dominantKeyOf(tonic: NoteLike, key: KeyLike): ResolvedKey;
216
362
  /**
217
363
  * The subdominant key: the key a fifth below, in the mode this key's third
218
364
  * names, and the exact inverse of {@link dominantKeyOf} on the tonic spelling.
@@ -231,7 +377,7 @@ declare function dominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
231
377
  * ```
232
378
  * @category Scales
233
379
  */
234
- declare function subdominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
380
+ declare function subdominantKeyOf(tonic: NoteLike, key: KeyLike): ResolvedKey;
235
381
  /**
236
382
  * The same sounding key written the other way round the circle of fifths, or
237
383
  * null when it has no second spelling.
@@ -268,18 +414,25 @@ declare function subdominantKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey;
268
414
  * ```
269
415
  * @category Scales
270
416
  */
271
- declare function enharmonicKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey | null;
417
+ declare function enharmonicKeyOf(tonic: NoteLike, key: KeyLike): ResolvedKey | null;
272
418
  /**
273
419
  * The six closely related keys of a key, each tagged with its relation.
274
420
  *
275
421
  * These are the keys the German and Japanese teaching tradition counts as a
276
422
  * key's near relations: the relative and parallel keys, the dominant and the
277
423
  * subdominant, and the relatives of those two. For C major they are A minor,
278
- * C minor, G major, F major, E minor and D minor, in that order. Five of them
279
- * are written within one accidental of the key's own signature — the relative
280
- * shares it exactly and the other four stand one away; the parallel key does
281
- * not — C minor is three flats away from C major — and belongs to the set for
282
- * the tonic it shares rather than for the signature it carries.
424
+ * C minor, G major, F major, E minor and D minor, in that order. For a plain
425
+ * major or minor key five of them are written within one accidental of the key's
426
+ * own signature — the relative shares it exactly and the other four stand one
427
+ * away; the parallel key does not — C minor is three flats away from C major —
428
+ * and belongs to the set for the tonic it shares rather than for the signature
429
+ * it carries.
430
+ *
431
+ * A key that is not a plain major or minor is read through the mode its third
432
+ * names, so its relations are walked from where its tonic stands on the circle
433
+ * rather than from its own signature, and the accidental counts above do not
434
+ * describe them: the relative of D dorian is F major, one flat against dorian's
435
+ * none.
283
436
  *
284
437
  * @param tonic The spelled tonic of the key, as a note name, note data, or a
285
438
  * `Note`.
@@ -296,7 +449,7 @@ declare function enharmonicKeyOf(tonic: NoteLike, key: KeyLike): SpelledKey | nu
296
449
  * ```
297
450
  * @category Scales
298
451
  */
299
- declare function relatedKeysOf(tonic: NoteLike, key: KeyLike): (SpelledKey & {
452
+ declare function relatedKeysOf(tonic: NoteLike, key: KeyLike): (ResolvedKey & {
300
453
  relation: KeyRelation;
301
454
  })[];
302
455
  /**
@@ -321,14 +474,14 @@ declare function relatedKeysOf(tonic: NoteLike, key: KeyLike): (SpelledKey & {
321
474
  * falls outside [-12, 12] fifths.
322
475
  * @example
323
476
  * ```ts
324
- * import { keyRelationBetween, majorKey, minorKey, parseNote } from '@libraz/libcantus';
325
- * const cMajor = { tonic: parseNote('C'), key: majorKey(0) };
326
- * keyRelationBetween(cMajor, { tonic: parseNote('A'), key: minorKey(9) }); // 'relative'
327
- * keyRelationBetween(cMajor, { tonic: parseNote('Eb'), key: minorKey(3) }); // null
477
+ * import { keyRelationBetween, majorKey, minorKey, spelledKeyOf } from '@libraz/libcantus';
478
+ * const cMajor = spelledKeyOf(majorKey(0));
479
+ * keyRelationBetween(cMajor, spelledKeyOf(minorKey(9))); // 'relative'
480
+ * keyRelationBetween(cMajor, spelledKeyOf(minorKey(3))); // null
328
481
  * ```
329
482
  * @category Scales
330
483
  */
331
- declare function keyRelationBetween(a: SpelledKey, b: SpelledKey): KeyRelation | null;
484
+ declare function keyRelationBetween(a: ResolvedKey, b: ResolvedKey): KeyRelation | null;
332
485
 
333
486
  /**
334
487
  * The structured reading of a chord: what it is built from, not what it is
@@ -783,10 +936,14 @@ declare function isChordMember(pitch: number, chord: Chord | null): boolean;
783
936
  /**
784
937
  * Determine a pitch's harmonic role within a chord.
785
938
  *
786
- * The role is derived from the pitch's interval above the chord root, reduced
787
- * modulo 12. A major sixth (9) reads as a `sixth` for sixth chords but as a
788
- * `seventh` for a diminished-seventh chord; ninths and other tensions have no
789
- * basic role and return null.
939
+ * A chord carrying its own tone spellings is read by them, since the degree it
940
+ * writes a tone as is the degree that tone plays: the ten semitones above the
941
+ * root of a German sixth are written five letters up, so they are its sixth and
942
+ * not a seventh owing a step downward. Everywhere else the role is derived from
943
+ * the pitch's interval above the chord root, reduced modulo 12. A major sixth
944
+ * (9) reads as a `sixth` for sixth chords but as a `seventh` for a
945
+ * diminished-seventh chord; ninths and other tensions have no basic role and
946
+ * return null.
790
947
  *
791
948
  * @param pitch MIDI pitch or bare pitch class.
792
949
  * @param chord The chord providing the root reference.
@@ -899,10 +1056,13 @@ declare const PROFILE_WEIGHTS: Readonly<Record<SafetyProfile, Readonly<ProfileWe
899
1056
  *
900
1057
  * @param profile The profile whose table to start from.
901
1058
  * @param overrides Fields to replace; anything absent keeps the profile's value.
1059
+ * A field written as an explicit `undefined` is absent, not a replacement.
902
1060
  * @returns The complete weight table to score with.
903
- * @throws If `profile` is not one of the profile names
904
- * ({@link InvalidInputError}). A misspelled profile scored under a default
905
- * table would rank candidates by a style the caller did not ask for.
1061
+ * @throws If `profile` is not one of the profile names, or an override is not a
1062
+ * finite number ({@link InvalidInputError}). A misspelled profile scored under
1063
+ * a default table would rank candidates by a style the caller did not ask for,
1064
+ * and a single non-finite weight poisons every candidate's score, which turns
1065
+ * the ranking into the order the candidates happened to be enumerated in.
906
1066
  * @example
907
1067
  * ```ts
908
1068
  * import { profileWeights } from '@libraz/libcantus';
@@ -924,25 +1084,73 @@ declare enum NoteSafety {
924
1084
  /**
925
1085
  * Bit flags describing why a pitch received its verdict.
926
1086
  *
1087
+ * Several flags can describe one pitch, so test the combination rather than a
1088
+ * flag alone: an avoid eleventh carries `Tension` as well as `AvoidNote`, and a
1089
+ * usable tension is `Tension` without `AvoidNote`. `ChordTone`, `MinorSecond`
1090
+ * and `MajorSeventh` are informational and never lower the verdict on their
1091
+ * own; every other flag sets at least a warning, and the profile decides how
1092
+ * far past that it goes.
1093
+ *
927
1094
  * @category Arrangement & Analysis
928
1095
  */
929
1096
  declare enum ReasonFlag {
1097
+ /** The candidate is a member of the chord. Informational. */
930
1098
  ChordTone = 1,
1099
+ /**
1100
+ * The candidate is a ninth, eleventh or thirteenth above the root, not a chord
1101
+ * tone. Also set alongside `AvoidNote` when the avoid note is the eleventh.
1102
+ */
931
1103
  Tension = 2,
1104
+ /**
1105
+ * The eleventh over a chord with a major third and no fourth, or the major
1106
+ * seventh over a dominant. Always set with `NeedsResolution`, and with
1107
+ * `Tension` for the eleventh.
1108
+ */
932
1109
  AvoidNote = 4,
1110
+ /** The candidate is in the key but neither a chord tone nor a tension. */
933
1111
  ScaleTone = 8,
1112
+ /** The candidate is outside the key and not a chord tone. */
934
1113
  NonScale = 16,
1114
+ /** The candidate lies outside `vocalLow`..`vocalHigh`. */
935
1115
  OutOfRange = 32,
1116
+ /** The candidate, not itself a chord tone, is a tritone from one. */
936
1117
  Tritone = 64,
1118
+ /**
1119
+ * The move from `prevPitch` is a forbidden melodic leap as
1120
+ * {@link isForbiddenMelodicLeap} defines it: a tritone, either seventh, or
1121
+ * anything wider than an octave. An octave itself is not. The profile sets
1122
+ * the severity, not the set of leaps. Set with `MelodicTritone` on a tritone
1123
+ * and with `MajorSeventh` on a major seventh.
1124
+ */
937
1125
  LargeLeap = 128,
1126
+ /** The move from `prevPitch` reduces to a semitone, the minor ninth included. Informational. */
938
1127
  MinorSecond = 256,
1128
+ /** The move from `prevPitch` reduces to a major seventh; always with `LargeLeap`. Informational. */
939
1129
  MajorSeventh = 512,
1130
+ /**
1131
+ * On a strong beat, the candidate is dissonant against at least one of
1132
+ * `otherVoices`, apart from the tritone or seventh two chord tones spell. A
1133
+ * perfect fourth counts only when one of the two is the lowest sounding voice,
1134
+ * and under `pop` not when both are chord tones — an inverted chord.
1135
+ */
940
1136
  VerticalDissonance = 1024,
1137
+ /** The candidate moves in parallel perfect intervals with at least one of `otherVoices`. */
941
1138
  ParallelPerfect = 2048,
1139
+ /**
1140
+ * The candidate and another voice, the two outer voices of the texture, reach
1141
+ * a perfect interval by similar motion with the upper one leaping.
1142
+ */
942
1143
  HiddenParallel = 4096,
1144
+ /** The candidate crosses at least one of `otherVoices` since the previous step. */
943
1145
  VoiceCrossing = 8192,
1146
+ /**
1147
+ * A held pitch, consonant with a voice on the previous step, now dissonant
1148
+ * against it. Always with `VerticalDissonance`.
1149
+ */
944
1150
  Suspension = 16384,
1151
+ /** The candidate should move by step to `resolveTo`. Set with `AvoidNote`. */
945
1152
  NeedsResolution = 32768,
1153
+ /** The move from `prevPitch` reduces to a tritone; always with `LargeLeap`. */
946
1154
  MelodicTritone = 65536
947
1155
  }
948
1156
  /**
@@ -970,6 +1178,20 @@ type SafetyQuery = {
970
1178
  vocalLow?: number;
971
1179
  vocalHigh?: number;
972
1180
  };
1181
+ /**
1182
+ * The reasons one of `otherVoices` contributes to a verdict.
1183
+ *
1184
+ * @category Arrangement & Analysis
1185
+ */
1186
+ type SafetyPartner = {
1187
+ /** The voice's index in {@link SafetyQuery.otherVoices}. */
1188
+ index: number;
1189
+ /**
1190
+ * The pairwise flags this voice raises: `VerticalDissonance`, `Suspension`,
1191
+ * `ParallelPerfect`, `HiddenParallel` and `VoiceCrossing`.
1192
+ */
1193
+ reasons: number;
1194
+ };
973
1195
  /**
974
1196
  * The verdict, reason flags, and optional resolution guidance.
975
1197
  *
@@ -980,6 +1202,12 @@ type SafetyResult = {
980
1202
  reasons: number;
981
1203
  resolveTo?: number;
982
1204
  suggestions?: number[];
1205
+ /**
1206
+ * The voices behind the pairwise flags in `reasons`, in `otherVoices` order,
1207
+ * when {@link EvaluateSafetyOptions.partners} asked for them and any voice
1208
+ * raised one. Their `reasons` together are exactly the pairwise flags set.
1209
+ */
1210
+ partners?: SafetyPartner[];
983
1211
  rationale?: string;
984
1212
  };
985
1213
  /**
@@ -1024,6 +1252,14 @@ type EvaluateSafetyOptions = {
1024
1252
  * @defaultValue true
1025
1253
  */
1026
1254
  suggestions?: boolean;
1255
+ /**
1256
+ * Whether to report which of `otherVoices` raised each pairwise flag, as
1257
+ * {@link SafetyResult.partners}. The bitmask alone says a parallel fifth is
1258
+ * there; this says against which voice.
1259
+ *
1260
+ * @defaultValue false
1261
+ */
1262
+ partners?: boolean;
1027
1263
  };
1028
1264
  /**
1029
1265
  * Enumerate placeable pitches in a range, chord tones first, descending.
@@ -1049,92 +1285,12 @@ type EvaluateSafetyOptions = {
1049
1285
  declare function enumerateSafePitches(q: Omit<SafetyQuery, 'candidatePitch'>, pitchLow: number, pitchHigh: number): number[];
1050
1286
 
1051
1287
  /**
1052
- * Lead-sheet chord symbol parsing and formatting.
1053
- *
1054
- * Bridges the informal `<root><quality>[/<bass>]` notation musicians write
1055
- * (`Cmaj7`, `F#m7b5`, `Bb7`, `C/G`) and the library's structured {@link Chord}
1056
- * type, so callers can accept/emit plain text at the edges of the API.
1057
- *
1058
- * The root and the slash bass are note names, so they are read and written in
1059
- * whichever notation system the caller names — a German chart's `H7`, `As7` and
1060
- * `Ges/B` included. Unlike a note name, a chord symbol is never attributed to a
1061
- * system on its own: German `B` is a B flat and English `B` a B natural, and a
1062
- * symbol carries no `dur`/`moll` word to break the tie, so the most common
1063
- * symbol in the vocabulary would otherwise change chord with the source it came
1064
- * from. With no system given a symbol is English.
1065
- */
1066
-
1067
- /**
1068
- * Parse a lead-sheet chord symbol into a {@link Chord}.
1069
- *
1070
- * Accepts `<root><quality>[/<bass>]`, e.g. `Cmaj7`, `F#m7b5`, `Bb7`, `C/G`,
1071
- * `C6/9`, `C6/9/E`. Root and bass letters are case-insensitive, matching
1072
- * {@link parseNote}. A `/` is ambiguous between a slash bass and the `6/9`
1073
- * quality; the text splits at the last `/` whose right-hand side is a valid
1074
- * bare note preceded by a recognized quality, so `6/9` (with or without a
1075
- * further slash bass) parses as a quality.
1076
- *
1077
- * The root and bass are read in `system`, so a German chart's `H7`, `As7` and
1078
- * `Ges/B` are read by asking for `'german'`. The system is never inferred: with
1079
- * none given the symbol is English, where `B` is the B natural and `H` no note
1080
- * at all. Case carries nothing in a chord symbol — unlike a German key name,
1081
- * where the case of the tonic is the mode — so the quality suffix alone says
1082
- * major or minor. Where a German root's flat suffix could equally open the
1083
- * quality, the longest root that leaves a known quality behind wins: `Asus4` is
1084
- * an A suspended chord and `Assus4` the A flat one.
1085
- *
1086
- * The quality suffix is read structurally when no name covers it, so a chart's
1087
- * bracketed tensions parse as readily as the names do: `Cmaj7(#11)`,
1088
- * `C7(b9,#11)`, `C7(13)`, `Csus4(add9)`, `C-Δ9`, `C6/9(#11)`. A `/` between two
1089
- * figures separates them as it does inside brackets, which is what the sixth
1090
- * and ninth of a `6/9` are. Such a chord carries the tones it names and reports
1091
- * the nearest quality name (see {@link chordSpecQuality}); read
1092
- * {@link chordSpecOf} for its structure.
1093
- *
1094
- * The parsed chord carries the root/bass spellings as enharmonic hints
1095
- * (`rootSpelling`/`bassSpelling`) so {@link formatChordSymbol} can reproduce
1096
- * flat spellings such as `Bbmaj7` instead of respelling them with sharps.
1097
- *
1098
- * @param text The chord symbol text.
1099
- * @param opts `system` reads the root and bass in that notation system instead
1100
- * of English.
1101
- * @returns The parsed chord.
1102
- * @throws If the root or quality is not recognized in that system. Use
1103
- * {@link tryParseChordSymbol} where failure is ordinary, such as a chord
1104
- * field read on every keystroke.
1105
- * @example
1106
- * ```ts
1107
- * import { parseChordSymbol, formatChordSymbol } from '@libraz/libcantus';
1108
- * const c = parseChordSymbol('Bbmaj7');
1109
- * formatChordSymbol(c); // 'Bbmaj7' — flat spelling is preserved on round-trip
1110
- * parseChordSymbol('H7', { system: 'german' }).rootPc; // 11 — the B natural
1111
- * parseChordSymbol('B', { system: 'german' }).rootPc; // 10 — the German B flat
1112
- * ```
1113
- * @category Chords
1114
- */
1115
- declare function parseChordSymbol(text: string, opts?: NoteNameOptions): Chord;
1116
- /**
1117
- * Parse a lead-sheet chord symbol, reporting failure instead of throwing it.
1118
- *
1119
- * The same reading as {@link parseChordSymbol} — that function is this one with
1120
- * its error thrown — for the callers where a symbol that does not parse yet is
1121
- * the normal state of the input rather than a fault: a chord field says what is
1122
- * wrong with what has been typed so far, and does not need a `try` around every
1123
- * keystroke to do it.
1124
- *
1125
- * @param text The chord symbol text.
1126
- * @param opts `system` reads the root and bass in that notation system instead
1127
- * of English.
1128
- * @returns The chord, or the error explaining why the text is not one.
1129
- * @example
1130
- * ```ts
1131
- * import { tryParseChordSymbol } from '@libraz/libcantus';
1132
- * const result = tryParseChordSymbol('Cmaj7(#11)');
1133
- * result.ok ? result.value.intervals : result.error.message;
1134
- * ```
1135
- * @category Chords
1288
+ * Reading a caller's chord into the one shape the library works in.
1289
+ *
1290
+ * The widening counterpart of the other `to*Data` coercers, kept beside the
1291
+ * grammar it falls back on rather than inside it: what a chord symbol means is
1292
+ * the parser's question, and what shapes stand for a chord is this one's.
1136
1293
  */
1137
- declare function tryParseChordSymbol(text: string, opts?: NoteNameOptions): ParseResult<Chord>;
1138
1294
  /**
1139
1295
  * Anything that names a chord: a chord symbol, plain chord data, or a value
1140
1296
  * that serializes to chord data such as the `Chord` class.
@@ -1169,74 +1325,5 @@ type ChordLike = string | Chord | {
1169
1325
  * @category Chords
1170
1326
  */
1171
1327
  declare function toChordData(value: ChordLike): Chord;
1172
- /**
1173
- * How a chord symbol is written: in which notation system, and on which side of
1174
- * the enharmonic fence.
1175
- *
1176
- * @category Chords
1177
- */
1178
- type ChordSymbolOptions = NoteNameOptions & {
1179
- /**
1180
- * Prefer flat spellings over sharps for an altered root or bass; passing
1181
- * either `true` or `false` overrides any spelling hint the chord carries.
1182
- */
1183
- flats?: boolean;
1184
- };
1185
- /**
1186
- * Format a {@link Chord} as a lead-sheet chord symbol.
1187
- *
1188
- * The inverse of {@link parseChordSymbol} in every notation system: each
1189
- * quality maps to one canonical suffix spelling, the root and bass are written
1190
- * in `system`, and a slash bass is appended only when it differs from the root.
1191
- * Every symbol written here reads back through {@link parseChordSymbol} as the
1192
- * same root, sounding bass and tones — whatever combination of tensions the
1193
- * chord carries, and whether or not a quality name covers it.
1194
- * When the chord carries `rootSpelling`/`bassSpelling` hints (as chords from
1195
- * {@link parseChordSymbol} do) and no explicit `flats` preference is given, the
1196
- * hints are reused so flat symbols round-trip unchanged.
1197
- *
1198
- * The suffix is written from the chord's own tones (see {@link chordSpecOf}),
1199
- * not from its quality name, so a chord carrying alterations no name covers is
1200
- * written out in full — `C7(b9,#11)` — rather than under the nearest name. A
1201
- * tone the chord leaves out is not written: the symbol names the harmony, so a
1202
- * fifth-less dominant is still `C7`.
1203
- *
1204
- * @param chord The chord to format, as a chord symbol, chord data, or a
1205
- * `Chord`.
1206
- * @param opts `system` writes the root and bass in that notation system instead
1207
- * of English; `flats` overrides the spelling hints, as
1208
- * {@link ChordSymbolOptions} describes.
1209
- * @returns The chord symbol text.
1210
- * @example
1211
- * ```ts
1212
- * import { makeChord, formatChordSymbol, parseChordSymbol } from '@libraz/libcantus';
1213
- * formatChordSymbol(makeChord(0, 'min7')); // 'Cm7'
1214
- * formatChordSymbol(parseChordSymbol('Bb7'), { system: 'german' }); // 'B7'
1215
- * formatChordSymbol(parseChordSymbol('B7'), { system: 'german' }); // 'H7'
1216
- * ```
1217
- * @category Chords
1218
- */
1219
- declare function formatChordSymbol(chord: ChordLike, opts?: ChordSymbolOptions): string;
1220
- /**
1221
- * Transpose a chord symbol by a number of semitones.
1222
- *
1223
- * The symbol is read in the same system it is written back in, so a German
1224
- * chart stays German across the transposition.
1225
- *
1226
- * @param text The chord symbol text.
1227
- * @param semitones Signed semitone offset to apply to the root and bass.
1228
- * @param opts Options for reading and writing the symbol; see
1229
- * {@link ChordSymbolOptions}.
1230
- * @returns The transposed chord symbol text.
1231
- * @throws If `text` does not parse as a chord symbol.
1232
- * @example
1233
- * ```ts
1234
- * import { transposeChordSymbol } from '@libraz/libcantus';
1235
- * transposeChordSymbol('C/G', 2); // 'D/A'
1236
- * transposeChordSymbol('H7', 1, { system: 'german' }); // 'C7'
1237
- * ```
1238
- * @category Chords
1239
- */
1240
- declare function transposeChordSymbol(text: string, semitones: number, opts?: ChordSymbolOptions): string;
1241
1328
 
1242
- export { toChordData as $, type Alteration as A, diatonicTriad as B, type ChordBase as C, dominantKeyOf as D, type EvaluateSafetyOptions as E, enharmonicKeyOf as F, enumerateSafePitches as G, evaluateSafety as H, formatChordSymbol as I, intervalAboveRoot as J, type KeyLike as K, isChordMember as L, keyRelationBetween as M, NoteSafety as N, makeChord as O, PROFILE_WEIGHTS as P, parallelKeyOf as Q, ReasonFlag as R, type SafetyProfile as S, parseChordSymbol as T, profileWeights as U, type VoiceSnapshot as V, relatedKeysOf as W, relativeKeyOf as X, spanFromChord as Y, spelledKeyOf as Z, subdominantKeyOf as _, type AlteredDegree as a, toKeyScale as a0, transposeChord as a1, transposeChordSymbol as a2, tryParseChordSymbol as a3, type Chord as b, type ChordLike as c, type ChordQuality as d, type ChordSegment as e, type ChordSeventh as f, type ChordSpan as g, type ChordSpec as h, type ChordSymbolOptions as i, type ChordToneRole as j, type KeyRelation as k, type PitchSpelling as l, type ProfileWeights as m, type SafetyQuery as n, type SafetyResult as o, type SpelledKey as p, chordFromDegree as q, chordFromSpan as r, chordFromSpec as s, chordPitchClasses as t, chordQualities as u, chordSpecIntervals as v, chordSpecOf as w, chordSpecQuality as x, chordToneRole as y, diatonicSeventh as z };
1329
+ export { subdominantKeyOf as $, type Alteration as A, chordSpecQuality as B, type ChordBase as C, chordToneRole as D, type EvaluateSafetyOptions as E, diatonicSeventh as F, diatonicTriad as G, dominantKeyOf as H, enharmonicKeyOf as I, enumerateSafePitches as J, type KeyLike as K, evaluateSafety as L, intervalAboveRoot as M, NoteSafety as N, isChordMember as O, PROFILE_WEIGHTS as P, keyRelationBetween as Q, ReasonFlag as R, type SafetyPartner as S, makeChord as T, parallelKeyOf as U, type VoiceSnapshot as V, profileWeights as W, relatedKeysOf as X, relativeKeyOf as Y, spanFromChord as Z, spelledKeyOf as _, type AlteredDegree as a, toChordData as a0, toKeyScale as a1, transposeChord as a2, type Chord as b, type ChordLike as c, type ChordQuality as d, type ChordSegment as e, type ChordSeventh as f, type ChordSpan as g, type ChordSpec as h, type ChordToneRole as i, type KeyRelation as j, type KeyVariant as k, type PitchSpelling as l, type ProfileWeights as m, type ResolvedKey as n, type SafetyProfile as o, type SafetyQuery as p, type SafetyResult as q, type SpelledKeyLike as r, type SpelledKeyScale as s, chordFromDegree as t, chordFromSpan as u, chordFromSpec as v, chordPitchClasses as w, chordQualities as x, chordSpecIntervals as y, chordSpecOf as z };