@libraz/libcantus 0.9.3 → 0.9.4

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 (198) hide show
  1. package/README.md +2 -2
  2. package/dist/analyze/index.cjs +39 -1947
  3. package/dist/analyze/index.cjs.map +1 -1
  4. package/dist/analyze/index.d.cts +197 -37
  5. package/dist/analyze/index.d.ts +197 -37
  6. package/dist/analyze/index.js +19 -8
  7. package/dist/chunk-36N73KZ3.cjs +246 -0
  8. package/dist/chunk-36N73KZ3.cjs.map +1 -0
  9. package/dist/chunk-4HLLZPXT.cjs +3077 -0
  10. package/dist/chunk-4HLLZPXT.cjs.map +1 -0
  11. package/dist/chunk-7MGZ7D62.cjs +498 -0
  12. package/dist/chunk-7MGZ7D62.cjs.map +1 -0
  13. package/dist/{chunk-4MHA432G.js → chunk-7NKDPF74.js} +403 -180
  14. package/dist/chunk-7NKDPF74.js.map +1 -0
  15. package/dist/chunk-CAN76NCU.cjs +679 -0
  16. package/dist/chunk-CAN76NCU.cjs.map +1 -0
  17. package/dist/{chunk-MIWO425M.js → chunk-D2FAJBYL.js} +101 -34
  18. package/dist/chunk-D2FAJBYL.js.map +1 -0
  19. package/dist/{chunk-SGU7SPLK.js → chunk-EHVEBWM5.js} +1056 -531
  20. package/dist/chunk-EHVEBWM5.js.map +1 -0
  21. package/dist/chunk-FCANXBAT.cjs +30 -0
  22. package/dist/chunk-FCANXBAT.cjs.map +1 -0
  23. package/dist/chunk-FNDWXRRI.cjs +479 -0
  24. package/dist/chunk-FNDWXRRI.cjs.map +1 -0
  25. package/dist/{chunk-IT2O4XFE.js → chunk-GQSGS2N4.js} +43 -24
  26. package/dist/chunk-GQSGS2N4.js.map +1 -0
  27. package/dist/{chunk-62QZKCJQ.js → chunk-HKGYCQ34.js} +10 -6
  28. package/dist/chunk-HKGYCQ34.js.map +1 -0
  29. package/dist/{chunk-AKLYPWIN.js → chunk-I24MV65U.js} +29 -6
  30. package/dist/chunk-I24MV65U.js.map +1 -0
  31. package/dist/{chunk-CHMXELDS.js → chunk-KMHOICIY.js} +311 -181
  32. package/dist/chunk-KMHOICIY.js.map +1 -0
  33. package/dist/chunk-LW53AHQU.cjs +1117 -0
  34. package/dist/chunk-LW53AHQU.cjs.map +1 -0
  35. package/dist/{chunk-2ZWMSJH6.js → chunk-NKIBQ6PT.js} +2 -2
  36. package/dist/chunk-NKIBQ6PT.js.map +1 -0
  37. package/dist/chunk-NP4NA2FB.cjs +106 -0
  38. package/dist/chunk-NP4NA2FB.cjs.map +1 -0
  39. package/dist/chunk-NVE34YGZ.cjs +1449 -0
  40. package/dist/chunk-NVE34YGZ.cjs.map +1 -0
  41. package/dist/{chunk-EH6EM37U.js → chunk-NYYWMV2C.js} +61 -49
  42. package/dist/chunk-NYYWMV2C.js.map +1 -0
  43. package/dist/{chunk-GOAW4VYL.js → chunk-Q2XUP227.js} +243 -117
  44. package/dist/chunk-Q2XUP227.js.map +1 -0
  45. package/dist/chunk-Q6OP4U43.cjs +327 -0
  46. package/dist/chunk-Q6OP4U43.cjs.map +1 -0
  47. package/dist/chunk-RC6GBMCD.cjs +845 -0
  48. package/dist/chunk-RC6GBMCD.cjs.map +1 -0
  49. package/dist/{chunk-C5XYKUVG.js → chunk-RK35RIN7.js} +370 -157
  50. package/dist/chunk-RK35RIN7.js.map +1 -0
  51. package/dist/chunk-RUMU3UA6.cjs +53 -0
  52. package/dist/chunk-RUMU3UA6.cjs.map +1 -0
  53. package/dist/chunk-T64C7ZD7.cjs +1 -0
  54. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  55. package/dist/chunk-XEBG3G53.js +498 -0
  56. package/dist/chunk-XEBG3G53.js.map +1 -0
  57. package/dist/{chunk-I5R7ZBJS.js → chunk-YLEMGEFH.js} +103 -59
  58. package/dist/chunk-YLEMGEFH.js.map +1 -0
  59. package/dist/chunk-ZEJR4TKL.cjs +173 -0
  60. package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
  61. package/dist/core/index.cjs +130 -647
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +301 -39
  64. package/dist/core/index.d.ts +301 -39
  65. package/dist/core/index.js +54 -16
  66. package/dist/{function-DYt4iKsn.d.cts → function-BtiZb9i9.d.ts} +28 -7
  67. package/dist/{function-DVH4Eusr.d.ts → function-DNugAOrt.d.cts} +28 -7
  68. package/dist/generate/index.cjs +28 -4085
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +370 -62
  71. package/dist/generate/index.d.ts +370 -62
  72. package/dist/generate/index.js +23 -9
  73. package/dist/{index-CxqBQtH1.d.cts → index-6VR8-kit.d.ts} +19 -4
  74. package/dist/index-70p168VM.d.ts +192 -0
  75. package/dist/{index-CpOsP1y9.d.cts → index-Be74mcon.d.cts} +18 -17
  76. package/dist/index-BtXT6UQ0.d.cts +192 -0
  77. package/dist/{index-DxvARIP2.d.ts → index-C5UBbQ4G.d.ts} +18 -17
  78. package/dist/{index-Bt4tOUD9.d.ts → index-C83mZI1P.d.ts} +93 -8
  79. package/dist/{index-Bg5dYMmZ.d.ts → index-D6b6na69.d.cts} +14 -11
  80. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  81. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  82. package/dist/index-OG65ocOr.d.cts +272 -0
  83. package/dist/index-OG65ocOr.d.ts +272 -0
  84. package/dist/{index-Jc0Ryi79.d.ts → index-tJhkmQgE.d.cts} +19 -4
  85. package/dist/{index-Dikst44v.d.cts → index-tSQh-xwY.d.ts} +14 -11
  86. package/dist/{index-BNlGP86A.d.cts → index-y_ak9_Hi.d.cts} +93 -8
  87. package/dist/index.cjs +419 -7383
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.d.cts +14 -14
  90. package/dist/index.d.ts +14 -14
  91. package/dist/index.js +111 -27
  92. package/dist/model/index.cjs +8 -2562
  93. package/dist/model/index.cjs.map +1 -1
  94. package/dist/model/index.d.cts +433 -75
  95. package/dist/model/index.d.ts +433 -75
  96. package/dist/model/index.js +6 -7
  97. package/dist/styled-DIRkk9Uz.d.ts +506 -0
  98. package/dist/styled-DJbkJQfj.d.cts +506 -0
  99. package/dist/theory/index.cjs +172 -1822
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +151 -233
  102. package/dist/theory/index.d.ts +151 -233
  103. package/dist/theory/index.js +30 -7
  104. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  105. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  106. package/package.json +24 -6
  107. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  108. package/dist/chunk-4MHA432G.js.map +0 -1
  109. package/dist/chunk-62QZKCJQ.js.map +0 -1
  110. package/dist/chunk-AKLYPWIN.js.map +0 -1
  111. package/dist/chunk-C5XYKUVG.js.map +0 -1
  112. package/dist/chunk-CHMXELDS.js.map +0 -1
  113. package/dist/chunk-DYWFNC4A.js +0 -152
  114. package/dist/chunk-DYWFNC4A.js.map +0 -1
  115. package/dist/chunk-EH6EM37U.js.map +0 -1
  116. package/dist/chunk-FBQK3ZQN.js +0 -131
  117. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  118. package/dist/chunk-GOAW4VYL.js.map +0 -1
  119. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  120. package/dist/chunk-IT2O4XFE.js.map +0 -1
  121. package/dist/chunk-MIWO425M.js.map +0 -1
  122. package/dist/chunk-SGU7SPLK.js.map +0 -1
  123. package/dist/index-CGMZNCx6.d.cts +0 -140
  124. package/dist/index-CGMZNCx6.d.ts +0 -140
  125. package/dist/index-CnZCo29u.d.cts +0 -108
  126. package/dist/index-JnF4v9bx.d.ts +0 -108
  127. package/dist/styled-DZ7GjShb.d.ts +0 -282
  128. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  129. package/src/analyze/arrange/index.ts +0 -21
  130. package/src/analyze/arrange/internal.ts +0 -127
  131. package/src/analyze/arrange/tension.ts +0 -199
  132. package/src/analyze/arrange/tracks.ts +0 -312
  133. package/src/analyze/detect/index.ts +0 -272
  134. package/src/analyze/functional/borrowed.ts +0 -84
  135. package/src/analyze/functional/cadence.ts +0 -66
  136. package/src/analyze/functional/function.ts +0 -184
  137. package/src/analyze/functional/index.ts +0 -23
  138. package/src/analyze/functional/internal.ts +0 -29
  139. package/src/analyze/functional/roman.ts +0 -402
  140. package/src/analyze/index.ts +0 -49
  141. package/src/analyze/timeline/index.ts +0 -451
  142. package/src/analyze/voice/index.ts +0 -339
  143. package/src/core/event-index/index.ts +0 -104
  144. package/src/core/index.ts +0 -61
  145. package/src/core/interval/index.ts +0 -81
  146. package/src/core/meter/index.ts +0 -302
  147. package/src/core/pitch/index.ts +0 -311
  148. package/src/core/random/index.ts +0 -76
  149. package/src/core/tuning/index.ts +0 -186
  150. package/src/core/types.ts +0 -29
  151. package/src/core/validation/index.ts +0 -119
  152. package/src/generate/bass/index.ts +0 -373
  153. package/src/generate/countermelody/index.ts +0 -470
  154. package/src/generate/drums/beat.ts +0 -266
  155. package/src/generate/drums/euclid.ts +0 -65
  156. package/src/generate/drums/fills.ts +0 -356
  157. package/src/generate/drums/ghost.ts +0 -119
  158. package/src/generate/drums/hihat.ts +0 -285
  159. package/src/generate/drums/hit.ts +0 -31
  160. package/src/generate/drums/index.ts +0 -361
  161. package/src/generate/drums/internal.ts +0 -257
  162. package/src/generate/drums/kick.ts +0 -160
  163. package/src/generate/drums/percussion.ts +0 -121
  164. package/src/generate/drums/rng.ts +0 -7
  165. package/src/generate/drums/swing.ts +0 -71
  166. package/src/generate/groove/index.ts +0 -351
  167. package/src/generate/harmonize/index.ts +0 -451
  168. package/src/generate/index.ts +0 -58
  169. package/src/generate/motif/index.ts +0 -422
  170. package/src/generate/progression/index.ts +0 -362
  171. package/src/generate/reharmony/index.ts +0 -323
  172. package/src/generate/rhythm/index.ts +0 -175
  173. package/src/index.ts +0 -13
  174. package/src/model/chord.ts +0 -442
  175. package/src/model/index.ts +0 -15
  176. package/src/model/interval.ts +0 -82
  177. package/src/model/key.ts +0 -266
  178. package/src/model/note.ts +0 -176
  179. package/src/model/progression.ts +0 -161
  180. package/src/model/shared.ts +0 -12
  181. package/src/theory/chord/index.ts +0 -375
  182. package/src/theory/chordscale/index.ts +0 -374
  183. package/src/theory/counterpoint/index.ts +0 -230
  184. package/src/theory/harmony/index.ts +0 -77
  185. package/src/theory/index.ts +0 -112
  186. package/src/theory/safety/index.ts +0 -488
  187. package/src/theory/scale/degrees.ts +0 -123
  188. package/src/theory/scale/index.ts +0 -28
  189. package/src/theory/scale/key.ts +0 -56
  190. package/src/theory/scale/masks.ts +0 -153
  191. package/src/theory/spelling/index.ts +0 -249
  192. package/src/theory/symbol/index.ts +0 -268
  193. package/src/theory/voicing/index.ts +0 -5
  194. package/src/theory/voicing/internal.ts +0 -168
  195. package/src/theory/voicing/leading.ts +0 -112
  196. package/src/theory/voicing/satb.ts +0 -216
  197. package/src/theory/voicing/styled.ts +0 -199
  198. package/tsconfig.json +0 -23
