@libraz/libcantus 0.9.4 → 1.0.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 (186) hide show
  1. package/README.md +61 -261
  2. package/README_ja.md +115 -0
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +213 -456
  6. package/dist/analyze/index.d.ts +213 -456
  7. package/dist/analyze/index.js +56 -9
  8. package/dist/chunk-3JXCW6DX.cjs +3220 -0
  9. package/dist/chunk-3JXCW6DX.cjs.map +1 -0
  10. package/dist/chunk-4A2RMUCC.js +7647 -0
  11. package/dist/chunk-4A2RMUCC.js.map +1 -0
  12. package/dist/chunk-6XPTBE7Y.cjs +49 -0
  13. package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
  14. package/dist/chunk-AFM2ULJH.cjs +5121 -0
  15. package/dist/chunk-AFM2ULJH.cjs.map +1 -0
  16. package/dist/chunk-E3F4AHAH.cjs +690 -0
  17. package/dist/chunk-E3F4AHAH.cjs.map +1 -0
  18. package/dist/chunk-ELCTZUJJ.js +379 -0
  19. package/dist/chunk-ELCTZUJJ.js.map +1 -0
  20. package/dist/chunk-FVSI36UV.js +690 -0
  21. package/dist/chunk-FVSI36UV.js.map +1 -0
  22. package/dist/chunk-GEDTJHAC.cjs +1 -0
  23. package/dist/chunk-GEDTJHAC.cjs.map +1 -0
  24. package/dist/chunk-GOZG57ER.js +3220 -0
  25. package/dist/chunk-GOZG57ER.js.map +1 -0
  26. package/dist/chunk-H3VORETI.js +2243 -0
  27. package/dist/chunk-H3VORETI.js.map +1 -0
  28. package/dist/chunk-H42UHPYW.js +1577 -0
  29. package/dist/chunk-H42UHPYW.js.map +1 -0
  30. package/dist/chunk-HFPS7JFD.js +1 -0
  31. package/dist/chunk-INHVXP6Z.js +3053 -0
  32. package/dist/chunk-INHVXP6Z.js.map +1 -0
  33. package/dist/chunk-K4ZJUGF4.cjs +379 -0
  34. package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
  35. package/dist/chunk-KNJEKLKV.js +49 -0
  36. package/dist/chunk-KNJEKLKV.js.map +1 -0
  37. package/dist/chunk-M4CWTJWJ.cjs +7647 -0
  38. package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
  39. package/dist/chunk-MXO72PLY.cjs +211 -0
  40. package/dist/chunk-MXO72PLY.cjs.map +1 -0
  41. package/dist/chunk-NLEU4QSE.cjs +360 -0
  42. package/dist/chunk-NLEU4QSE.cjs.map +1 -0
  43. package/dist/chunk-OE6WK2Y2.cjs +1577 -0
  44. package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
  45. package/dist/chunk-Q2WGKJNN.cjs +1064 -0
  46. package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
  47. package/dist/chunk-QYQQ5J6E.js +1 -0
  48. package/dist/chunk-QYQQ5J6E.js.map +1 -0
  49. package/dist/chunk-RHIJ3S4I.cjs +1 -0
  50. package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
  51. package/dist/chunk-TTCLJX6D.js +211 -0
  52. package/dist/chunk-TTCLJX6D.js.map +1 -0
  53. package/dist/chunk-UADYAYAT.cjs +3053 -0
  54. package/dist/chunk-UADYAYAT.cjs.map +1 -0
  55. package/dist/chunk-VOHZB45S.js +5121 -0
  56. package/dist/chunk-VOHZB45S.js.map +1 -0
  57. package/dist/chunk-VTI6FOLB.js +1064 -0
  58. package/dist/chunk-VTI6FOLB.js.map +1 -0
  59. package/dist/chunk-WWURE4XT.cjs +2243 -0
  60. package/dist/chunk-WWURE4XT.cjs.map +1 -0
  61. package/dist/chunk-YH3BIKHH.js +360 -0
  62. package/dist/chunk-YH3BIKHH.js.map +1 -0
  63. package/dist/core/index.cjs +103 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +96 -345
  66. package/dist/core/index.d.ts +96 -345
  67. package/dist/core/index.js +104 -7
  68. package/dist/generate/index.cjs +89 -10
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +437 -1095
  71. package/dist/generate/index.d.ts +437 -1095
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
  75. package/dist/index-B9MNZboQ.d.cts +770 -0
  76. package/dist/index-B9MNZboQ.d.ts +770 -0
  77. package/dist/index-BGFW-8P2.d.cts +2307 -0
  78. package/dist/index-BO6xE9YU.d.cts +1804 -0
  79. package/dist/index-BkGmlyI3.d.cts +470 -0
  80. package/dist/index-CCNJRG-P.d.cts +1095 -0
  81. package/dist/index-CGpRv5sl.d.cts +643 -0
  82. package/dist/index-C_8o--VE.d.cts +1242 -0
  83. package/dist/index-CgHqxdps.d.ts +2307 -0
  84. package/dist/index-Chbst3Jx.d.ts +1343 -0
  85. package/dist/index-Cj49F892.d.ts +470 -0
  86. package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
  87. package/dist/index-D57QRbVn.d.ts +1804 -0
  88. package/dist/index-DInQqT8Q.d.cts +66 -0
  89. package/dist/index-DInQqT8Q.d.ts +66 -0
  90. package/dist/index-Dzr13q3d.d.cts +1343 -0
  91. package/dist/index-G1uwGGWQ.d.ts +643 -0
  92. package/dist/index-szBv3496.d.ts +1242 -0
  93. package/dist/index.cjs +358 -15
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +17 -15
  96. package/dist/index.d.ts +17 -15
  97. package/dist/index.js +407 -64
  98. package/dist/masks-DYbE485J.d.cts +445 -0
  99. package/dist/masks-DYbE485J.d.ts +445 -0
  100. package/dist/model/index.cjs +39 -7
  101. package/dist/model/index.cjs.map +1 -1
  102. package/dist/model/index.d.cts +4685 -243
  103. package/dist/model/index.d.ts +4685 -243
  104. package/dist/model/index.js +40 -8
  105. package/dist/positional-Cptlj-xs.d.ts +287 -0
  106. package/dist/positional-DvH_sd8u.d.cts +287 -0
  107. package/dist/theory/index.cjs +101 -7
  108. package/dist/theory/index.cjs.map +1 -1
  109. package/dist/theory/index.d.cts +513 -173
  110. package/dist/theory/index.d.ts +513 -173
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +16 -11
  113. package/dist/chunk-36N73KZ3.cjs +0 -246
  114. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  115. package/dist/chunk-4HLLZPXT.cjs +0 -3077
  116. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  117. package/dist/chunk-7MGZ7D62.cjs +0 -498
  118. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  119. package/dist/chunk-7NKDPF74.js +0 -3077
  120. package/dist/chunk-7NKDPF74.js.map +0 -1
  121. package/dist/chunk-CAN76NCU.cjs +0 -679
  122. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  123. package/dist/chunk-D2FAJBYL.js +0 -479
  124. package/dist/chunk-D2FAJBYL.js.map +0 -1
  125. package/dist/chunk-EHVEBWM5.js +0 -1449
  126. package/dist/chunk-EHVEBWM5.js.map +0 -1
  127. package/dist/chunk-FCANXBAT.cjs +0 -30
  128. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  129. package/dist/chunk-FNDWXRRI.cjs +0 -479
  130. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  131. package/dist/chunk-GQSGS2N4.js +0 -173
  132. package/dist/chunk-GQSGS2N4.js.map +0 -1
  133. package/dist/chunk-HKGYCQ34.js +0 -53
  134. package/dist/chunk-HKGYCQ34.js.map +0 -1
  135. package/dist/chunk-I24MV65U.js +0 -106
  136. package/dist/chunk-I24MV65U.js.map +0 -1
  137. package/dist/chunk-KMHOICIY.js +0 -679
  138. package/dist/chunk-KMHOICIY.js.map +0 -1
  139. package/dist/chunk-LW53AHQU.cjs +0 -1117
  140. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  141. package/dist/chunk-N6IHQUOX.js +0 -1
  142. package/dist/chunk-NKIBQ6PT.js +0 -30
  143. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  144. package/dist/chunk-NP4NA2FB.cjs +0 -106
  145. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  146. package/dist/chunk-NVE34YGZ.cjs +0 -1449
  147. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  148. package/dist/chunk-NYYWMV2C.js +0 -327
  149. package/dist/chunk-NYYWMV2C.js.map +0 -1
  150. package/dist/chunk-Q2XUP227.js +0 -845
  151. package/dist/chunk-Q2XUP227.js.map +0 -1
  152. package/dist/chunk-Q6OP4U43.cjs +0 -327
  153. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  154. package/dist/chunk-RC6GBMCD.cjs +0 -845
  155. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  156. package/dist/chunk-RK35RIN7.js +0 -1117
  157. package/dist/chunk-RK35RIN7.js.map +0 -1
  158. package/dist/chunk-RUMU3UA6.cjs +0 -53
  159. package/dist/chunk-RUMU3UA6.cjs.map +0 -1
  160. package/dist/chunk-T64C7ZD7.cjs +0 -1
  161. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  162. package/dist/chunk-XEBG3G53.js +0 -498
  163. package/dist/chunk-XEBG3G53.js.map +0 -1
  164. package/dist/chunk-YLEMGEFH.js +0 -246
  165. package/dist/chunk-YLEMGEFH.js.map +0 -1
  166. package/dist/chunk-ZEJR4TKL.cjs +0 -173
  167. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  168. package/dist/function-BtiZb9i9.d.ts +0 -208
  169. package/dist/function-DNugAOrt.d.cts +0 -208
  170. package/dist/index-6VR8-kit.d.ts +0 -143
  171. package/dist/index-70p168VM.d.ts +0 -192
  172. package/dist/index-Be74mcon.d.cts +0 -152
  173. package/dist/index-BtXT6UQ0.d.cts +0 -192
  174. package/dist/index-C5UBbQ4G.d.ts +0 -152
  175. package/dist/index-C83mZI1P.d.ts +0 -242
  176. package/dist/index-DXd9JikN.d.cts +0 -246
  177. package/dist/index-DXd9JikN.d.ts +0 -246
  178. package/dist/index-OG65ocOr.d.cts +0 -272
  179. package/dist/index-OG65ocOr.d.ts +0 -272
  180. package/dist/index-tJhkmQgE.d.cts +0 -143
  181. package/dist/index-y_ak9_Hi.d.cts +0 -242
  182. package/dist/styled-DIRkk9Uz.d.ts +0 -506
  183. package/dist/styled-DJbkJQfj.d.cts +0 -506
  184. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  185. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  186. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -0,0 +1,1095 @@
