@libraz/libcantus 0.9.2 → 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 (200) hide show
  1. package/README.md +6 -5
  2. package/dist/analyze/index.cjs +41 -1676
  3. package/dist/analyze/index.cjs.map +1 -1
  4. package/dist/analyze/index.d.cts +194 -122
  5. package/dist/analyze/index.d.ts +194 -122
  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-R3S2LUBH.js → chunk-7NKDPF74.js} +660 -305
  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-7Y7WF64D.js → chunk-D2FAJBYL.js} +104 -35
  18. package/dist/chunk-D2FAJBYL.js.map +1 -0
  19. package/dist/{chunk-COKTX2G5.js → chunk-EHVEBWM5.js} +1052 -510
  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-GQKN5KDT.js → chunk-GQSGS2N4.js} +43 -29
  26. package/dist/chunk-GQSGS2N4.js.map +1 -0
  27. package/dist/chunk-HKGYCQ34.js +53 -0
  28. package/dist/chunk-HKGYCQ34.js.map +1 -0
  29. package/dist/chunk-I24MV65U.js +106 -0
  30. package/dist/chunk-I24MV65U.js.map +1 -0
  31. package/dist/{chunk-LN7ZZMD2.js → chunk-KMHOICIY.js} +322 -173
  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-N6IHQUOX.js +1 -0
  36. package/dist/chunk-N6IHQUOX.js.map +1 -0
  37. package/dist/chunk-NKIBQ6PT.js +30 -0
  38. package/dist/chunk-NKIBQ6PT.js.map +1 -0
  39. package/dist/chunk-NP4NA2FB.cjs +106 -0
  40. package/dist/chunk-NP4NA2FB.cjs.map +1 -0
  41. package/dist/chunk-NVE34YGZ.cjs +1449 -0
  42. package/dist/chunk-NVE34YGZ.cjs.map +1 -0
  43. package/dist/{chunk-A4532ZFR.js → chunk-NYYWMV2C.js} +115 -47
  44. package/dist/chunk-NYYWMV2C.js.map +1 -0
  45. package/dist/{chunk-5HTNMQW4.js → chunk-Q2XUP227.js} +310 -118
  46. package/dist/chunk-Q2XUP227.js.map +1 -0
  47. package/dist/chunk-Q6OP4U43.cjs +327 -0
  48. package/dist/chunk-Q6OP4U43.cjs.map +1 -0
  49. package/dist/chunk-RC6GBMCD.cjs +845 -0
  50. package/dist/chunk-RC6GBMCD.cjs.map +1 -0
  51. package/dist/{chunk-XTC3X6MS.js → chunk-RK35RIN7.js} +453 -152
  52. package/dist/chunk-RK35RIN7.js.map +1 -0
  53. package/dist/chunk-RUMU3UA6.cjs +53 -0
  54. package/dist/chunk-RUMU3UA6.cjs.map +1 -0
  55. package/dist/chunk-T64C7ZD7.cjs +1 -0
  56. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  57. package/dist/chunk-XEBG3G53.js +498 -0
  58. package/dist/chunk-XEBG3G53.js.map +1 -0
  59. package/dist/chunk-YLEMGEFH.js +246 -0
  60. package/dist/chunk-YLEMGEFH.js.map +1 -0
  61. package/dist/chunk-ZEJR4TKL.cjs +173 -0
  62. package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
  63. package/dist/core/index.cjs +130 -431
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +331 -17
  66. package/dist/core/index.d.ts +331 -17
  67. package/dist/core/index.js +71 -13
  68. package/dist/{function-2bJW1Zp_.d.ts → function-BtiZb9i9.d.ts} +28 -7
  69. package/dist/{function-r2sh-jOO.d.cts → function-DNugAOrt.d.cts} +28 -7
  70. package/dist/generate/index.cjs +30 -3751
  71. package/dist/generate/index.cjs.map +1 -1
  72. package/dist/generate/index.d.cts +373 -59
  73. package/dist/generate/index.d.ts +373 -59
  74. package/dist/generate/index.js +23 -9
  75. package/dist/{index-DZ1M21do.d.cts → index-6VR8-kit.d.ts} +20 -4
  76. package/dist/index-70p168VM.d.ts +192 -0
  77. package/dist/{index-D_ZTUNKp.d.cts → index-Be74mcon.d.cts} +18 -17
  78. package/dist/index-BtXT6UQ0.d.cts +192 -0
  79. package/dist/{index-BMPxwjwV.d.ts → index-C5UBbQ4G.d.ts} +18 -17
  80. package/dist/{index-BOBXBNwP.d.cts → index-C83mZI1P.d.ts} +93 -8
  81. package/dist/{index-BdeQ1r_p.d.cts → index-D6b6na69.d.cts} +14 -11
  82. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  83. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  84. package/dist/index-OG65ocOr.d.cts +272 -0
  85. package/dist/index-OG65ocOr.d.ts +272 -0
  86. package/dist/{index-BreoJRw0.d.ts → index-tJhkmQgE.d.cts} +20 -4
  87. package/dist/{index-CP3wAfEH.d.ts → index-tSQh-xwY.d.ts} +14 -11
  88. package/dist/{index-CM5W4rcm.d.ts → index-y_ak9_Hi.d.cts} +93 -8
  89. package/dist/index.cjs +419 -6848
  90. package/dist/index.cjs.map +1 -1
  91. package/dist/index.d.cts +14 -13
  92. package/dist/index.d.ts +14 -13
  93. package/dist/index.js +132 -27
  94. package/dist/model/index.cjs +8 -2423
  95. package/dist/model/index.cjs.map +1 -1
  96. package/dist/model/index.d.cts +433 -74
  97. package/dist/model/index.d.ts +433 -74
  98. package/dist/model/index.js +6 -7
  99. package/dist/styled-DIRkk9Uz.d.ts +506 -0
  100. package/dist/styled-DJbkJQfj.d.cts +506 -0
  101. package/dist/theory/index.cjs +172 -1676
  102. package/dist/theory/index.cjs.map +1 -1
  103. package/dist/theory/index.d.cts +151 -233
  104. package/dist/theory/index.d.ts +151 -233
  105. package/dist/theory/index.js +31 -7
  106. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  107. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  108. package/package.json +73 -24
  109. package/dist/chunk-5HTNMQW4.js.map +0 -1
  110. package/dist/chunk-7Y7WF64D.js.map +0 -1
  111. package/dist/chunk-A4532ZFR.js.map +0 -1
  112. package/dist/chunk-BHUT2S5G.js +0 -22
  113. package/dist/chunk-BHUT2S5G.js.map +0 -1
  114. package/dist/chunk-CCBLVAPK.js +0 -42
  115. package/dist/chunk-CCBLVAPK.js.map +0 -1
  116. package/dist/chunk-COKTX2G5.js.map +0 -1
  117. package/dist/chunk-DYWFNC4A.js +0 -152
  118. package/dist/chunk-DYWFNC4A.js.map +0 -1
  119. package/dist/chunk-GQKN5KDT.js.map +0 -1
  120. package/dist/chunk-LN7ZZMD2.js.map +0 -1
  121. package/dist/chunk-MSWFZF7J.js +0 -118
  122. package/dist/chunk-MSWFZF7J.js.map +0 -1
  123. package/dist/chunk-R3S2LUBH.js.map +0 -1
  124. package/dist/chunk-SDM3VJJ5.js +0 -41
  125. package/dist/chunk-SDM3VJJ5.js.map +0 -1
  126. package/dist/chunk-XTC3X6MS.js.map +0 -1
  127. package/dist/chunk-ZXW5YIWK.js +0 -55
  128. package/dist/chunk-ZXW5YIWK.js.map +0 -1
  129. package/dist/index-CGMZNCx6.d.cts +0 -140
  130. package/dist/index-CGMZNCx6.d.ts +0 -140
  131. package/dist/styled-BjXtOFNk.d.cts +0 -282
  132. package/dist/styled-D7BAx3S3.d.ts +0 -282
  133. package/src/analyze/arrange/index.ts +0 -21
  134. package/src/analyze/arrange/internal.ts +0 -125
  135. package/src/analyze/arrange/tension.ts +0 -187
  136. package/src/analyze/arrange/tracks.ts +0 -279
  137. package/src/analyze/detect/index.ts +0 -235
  138. package/src/analyze/functional/borrowed.ts +0 -84
  139. package/src/analyze/functional/cadence.ts +0 -66
  140. package/src/analyze/functional/function.ts +0 -184
  141. package/src/analyze/functional/index.ts +0 -23
  142. package/src/analyze/functional/internal.ts +0 -29
  143. package/src/analyze/functional/roman.ts +0 -393
  144. package/src/analyze/index.ts +0 -49
  145. package/src/analyze/timeline/index.ts +0 -415
  146. package/src/analyze/voice/index.ts +0 -330
  147. package/src/core/index.ts +0 -47
  148. package/src/core/interval/index.ts +0 -81
  149. package/src/core/meter/index.ts +0 -282
  150. package/src/core/pitch/index.ts +0 -311
  151. package/src/core/random/index.ts +0 -53
  152. package/src/core/tuning/index.ts +0 -156
  153. package/src/core/types.ts +0 -29
  154. package/src/generate/bass/index.ts +0 -350
  155. package/src/generate/countermelody/index.ts +0 -379
  156. package/src/generate/drums/beat.ts +0 -266
  157. package/src/generate/drums/euclid.ts +0 -58
  158. package/src/generate/drums/fills.ts +0 -356
  159. package/src/generate/drums/ghost.ts +0 -119
  160. package/src/generate/drums/hihat.ts +0 -285
  161. package/src/generate/drums/hit.ts +0 -31
  162. package/src/generate/drums/index.ts +0 -329
  163. package/src/generate/drums/internal.ts +0 -257
  164. package/src/generate/drums/kick.ts +0 -160
  165. package/src/generate/drums/percussion.ts +0 -121
  166. package/src/generate/drums/rng.ts +0 -7
  167. package/src/generate/drums/swing.ts +0 -71
  168. package/src/generate/groove/index.ts +0 -326
  169. package/src/generate/harmonize/index.ts +0 -440
  170. package/src/generate/index.ts +0 -59
  171. package/src/generate/motif/index.ts +0 -398
  172. package/src/generate/progression/index.ts +0 -370
  173. package/src/generate/reharmony/index.ts +0 -330
  174. package/src/generate/rhythm/index.ts +0 -165
  175. package/src/index.ts +0 -247
  176. package/src/model/chord.ts +0 -423
  177. package/src/model/index.ts +0 -15
  178. package/src/model/interval.ts +0 -82
  179. package/src/model/key.ts +0 -266
  180. package/src/model/note.ts +0 -176
  181. package/src/model/progression.ts +0 -161
  182. package/src/model/shared.ts +0 -12
  183. package/src/theory/chord/index.ts +0 -373
  184. package/src/theory/chordscale/index.ts +0 -374
  185. package/src/theory/counterpoint/index.ts +0 -230
  186. package/src/theory/harmony/index.ts +0 -92
  187. package/src/theory/index.ts +0 -107
  188. package/src/theory/safety/index.ts +0 -419
  189. package/src/theory/scale/degrees.ts +0 -123
  190. package/src/theory/scale/index.ts +0 -28
  191. package/src/theory/scale/key.ts +0 -56
  192. package/src/theory/scale/masks.ts +0 -153
  193. package/src/theory/spelling/index.ts +0 -182
  194. package/src/theory/symbol/index.ts +0 -268
  195. package/src/theory/voicing/index.ts +0 -5
  196. package/src/theory/voicing/internal.ts +0 -168
  197. package/src/theory/voicing/leading.ts +0 -112
  198. package/src/theory/voicing/satb.ts +0 -195
  199. package/src/theory/voicing/styled.ts +0 -176
  200. package/tsconfig.json +0 -23