@@ -1,9 +1,122 @@
1
- import { D as DetectChordOptions } from '../index-CnZCo29u.cjs';
2
- import { H as HarmonicFunction, a as ChordAnalysis, C as Cadence, B as BorrowedSource } from '../function-DYt4iKsn.cjs';
3
- import { a as ChordQuality, C as Chord$1 } from '../index-BNlGP86A.cjs';
4
- import { d as VoicingOptions, c as StyledVoicingOptions, C as ChordScaleMatch } from '../styled-ZTbdaVFM.cjs';
5
- import { N as Note$1, S as SpelledInterval } from '../index-CGMZNCx6.cjs';
6
- import { K as KeyScale } from '../types-MGP29r3t.cjs';
1
+ import { a as DetectKeyOptions, D as DetectChordOptions, C as ChordMatch } from '../index-BtXT6UQ0.cjs';
2
+ import { H as HarmonicFunction, a as ChordAnalysis, C as Cadence, B as BorrowedSource } from '../function-DNugAOrt.cjs';
3
+ import { K as KeyScale } from '../types-BN6_Q9Kl.cjs';
4
+ import { a as ChordQuality, c as ChordSpan, C as Chord$1 } from '../index-y_ak9_Hi.cjs';
5
+ export { P as PitchSpelling } from '../index-y_ak9_Hi.cjs';
6
+ import { I as IntervalQualityLabel, S as SpelledInterval, N as Note$1 } from '../index-OG65ocOr.cjs';
7
+ import { r as VoicingOptions, q as StyledVoicingOptions, a as ChordScaleMatch, p as ScaleNameInput } from '../styled-DJbkJQfj.cjs';
8
+ export { o as ScaleName, V as VoiceRange, s as VoicingStyle } from '../styled-DJbkJQfj.cjs';
9
+
10
+ /**
11
+ * An immutable spelled interval value: a diatonic number, a quality label, and
12
+ * a signed semitone span. A thin convenience wrapper over the pitch module's
13
+ * plain interval result.
14
+ *
15
+ * @category Class API
16
+ * @example
17
+ * ```ts
18
+ * import { Interval, Note } from '@libraz/libcantus';
19
+ * Interval.between(Note.of('C4'), Note.of('G4')).name; // 'P5'
20
+ * ```
21
+ */
22
+ declare class Interval {
23
+ #private;
24
+ private constructor();
25
+ /**
26
+ * The spelled interval between two notes.
27
+ *
28
+ * @param a The first note.
29
+ * @param b The second note.
30
+ * @returns The interval from `a` to `b`.
31
+ */
32
+ static between(a: Note, b: Note): Interval;
33
+ /**
34
+ * Build an interval from explicit components.
35
+ *
36
+ * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
37
+ * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
38
+ * @param semitones Signed semitone span; its magnitude must be the span the
39
+ * number and quality describe.
40
+ * @returns The interval.
41
+ * @throws If the three components do not describe the same interval — a
42
+ * `P5` spanning 8 semitones is not a value any other method can produce.
43
+ */
44
+ static of(numberValue: number, quality: IntervalQualityLabel, semitones: number): Interval;
45
+ /**
46
+ * Wrap a plain spelled interval, as returned by the pitch module.
47
+ *
48
+ * @param data The plain interval.
49
+ * @returns The wrapped interval.
50
+ */
51
+ static fromData(data: SpelledInterval): Interval;
52
+ /**
53
+ * Rebuild an interval from its {@link Interval.toJSON} output.
54
+ *
55
+ * @param data The serialized interval.
56
+ * @returns The wrapped interval.
57
+ */
58
+ static fromJSON(data: SpelledInterval): Interval;
59
+ /**
60
+ * Parse an interval name such as `'P5'`, `'m3'`, or `'AA4'`.
61
+ *
62
+ * @param name The interval name.
63
+ * @returns The ascending interval of that name.
64
+ * @throws If the name is not a quality label followed by a diatonic number.
65
+ */
66
+ static parse(name: string): Interval;
67
+ /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
68
+ get number(): number;
69
+ /** Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`. */
70
+ get quality(): IntervalQualityLabel;
71
+ /** Signed semitone span. */
72
+ get semitones(): number;
73
+ /** A readable label composed of quality and number, e.g. `'P5'` or `'M3'`. */
74
+ get name(): string;
75
+ /**
76
+ * The interval's inversion: the complement that completes the octave.
77
+ *
78
+ * A compound interval is reduced to its simple form first, and the result is
79
+ * always ascending — an inversion answers "what is left of the octave",
80
+ * which has no direction of its own.
81
+ *
82
+ * @returns The inverted interval, e.g. `M3` becomes `m6`.
83
+ */
84
+ invert(): Interval;
85
+ /**
86
+ * Whether the interval is consonant.
87
+ *
88
+ * @param twoVoice When true, the perfect fourth counts as a dissonance,
89
+ * matching two-voice counterpoint.
90
+ * @returns True if the interval is consonant in that context.
91
+ */
92
+ isConsonant(twoVoice?: boolean): boolean;
93
+ /**
94
+ * Whether another interval is spelled identically.
95
+ *
96
+ * Enharmonic equivalents are not equal: an augmented second and a minor
97
+ * third span the same distance but are different intervals.
98
+ *
99
+ * @param other The interval to compare with.
100
+ * @returns True when number, quality, and span all match.
101
+ */
102
+ equals(other: Interval): boolean;
103
+ /**
104
+ * The plain interval data, for JSON serialization.
105
+ *
106
+ * Private class fields do not serialize, so an explicit `toJSON` keeps
107
+ * `JSON.stringify(interval)` from collapsing to `{}`.
108
+ *
109
+ * @returns The diatonic number, quality, and semitone span.
110
+ */
111
+ toJSON(): SpelledInterval;
112
+ /**
113
+ * The interval's name, so a template literal or a log line reads as the
114
+ * interval.
115
+ *
116
+ * @returns The name, e.g. `'M3'`.
117
+ */
118
+ toString(): string;
119
+ }
7
120
 