1
+ import { f as Note, d as KeyScale, h as NoteLike, I as IntervalLike, S as SpelledInterval } from './index-B9MNZboQ.js';
2
+ import { r as ScaleName, u as WorldScaleName, s as ScaleNameInput } from './masks-DYbE485J.js';
3
+ import { c as ChordLike, b as Chord, K as KeyLike } from './index-szBv3496.js';
4
+
5
+ /**
6
+ * Which of the two modes a key signature is read in.
7
+ *
8
+ * A signature names two keys — three sharps is both A major and F# minor — so
9
+ * every entry point that turns a signature into a key, or that names the mode
10
+ * of a key relation, selects between them with this.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import type { KeyMode } from '@libraz/libcantus';
15
+ * const mode: KeyMode = 'minor';
16
+ * ```
17
+ * @category Scales
18
+ */
19
+ type KeyMode = 'major' | 'minor';
20
+ /**
21
+ * The key signature of a key, as a signed count of sharps (positive) or flats
22
+ * (negative) — the `fifths` value a notation format such as MusicXML writes.
23
+ *
24
+ * The tonic's letter and accidental place the key on the circle of fifths, and
25
+ * the mode moves it from there: lydian +1, major 0, mixolydian -1, dorian -2,
26
+ * natural minor -3, phrygian -4, locrian -5.
27
+ *
28
+ * A scale that is not a diatonic mode has no signature of its own, so it takes
29
+ * the signature of its parallel major or minor: a scale with a minor third and
30
+ * no major third counts as minor (-3), everything else as major (0). This is
31
+ * why A harmonic minor reports 0 — its G# is written as an accidental, not in
32
+ * the signature — and why A melodic minor reports 0 as well.
33
+ *
34
+ * The result is not clamped: a theoretical key such as Fb major reports -8.
35
+ * Clamp to ±7 if the consuming format cannot express more.
36
+ *
37
+ * @param tonic The spelled tonic, as a note name, note data, or a `Note`; its
38
+ * accidental is worth seven fifths.
39
+ * @param key The key/scale. This one stays a plain key/scale: it reads any
40
+ * 12-bit mask, including the tonic-less ones a key name cannot describe.
41
+ * @returns The signed number of sharps (positive) or flats (negative).
42
+ * @example
43
+ * ```ts
44
+ * import { keySignatureFifths, majorKey, minorKey, parseNote } from '@libraz/libcantus';
45
+ * keySignatureFifths(parseNote('Bb'), majorKey(10)); // -2
46
+ * keySignatureFifths('A', minorKey(9)); // 0
47
+ * ```
48
+ * @category Scales
49
+ */
50
+ declare function keySignatureFifths(tonic: NoteLike, key: KeyScale): number;
51
+ /**
52
+ * The key a signature denotes: the inverse of {@link keySignatureFifths} for
53
+ * the two modes a signature is conventionally read in.
54
+ *
55
+ * A signature names two keys — three sharps is both A major and F# minor — so
56
+ * the mode decides which one is built. The tonic is spelled the way that key is
57
+ * written, including the theoretical keys past ±7 (nine sharps is B# major).
58
+ *
59
+ * @param fifths The signed number of sharps (positive) or flats (negative).
60
+ * @param mode Which of the signature's two keys to build; defaults to major.
61
+ * @returns The spelled tonic and the matching key/scale.
62
+ * @throws If `fifths` is not an integer in [-12, 12], or `mode` is not one of
63
+ * `'major'` or `'minor'`.
64
+ * @example
65
+ * ```ts
66
+ * import { keyFromFifths, formatNote } from '@libraz/libcantus';
67
+ * formatNote(keyFromFifths(-2).tonic); // 'Bb'
68
+ * formatNote(keyFromFifths(-2, 'minor').tonic); // 'G'
69
+ * ```
70
+ * @category Scales
71
+ */
72
+ declare function keyFromFifths(fifths: number, mode?: KeyMode): {
73
+ tonic: Note;
74
+ key: KeyScale;
75
+ };
76
+
77
+ /**
78
+ * Scale systems: which pitch organisations functional harmony was built for,
79
+ * and which ones it would only be imposed on.
80
+ *
81
+ * Roman numerals, tonic/subdominant/dominant function and cadence detection all
82
+ * presuppose Western common practice. A `KeyScale` is a root pitch class and a
83
+ * twelve-bit mask, which says nothing about the tradition the scale came from,
84
+ * so analysis code that wants to branch instead of assuming needs this
85
+ * classification to branch on.
86
+ */
87
+
88
+ /**
89
+ * The kind of pitch organisation a scale belongs to.
90
+ *
91
+ * - `'common-practice'`: the scale functional harmony is defined on — a tonic,
92
+ * a dominant carrying a leading tone, and a triad available on every degree.
93
+ * - `'modal'`: a seven-note rotation or alteration of that material. Degrees
94
+ * and Roman numerals still describe it, but a dominant-to-tonic cadence is
95
+ * not what holds the music together.
96
+ * - `'non-functional'`: the collection carries no chord function of its own. A
97
+ * gapped or symmetric set takes its harmony from whatever key it is used
98
+ * over, and a scale from a tradition organised melodically rather than by
99
+ * chords has no functional reading at all.
100
+ *
101
+ * @category Scales
102
+ */
103
+ type ScaleSystem = 'common-practice' | 'modal' | 'non-functional';
104
+ /**
105
+ * The system each built-in scale belongs to.
106
+ *
107
+ * Every name in {@link NAMED_SCALES} and {@link WORLD_SCALES} appears here, so
108
+ * a scale added without a classification fails to compile rather than falling
109
+ * silently into a default. Aliases are classified through the name they resolve
110
+ * to; use {@link scaleSystemOf} to look one up.
111
+ *
112
+ * @category Scales
113
+ */
114
+ declare const SCALE_SYSTEMS: Readonly<Record<ScaleName | WorldScaleName, ScaleSystem>>;
115
+ /**
116
+ * The system a scale belongs to.
117
+ *
118
+ * A name — canonical or an alias — is answered exactly: `'nahawand'` is a maqam
119
+ * even though its mask is the natural minor's. A `KeyScale` carries no
120
+ * tradition, so it is answered by its mask, under the Western reading of that
121
+ * mask; a mask that is no built-in scale answers undefined.
122
+ *
123
+ * @param scale The scale name, or a key/scale to read the mask of.
124
+ * @returns The system, or undefined for a mask that names no built-in scale.
125
+ * @throws If `scale` is a string naming no built-in scale. An unknown name is a
126
+ * typo, and answering it the way an unrecognised mask is answered would hide
127
+ * that.
128
+ * @example
129
+ * ```ts
130
+ * import { scaleSystemOf, majorKey } from '@libraz/libcantus';
131
+ * scaleSystemOf('harmonicMinor'); // 'common-practice'
132
+ * scaleSystemOf('dorian'); // 'modal'
133
+ * scaleSystemOf('hijaz'); // 'non-functional'
134
+ * scaleSystemOf(majorKey(0)); // 'common-practice'
135
+ * ```
136
+ * @category Scales
137
+ */
138
+ declare function scaleSystemOf(scale: KeyScale | ScaleNameInput): ScaleSystem | undefined;
139
+ /**
140
+ * Whether functional (Roman-numeral) analysis applies to a scale.
141
+ *
142
+ * True for common-practice and modal scales, false for everything classed
143
+ * `'non-functional'` — analysis that would otherwise read a raga as a chord
144
+ * progression can ask here first.
145
+ *
146
+ * The two ways of naming a scale are answered differently, exactly as
147
+ * {@link scaleSystemOf} answers them. A name carries its tradition, so
148
+ * `'hijaz'` is false. A `KeyScale` carries only a mask, and a mask shared with
149
+ * a Western scale is read the Western way — maqam Hijaz and the phrygian
150
+ * dominant are the same seven pitch classes, so that mask is modal and answers
151
+ * true. A mask that names no built-in scale answers true as well, which keeps a
152
+ * caller's own mask being analysed the way it always has been. Pass the name
153
+ * whenever the tradition is known and the answer should follow it.
154
+ *
155
+ * @param scale The scale name, or a key/scale to read the mask of.
156
+ * @returns True when functional harmony describes the scale.
157
+ * @throws If `scale` is a string naming no built-in scale.
158
+ * @example
159
+ * ```ts
160
+ * import { supportsFunctionalHarmony, scaleByName } from '@libraz/libcantus';
161
+ * supportsFunctionalHarmony('harmonicMinor'); // true
162
+ * supportsFunctionalHarmony('bhairav'); // false
163
+ * supportsFunctionalHarmony('hijaz'); // false — named, so read as a maqam
164
+ * supportsFunctionalHarmony(scaleByName('hijaz', 0)); // true — its mask is the phrygian dominant
165
+ * supportsFunctionalHarmony(scaleByName('ryukyu', 0)); // false — a mask no Western scale shares
166
+ * ```
167
+ * @category Scales
168
+ */
169
+ declare function supportsFunctionalHarmony(scale: KeyScale | ScaleNameInput): boolean;
170
+
171
+ /**
172
+ * Test whether every chord pitch class is contained in a scale.
173
+ *
174
+ * @param chordPcs The chord's pitch classes (0..11).
175
+ * @param scaleMask The scale's 12-bit mode mask.
176
+ * @param scaleRootPc The pitch class the mask is rooted on.
177
+ * @returns True if the scale is a superset of the chord.
178
+ *
179
+ * @category Scales
180
+ */
181
+ declare function scaleMatchesChord(chordPcs: number[], scaleMask: number, scaleRootPc: number): boolean;
182
+ /**
183
+ * A named scale rooted on a pitch class that fits over a chord.
184
+ *
185
+ * @category Scales
186
+ */
187
+ type ChordScaleMatch = {
188
+ name: string;
189
+ rootPc: number;
190
+ };
191
+ /**
192
+ * List the scales that fit over a chord, best fit first.
193
+ *
194
+ * Only the chord root is considered as the scale root, matching the
195
+ * conventional chord-scale relationship. Every entry of {@link NAMED_SCALES}
196
+ * whose pitch-class set is a superset of the chord's is returned once per
197
+ * distinct pitch-class set (aliased masks such as major/ionian report only the
198
+ * modal name), ranked by fewest extra scale tones beyond the chord and then by
199
+ * conventional
200
+ * preference. For bare triads and smaller chords, heptatonic scales rank before
201
+ * pentatonics and other sizes: a pentatonic adds no modal color over a triad,
202
+ * so the seven-note modes are the more useful answer. The chromatic scale is
203
+ * only returned as a fallback when no other scale contains the chord.
204
+ *
205
+ * The ranking is by fit, so the first entry is the tightest-fitting scale
206
+ * rather than the idiomatic choice: a four-note chord that a pentatonic covers
207
+ * exactly puts that pentatonic ahead of the mode a player would name. Read the
208
+ * whole list, or pick by name, when the idiomatic scale is what is wanted;
209
+ * {@link scalesForChanges} makes that choice for a progression.
210
+ *
211
+ * @param chord The chord to fit scales over, as a chord symbol, chord data, or
212
+ * a `Chord`.
213
+ * @returns The matching scales rooted on the chord root, best fit first.
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * import { makeChord, chordScales } from '@libraz/libcantus';
218
+ * const scales = chordScales(makeChord(0, 'maj7'));
219
+ * // scales for Cmaj7, best fit first; each is { name, rootPc: 0 }
220
+ * ```
221
+ *
222
+ * @category Scales
223
+ */
224
+ declare function chordScales(chord: ChordLike): ChordScaleMatch[];
225
+ /**
226
+ * Which use an avoid note is judged for: sounding it, or passing through it.
227
+ *
228
+ * `'harmonic'` is the Berklee-style rule in full — a tone that must not be
229
+ * sounded against the chord. `'melodic'` keeps only what a line cannot pass
230
+ * through either, which is the semitone above the root; every other clash
231
+ * resolves as the line moves on.
232
+ *
233
+ * @category Scales
234
+ */
235
+ type AvoidNoteUse = 'harmonic' | 'melodic';
236
+ /**
237
+ * Options for {@link avoidNotes}.
238
+ *
239
+ * @category Scales
240
+ */
241
+ type AvoidNotesOptions = {
242
+ use?: AvoidNoteUse;
243
+ };
244
+ /**
245
+ * List a scale's avoid notes over a chord.
246
+ *
247
+ * An avoid note is a non-chord scale tone that lies a semitone directly above a
248
+ * chord tone, or the third a suspension displaced; sounding it against the
249
+ * chord clashes. The scale is rooted on the chord root. If the scale does not
250
+ * contain the chord, no avoid notes exist.
251
+ *
252
+ * The rule states which tones must not be *sounded* against the chord, which is
253
+ * not the same question as which tones a line may not touch. Pass
254
+ * `{ use: 'melodic' }` for the melodic reading: only the semitone above the
255
+ * root survives it, so the result is always a subset of the harmonic one.
256
+ *
257
+ * @param chord The chord providing the chord tones, as a chord symbol, chord
258
+ * data, or a `Chord`.
259
+ * @param scaleName A key of {@link NAMED_SCALES}.
260
+ * @param opts Set `use: 'melodic'` to judge a line rather than a voicing.
261
+ * @returns The avoid-note pitch classes, sorted ascending in [0, 11].
262
+ * @throws If `scaleName` is not a built-in scale. An empty result already
263
+ * means "this scale has no avoid notes over this chord"; answering a typo
264
+ * the same way would make the two indistinguishable.
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * import { makeChord, avoidNotes } from '@libraz/libcantus';
269
+ * avoidNotes(makeChord(0, 'maj7'), 'ionian'); // [5] — F clashes with the third
270
+ * avoidNotes(makeChord(0, 'maj7'), 'ionian', { use: 'melodic' }); // [] — a line may pass through it
271
+ * ```
272
+ *
273
+ * @category Scales
274
+ */
275
+ declare function avoidNotes(chord: ChordLike, scaleName: ScaleNameInput, opts?: AvoidNotesOptions): number[];
276
+ /**
277
+ * Options for {@link availableTensions}.
278
+ *
279
+ * @category Scales
280
+ */
281
+ type AvailableTensionsOptions = {
282
+ /**
283
+ * The chord this one resolves to, as a chord symbol, chord data, or a
284
+ * `Chord`.
285
+ */
286
+ resolvesTo?: ChordLike;
287
+ };
288
+ /**
289
+ * List a scale's available tensions over a chord.
290
+ *
291
+ * Available tensions are scale tones that are neither chord tones nor avoid
292
+ * notes, i.e. the usable color tones (typically the 9/11/13 region). The scale
293
+ * is rooted on the chord root. If the scale does not contain the chord, there
294
+ * are no available tensions.
295
+ *
296
+ * A tension's availability also depends on what the chord is doing. Pass
297
+ * `resolvesTo` to say which chord this one resolves to: a dominant seventh
298
+ * resolving down a fifth to a minor tonic takes its flat ninth, sharp ninth and
299
+ * flat thirteenth, which read on their own would each be an avoid note. Only
300
+ * scale tones are ever added, so the result stays within the scale.
301
+ *
302
+ * @param chord The chord providing the chord tones, as a chord symbol, chord
303
+ * data, or a `Chord`.
304
+ * @param scaleName A key of {@link NAMED_SCALES}.
305
+ * @param opts Set `resolvesTo` to the chord this one resolves to.
306
+ * @returns The available-tension pitch classes, sorted ascending in [0, 11].
307
+ * @throws If `scaleName` is not a built-in scale, as {@link avoidNotes} does.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * import { makeChord, availableTensions } from '@libraz/libcantus';
312
+ * availableTensions(makeChord(0, 'maj7'), 'ionian'); // [2, 9] — color tones over Cmaj7
313
+ * availableTensions(makeChord(7, 'dom7'), 'phrygianDominant', { resolvesTo: makeChord(0, 'min') });
314
+ * // [3, 8] — the b13 and b9 the resolution to C minor makes available
315
+ * ```
316
+ *
317
+ * @category Scales
318
+ */
319
+ declare function availableTensions(chord: ChordLike, scaleName: ScaleNameInput, opts?: AvailableTensionsOptions): number[];
320
+ /**
321
+ * A scale fit over a chord, with its non-chord tones sorted by what may be done
322
+ * with them.
323
+ *
324
+ * `avoid`, `passing` and `tensions` partition the scale tones the chord does
325
+ * not state: `avoid` may not be played at all, `passing` may be passed through
326
+ * melodically but not sounded against the chord, and `tensions` may be added
327
+ * freely as color. `avoid` is {@link avoidNotes} under `use: 'melodic'`, and
328
+ * `avoid` together with `passing` is the harmonic reading.
329
+ *
330
+ * @category Scales
331
+ */
332
+ type ChordScaleReportEntry = {
333
+ name: string;
334
+ rootPc: number;
335
+ avoid: number[];
336
+ passing: number[];
337
+ tensions: number[];
338
+ };
339
+ /**
340
+ * Report the best-fitting scales for a chord with their avoid notes and tensions.
341
+ *
342
+ * Combines {@link chordScales}, {@link avoidNotes}, and {@link availableTensions}
343
+ * into a single ergonomic result, ordered best fit first. The report is read as
344
+ * playing advice, so the tones a line may pass through are reported apart from
345
+ * the ones it may not touch; see {@link ChordScaleReportEntry}.
346
+ *
347
+ * @param chord The chord to analyze, as a chord symbol, chord data, or a
348
+ * `Chord`.
349
+ * @param limit Optional maximum number of scales to report; all by default.
350
+ * @returns One entry per reported scale, best fit first.
351
+ *
352
+ * @category Scales
353
+ */
354
+ declare function chordScaleReport(chord: ChordLike, limit?: number): ChordScaleReportEntry[];
355
+ /**
356
+ * A chord paired with the scale chosen for it by {@link scalesForChanges}.
357
+ *
358
+ * @category Scales
359
+ */
360
+ type ScaleChoice = {
361
+ chord: Chord;
362
+ scale: ChordScaleMatch;
363
+ };
364
+ /**
365
+ * Choose one scale per chord across a progression, favoring continuity.
366
+ *
367
+ * Each chord's {@link chordScales} candidates form a stage in a Viterbi-style
368
+ * dynamic program. The transition cost between adjacent choices is the number
369
+ * of pitch classes that differ between their pitch-class sets (the symmetric
370
+ * difference), plus a small penalty for straying from a chord's best-fit scale
371
+ * so that ties break toward the tighter fit. The minimum-total-cost path is
372
+ * returned, one {@link ScaleChoice} per input chord in the original order.
373
+ *
374
+ * Each choice pairs its scale with the chord exactly as it was handed in, so
375
+ * this takes plain chord data rather than the wider forms the entry points
376
+ * above accept.
377
+ *
378
+ * @param chords The chord sequence to choose scales for.
379
+ * @returns One scale choice per chord, in input order.
380
+ *
381
+ * @category Scales
382
+ */
383
+ declare function scalesForChanges(chords: readonly Chord[]): ScaleChoice[];
384
+
385
+ /**
386
+ * Voice independence as a measurement rather than a verdict.
387
+ *
388
+ * The species rules answer "is this allowed", which is the wrong question of a
389
+ * pop arrangement: a harmony line in parallel thirds, a power-chord riff and a
390
+ * pedal point all break them and all are the intended sound. This module
391
+ * reports how independent two lines actually are and leaves the judgement to
392
+ * the caller.
393
+ */
394
+
395
+ /**
396
+ * How two lines move against each other, and how far apart they sit.
397
+ *
398
+ * @category Voicing & Counterpoint
399
+ */
400
+ type VoiceIndependenceReport = {
401
+ /**
402
+ * Share of the moves that fall into each motion type, summing to 1. A slot
403
+ * pair in which neither voice moves is not a move and is left out entirely,
404
+ * so an accompaniment that mostly sits still can still report mostly contrary
405
+ * motion. All four are 0 when nothing moves.
406
+ */
407
+ motion: {
408
+ /** The voices move in opposite directions. */
409
+ contrary: number;
410
+ /** One voice holds while the other moves. */
411
+ oblique: number;
412
+ /** Both rise or both fall, and the interval between them changes. */
413
+ similar: number;
414
+ /** Both rise or both fall, keeping the same interval. */
415
+ parallel: number;
416
+ };
417
+ /**
418
+ * Share of the slots where the lead holds or rests on which the counter
419
+ * attacks: 1 when the counter fills every gap the lead leaves, 0 when it moves
420
+ * only where the lead does. It is 0 when the lead never holds or rests, since
421
+ * there was nothing to complement.
422
+ */
423
+ rhythmicComplementarity: number;
424
+ /** Distance between the two lines, in semitones, over the slots where both sound. */
425
+ separation: {
426
+ /** Mean distance; 0 when the two lines never sound together. */
427
+ mean: number;
428
+ /** Closest approach; 0 when the two lines never sound together. */
429
+ min: number;
430
+ };
431
+ /**
432
+ * Slots at which the two lines sit in the opposite order to the one they keep
433
+ * overall. A unison is not a crossing.
434
+ */
435
+ crossings: number;
436
+ /** Longest run of consecutive slots sounding a perfect consonance. */
437
+ longestPerfectRun: number;
438
+ /** Slots at which both lines sound, the denominator of `separation`. */
439
+ sounding: number;
440
+ };
441
+ /**
442
+ * Options controlling {@link voiceIndependence}.
443
+ *
444
+ * @category Voicing & Counterpoint
445
+ */
446
+ type VoiceIndependenceOptions = {
447
+ /**
448
+ * Which slots are fresh attacks in the lead. Without it a slot counts as an
449
+ * attack when its note differs from the previous slot's, which reads a
450
+ * deliberately repeated note as a sustain.
451
+ */
452
+ leadAttacks?: readonly boolean[];
453
+ /** Which slots are fresh attacks in the counter; read as `leadAttacks` is. */
454
+ counterAttacks?: readonly boolean[];
455
+ /**
456
+ * Count the perfect fourth among the perfect consonances when measuring
457
+ * `longestPerfectRun`. Useful for quartal writing, where the fourth is the
458
+ * interval being sustained.
459
+ *
460
+ * @defaultValue false
461
+ */
462
+ countFourths?: boolean;
463
+ };
464
+ /**
465
+ * Measure how independent a counter line is from the lead it accompanies.
466
+ *
467
+ * Both lines are index-aligned samples: one slot per beat (or whatever grid the
468
+ * caller works on), `null` for a rest, and a repeated note for a sustain. That
469
+ * is what makes the rhythmic reading possible without a duration model, and
470
+ * what makes a re-attacked repeated note look like a sustain unless
471
+ * `leadAttacks` / `counterAttacks` say otherwise. Notes must carry octaves,
472
+ * since separation and motion are measured between sounding pitches.
473
+ *
474
+ * Nothing here is a verdict. Parallel thirds report as parallel motion with a
475
+ * small separation, a pedal point as oblique motion, a power-chord line as a
476
+ * long perfect run — all facts about the texture, not faults in it.
477
+ *
478
+ * @param lead The leading line, one entry per slot.
479
+ * @param counter The accompanying line, aligned slot for slot with `lead`.
480
+ * @param opts Attack flags and the treatment of the fourth.
481
+ * @returns The motion breakdown, rhythmic complementarity, separation,
482
+ * crossings, and the longest perfect-consonance run.
483
+ * @throws If the two lines differ in length, if an attack array is supplied at
484
+ * another length than the lines, or if a note carries no octave.
485
+ * @example
486
+ * ```ts
487
+ * import { parseNote, voiceIndependence } from '@libraz/libcantus';
488
+ * const lead = ['C5', 'D5', 'E5'].map((n) => parseNote(n));
489
+ * const counter = ['E4', 'F4', 'G4'].map((n) => parseNote(n));
490
+ * voiceIndependence(lead, counter).motion.parallel; // 1 — a harmony line in sixths
491
+ * ```
492
+ * @category Voicing & Counterpoint
493
+ */
494
+ declare function voiceIndependence(lead: readonly (Note | null)[], counter: readonly (Note | null)[], opts?: VoiceIndependenceOptions): VoiceIndependenceReport;
495
+
496
+ /**
497
+ * An inclusive MIDI pitch range for a single voice.
498
+ *
499
+ * @category Voicing & Counterpoint
500
+ */
501
+ type VoiceRange = {
502
+ readonly min: number;
503
+ readonly max: number;
504
+ };
505
+ /**
506
+ * Default four-voice SATB ranges, ascending (index 0 = lowest):
507
+ * bass E2–C4 (40–60), tenor C3–G4 (48–67), alto G3–D5 (55–74),
508
+ * soprano C4–G5 (60–79).
509
+ *
510
+ * @category Voicing & Counterpoint
511
+ */
512
+ declare const SATB_RANGES: readonly Readonly<VoiceRange>[];
513
+ /**
514
+ * Options controlling {@link voiceChord} and {@link voiceProgression}.
515
+ *
516
+ * @category Voicing & Counterpoint
517
+ */
518
+ type VoicingOptions = {
519
+ /**
520
+ * Number of voices to realize. Ignored when `ranges` is given.
521
+ *
522
+ * @defaultValue 4
523
+ */
524
+ voices?: number;
525
+ /** Explicit per-voice ranges, ascending (index 0 = lowest). Takes precedence over `voices`. */
526
+ ranges?: VoiceRange[];
527
+ /**
528
+ * Maximum spacing in semitones between adjacent upper voices.
529
+ *
530
+ * @defaultValue 12
531
+ */
532
+ maxSpacing?: number;
533
+ /**
534
+ * Maximum candidate voicings evaluated for one chord.
535
+ *
536
+ * Raise this for large voicings when exact optimum matters more than bounded
537
+ * search time.
538
+ *
539
+ * @defaultValue 4000
540
+ */
541
+ maxCandidates?: number;
542
+ /**
543
+ * Maximum number of chords {@link voiceProgression} will voice. The search
544
+ * per chord is bounded internally, so the cost of a progression is linear in
545
+ * its length; this is the guard against an unbounded caller, not a limit on
546
+ * the search.
547
+ *
548
+ * @defaultValue 1000000
549
+ */
550
+ budget?: number;
551
+ /**
552
+ * The prevailing key, as a key name, a key/scale, or a `Key`. Supplying it
553
+ * enables the rules that only make sense relative to a tonic: the leading
554
+ * tone is neither doubled nor left unresolved. Without it, voicings are
555
+ * chosen from chord structure and voice-leading distance alone.
556
+ */
557
+ key?: KeyLike;
558
+ /**
559
+ * The chord that produced the current voicing passed to {@link nextVoicing},
560
+ * as a chord symbol, chord data, or a `Chord`. When supplied, chordal-seventh
561
+ * resolution is scored exactly as it is by {@link voiceProgression}.
562
+ */
563
+ previousChord?: ChordLike;
564
+ };
565
+ /**
566
+ * Realize a single chord as one MIDI pitch per voice, ascending (index 0 =
567
+ * lowest). The bass voice takes the chord's `bassPc` when set, otherwise the
568
+ * root; upper voices take chord pitch classes, doubling the root or fifth as
569
+ * needed to fill all voices. The result stays inside each voice's range, keeps
570
+ * adjacent upper voices within `maxSpacing`, avoids voice crossing, and is
571
+ * deterministic: a compact close-position voicing centered in the ranges.
572
+ *
573
+ * @param chord The chord to voice.
574
+ * @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
575
+ * @returns MIDI pitches, ascending, one per voice.
576
+ * @throws If no voicing fits the given ranges.
577
+ * @example
578
+ * ```ts
579
+ * import { parseChordSymbol, voiceChord } from '@libraz/libcantus';
580
+ * const chord = parseChordSymbol('Cmaj7');
581
+ * voiceChord(chord); // four ascending MIDI pitches within the SATB ranges
582
+ * ```
583
+ * @category Voicing & Counterpoint
584
+ */
585
+ declare function voiceChord(chord: ChordLike, opts?: VoicingOptions): number[];
586
+ /**
587
+ * Voice a chord progression with smooth, bounded SATB-style leading.
588
+ *
589
+ * Each chord is chosen from a deterministic candidate set, minimizing motion
590
+ * while penalizing counterpoint violations. A supplied key additionally
591
+ * resolves leading tones and chordal sevenths.
592
+ *
593
+ * @param chords The chords to voice in order.
594
+ * @param opts Voicing options; defaults to four voices in {@link SATB_RANGES}.
595
+ * @returns One ascending MIDI voicing per chord.
596
+ * @throws {@link NoSolutionError} with the failing chord index when a chord
597
+ * cannot fit the requested ranges.
598
+ * @category Voicing & Counterpoint
599
+ */
600
+ declare function voiceProgression(chords: readonly ChordLike[], opts?: VoicingOptions): number[][];
601
+
602
+ /**
603
+ * A tertian voicing style for {@link voiceChordStyled}.
604
+ *
605
+ * - `close`: the plain close-position tertian stack.
606
+ * - `drop2`: the second voice from the top dropped an octave (drop-2 voicing).
607
+ * - `drop3`: the third voice from the top dropped an octave (drop-3 voicing).
608
+ * - `shell`: root plus guide tones (third and seventh) for seventh chords, or
609
+ * root/third/fifth for triads; the fifth and tensions are omitted.
610
+ * - `rootless`: the root omitted, keeping third/fifth/seventh and tensions
611
+ * (a typical left-hand jazz voicing).
612
+ *
613
+ * @category Voicing & Counterpoint
614
+ */
615
+ type VoicingStyle = 'close' | 'drop2' | 'drop3' | 'shell' | 'rootless';
616
+ /**
617
+ * Options controlling {@link voiceChordStyled}.
618
+ *
619
+ * @category Voicing & Counterpoint
620
+ */
621
+ type StyledVoicingOptions = {
622
+ /**
623
+ * Voicing style to build.
624
+ *
625
+ * @defaultValue 'close'
626
+ */
627
+ style?: VoicingStyle;
628
+ /** Constrain the highest voice to this pitch class (0..11) when given. */
629
+ topNote?: number;
630
+ /**
631
+ * Base octave in scientific pitch notation: octave 4 starts the stack near
632
+ * middle C (MIDI 60), i.e. near `12 * (octave + 1)`.
633
+ *
634
+ * @defaultValue 4
635
+ */
636
+ octave?: number;
637
+ /**
638
+ * Omit the root regardless of style (relevant for jazz voicings).
639
+ *
640
+ * @defaultValue false
641
+ */
642
+ rootless?: boolean;
643
+ };
644
+ /**
645
+ * Realize a single chord as an explicit tertian voicing in a chosen style,
646
+ * independent of the SATB range search used by {@link voiceChord}.
647
+ *
648
+ * The chord tones are stacked in close position from the bass (`bassPc`, else
649
+ * `rootPc`) in the given scientific-pitch `octave` upward (octave 4 starts the
650
+ * bass near C4 = MIDI 60, matching {@link midiToNote} and the bass module).
651
+ * When `topNote` is given the stack is rotated so the highest voice's pitch
652
+ * class equals that note (or the nearest chord tone). The chosen
653
+ * {@link VoicingStyle} then transforms the stack, and the result is returned as
654
+ * ascending MIDI pitches (index 0 = lowest).
655
+ *
656
+ * Every returned pitch is a valid MIDI number: an octave whose stack would run
657
+ * off either end of the 0..127 range is rejected rather than voiced out of
658
+ * range, matching {@link nextVoicing}, which clamps its derived ranges.
659
+ *
660
+ * @param chord The chord to voice.
661
+ * @param opts Styled voicing options; defaults to a close voicing at octave 4.
662
+ * @returns MIDI pitches, ascending, one per retained voice.
663
+ * @throws If the voicing would not fit inside MIDI 0..127 at the given octave.
664
+ * @example
665
+ * ```ts
666
+ * import { parseChordSymbol, voiceChordStyled } from '@libraz/libcantus';
667
+ * const chord = parseChordSymbol('Dm7');
668
+ * voiceChordStyled(chord, { style: 'drop2' }); // ascending MIDI pitches, drop-2 voicing
669
+ * ```
670
+ * @category Voicing & Counterpoint
671
+ */
672
+ declare function voiceChordStyled(chord: Chord, opts?: StyledVoicingOptions): number[];
673
+
674
+ /**
675
+ * Species counterpoint: mark a two-voice exercise against the sixteenth-century
676
+ * rules.
677
+ *
678
+ * This is the conservatory exercise, not a general style checker. It grades a
679
+ * counterpoint written above or below a given cantus firmus in one of the five
680
+ * species, and reports what it finds as the same {@link PartWritingViolation}
681
+ * the four-part checker uses.
682
+ *
683
+ * Both voices arrive as spelled notes, because half of what the rules forbid is
684
+ * invisible in a pitch: the augmented second of the harmonic minor is a minor
685
+ * third by ear, and the diminished fourth that may not stand as a consonance is
686
+ * a major third by ear.
687
+ */
688
+
689
+ /**
690
+ * Which species an exercise is written in: note against note, two, three or
691
+ * four notes against one, suspensions, or the florid mixture of them all.
692
+ *
693
+ * @category Voicing & Counterpoint
694
+ */
695
+ type Species = 1 | 2 | 3 | 4 | 5;
696
+ /**
697
+ * Options controlling {@link checkSpecies}.
698
+ *
699
+ * @category Voicing & Counterpoint
700
+ */
701
+ type SpeciesOptions = {
702
+ /**
703
+ * Length of each counterpoint note, measured in cantus-firmus notes: 1 is a
704
+ * whole note filling a measure, 0.5 a half, 0.25 a quarter.
705
+ *
706
+ * The first four species have a fixed ratio, so the durations are implied by
707
+ * the note count and this is only needed to describe something unusual. The
708
+ * fifth species mixes note values by definition and cannot be read without it.
709
+ */
710
+ durations?: readonly number[];
711
+ /**
712
+ * Which side of the cantus firmus the counterpoint is written on. Defaults to
713
+ * whichever side it actually lies on, by mean pitch.
714
+ */
715
+ counterpointAbove?: boolean;
716
+ };
717
+ /**
718
+ * Check a species counterpoint exercise and report every rule it breaks.
719
+ *
720
+ * The cantus firmus is one note per measure. The counterpoint is aligned to it
721
+ * by position rather than by written rhythm: the first four species have a fixed
722
+ * number of notes per measure, so the note count alone says where each note
723
+ * falls, and a closing measure written as a single whole note is accepted as the
724
+ * convention it is. The fifth species mixes note values and therefore needs
725
+ * `opts.durations`, given in cantus-firmus notes.
726
+ *
727
+ * Judged are the vertical intervals under each species' dissonance licence
728
+ * (none in the first, a passing dissonance on the weak half in the second and
729
+ * third, a prepared suspension on the downbeat in the fourth, both in the
730
+ * fifth; the third and fifth species also write the *nota cambiata* and the
731
+ * double neighbour, which quit a dissonance by leap), parallel and hidden
732
+ * perfects and the *battuta* between the voices, augmented and otherwise
733
+ * forbidden leaps along the counterpoint, the shape of the counterpoint as a
734
+ * line, and the opening and closing formulas. Violations name the cantus firmus
735
+ * as voice 0 and the counterpoint as voice 1, and index into the counterpoint.
736
+ *
737
+ * @param cantusFirmus The given voice, one spelled note per measure.
738
+ * @param counterpoint The written voice, in spelled notes.
739
+ * @param species Which species the exercise is written in.
740
+ * @param mode The mode the exercise is in.
741
+ * @param opts Note lengths, and which side the counterpoint is written on.
742
+ * @returns Every violation found, in the order the exercise commits them; an
743
+ * empty array for a clean exercise.
744
+ * @throws If the species is not one of the five, either voice is empty, a note
745
+ * carries no octave, or the fifth species is given without durations.
746
+ * @example
747
+ * ```ts
748
+ * import { checkSpecies, majorKey, parseNote } from '@libraz/libcantus';
749
+ * const cf = ['C4', 'D4', 'E4', 'D4', 'C4'].map((n) => parseNote(n));
750
+ * const cp = ['C5', 'A4', 'G4', 'B4', 'C5'].map((n) => parseNote(n));
751
+ * checkSpecies(cf, cp, 1, majorKey(0)); // [] — a clean first-species exercise
752
+ * ```
753
+ * @category Voicing & Counterpoint
754
+ */
755
+ declare function checkSpecies(cantusFirmus: readonly Note[], counterpoint: readonly Note[], species: Species, mode: KeyScale, opts?: SpeciesOptions): PartWritingViolation[];
756
+
757
+ /**
758
+ * Part writing: grade a finished exercise against the classical four-part rules.
759
+ *
760
+ * The counterpoint predicates each judge one pair of voices at one moment; this
761
+ * module walks a whole progression and reports every violation it finds, so a
762
+ * completed exercise can be marked in a single call.
763
+ *
764
+ * The input is spelled notes rather than MIDI pitches because two of the rules
765
+ * cannot be decided without them: a cross relation is one letter carrying two
766
+ * different accidentals, and an augmented second is a minor third under another
767
+ * name. A caller holding only pitches spells them with {@link spellVoicing}.
768
+ */
769
+
770
+ /**
771
+ * The rule a {@link PartWritingViolation} breaks.
772
+ *
773
+ * - `parallelFifth`, `parallelOctave`: consecutive perfect intervals of the same
774
+ * kind between one pair of voices. A parallel unison is reported as a
775
+ * `parallelOctave`, since it is the same perfect class.
776
+ * - `hiddenPerfect`: the outer voices reach a perfect fifth or octave by similar
777
+ * motion with a leap in the upper voice.
778
+ * - `crossRelation`: one letter carries two different accidentals in two
779
+ * different voices across a chord change, exposed — neither note led into by
780
+ * step, at least one of them in an outer voice, and neither chord one of the
781
+ * chromatic harmonies that are written with the contradiction.
782
+ * - `voiceCrossing`, `overlap`, `spacing`, `range`: the texture rules.
783
+ * - `unresolvedLeadingTone`, `unresolvedSeventh`: a tendency tone that does not
784
+ * go where it must.
785
+ * - `augmentedMelodicInterval`: a single voice moves by an augmented interval.
786
+ *
787
+ * The rest are raised by {@link checkSpecies} alone, since they describe faults
788
+ * only a species exercise can commit:
789
+ *
790
+ * - `wrongRhythmicRatio`: the counterpoint does not present the number of notes
791
+ * per cantus-firmus note its species requires.
792
+ * - `unpreparedDissonance`: a dissonance falls where the species allows none, or
793
+ * is reached or left by leap where it must be passed through by step.
794
+ * - `unresolvedSuspension`: a fourth-species suspension that does not fall by
795
+ * step onto the following consonance.
796
+ * - `illegalLeap`: a melodic leap the style forbids — either seventh, the
797
+ * tritone, or anything wider than an octave.
798
+ * - `battuta`: an octave or unison reached by contrary motion with the upper
799
+ * voice leaping down.
800
+ * - `missingCadence`: the exercise does not open or close on the formula its
801
+ * species requires.
802
+ * - `melodicShape`: the counterpoint is a poor line — it has no single climax,
803
+ * leaps too often or too far one way, leaves a wide leap unanswered, outlines
804
+ * a tritone, or repeats a note where the species keeps moving. The rationale
805
+ * says which.
806
+ *
807
+ * @category Voicing & Counterpoint
808
+ */
809
+ type PartWritingViolationKind = 'parallelFifth' | 'parallelOctave' | 'hiddenPerfect' | 'crossRelation' | 'voiceCrossing' | 'overlap' | 'spacing' | 'range' | 'unresolvedLeadingTone' | 'unresolvedSeventh' | 'augmentedMelodicInterval' | 'wrongRhythmicRatio' | 'unpreparedDissonance' | 'unresolvedSuspension' | 'illegalLeap' | 'battuta' | 'missingCadence' | 'melodicShape';
810
+ /**
811
+ * One broken rule, located in the progression and in the texture.
812
+ *
813
+ * @category Voicing & Counterpoint
814
+ */
815
+ type PartWritingViolation = {
816
+ /** The rule that was broken. */
817
+ kind: PartWritingViolationKind;
818
+ /**
819
+ * The voices involved, by index into a voicing (0 = lowest). A rule about a
820
+ * pair names both, ascending; a rule about a single line names one. A
821
+ * `crossRelation` is the exception: it names the voice holding the earlier
822
+ * note first, whichever is higher. In a species exercise there are only two
823
+ * voices and they are named by role instead: 0 is the cantus firmus and 1 the
824
+ * counterpoint, whichever lies above.
825
+ */
826
+ voices: number[];
827
+ /**
828
+ * Index of the chord the motion starts on; in a species exercise, the index
829
+ * into the counterpoint.
830
+ */
831
+ fromIndex: number;
832
+ /** Index of the chord it ends on; equal to `fromIndex` for a vertical rule. */
833
+ toIndex: number;
834
+ /** A short sentence naming the musical reason. */
835
+ rationale: string;
836
+ };
837
+ /**
838
+ * A chord realized as one spelled note per voice, ascending (index 0 = lowest),
839
+ * matching the voice order {@link voiceChord} and {@link SATB_RANGES} use.
840
+ *
841
+ * Every note must carry an octave: the rules measure real distances between
842
+ * sounding pitches, which a bare pitch class does not have.
843
+ *
844
+ * @category Voicing & Counterpoint
845
+ */
846
+ type SpelledVoicing = Note[];
847
+ /**
848
+ * Options controlling {@link checkPartWriting}.
849
+ *
850
+ * @category Voicing & Counterpoint
851
+ */
852
+ type PartWritingOptions = {
853
+ /**
854
+ * Per-voice ranges, ascending (index 0 = lowest), against which the `range`
855
+ * rule is judged. Defaults to {@link SATB_RANGES} for a four-voice exercise;
856
+ * for any other voice count there is no conventional compass to assume, so
857
+ * the rule is skipped unless ranges are given. Given ranges must cover every
858
+ * voice of the widest chord, and each must be finite with `min` no greater
859
+ * than `max`.
860
+ */
861
+ ranges?: VoiceRange[];
862
+ /**
863
+ * Maximum spacing in semitones between adjacent upper voices. The bass-tenor
864
+ * pair is exempt, as convention has it. Must be finite and non-negative.
865
+ *
866
+ * @defaultValue 12
867
+ */
868
+ maxSpacing?: number;
869
+ };
870
+ /**
871
+ * Spell a voicing given as MIDI pitches, so it can be checked by
872
+ * {@link checkPartWriting}.
873
+ *
874
+ * Each pitch is named against the chord sounding under it and then the key, so a
875
+ * chord tone takes the letter its interval above the root implies: the third of
876
+ * a D major chord in C major spells F#, not Gb. The octave is kept, since the
877
+ * rules measure real distances.
878
+ *
879
+ * @param voicing MIDI pitches, ascending (index 0 = lowest).
880
+ * @param chord The chord sounding, supplying the enharmonic evidence.
881
+ * @param key The key the exercise is written in.
882
+ * @returns One spelled note per pitch, in the same order.
883
+ * @example
884
+ * ```ts
885
+ * import { majorKey, makeChord, noteNames, spellVoicing } from '@libraz/libcantus';
886
+ * noteNames(spellVoicing([50, 57, 66, 69], makeChord(2, 'maj'), majorKey(0)));
887
+ * // ['D3', 'A3', 'F#4', 'A4'] — the third of D spells F# in C major
888
+ * ```
889
+ * @category Voicing & Counterpoint
890
+ */
891
+ declare function spellVoicing(voicing: readonly number[], chord: Chord, key: KeyScale): SpelledVoicing;
892
+ /**
893
+ * Check a finished part-writing exercise and report every rule it breaks.
894
+ *
895
+ * The voicings are given one per chord, each ascending with one spelled note per
896
+ * voice (index 0 = lowest, the bass), the voice order {@link voiceChord} and
897
+ * {@link SATB_RANGES} already use. Notes must carry octaves.
898
+ *
899
+ * Each chord is checked for voice crossing, over-wide spacing between adjacent
900
+ * upper voices, and voices outside their range; each pair of consecutive chords
901
+ * for parallel and hidden perfects, voice overlap, cross relations, augmented
902
+ * melodic intervals, and unresolved tendency tones. Violations come back in
903
+ * musical order: the rules inside a chord, then the rules taking it to the next.
904
+ *
905
+ * @param voicings One voicing per chord, ascending, in spelled notes.
906
+ * @param chords The chords those voicings realize, in the same order.
907
+ * @param key The key the exercise is written in; it names the leading tone.
908
+ * @param opts Ranges and the upper-voice spacing limit. Both are validated
909
+ * before any rule runs: an empty result has to mean that nothing was broken,
910
+ * never that a rule could not be evaluated.
911
+ * @returns Every violation found, in musical order; an empty array for a clean
912
+ * exercise.
913
+ * @throws If the two arrays differ in length, a note carries no octave, the
914
+ * spacing limit is not a non-negative finite number, or the ranges are empty,
915
+ * malformed, or too few for the voices being checked.
916
+ * @example
917
+ * ```ts
918
+ * import { checkPartWriting, majorKey, makeChord, spellVoicing } from '@libraz/libcantus';
919
+ * const key = majorKey(0);
920
+ * const chords = [makeChord(0, 'maj'), makeChord(2, 'min')];
921
+ * const voicings = [[48, 55, 64, 72], [50, 57, 65, 69]].map((pitches, i) =>
922
+ * spellVoicing(pitches, chords[i] ?? chords[0], key),
923
+ * );
924
+ * const violations = checkPartWriting(voicings, chords, key);
925
+ * violations.map((violation) => violation.kind); // ['parallelFifth']
926
+ * violations[0]?.voices; // [0, 1]
927
+ * ```
928
+ * @category Voicing & Counterpoint
929
+ */
930
+ declare function checkPartWriting(voicings: readonly SpelledVoicing[], chords: readonly Chord[], key: KeyScale, opts?: PartWritingOptions): PartWritingViolation[];
931
+
932
+ /**
933
+ * Transposing instruments: the fixed interval between the pitch a player reads
934
+ * and the pitch the instrument sounds.
935
+ *
936
+ * The distance is a spelled interval, never a semitone count. A clarinet in A
937
+ * sounds a minor third below what it reads, so a written C sounds A, a written
938
+ * D sounds B, and a written D# sounds B#: the letter always moves by the
939
+ * interval's diatonic number, where a bare count of three semitones would take
940
+ * the last of those to C natural and lose the letter the part is written on.
941
+ *
942
+ * The octave is part of that interval rather than folded away into a pitch
943
+ * class, so instruments pitched in the same key stay apart: the alto saxophone
944
+ * sounds a major sixth lower, the baritone saxophone a major thirteenth (the
945
+ * same key an octave further down), and the E flat clarinet a minor third
946
+ * higher. The pure octave transposers — piccolo, double bass, contrabassoon,
947
+ * guitar — are a perfect octave, a unison-class interval displaced by one
948
+ * octave.
949
+ */
950
+
951
+ /**
952
+ * The interval from a written pitch to the pitch the instrument sounds, one
953
+ * entry per built-in instrument.
954
+ *
955
+ * Each value is an interval name as {@link parseInterval} reads it, in the
956
+ * written-to-sounding direction: a leading `'-'` marks an instrument that
957
+ * sounds below what it reads, which is most of them. A transposition wider than
958
+ * an octave is named by its compound interval rather than reduced, because the
959
+ * octave is exactly what separates the tenor saxophone from the soprano and the
960
+ * baritone from the alto.
961
+ *
962
+ * @example
963
+ * ```ts
964
+ * import { TRANSPOSING_INSTRUMENTS } from '@libraz/libcantus';
965
+ * TRANSPOSING_INSTRUMENTS.clarinetA; // '-m3' — sounds a minor third lower
966
+ * TRANSPOSING_INSTRUMENTS.piccolo; // 'P8' — sounds an octave higher
967
+ * ```
968
+ * @category Pitch & Intervals
969
+ */
970
+ declare const TRANSPOSING_INSTRUMENTS: Readonly<{
971
+ clarinetBb: "-M2";
972
+ trumpetBb: "-M2";
973
+ sopranoSax: "-M2";
974
+ tenorSax: "-M9";
975
+ bassClarinet: "-M9";
976
+ clarinetA: "-m3";
977
+ hornF: "-P5";
978
+ englishHorn: "-P5";
979
+ clarinetEb: "m3";
980
+ altoSax: "-M6";
981
+ baritoneSax: "-M13";
982
+ trumpetD: "M2";
983
+ piccolo: "P8";
984
+ doubleBass: "-P8";
985
+ contrabassoon: "-P8";
986
+ guitar: "-P8";
987
+ }>;
988
+ /**
989
+ * The name of a built-in transposing instrument, as
990
+ * {@link TRANSPOSING_INSTRUMENTS} keys them.
991
+ *
992
+ * @example
993
+ * ```ts
994
+ * import type { TransposingInstrumentName } from '@libraz/libcantus';
995
+ * const part: TransposingInstrumentName = 'clarinetA';
996
+ * ```
997
+ * @category Pitch & Intervals
998
+ */
999
+ type TransposingInstrumentName = keyof typeof TRANSPOSING_INSTRUMENTS;
1000
+ /**
1001
+ * A transposing instrument: one of the built-in names, or a spelled interval
1002
+ * for an instrument the table does not carry.
1003
+ *
1004
+ * An interval is read in the same written-to-sounding direction as the table,
1005
+ * so `'-M2'` is any instrument in B flat and `'P8'` any that sounds an octave
1006
+ * up. The names complete in an editor while any other string is still accepted,
1007
+ * which is what lets an instrument read from configuration go straight in.
1008
+ *
1009
+ * @example
1010
+ * ```ts
1011
+ * import type { TransposingInstrument } from '@libraz/libcantus';
1012
+ * const clarinet: TransposingInstrument = 'clarinetA';
1013
+ * const altoFlute: TransposingInstrument = '-P4';
1014
+ * ```
1015
+ * @category Pitch & Intervals
1016
+ */
1017
+ type TransposingInstrument = TransposingInstrumentName | (string & {}) | Exclude<IntervalLike, string>;
1018
+ /**
1019
+ * The interval a transposing instrument sounds at, measured from what it reads.
1020
+ *
1021
+ * This is the table's own direction: the answer descends for an instrument that
1022
+ * sounds below the printed part, which is most of them. Applying it to a
1023
+ * written pitch is {@link toSoundingPitch}, and applying it to a concert key is
1024
+ * how a written key is read back as the key it sounds in.
1025
+ *
1026
+ * @param instrument A built-in instrument name, or an interval naming a
1027
+ * transposition the table does not carry.
1028
+ * @returns The written-to-sounding interval.
1029
+ * @throws If the value is neither a known instrument nor a spelled interval.
1030
+ * @example
1031
+ * ```ts
1032
+ * import { instrumentTransposition } from '@libraz/libcantus';
1033
+ * instrumentTransposition('clarinetA');
1034
+ * // { number: 3, quality: 'm', semitones: -3, descending: true }
1035
+ * instrumentTransposition('piccolo'); // { number: 8, quality: 'P', semitones: 12 }
1036
+ * ```
1037
+ * @category Pitch & Intervals
1038
+ */
1039
+ declare function instrumentTransposition(instrument: TransposingInstrument): SpelledInterval;
1040
+ /**
1041
+ * The pitch an instrument sounds for a written note.
1042
+ *
1043
+ * The interval decides the letter, so the spelling of the part survives the
1044
+ * conversion: on a clarinet in A a written C sounds A, a written D sounds B,
1045
+ * and a written D# sounds B# — a semitone count alone could answer C natural
1046
+ * for that last one and lose the letter the part is written on. A note carrying
1047
+ * an octave moves register with it — a written C4 sounds A3 — while an
1048
+ * octave-less note stays octave-less, so a pure octave transposer leaves it
1049
+ * alone.
1050
+ *
1051
+ * @param note The written note, as the player reads it: a note name, a MIDI
1052
+ * number, note data, or a `Note`.
1053
+ * @param instrument A built-in instrument name, or an interval naming a
1054
+ * transposition the table does not carry.
1055
+ * @returns The sounding note, at concert pitch.
1056
+ * @throws If the note is malformed, or the instrument is neither a known name
1057
+ * nor a spelled interval.
1058
+ * @example
1059
+ * ```ts
1060
+ * import { formatNote, parseNote, toSoundingPitch } from '@libraz/libcantus';
1061
+ * formatNote(toSoundingPitch(parseNote('C4'), 'clarinetA')); // 'A3'
1062
+ * formatNote(toSoundingPitch('D#4', 'clarinetA')); // 'B#3'
1063
+ * formatNote(toSoundingPitch(parseNote('C4'), 'piccolo')); // 'C5'
1064
+ * ```
1065
+ * @category Pitch & Intervals
1066
+ */
1067
+ declare function toSoundingPitch(note: NoteLike, instrument: TransposingInstrument): Note;
1068
+ /**
1069
+ * The note an instrument must read to sound a given concert pitch.
1070
+ *
1071
+ * The exact inverse of {@link toSoundingPitch} for every instrument: the same
1072
+ * interval applied the other way, so the letter and the octave both come back.
1073
+ * A concert A3 is written C4 for a clarinet in A, and a concert E flat 3 is
1074
+ * written C4 for an alto saxophone but C5 for a baritone saxophone, which reads
1075
+ * the same key an octave lower.
1076
+ *
1077
+ * @param note The sounding note, at concert pitch: a note name, a MIDI number,
1078
+ * note data, or a `Note`.
1079
+ * @param instrument A built-in instrument name, or an interval naming a
1080
+ * transposition the table does not carry.
1081
+ * @returns The written note, as the player reads it.
1082
+ * @throws If the note is malformed, or the instrument is neither a known name
1083
+ * nor a spelled interval.
1084
+ * @example
1085
+ * ```ts
1086
+ * import { formatNote, parseNote, toWrittenPitch } from '@libraz/libcantus';
1087
+ * formatNote(toWrittenPitch(parseNote('A3'), 'clarinetA')); // 'C4'
1088
+ * formatNote(toWrittenPitch('Eb3', 'altoSax')); // 'C4'
1089
+ * formatNote(toWrittenPitch(parseNote('Eb3'), 'baritoneSax')); // 'C5'
1090
+ * ```
1091
+ * @category Pitch & Intervals
1092
+ */
1093
+ declare function toWrittenPitch(note: NoteLike, instrument: TransposingInstrument): Note;
1094
+
1095
+ export { type AvailableTensionsOptions as A, keySignatureFifths as B, type ChordScaleMatch as C, scaleMatchesChord as D, scaleSystemOf as E, scalesForChanges as F, spellVoicing as G, supportsFunctionalHarmony as H, toSoundingPitch as I, toWrittenPitch as J, type KeyMode as K, voiceChord as L, voiceChordStyled as M, voiceIndependence as N, voiceProgression as O, type PartWritingOptions as P, SATB_RANGES as S, TRANSPOSING_INSTRUMENTS as T, type VoiceIndependenceOptions as V, type AvoidNoteUse as a, type AvoidNotesOptions as b, type ChordScaleReportEntry as c, type PartWritingViolation as d, type PartWritingViolationKind as e, SCALE_SYSTEMS as f, type ScaleChoice as g, type ScaleSystem as h, type Species as i, type SpeciesOptions as j, type SpelledVoicing as k, type StyledVoicingOptions as l, type TransposingInstrument as m, type TransposingInstrumentName as n, type VoiceIndependenceReport as o, type VoiceRange as p, type VoicingOptions as q, type VoicingStyle as r, availableTensions as s, avoidNotes as t, checkPartWriting as u, checkSpecies as v, chordScaleReport as w, chordScales as x, instrumentTransposition as y, keyFromFifths as z };