@@ -1,8 +1,122 @@
1
- import { H as HarmonicFunction, a as ChordAnalysis, C as Cadence, B as BorrowedSource } from '../function-2bJW1Zp_.js';
2
- import { a as ChordQuality, C as Chord$1 } from '../index-CM5W4rcm.js';
3
- import { d as VoicingOptions, c as StyledVoicingOptions, C as ChordScaleMatch } from '../styled-D7BAx3S3.js';
4
- import { N as Note$1, S as SpelledInterval } from '../index-CGMZNCx6.js';
5
- import { K as KeyScale } from '../types-MGP29r3t.js';
1
+ import { a as DetectKeyOptions, D as DetectChordOptions, C as ChordMatch } from '../index-70p168VM.js';
2
+ import { H as HarmonicFunction, a as ChordAnalysis, C as Cadence, B as BorrowedSource } from '../function-BtiZb9i9.js';
3
+ import { K as KeyScale } from '../types-BN6_Q9Kl.js';
4
+ import { a as ChordQuality, c as ChordSpan, C as Chord$1 } from '../index-C83mZI1P.js';
5
+ export { P as PitchSpelling } from '../index-C83mZI1P.js';
6
+ import { I as IntervalQualityLabel, S as SpelledInterval, N as Note$1 } from '../index-OG65ocOr.js';
7
+ import { r as VoicingOptions, q as StyledVoicingOptions, a as ChordScaleMatch, p as ScaleNameInput } from '../styled-DIRkk9Uz.js';
8
+ export { o as ScaleName, V as VoiceRange, s as VoicingStyle } from '../styled-DIRkk9Uz.js';
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
+ }
6
120
 