8
121
  /**
9
122
  * An immutable spelled note: a diatonic letter plus a chromatic alteration and
@@ -23,6 +136,7 @@ declare class Note {
23
136
  * Wrap a plain note object.
24
137
  *
25
138
  * @param data The spelled note; it is copied, never retained or mutated.
139
+ * @throws If `letter` is not an integer in 0..6.
26
140
  */
27
141
  constructor(data: Note$1);
28
142
  /**
@@ -46,6 +160,7 @@ declare class Note {
46
160
  *
47
161
  * @param data The plain note.
48
162
  * @returns The wrapped note.
163
+ * @throws If `letter` is not an integer in 0..6.
49
164
  */
50
165
  static fromData(data: Note$1): Note;
51
166
  /** The note rendered as scientific pitch notation, e.g. `'G4'` or `'Bb'`. */
@@ -67,24 +182,59 @@ declare class Note {
67
182
  /** A copy of the underlying plain note object. */
68
183
  get data(): Note$1;
69
184
  /**
70
- * Transpose by a signed number of semitones.
185
+ * Transpose by a signed number of semitones, keeping the spelling.
71
186
  *
72
- * When the note carries an octave the transposition happens in MIDI space and
73
- * the result is spelled with a sharp preference. An octave-less note stays
187
+ * The letter moves by the diatonic distance of the conventional interval for
188
+ * that many semitones, so `Ab4` up a major second is `Bb4` rather than `A#4`
189
+ * and a flat key stays on the flat side. An octave-less note stays
74
190
  * octave-less: only its pitch class is moved. Transposing by zero is the
75
191
  * identity: the original spelling is preserved (no enharmonic respelling).
76
192
  *
77
193
  * @param semitones The signed semitone offset.
194
+ * @param opts `spelling` forces the result onto the sharp or flat side
195
+ * instead of following this note's letter.
78
196
  * @returns The transposed note.
197
+ * @example
198
+ * ```ts
199
+ * import { Note } from '@libraz/libcantus';
200
+ * Note.of('Ab4').transpose(2).name; // 'Bb4'
201
+ * Note.of('Ab4').transpose(2, { spelling: 'sharp' }).name; // 'A#4'
202
+ * ```
79
203
  */
80
- transpose(semitones: number): Note;
204
+ transpose(semitones: number, opts?: {
205
+ spelling?: 'sharp' | 'flat';
206
+ }): Note;
81
207
  /**
82
208
  * The spelled interval from this note to another.
83
209
  *
84
210
  * @param other The second note.
85
- * @returns The diatonic number, quality, and semitone span.
211
+ * @returns The interval, which {@link Interval.toJSON} unwraps to plain data.
212
+ */
213
+ intervalTo(other: Note): Interval;
214
+ /**
215
+ * Transpose by a spelled interval, keeping the spelling the interval names.
216
+ *
217
+ * Unlike {@link Note.transpose}, which picks a letter from the semitone
218
+ * count, the interval's diatonic number decides the letter: C up an
219
+ * augmented second is D#, not Eb.
220
+ *
221
+ * @param interval The interval to apply; a descending interval moves down.
222
+ * @returns The transposed note.
223
+ * @example
224
+ * ```ts
225
+ * import { Interval, Note } from '@libraz/libcantus';
226
+ * Note.of('C4').transposeBy(Interval.parse('A2')).name; // 'D#4'
227
+ * ```
228
+ */
229
+ transposeBy(interval: Interval): Note;
230
+ /**
231
+ * Rebuild a note from its {@link Note.toJSON} output.
232
+ *
233
+ * @param data The serialized note.
234
+ * @returns The wrapped note.
235
+ * @throws If `letter` is not an integer in 0..6.
86
236
  */
87
- intervalTo(other: Note): SpelledInterval;
237
+ static fromJSON(data: Note$1): Note;
88
238
  /**
89
239
  * Whether another note has the same letter, alteration, and octave.
90
240
  *
@@ -101,6 +251,12 @@ declare class Note {
101
251
  * @returns A copy of the underlying plain note object.
102
252
  */
103
253
  toJSON(): Note$1;
254
+ /**
255
+ * The note's name, so a template literal or a log line reads as the note.
256
+ *
257
+ * @returns The spelled name, e.g. `'Bb3'`.
258
+ */
259
+ toString(): string;
104
260
  }
105
261
 
106
262
  /**
@@ -146,20 +302,71 @@ declare class Key {
146
302
  * A key on a named scale (e.g. `'dorian'`, `'harmonicMinor'`).
147
303
  *
148
304
  * @param name The scale name, a key of the scale module's named-scale table.
149
- * @param root Tonic as a note name or a pitch class.
305
+ * @param root Tonic as a note name or a pitch class; a numeric root is
306
+ * spelled with whichever accidental side yields the fewest accidentals
307
+ * across the scale, exactly as {@link Key.major} does.
150
308
  * @returns The key.
151
309
  * @throws If the name is not a known scale.
152
310
  */
153
311
  static named(name: string, root: string | number): Key;
154
312
  /**
155
313
  * Wrap an existing `KeyScale`, synthesizing a spelled tonic when none is
156
- * given (flat-preferred for minor-third scales, sharp-preferred otherwise).
314
+ * given.
315
+ *
316
+ * The synthesized tonic is chosen the same way as for a numeric root
317
+ * elsewhere: whichever accidental side spells the scale with the fewest
318
+ * accidentals. This is what keeps `Key.of(detectKey(...).scale)` from handing
319
+ * every downstream chord a double-sharp spelling.
157
320
  *
158
321
  * @param scale The key/scale to wrap.
159
- * @param tonic Optional spelled tonic.
322
+ * @param tonic Optional spelled tonic; must spell the scale's root pitch class.
160
323
  * @returns The key.
324
+ * @throws If the given tonic is not the scale's root pitch class.
161
325
  */