7
121
  /**
8
122
  * An immutable spelled note: a diatonic letter plus a chromatic alteration and
@@ -22,6 +136,7 @@ declare class Note {
22
136
  * Wrap a plain note object.
23
137
  *
24
138
  * @param data The spelled note; it is copied, never retained or mutated.
139
+ * @throws If `letter` is not an integer in 0..6.
25
140
  */
26
141
  constructor(data: Note$1);
27
142
  /**
@@ -45,6 +160,7 @@ declare class Note {
45
160
  *
46
161
  * @param data The plain note.
47
162
  * @returns The wrapped note.
163
+ * @throws If `letter` is not an integer in 0..6.
48
164
  */
49
165
  static fromData(data: Note$1): Note;
50
166
  /** The note rendered as scientific pitch notation, e.g. `'G4'` or `'Bb'`. */
@@ -66,24 +182,59 @@ declare class Note {
66
182
  /** A copy of the underlying plain note object. */
67
183
  get data(): Note$1;
68
184
  /**
69
- * Transpose by a signed number of semitones.
185
+ * Transpose by a signed number of semitones, keeping the spelling.
70
186
  *
71
- * When the note carries an octave the transposition happens in MIDI space and
72
- * 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
73
190
  * octave-less: only its pitch class is moved. Transposing by zero is the
74
191
  * identity: the original spelling is preserved (no enharmonic respelling).
75
192
  *
76
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.
77
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
+ * ```
78
203
  */
79
- transpose(semitones: number): Note;
204
+ transpose(semitones: number, opts?: {
205
+ spelling?: 'sharp' | 'flat';
206
+ }): Note;
80
207
  /**
81
208
  * The spelled interval from this note to another.
82
209
  *
83
210
  * @param other The second note.
84
- * @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.
85
236
  */
86
- intervalTo(other: Note): SpelledInterval;
237
+ static fromJSON(data: Note$1): Note;
87
238
  /**
88
239
  * Whether another note has the same letter, alteration, and octave.
89
240
  *
@@ -100,6 +251,12 @@ declare class Note {
100
251
  * @returns A copy of the underlying plain note object.
101
252
  */
102
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;
103
260
  }