162
326
  static of(scale: KeyScale, tonic?: Note): Key;
327
+ /**
328
+ * Rebuild a key from its {@link Key.toJSON} output.
329
+ *
330
+ * @param data The serialized key and tonic.
331
+ * @returns The key.
332
+ */
333
+ static fromJSON(data: {
334
+ scale: KeyScale;
335
+ tonic: Note$1;
336
+ }): Key;
337
+ /**
338
+ * Identify the keys a set of pitches fits, best interpretation first.
339
+ *
340
+ * The counterpart of {@link Chord.detect}.
341
+ *
342
+ * @param pitches MIDI pitches or bare pitch classes.
343
+ * @param opts How to weigh the input; see {@link DetectKeyOptions}.
344
+ * @returns Ranked keys (may be empty).
345
+ */
346
+ static detect(pitches: readonly number[], opts?: DetectKeyOptions): Key[];
347
+ /**
348
+ * The single best key interpretation of a pitch set.
349
+ *
350
+ * @param pitches MIDI pitches or bare pitch classes.
351
+ * @param opts How to weigh the input; see {@link DetectKeyOptions}.
352
+ * @returns The top-ranked key, or null when nothing matches.
353
+ * @example
354
+ * ```ts
355
+ * import { Key } from '@libraz/libcantus';
356
+ * Key.detectBest([0, 2, 4, 5, 7, 9, 11])?.toString(); // 'C major'
357
+ * ```
358
+ */
359
+ static detectBest(pitches: readonly number[], opts?: DetectKeyOptions): Key | null;
360
+ /**
361
+ * Whether another key has the same tonic pitch class and mode.
362
+ *
363
+ * The spelled tonic is not compared: C# major and Db major are the same key
364
+ * written two ways.
365
+ *
366
+ * @param other The key to compare.
367
+ * @returns True when tonic and mode mask both match.
368
+ */
369
+ equals(other: Key): boolean;
163
370
  /** A copy of the underlying plain `KeyScale`. */
164
371
  get scale(): KeyScale;
165
372
  /** The spelled tonic. */