104
261
 
105
262
  /**
@@ -145,20 +302,71 @@ declare class Key {
145
302
  * A key on a named scale (e.g. `'dorian'`, `'harmonicMinor'`).
146
303
  *
147
304
  * @param name The scale name, a key of the scale module's named-scale table.
148
- * @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.
149
308
  * @returns The key.
150
309
  * @throws If the name is not a known scale.
151
310
  */
152
311
  static named(name: string, root: string | number): Key;
153
312
  /**
154
313
  * Wrap an existing `KeyScale`, synthesizing a spelled tonic when none is
155
- * 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.
156
320
  *
157
321
  * @param scale The key/scale to wrap.
158
- * @param tonic Optional spelled tonic.
322
+ * @param tonic Optional spelled tonic; must spell the scale's root pitch class.
159
323
  * @returns The key.
324
+ * @throws If the given tonic is not the scale's root pitch class.
160
325
  */
161
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;
162
370
  /** A copy of the underlying plain `KeyScale`. */
163
371
  get scale(): KeyScale;
164
372
  /** The spelled tonic. */
@@ -173,6 +381,22 @@ declare class Key {
173
381
  * @returns One pitch class per scale degree.
174
382
  */
175
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;
176
400
  /**
177
401
  * The spelled scale, one note per degree (e.g. C D E F G A B for C major).
178
402
  *
@@ -201,6 +425,10 @@ declare class Key {
201
425
  * @param degree 0-based scale degree of the chord root.
202
426
  * @param quality Optional chord quality.
203
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.
204
432
  */
205
433
  chord(degree: number, quality?: ChordQuality): Chord;
206
434
  /**
@@ -208,6 +436,8 @@ declare class Key {
208
436
  *
209
437
  * @param degree 0-based scale degree of the chord root.
210
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.
211
441
  */
212
442
  diatonicTriad(degree: number): Chord;
213
443
  /**
@@ -215,6 +445,8 @@ declare class Key {
215
445
  *
216
446
  * @param degree 0-based scale degree of the chord root.
217
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.
218
450
  */
219
451
  diatonicSeventh(degree: number): Chord;
220
452
  /**
@@ -247,6 +479,14 @@ declare class Key {
247
479
  scale: KeyScale;
248
480
  tonic: Note$1;
249
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;
250
490
  }
251
491
 
252
492
  /**
@@ -271,12 +511,69 @@ declare class Progression {
271
511
  * @param key Optional key context for analysis methods.
272
512
  */
273
513
  constructor(chords: readonly Chord[], key?: Key);
274
- /** 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
+ */
275
553
  get chords(): readonly Chord[];
276
554
  /** The carried key context, if any. */
277
555
  get key(): Key | undefined;
278
556
  /** The number of chords. */
279
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;
280
577
  /**
281
578
  * A copy of this progression with a chord appended.
282
579
  *
@@ -338,6 +635,28 @@ declare class Progression {
338
635
  *
339
636
  * @returns The chord data sequence and the carried key, if any.
340
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;
341
660
  toJSON(): {
342
661
  chords: Chord$1[];
343
662
  key: {
@@ -374,10 +693,11 @@ declare class Chord {
374
693
  *
375
694
  * @param root Root as a note name (e.g. `'Eb'`) or a pitch class.
376
695
  * @param quality The chord quality.
377
- * @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.
378
698
  * @returns The chord (without key context).
379
699
  */
380
- static of(root: string | number, quality: ChordQuality, bass?: number): Chord;
700
+ static of(root: string | number, quality: ChordQuality, bass?: string | number): Chord;
381
701
  /**
382
702
  * Wrap an existing plain chord object.
383
703
  *
@@ -385,6 +705,24 @@ declare class Chord {
385
705
  * @returns The wrapped chord (without key context).
386
706
  */
387
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;
388
726
  /**
389
727
  * Parse a lead-sheet chord symbol (e.g. `'Cmaj7'`, `'F#m7b5'`, `'C/G'`).
390
728
  *
@@ -397,16 +735,40 @@ declare class Chord {
397
735
  * Identify the chords matching a set of pitches, best interpretation first.
398
736
  *
399
737
  * @param pitches MIDI pitches or bare pitch classes.
738
+ * @param opts How to interpret the input; see {@link DetectChordOptions}.
400
739
  * @returns Ranked chord interpretations (may be empty).
401
740
  */
402
- static detect(pitches: number[]): 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
+ }[];
403
764
  /**
404
765
  * The single best chord interpretation of a pitch set.
405
766
  *
406
767
  * @param pitches MIDI pitches or bare pitch classes.
768
+ * @param opts How to interpret the input; see {@link DetectChordOptions}.
407
769
  * @returns The top-ranked chord, or null when nothing matches.
408
770
  */