@@ -174,6 +381,22 @@ declare class Key {
174
381
  * @returns One pitch class per scale degree.
175
382
  */
176
383
  pitchClasses(): number[];
384
+ /**
385
+ * Transpose the key by a number of semitones.
386
+ *
387
+ * The mode mask is unchanged, so the scale keeps its shape; only the tonic
388
+ * moves. The new tonic is spelled the way that key is conventionally written
389
+ * — D major, not C## major.
390
+ *
391
+ * @param semitones The signed semitone offset.
392
+ * @returns The transposed key.
393
+ * @example
394
+ * ```ts
395
+ * import { Key } from '@libraz/libcantus';
396
+ * Key.major('C').transpose(2).toString(); // 'D major'
397
+ * ```
398
+ */
399
+ transpose(semitones: number): Key;
177
400
  /**
178
401
  * The spelled scale, one note per degree (e.g. C D E F G A B for C major).
179
402
  *
@@ -202,6 +425,10 @@ declare class Key {
202
425
  * @param degree 0-based scale degree of the chord root.
203
426
  * @param quality Optional chord quality.
204
427
  * @returns The chord, with this key attached.
428
+ * @throws Without a `quality`, if this key's scale is not heptatonic —
429
+ * stacking thirds needs seven degrees, so the pentatonic, blues,
430
+ * whole-tone, octatonic and chromatic scales have no diatonic triad. Pass
431
+ * an explicit `quality` for those.
205
432
  */
206
433
  chord(degree: number, quality?: ChordQuality): Chord;
207
434
  /**
@@ -209,6 +436,8 @@ declare class Key {
209
436
  *
210
437
  * @param degree 0-based scale degree of the chord root.
211
438
  * @returns The triad, with this key attached.
439
+ * @throws If this key's scale is not heptatonic; stacking thirds needs seven
440
+ * degrees. Use {@link Key.chord} with an explicit quality instead.
212
441
  */
213
442
  diatonicTriad(degree: number): Chord;
214
443
  /**
@@ -216,6 +445,8 @@ declare class Key {
216
445
  *
217
446
  * @param degree 0-based scale degree of the chord root.
218
447
  * @returns The seventh chord, with this key attached.
448
+ * @throws If this key's scale is not heptatonic; stacking thirds needs seven
449
+ * degrees. Use {@link Key.chord} with an explicit quality instead.
219
450
  */
220
451
  diatonicSeventh(degree: number): Chord;
221
452
  /**
@@ -248,6 +479,14 @@ declare class Key {
248
479
  scale: KeyScale;
249
480
  tonic: Note$1;
250
481
  };
482
+ /**
483
+ * The key's tonic and mode, so a template literal or a log line reads as the
484
+ * key. Only the major/minor distinction is named, since a mask does not carry
485
+ * a scale name.
486
+ *
487
+ * @returns The name, e.g. `'C major'` or `'A minor'`.
488
+ */
489
+ toString(): string;
251
490
  }
252
491
 
253
492
  /**
@@ -272,12 +511,69 @@ declare class Progression {
272
511
  * @param key Optional key context for analysis methods.
273
512
  */
274
513
  constructor(chords: readonly Chord[], key?: Key);
275
- /** A copy of the chord sequence. */
514
+ /**
515
+ * Build a progression from the {@link ChordSpan} records the generators
516
+ * return, keeping their order.
517
+ *
518
+ * The spans' `startBeat`, `degree`, and `secondaryDominant` are analysis
519
+ * annotations that a chord sequence does not carry; only the harmony crosses
520
+ * over. Keep the spans themselves if the timing matters.
521
+ *
522
+ * @param spans The chord spans, in order.
523
+ * @param key Optional key context for the analysis methods.
524
+ * @returns The progression.
525
+ * @example
526
+ * ```ts
527
+ * import { generateProgression, Key, Progression } from '@libraz/libcantus';
528
+ * const key = Key.major('C');
529
+ * const spans = generateProgression({ key: key.scale, style: 'dance', bars: 4 });
530
+ * Progression.fromSpans(spans, key).roman();
531
+ * ```
532
+ */
533
+ static fromSpans(spans: readonly ChordSpan[], key?: Key): Progression;
534
+ /**
535
+ * Rebuild a progression from its {@link Progression.toJSON} output.
536
+ *
537
+ * @param data The serialized chords and key.
538
+ * @returns The progression.
539
+ */
540
+ static fromJSON(data: {
541
+ chords: Chord$1[];
542
+ key?: {
543
+ scale: KeyScale;
544
+ tonic: Note$1;
545
+ } | undefined;
546
+ }): Progression;
547
+ /**
548
+ * The chord sequence.
549
+ *
550
+ * The array is the progression's own and is frozen rather than copied, so
551
+ * reading it in a loop stays linear.
552
+ */
276
553
  get chords(): readonly Chord[];
277
554
  /** The carried key context, if any. */
278
555
  get key(): Key | undefined;
279
556
  /** The number of chords. */
280
557
  get length(): number;
558
+ /**
559
+ * The chord at an index.
560
+ *
561
+ * @param index 0-based position; a negative index counts from the end.
562
+ * @returns The chord, or undefined when the index is out of range.
563
+ */
564
+ at(index: number): Chord | undefined;
565
+ /** Iterate the chords in order, so a progression works with `for...of`. */
566
+ [Symbol.iterator](): IterableIterator<Chord>;
567
+ /**
568
+ * Whether another progression holds the same chords in the same order.
569
+ *
570
+ * The key context is not compared: it is an analysis lens, not part of the
571
+ * harmony. Chord equality follows {@link Chord.equals}.
572
+ *
573
+ * @param other The progression to compare.
574
+ * @returns True when the chord sequences match.
575
+ */
576
+ equals(other: Progression): boolean;
281
577
  /**
282
578
  * A copy of this progression with a chord appended.
283
579
  *
@@ -339,6 +635,28 @@ declare class Progression {
339
635
  *
340
636
  * @returns The chord data sequence and the carried key, if any.
341
637
  */
638
+ /**
639
+ * Transpose every chord by a number of semitones.
640
+ *
641
+ * A carried key moves with the chords, so the progression keeps its degrees
642
+ * and functions in the new key.
643
+ *
644
+ * @param semitones The signed semitone offset.
645
+ * @returns The transposed progression.
646
+ * @example
647
+ * ```ts
648
+ * import { Chord, Progression } from '@libraz/libcantus';
649
+ * new Progression([Chord.parse('C'), Chord.parse('G')]).transpose(2).toString(); // 'D A'
650
+ * ```
651
+ */
652
+ transpose(semitones: number): Progression;
653
+ /**
654
+ * The chord symbols separated by spaces, so a template literal or a log line
655
+ * reads as the progression.
656
+ *
657
+ * @returns The symbols in order, e.g. `'C Am F G'`.
658
+ */
659
+ toString(): string;
342
660
  toJSON(): {
343
661
  chords: Chord$1[];
344
662
  key: {
@@ -375,10 +693,11 @@ declare class Chord {
375
693
  *
376
694
  * @param root Root as a note name (e.g. `'Eb'`) or a pitch class.
377
695
  * @param quality The chord quality.
378
- * @param bass Optional slash-chord bass pitch class.
696
+ * @param bass Optional slash-chord bass, as a note name (e.g. `'Bb'`) or a
697
+ * pitch class. A named bass keeps its own spelling.
379
698
  * @returns The chord (without key context).
380
699
  */
381
- static of(root: string | number, quality: ChordQuality, bass?: number): Chord;
700
+ static of(root: string | number, quality: ChordQuality, bass?: string | number): Chord;
382
701
  /**
383
702
  * Wrap an existing plain chord object.
384
703
  *
@@ -386,6 +705,24 @@ declare class Chord {
386
705
  * @returns The wrapped chord (without key context).
387
706
  */
388
707
  static from(data: Chord$1): Chord;
708
+ /**
709
+ * Alias of {@link Chord.from}, matching the `fromData` factory on the other
710
+ * classes.
711
+ *
712
+ * @param data The plain chord.
713
+ * @returns The wrapped chord (without key context).
714
+ */
715
+ static fromData(data: Chord$1): Chord;
716
+ /**
717
+ * Rebuild a chord from its {@link Chord.toJSON} output.
718
+ *
719
+ * The key context is not serialized, so the result carries none; re-attach
720
+ * one with {@link Chord.withKey}.
721
+ *
722
+ * @param data The serialized chord.
723
+ * @returns The wrapped chord.
724
+ */
725
+ static fromJSON(data: Chord$1): Chord;
389
726
  /**
390
727
  * Parse a lead-sheet chord symbol (e.g. `'Cmaj7'`, `'F#m7b5'`, `'C/G'`).
391
728
  *
@@ -398,16 +735,40 @@ declare class Chord {
398
735
  * Identify the chords matching a set of pitches, best interpretation first.
399
736
  *
400
737
  * @param pitches MIDI pitches or bare pitch classes.
738
+ * @param opts How to interpret the input; see {@link DetectChordOptions}.
401
739
  * @returns Ranked chord interpretations (may be empty).
402
740
  */
403
- static detect(pitches: number[], opts?: DetectChordOptions): Chord[];
741
+ static detect(pitches: readonly number[], opts?: DetectChordOptions): Chord[];
742
+ /**
743
+ * Identify the chords matching a set of pitches, keeping each match's
744
+ * recognition metadata beside the chord.
745
+ *
746
+ * {@link Chord.detect} discards the confidence signals a recognition UI needs
747
+ * — whether the set matched exactly, which chord tones were missing, which
748
+ * input notes were foreign, and which inversion the bass implies.
749
+ *
750
+ * @param pitches MIDI pitches or bare pitch classes.
751
+ * @param opts How to interpret the input; see {@link DetectChordOptions}.
752
+ * @returns Ranked interpretations, each with its chord and its match record.
753
+ * @example
754
+ * ```ts
755
+ * import { Chord } from '@libraz/libcantus';
756
+ * const [best] = Chord.detectMatches([60, 64, 67]);
757
+ * best?.match.exact; // true
758
+ * ```
759
+ */
760
+ static detectMatches(pitches: readonly number[], opts?: DetectChordOptions): {
761
+ chord: Chord;
762
+ match: ChordMatch;
763
+ }[];
404
764
  /**
405
765
  * The single best chord interpretation of a pitch set.
406
766
  *
407
767
  * @param pitches MIDI pitches or bare pitch classes.
768
+ * @param opts How to interpret the input; see {@link DetectChordOptions}.
408
769
  * @returns The top-ranked chord, or null when nothing matches.
409
770
  */
410
- static detectBest(pitches: number[], opts?: DetectChordOptions): Chord | null;
771
+ static detectBest(pitches: readonly number[], opts?: DetectChordOptions): Chord | null;
411
772
  /** The root pitch class (0..11). */
412
773
  get rootPc(): number;
413
774
  /** The chord quality. */
@@ -416,13 +777,22 @@ declare class Chord {
416
777
  get intervals(): number[];
417
778
  /** The slash-chord bass pitch class, or undefined in root position. */
418
779
  get bassPc(): number | undefined;
419
- /** A copy of the underlying plain chord object. */
780
+ /**
781
+ * A copy of the underlying plain chord object, including the spellings
782
+ * derived from any attached key.
783
+ */
420
784
  get data(): Chord$1;
421
785
  /** The carried key context, if any. */
422
786
  get key(): Key | undefined;
423
787
  /**
424
788
  * A copy of this chord carrying the given key context.
425
789
  *
790
+ * Only a spelling the caller supplied (via `Chord.parse`, `Chord.of` with a
791
+ * named root, or plain data carrying a hint) survives; a spelling that came
792
+ * from a previously attached key is re-derived, so re-keying a progression
793
+ * during a modulation does not keep the old key's letters and the order of
794
+ * `withKey` calls does not affect the result.
795
+ *
426
796
  * @param key The key context to attach.
427
797
  * @returns The new chord.
428
798
  */
@@ -485,6 +855,9 @@ declare class Chord {
485
855
  /**
486
856
  * Realize the chord as one MIDI pitch per voice, ascending.
487
857
  *
858
+ * A chord that carries a key passes it to the voicer, so the leading tone is
859
+ * not doubled; an explicit `opts.key` overrides it.
860
+ *
488
861
  * @param opts Voicing options; defaults to four SATB voices.
489
862
  * @returns MIDI pitches, ascending, one per voice.
490
863
  * @throws If no voicing fits the given ranges.
@@ -521,6 +894,23 @@ declare class Chord {
521
894
  * @throws If the chord has no intervals.
522
895
  */
523
896
  invert(n: number): Chord;
897
+ /**
898
+ * Transpose the chord by a number of semitones.
899
+ *
900
+ * The quality and interval template are carried over, so a chord that a
901
+ * symbol round-trip could not express — a custom interval set, an inversion —
902
+ * survives. A carried key moves with the chord, so the transposed chord keeps
903
+ * the same degree and function inside the transposed key.
904
+ *
905
+ * @param semitones The signed semitone offset.
906
+ * @returns The transposed chord, in the transposed key when one is carried.
907
+ * @example
908
+ * ```ts
909
+ * import { Chord } from '@libraz/libcantus';
910
+ * Chord.parse('C/G').transpose(2).symbol(); // 'D/A'
911
+ * ```
912
+ */
913
+ transpose(semitones: number): Chord;
524
914
  /**
525
915
  * The named scales that fit over this chord, best fit first, rooted on the
526
916
  * chord root.
@@ -534,23 +924,36 @@ declare class Chord {
534
924
  *
535
925
  * @param scaleName A named scale, rooted on the chord root.
536
926
  * @returns Tension pitch classes, ascending in [0, 11].
927
+ * @throws If `scaleName` is not a built-in scale.
537
928
  */
538
- tensions(scaleName: string): number[];
929
+ tensions(scaleName: ScaleNameInput): number[];
539
930
  /**
540
931
  * The avoid notes (scale tones a semitone above a chord tone) of a scale
541
932
  * over this chord.
542
933
  *
543
934
  * @param scaleName A named scale, rooted on the chord root.
544
935
  * @returns Avoid-note pitch classes, ascending in [0, 11].
936
+ * @throws If `scaleName` is not a built-in scale.
545
937
  */
546
- avoidNotes(scaleName: string): number[];
938
+ avoidNotes(scaleName: ScaleNameInput): number[];
547
939
  /**
548
940
  * Spell the chord tones with letter names, root first, in the key's spelling.
549
941
  *
942
+ * A chord that already knows how its root is spelled — one from
943
+ * `Chord.parse`, or `Chord.of` with a named root — can spell itself with no
944
+ * key at all; a key is only needed to choose a spelling for a bare pitch
945
+ * class.
946
+ *
947
+ * A slash bass outside the chord tones is appended, so the spelled notes
948
+ * are the notes the chord sounds — the same set {@link Chord.pitchClasses}
949
+ * reports.
950
+ *
550
951
  * @param key Key providing the spelled tonic; falls back to the carried
551
- * context.
552
- * @returns Spelled octave-less notes in the chord's own (tertian) order.
553
- * @throws If no key is given and none is carried.
952
+ * context, then to the chord's own root spelling.
953
+ * @returns Spelled octave-less notes in the chord's own (tertian) order,
954
+ * with any foreign slash bass last.
955
+ * @throws If no key is given, none is carried, and the chord has no root
956
+ * spelling of its own.
554
957
  */
555
958
  spell(key?: Key): Note[];
556
959
  /**
@@ -575,57 +978,12 @@ declare class Chord {
575
978
  * @returns A copy of the underlying plain chord object.
576
979
  */
577
980
  toJSON(): Chord$1;
578
- }
579
-
580
- /**
581
- * An immutable spelled interval value: a diatonic number, a quality label, and
582
- * a signed semitone span. A thin convenience wrapper over the pitch module's
583
- * plain interval result.
584
- *
585
- * @category Class API
586
- * @example
587
- * ```ts
588
- * import { Interval, Note } from '@libraz/libcantus';
589
- * Interval.between(Note.of('C4'), Note.of('G4')).name; // 'P5'
590
- * ```
591
- */
592
- declare class Interval {
593
- #private;
594
- private constructor();
595
- /**
596
- * The spelled interval between two notes.
597
- *
598
- * @param a The first note.
599
- * @param b The second note.
600
- * @returns The interval from `a` to `b`.
601
- */
602
- static between(a: Note, b: Note): Interval;
603
981
  /**
604
- * Build an interval from explicit components.
982
+ * The chord symbol, so a template literal or a log line reads as the chord.
605
983
  *
606
- * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
607
- * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
608
- * @param semitones Signed semitone span.
609
- * @returns The interval.
984
+ * @returns The symbol, e.g. `'Cmaj7'`.
610
985
  */
611
- static of(numberValue: number, quality: string, semitones: number): Interval;
612
- /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
613
- get number(): number;
614
- /** Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`. */
615
- get quality(): string;
616
- /** Signed semitone span. */
617
- get semitones(): number;
618
- /** A readable label composed of quality and number, e.g. `'P5'` or `'M3'`. */
619
- get name(): string;
620
- /**
621
- * The plain interval data, for JSON serialization.
622
- *
623
- * Private class fields do not serialize, so an explicit `toJSON` keeps
624
- * `JSON.stringify(interval)` from collapsing to `{}`.
625
- *
626
- * @returns The diatonic number, quality, and semitone span.
627
- */
628
- toJSON(): SpelledInterval;
986
+ toString(): string;
629
987
  }
630
988
 
631
- export { Chord, Interval, Key, Note, Progression };
989
+ export { BorrowedSource, Cadence, Chord, ChordAnalysis, Chord$1 as ChordData, ChordMatch, ChordQuality, ChordScaleMatch, ChordSpan, DetectChordOptions, DetectKeyOptions, HarmonicFunction, Interval, IntervalQualityLabel, Key, KeyScale, Note, Note$1 as NoteData, Progression, ScaleNameInput, SpelledInterval, StyledVoicingOptions, VoicingOptions };