409
- static detectBest(pitches: number[]): Chord | null;
771
+ static detectBest(pitches: readonly number[], opts?: DetectChordOptions): Chord | null;
410
772
  /** The root pitch class (0..11). */
411
773
  get rootPc(): number;
412
774
  /** The chord quality. */
@@ -415,13 +777,22 @@ declare class Chord {
415
777
  get intervals(): number[];
416
778
  /** The slash-chord bass pitch class, or undefined in root position. */
417
779
  get bassPc(): number | undefined;
418
- /** 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
+ */
419
784
  get data(): Chord$1;
420
785
  /** The carried key context, if any. */
421
786
  get key(): Key | undefined;
422
787
  /**
423
788
  * A copy of this chord carrying the given key context.
424
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
+ *
425
796
  * @param key The key context to attach.
426
797
  * @returns The new chord.
427
798
  */
@@ -484,6 +855,9 @@ declare class Chord {
484
855
  /**
485
856
  * Realize the chord as one MIDI pitch per voice, ascending.
486
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
+ *
487
861
  * @param opts Voicing options; defaults to four SATB voices.
488
862
  * @returns MIDI pitches, ascending, one per voice.
489
863
  * @throws If no voicing fits the given ranges.
@@ -520,6 +894,23 @@ declare class Chord {
520
894
  * @throws If the chord has no intervals.
521
895
  */
522
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;
523
914
  /**
524
915
  * The named scales that fit over this chord, best fit first, rooted on the
525
916
  * chord root.
@@ -533,23 +924,36 @@ declare class Chord {
533
924
  *
534
925
  * @param scaleName A named scale, rooted on the chord root.
535
926
  * @returns Tension pitch classes, ascending in [0, 11].
927
+ * @throws If `scaleName` is not a built-in scale.
536
928
  */
537
- tensions(scaleName: string): number[];
929
+ tensions(scaleName: ScaleNameInput): number[];
538
930
  /**
539
931
  * The avoid notes (scale tones a semitone above a chord tone) of a scale
540
932
  * over this chord.
541
933
  *
542
934
  * @param scaleName A named scale, rooted on the chord root.
543
935
  * @returns Avoid-note pitch classes, ascending in [0, 11].
936
+ * @throws If `scaleName` is not a built-in scale.
544
937
  */
545
- avoidNotes(scaleName: string): number[];
938
+ avoidNotes(scaleName: ScaleNameInput): number[];
546
939
  /**
547
940
  * Spell the chord tones with letter names, root first, in the key's spelling.
548
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
+ *
549
951
  * @param key Key providing the spelled tonic; falls back to the carried
550
- * context.
551
- * @returns Spelled octave-less notes in the chord's own (tertian) order.
552
- * @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.
553
957
  */
554
958
  spell(key?: Key): Note[];
555
959
  /**
@@ -574,57 +978,12 @@ declare class Chord {
574
978
  * @returns A copy of the underlying plain chord object.
575
979
  */
576
980
  toJSON(): Chord$1;
577
- }
578
-
579
- /**
580
- * An immutable spelled interval value: a diatonic number, a quality label, and
581
- * a signed semitone span. A thin convenience wrapper over the pitch module's
582
- * plain interval result.
583
- *
584
- * @category Class API
585
- * @example
586
- * ```ts
587
- * import { Interval, Note } from '@libraz/libcantus';
588
- * Interval.between(Note.of('C4'), Note.of('G4')).name; // 'P5'
589
- * ```
590
- */
591
- declare class Interval {
592
- #private;
593
- private constructor();
594
- /**
595
- * The spelled interval between two notes.
596
- *
597
- * @param a The first note.
598
- * @param b The second note.
599
- * @returns The interval from `a` to `b`.
600
- */
601
- static between(a: Note, b: Note): Interval;
602
981
  /**
603
- * Build an interval from explicit components.
982
+ * The chord symbol, so a template literal or a log line reads as the chord.
604
983
  *
605
- * @param numberValue Diatonic size: 1 = unison, 2 = second, ... 8 = octave.
606
- * @param quality Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`.
607
- * @param semitones Signed semitone span.
608
- * @returns The interval.
984
+ * @returns The symbol, e.g. `'Cmaj7'`.
609
985
  */
610
- static of(numberValue: number, quality: string, semitones: number): Interval;
611
- /** Diatonic size: 1 = unison, 2 = second, ... 8 = octave, and beyond. */
612
- get number(): number;
613
- /** Quality label: `'P'`, `'M'`, `'m'`, or repeated `'A'`/`'d'`. */
614
- get quality(): string;
615
- /** Signed semitone span. */
616
- get semitones(): number;
617
- /** A readable label composed of quality and number, e.g. `'P5'` or `'M3'`. */
618
- get name(): string;
619
- /**
620
- * The plain interval data, for JSON serialization.
621
- *
622
- * Private class fields do not serialize, so an explicit `toJSON` keeps
623
- * `JSON.stringify(interval)` from collapsing to `{}`.
624
- *
625
- * @returns The diatonic number, quality, and semitone span.
626
- */
627
- toJSON(): SpelledInterval;
986
+ toString(): string;
628
987
  }
629
988
 
630
- 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 };