@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,13 +1,154 @@
1
- export { B as BarPosition, T as TimeSignature, b as barPositionToBeat, a as beatToBarPosition, c as beatsPerBar, f as formatTimeSignature, i as isCompound, d as isStrongBeat, m as metricWeight, p as parseTimeSignature, e as pulsesPerBar, t as tuplet } from '../index-Cl7mhOFM.js';
2
- export { N as NoteData, S as SpelledInterval, f as formatNote, m as midiToNote, n as noteToMidi, a as noteToPitchClass, p as parseNote, s as spelledInterval } from '../index-CGMZNCx6.js';
3
- export { K as KeyScale, N as NoteEvent } from '../types-MGP29r3t.js';
1
+ import { N as NoteEvent } from '../types-BN6_Q9Kl.js';
2
+ export { K as KeyScale } from '../types-BN6_Q9Kl.js';
3
+ import { T as TimeSignature } from '../index-DXd9JikN.js';
4
+ export { B as BarPosition, b as barPositionToBeat, a as barPositionToPulse, c as beatToBarPosition, d as beatsPerBar, f as formatBarPosition, e as formatTimeSignature, i as isCompound, g as isStrongBeat, m as metricWeight, p as parseTimeSignature, h as pulseBeats, j as pulsesPerBar, t as tuplet } from '../index-DXd9JikN.js';
5
+ export { I as IntervalQualityLabel, N as NoteData, S as SpelledInterval, d as diatonicLetterOf, f as formatNote, i as intervalSemitones, m as midiToNote, n as naturalPitchClassOf, a as noteToMidi, b as noteToPitchClass, p as parseInterval, c as parseNote, e as pitchClassOf, s as spelledInterval, t as transposeByInterval, g as transposeNote } from '../index-OG65ocOr.js';
4
6
 
5
7
  /**
6
- * Counterpoint classification of a harmonic interval.
8
+ * The library's error taxonomy.
7
9
  *
10
+ * Every failure this library raises falls into one of three kinds, and the kind
11
+ * decides what a caller can do about it. Batch-voicing a lead sheet, for
12
+ * instance, has to tell "fix the input" apart from "loosen the constraints",
13
+ * and a message string is not a contract.
14
+ *
15
+ * The classes extend the built-in error types a caller would otherwise catch —
16
+ * a rejected argument is still a `RangeError` — so the `code` narrows an
17
+ * existing category rather than replacing it.
18
+ */
19
+ /**
20
+ * What kind of failure an error reports.
21
+ *
22
+ * - `INVALID_INPUT`: an argument is malformed or out of range. The caller's
23
+ * input must change.
24
+ * - `NO_SOLUTION`: the input is valid but nothing satisfies the constraints.
25
+ * The constraints must change, or the failure must be accepted.
26
+ * - `BUDGET_EXCEEDED`: the requested work is larger than the synchronous
27
+ * generation budget. The request must be smaller, or the budget larger.
28
+ *
29
+ * @category Core
30
+ */
31
+ type LibcantusErrorCode = 'INVALID_INPUT' | 'NO_SOLUTION' | 'BUDGET_EXCEEDED';
32
+ /**
33
+ * A rejected argument: malformed, out of range, or naming something unknown.
34
+ *
35
+ * @category Core
36
+ */
37
+ declare class InvalidInputError extends RangeError {
38
+ readonly code: "INVALID_INPUT";
39
+ readonly name = "InvalidInputError";
40
+ }
41
+ /**
42
+ * A valid request with no answer: no voicing fits the given ranges, no chord
43
+ * matches the pitches, no preset claims the style.
44
+ *
45
+ * @category Core
46
+ */
47
+ declare class NoSolutionError extends Error {
48
+ readonly code: "NO_SOLUTION";
49
+ readonly name = "NoSolutionError";
50
+ /**
51
+ * Where in a sequence the failure occurred, when the operation was working
52
+ * through one — the chord index of a progression, say.
53
+ */
54
+ readonly at: number | undefined;
55
+ constructor(message: string, options?: {
56
+ at?: number;
57
+ });
58
+ }
59
+ /**
60
+ * Work that would exceed the synchronous generation budget.
61
+ *
62
+ * @category Core
63
+ */
64
+ declare class BudgetExceededError extends RangeError {
65
+ readonly code: "BUDGET_EXCEEDED";
66
+ readonly name = "BudgetExceededError";
67
+ }
68
+ /**
69
+ * Whether a caught value is one of this library's coded errors.
70
+ *
71
+ * @param value The caught value.
72
+ * @returns True when it carries a {@link LibcantusErrorCode}.
73
+ * @example
74
+ * ```ts
75
+ * import { isLibcantusError, voiceChord, parseChordSymbol } from '@libraz/libcantus';
76
+ * try {
77
+ * voiceChord(parseChordSymbol('C'), { ranges: [] });
78
+ * } catch (error) {
79
+ * if (isLibcantusError(error) && error.code === 'NO_SOLUTION') {
80
+ * // loosen the constraints rather than blaming the input
81
+ * }
82
+ * }
83
+ * ```
84
+ * @category Core
85
+ */
86
+ declare function isLibcantusError(value: unknown): value is InvalidInputError | NoSolutionError | BudgetExceededError;
87
+
88
+ /**
89
+ * A validated note retaining its position in the caller's original array.
90
+ *
91
+ * @category Core
92
+ */
93
+ type IndexedNoteEvent = {
94
+ note: NoteEvent;
95
+ originalIndex: number;
96
+ endBeat: number;
97
+ };
98
+ /**
99
+ * Which note wins when several sound from the same onset.
100
+ *
101
+ * `'highest'` and `'lowest'` name a voice, so the answer does not depend on the
102
+ * order the caller happened to store the chord in; `'last'` takes the one
103
+ * latest in the input array.
104
+ *
105
+ * @category Core
106
+ */
107
+ type OnsetTieBreak = 'highest' | 'lowest' | 'last';
108
+ /**
109
+ * Binary-searchable, stable-sorted index over note onsets and active spans.
110
+ *
111
+ * @category Core
112
+ */
113
+ type NoteEventIndex = {
114
+ notes: IndexedNoteEvent[];
115
+ /**
116
+ * Latest-onset note sounding at `beat`. Simultaneous onsets are resolved by
117
+ * the index's {@link OnsetTieBreak}.
118
+ */
119
+ at: (beat: number) => IndexedNoteEvent | undefined;
120
+ /** Whether one or more sounding notes attack at `beat`. */
121
+ attacksAt: (beat: number) => boolean;
122
+ /** Unique sounding-note attack beats strictly inside `(startBeat, endBeat)`. */
123
+ onsetsBetween: (startBeat: number, endBeat: number) => number[];
124
+ };
125
+ /**
126
+ * Validate and stable-sort note events once, then expose logarithmic onset and
127
+ * active-note lookups. Non-positive-duration notes may be retained for callers
128
+ * that intentionally filter them later, but never count as sounding.
129
+ *
130
+ * @category Core
131
+ */
132
+ declare function createNoteEventIndex(events: readonly NoteEvent[], options?: {
133
+ allowNonPositiveDuration?: boolean;
134
+ tieBreak?: OnsetTieBreak;
135
+ budget?: number;
136
+ /** What the events are, for the validation error message. */
137
+ name?: string;
138
+ }): NoteEventIndex;
139
+
140
+ /**
141
+ * Counterpoint classification of a harmonic interval by consonance.
142
+ *
143
+ * Distinct from a spelled interval's quality label — see
144
+ * {@link IntervalQualityLabel}, which names the `'P'` / `'M'` / `'m'` / `'A'` /
145
+ * `'d'` spelling of an interval rather than how it sounds against another
146
+ * voice.
147
+ *
148
+ * @see {@link IntervalQualityLabel}
8
149
  * @category Pitch & Intervals
9
150
  */
10
- declare enum IntervalQuality {
151
+ declare enum ConsonanceClass {
11
152
  PerfectConsonance = 0,
12
153
  ImperfectConsonance = 1,
13
154
  Dissonance = 2
@@ -25,16 +166,22 @@ declare enum IntervalQuality {
25
166
  * @returns The counterpoint quality of the interval.
26
167
  * @example
27
168
  * ```ts
28
- * import { classifyInterval, IntervalQuality } from '@libraz/libcantus';
29
- * classifyInterval(7); // IntervalQuality.PerfectConsonance
30
- * classifyInterval(5); // IntervalQuality.Dissonance (two-voice)
31
- * classifyInterval(5, false); // IntervalQuality.ImperfectConsonance
169
+ * import { classifyInterval, ConsonanceClass } from '@libraz/libcantus';
170
+ * classifyInterval(7); // ConsonanceClass.PerfectConsonance
171
+ * classifyInterval(5); // ConsonanceClass.Dissonance (two-voice)
172
+ * classifyInterval(5, false); // ConsonanceClass.ImperfectConsonance
32
173
  * ```
33
174
  * @category Pitch & Intervals
34
175
  */
35
- declare function classifyInterval(semitones: number, twoVoice?: boolean): IntervalQuality;
176
+ declare function classifyInterval(semitones: number, twoVoice?: boolean): ConsonanceClass;
36
177
  /**
37
- * Test whether an interval is a perfect interval (unison/octave or fifth).
178
+ * Test whether an interval is a perfect consonance: the unison/octave or the
179
+ * fifth.
180
+ *
181
+ * The perfect fourth is deliberately excluded. This is the predicate the
182
+ * parallel-motion rules are written against, where the fourth behaves as a
183
+ * dissonance between two voices; {@link classifyInterval} is the general
184
+ * classifier, and it treats the fourth as context-dependent.
38
185
  *
39
186
  * @param semitones Interval size in semitones (may be negative or compound).
40
187
  * @returns True for the unison/octave (0) and the perfect fifth (7) mod 12.
@@ -109,11 +256,11 @@ declare function createRng(seed: number): Rng;
109
256
  */
110
257
  type Tuning = {
111
258
  /** Step index (MIDI number when `divisions` is 12) whose frequency is `refFreq`. */
112
- refStep: number;
259
+ readonly refStep: number;
113
260
  /** Frequency in Hz of `refStep`. */
114
- refFreq: number;
261
+ readonly refFreq: number;
115
262
  /** Equal divisions of the octave (12 standard; e.g. 19 or 31 for microtonal). */
116
- divisions: number;
263
+ readonly divisions: number;
117
264
  };
118
265
  /**
119
266
  * Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.
@@ -162,6 +309,67 @@ declare function frequencyOf(step: number, tuning?: Tuning): number;
162
309
  * @category Pitch & Intervals
163
310
  */
164
311
  declare function nearestStep(freq: number, tuning?: Tuning): number;
312
+ /**
313
+ * Step index of a frequency under a tuning, unrounded.
314
+ *
315
+ * The exact inverse of {@link frequencyOf}. {@link nearestStep} rounds it,
316
+ * which discards how far off the pitch actually is — the figure a tuner
317
+ * display, a pitch-bend amount, or an intonation check is built on.
318
+ *
319
+ * @param freq Frequency in Hz.
320
+ * @param tuning The tuning (default 12-TET).
321
+ * @returns The fractional step index.
322
+ * @example
323
+ * ```ts
324
+ * import { stepOf } from '@libraz/libcantus';
325
+ * stepOf(442); // slightly above 69, the step for A440
326
+ * ```
327
+ * @category Pitch & Intervals
328
+ */
329
+ declare function stepOf(freq: number, tuning?: Tuning): number;
330
+ /**
331
+ * How far a frequency sits from its nearest step, in cents.
332
+ *
333
+ * Positive means sharp of the step, negative flat.
334
+ *
335
+ * @param freq Frequency in Hz.
336
+ * @param tuning The tuning (default 12-TET).
337
+ * @returns The signed deviation in cents.
338
+ * @example
339
+ * ```ts
340
+ * import { centsFromNearestStep } from '@libraz/libcantus';
341
+ * Math.round(centsFromNearestStep(442)); // 8
342
+ * ```
343
+ * @category Pitch & Intervals
344
+ */
345
+ declare function centsFromNearestStep(freq: number, tuning?: Tuning): number;
346
+ /**
347
+ * Steps spanned by an interval in cents, unrounded — the inverse of
348
+ * {@link centsOfSteps}.
349
+ *
350
+ * @param cents The interval in cents.
351
+ * @param tuning The tuning (default 12-TET).
352
+ * @returns The fractional step count.
353
+ * @category Pitch & Intervals
354
+ */
355
+ declare function stepsOfCents(cents: number, tuning?: Tuning): number;
356
+ /**
357
+ * The frequency ratio an interval in cents spans — the inverse of
358
+ * {@link ratioToCents}.
359
+ *
360
+ * Multiply a frequency by this to move it by that many cents, which is how a
361
+ * cents offset (a pitch-bend spec, a tuning table entry) is applied.
362
+ *
363
+ * @param cents The interval in cents.
364
+ * @returns The frequency ratio.
365
+ * @example
366
+ * ```ts
367
+ * import { centsToRatio } from '@libraz/libcantus';
368
+ * 440 * centsToRatio(14); // A440 raised by 14 cents
369
+ * ```
370
+ * @category Pitch & Intervals
371
+ */
372
+ declare function centsToRatio(cents: number): number;
165
373
  /**
166
374
  * Interval in cents between two frequencies.
167
375
  *
@@ -196,15 +404,121 @@ declare function ratioToCents(numerator: number, denominator: number): number;
196
404
  *
197
405
  * @category Pitch & Intervals
198
406
  */
199
- declare const JUST_RATIOS: Record<number, [number, number]>;
407
+ declare const JUST_RATIOS: Readonly<Record<number, readonly [number, number]>>;
200
408
  /**
201
409
  * Cents by which a five-limit just interval departs from its 12-TET tempering
202
410
  * (positive means the just interval is wider).
203
411
  *
204
412
  * @param semitoneClass Semitone class in [0, 12].
205
- * @returns The deviation in cents, or NaN if the class has no listed ratio.
413
+ * @returns The deviation in cents.
414
+ * @throws If `semitoneClass` is not an integer in [0, 12].
206
415
  * @category Pitch & Intervals
207
416
  */
208
417
  declare function justDeviationCents(semitoneClass: number): number;
209
418
 
210
- export { IntervalQuality, JUST_RATIOS, type Rng, TWELVE_TET, type Tuning, centsBetweenFreq, centsOfSteps, classifyInterval, createRng, edo, frequencyOf, isConsonantInterval, isPerfectInterval, justDeviationCents, nearestStep, ratioToCents };
419
+ /**
420
+ * Default upper bound for synchronous event/window/candidate generation.
421
+ *
422
+ * @category Core
423
+ */
424
+ declare const DEFAULT_GENERATION_BUDGET = 1000000;
425
+ /**
426
+ * Require a finite JavaScript number and return it unchanged.
427
+ *
428
+ * @category Core
429
+ */
430
+ declare function assertFiniteNumber(value: number, name: string): number;
431
+ /**
432
+ * Require an integer in the inclusive range `[min, max]`.
433
+ *
434
+ * @category Core
435
+ */
436
+ declare function assertInteger(value: number, name: string, min?: number, max?: number): number;
437
+ /**
438
+ * Drop the notes that never sound.
439
+ *
440
+ * A MIDI or DAW import routinely carries zero-length artefacts. Every entry
441
+ * point in this library ignores them, but a caller holding such an array still
442
+ * has to decide what to do with it before comparing note counts or indices;
443
+ * this is the one filter to apply.
444
+ *
445
+ * @param events The note events to filter.
446
+ * @returns A new array containing only the notes with a positive duration.
447
+ * @example
448
+ * ```ts
449
+ * import { dropSilentNotes } from '@libraz/libcantus';
450
+ * dropSilentNotes([{ pitch: 60, startBeat: 0, durationBeat: 0 }]); // []
451
+ * ```
452
+ * @category Core
453
+ */
454
+ declare function dropSilentNotes(events: readonly NoteEvent[]): NoteEvent[];
455
+ /**
456
+ * Require a value to be one of a fixed set of names.
457
+ *
458
+ * TypeScript checks string-union options at compile time only, so a value that
459
+ * arrives from JSON, a config file, or a JavaScript caller reaches the engine
460
+ * unchecked and is then read against a table that has no entry for it —
461
+ * producing a silent `undefined`, a NaN velocity, or a crash far from the call.
462
+ *
463
+ * @param value The value to check.
464
+ * @param allowed Every accepted name.
465
+ * @param name What the value is, for the error message.
466
+ * @returns The value, narrowed to the allowed union.
467
+ * @throws If the value is not one of `allowed`.
468
+ *
469
+ * @category Core
470
+ */
471
+ declare function assertOneOf<const T extends string>(value: unknown, allowed: readonly T[], name: string): T;
472
+ /**
473
+ * Require a positive safe integer, optionally capped by `max`.
474
+ *
475
+ * @category Core
476
+ */
477
+ declare function assertPositiveInt(value: number, name: string, max?: number): number;
478
+ /**
479
+ * Require a finite number in the inclusive range `[min, max]`.
480
+ *
481
+ * @category Core
482
+ */
483
+ declare function assertRange(value: number, min: number, max: number, name: string): number;
484
+ /**
485
+ * Reject work estimates that would exceed a synchronous generation budget.
486
+ *
487
+ * @category Core
488
+ */
489
+ declare function assertGenerationBudget(estimated: number, name: string, limit?: number | undefined): number;
490
+ /**
491
+ * Validate a time signature, including additive grouping, before any early return.
492
+ *
493
+ * @category Core
494
+ */
495
+ declare function assertTimeSignature(ts: TimeSignature, name?: string): TimeSignature;
496
+ /**
497
+ * How strictly {@link assertNoteEvent} and {@link assertNoteEvents} read an
498
+ * event array.
499
+ *
500
+ * @category Core
501
+ */
502
+ type NoteEventAssertOptions = {
503
+ /**
504
+ * Accept notes that never sound. The analysis side does, so an array from a
505
+ * MIDI import can be validated before the zero-length artefacts are dropped.
506
+ */
507
+ allowNonPositiveDuration?: boolean;
508
+ /** Upper bound on the event count; defaults to the generation budget. */
509
+ budget?: number;
510
+ };
511
+ /**
512
+ * Validate the finite fields of one timeline note event.
513
+ *
514
+ * @category Core
515
+ */
516
+ declare function assertNoteEvent(event: NoteEvent, name?: string, options?: NoteEventAssertOptions): NoteEvent;
517
+ /**
518
+ * Validate an event array and its allocation budget without copying it.
519
+ *
520
+ * @category Core
521
+ */
522
+ declare function assertNoteEvents(events: readonly NoteEvent[], name?: string, options?: NoteEventAssertOptions): NoteEvent[];
523
+
524
+ export { BudgetExceededError, ConsonanceClass, DEFAULT_GENERATION_BUDGET, type IndexedNoteEvent, InvalidInputError, JUST_RATIOS, type LibcantusErrorCode, NoSolutionError, NoteEvent, type NoteEventAssertOptions, type NoteEventIndex, type OnsetTieBreak, type Rng, TWELVE_TET, TimeSignature, type Tuning, assertFiniteNumber, assertGenerationBudget, assertInteger, assertNoteEvent, assertNoteEvents, assertOneOf, assertPositiveInt, assertRange, assertTimeSignature, centsBetweenFreq, centsFromNearestStep, centsOfSteps, centsToRatio, classifyInterval, createNoteEventIndex, createRng, dropSilentNotes, edo, frequencyOf, isConsonantInterval, isLibcantusError, isPerfectInterval, justDeviationCents, nearestStep, ratioToCents, stepOf, stepsOfCents };
@@ -2,72 +2,130 @@ import {
2
2
  JUST_RATIOS,
3
3
  TWELVE_TET,
4
4
  centsBetweenFreq,
5
+ centsFromNearestStep,
5
6
  centsOfSteps,
7
+ centsToRatio,
6
8
  edo,
7
9
  frequencyOf,
8
10
  justDeviationCents,
9
11
  nearestStep,
10
- ratioToCents
11
- } from "../chunk-ZXW5YIWK.js";
12
+ ratioToCents,
13
+ stepOf,
14
+ stepsOfCents
15
+ } from "../chunk-I24MV65U.js";
12
16
  import {
13
17
  createRng
14
- } from "../chunk-BHUT2S5G.js";
18
+ } from "../chunk-HKGYCQ34.js";
15
19
  import {
16
20
  barPositionToBeat,
21
+ barPositionToPulse,
17
22
  beatToBarPosition,
18
23
  beatsPerBar,
24
+ createNoteEventIndex,
25
+ formatBarPosition,
19
26
  formatTimeSignature,
20
27
  isCompound,
21
28
  isStrongBeat,
22
29
  metricWeight,
23
30
  parseTimeSignature,
31
+ pulseBeats,
24
32
  pulsesPerBar,
25
33
  tuplet
26
- } from "../chunk-MSWFZF7J.js";
34
+ } from "../chunk-YLEMGEFH.js";
27
35
  import {
36
+ BudgetExceededError,
37
+ ConsonanceClass,
38
+ DEFAULT_GENERATION_BUDGET,
39
+ InvalidInputError,
40
+ NoSolutionError,
41
+ assertFiniteNumber,
42
+ assertGenerationBudget,
43
+ assertInteger,
44
+ assertNoteEvent,
45
+ assertNoteEvents,
46
+ assertOneOf,
47
+ assertPositiveInt,
48
+ assertRange,
49
+ assertTimeSignature,
50
+ classifyInterval,
51
+ diatonicLetterOf,
52
+ dropSilentNotes,
28
53
  formatNote,
54
+ intervalSemitones,
55
+ isConsonantInterval,
56
+ isLibcantusError,
57
+ isPerfectInterval,
29
58
  midiToNote,
59
+ naturalPitchClassOf,
30
60
  noteToMidi,
31
61
  noteToPitchClass,
62
+ parseInterval,
32
63
  parseNote,
33
- spelledInterval
34
- } from "../chunk-DYWFNC4A.js";
35
- import {
36
- IntervalQuality,
37
- classifyInterval,
38
- isConsonantInterval,
39
- isPerfectInterval
40
- } from "../chunk-CCBLVAPK.js";
64
+ pitchClassOf,
65
+ spelledInterval,
66
+ transposeByInterval,
67
+ transposeNote
68
+ } from "../chunk-XEBG3G53.js";
41
69
  export {
42
- IntervalQuality,
70
+ BudgetExceededError,
71
+ ConsonanceClass,
72
+ DEFAULT_GENERATION_BUDGET,
73
+ InvalidInputError,
43
74
  JUST_RATIOS,
75
+ NoSolutionError,
44
76
  TWELVE_TET,
77
+ assertFiniteNumber,
78
+ assertGenerationBudget,
79
+ assertInteger,
80
+ assertNoteEvent,
81
+ assertNoteEvents,
82
+ assertOneOf,
83
+ assertPositiveInt,
84
+ assertRange,
85
+ assertTimeSignature,
45
86
  barPositionToBeat,
87
+ barPositionToPulse,
46
88
  beatToBarPosition,
47
89
  beatsPerBar,
48
90
  centsBetweenFreq,
91
+ centsFromNearestStep,
49
92
  centsOfSteps,
93
+ centsToRatio,
50
94
  classifyInterval,
95
+ createNoteEventIndex,
51
96
  createRng,
97
+ diatonicLetterOf,
98
+ dropSilentNotes,
52
99
  edo,
100
+ formatBarPosition,
53
101
  formatNote,
54
102
  formatTimeSignature,
55
103
  frequencyOf,
104
+ intervalSemitones,
56
105
  isCompound,
57
106
  isConsonantInterval,
107
+ isLibcantusError,
58
108
  isPerfectInterval,
59
109
  isStrongBeat,
60
110
  justDeviationCents,
61
111
  metricWeight,
62
112
  midiToNote,
113
+ naturalPitchClassOf,
63
114
  nearestStep,
64
115
  noteToMidi,
65
116
  noteToPitchClass,
117
+ parseInterval,
66
118
  parseNote,
67
119
  parseTimeSignature,
120
+ pitchClassOf,
121
+ pulseBeats,
68
122
  pulsesPerBar,
69
123
  ratioToCents,
70
124
  spelledInterval,
125
+ stepOf,
126
+ stepsOfCents,
127
+ transposeByInterval,
128
+ transposeNote,
71
129
  tuplet
72
130
  };
73
131
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import { K as KeyScale } from './types-MGP29r3t.js';
2
- import { C as Chord } from './index-CM5W4rcm.js';
1
+ import { K as KeyScale } from './types-BN6_Q9Kl.js';
2
+ import { C as Chord } from './index-C83mZI1P.js';
3
3
 
4
4
  /**
5
5
  * Borrowed chords (modal interchange) and the Neapolitan.
@@ -14,7 +14,7 @@ import { C as Chord } from './index-CM5W4rcm.js';
14
14
  *
15
15
  * @category Functional Harmony
16
16
  */
17
- type BorrowedSource = 'parallel-minor' | 'parallel-major' | 'neapolitan' | null;
17
+ type BorrowedSource = 'parallelMinor' | 'parallelMajor' | 'neapolitan' | null;
18
18
  /**
19
19
  * Whether a chord is borrowed from the parallel mode (modal interchange).
20
20
  *
@@ -107,13 +107,33 @@ type HarmonicFunction = 'tonic' | 'subdominant' | 'dominant';
107
107
  */
108
108
  declare function isMinorKey(key: KeyScale): boolean;
109
109
  /**
110
- * The harmonic function of a chord in a key, from its root's offset above the
111
- * tonic. The mapping follows common-practice major-key function and is a useful
112
- * approximation in minor and for borrowed chords.
110
+ * The harmonic function of a chord in a key.
111
+ *
112
+ * The root's offset above the tonic gives the baseline mapping, which follows
113
+ * common-practice major-key function and is a useful approximation in minor and
114
+ * for borrowed chords. Chord quality then refines it where the sonority settles
115
+ * an ambiguity the root offset cannot:
116
+ *
117
+ * - A chord that *sounds* like a dominant — a major third with a minor seventh,
118
+ * or a diminished-family chord — and is not diatonic to the key has dominant
119
+ * function when its root resolves down a fifth or by a semitone onto a
120
+ * diatonic degree. This is what makes an applied dominant (`A7` in C, which
121
+ * tonicizes ii) and its tritone substitute (`Db7`) read as dominant rather
122
+ * than inheriting the function of the degree they happen to sit on.
123
+ * - The Neapolitan is subdominant.
124
+ * - A major triad on bVI or bVII of a major key is subdominant — the borrowed
125
+ * pop cadence chord, distinct from the bVII7 above, which has a seventh and
126
+ * is a dominant sonority.
113
127
  *
114
128
  * @param chord The chord.
115
129
  * @param key The prevailing key.
116
130
  * @returns The harmonic function.
131
+ * @example
132
+ * ```ts
133
+ * import { functionOf, makeChord, majorKey } from '@libraz/libcantus';
134
+ * functionOf(makeChord(9, 'dom7'), majorKey(0)); // 'dominant' — A7 tonicizes ii
135
+ * functionOf(makeChord(10, 'maj'), majorKey(0)); // 'subdominant' — borrowed bVII
136
+ * ```
117
137
  * @category Functional Harmony
118
138
  */
119
139
  declare function functionOf(chord: Chord, key: KeyScale): HarmonicFunction;
@@ -177,9 +197,10 @@ declare function analyzeChord(chord: Chord, key: KeyScale): ChordAnalysis;
177
197
  /**
178
198
  * The secondary dominant (V7) that tonicizes a scale degree.
179
199
  *
180
- * @param targetDegree 0-based scale degree to tonicize.
200
+ * @param targetDegree 0-based scale degree to tonicize, in 0..6.
181
201
  * @param key The prevailing key.
182
202
  * @returns A dominant-seventh chord a fifth above the target's root.
203
+ * @throws If `targetDegree` is not an integer in 0..6.
183
204
  * @category Functional Harmony
184
205
  */
185
206
  declare function secondaryDominant(targetDegree: number, key: KeyScale): Chord;
@@ -1,5 +1,5 @@
1
- import { K as KeyScale } from './types-MGP29r3t.cjs';
2
- import { C as Chord } from './index-BOBXBNwP.cjs';
1
+ import { K as KeyScale } from './types-BN6_Q9Kl.cjs';
2
+ import { C as Chord } from './index-y_ak9_Hi.cjs';
3
3
 
4
4
  /**
5
5
  * Borrowed chords (modal interchange) and the Neapolitan.
@@ -14,7 +14,7 @@ import { C as Chord } from './index-BOBXBNwP.cjs';
14
14
  *
15
15
  * @category Functional Harmony
16
16
  */
17
- type BorrowedSource = 'parallel-minor' | 'parallel-major' | 'neapolitan' | null;
17
+ type BorrowedSource = 'parallelMinor' | 'parallelMajor' | 'neapolitan' | null;
18
18
  /**
19
19
  * Whether a chord is borrowed from the parallel mode (modal interchange).
20
20
  *
@@ -107,13 +107,33 @@ type HarmonicFunction = 'tonic' | 'subdominant' | 'dominant';
107
107
  */
108
108
  declare function isMinorKey(key: KeyScale): boolean;
109
109
  /**
110
- * The harmonic function of a chord in a key, from its root's offset above the
111
- * tonic. The mapping follows common-practice major-key function and is a useful
112
- * approximation in minor and for borrowed chords.
110
+ * The harmonic function of a chord in a key.
111
+ *
112
+ * The root's offset above the tonic gives the baseline mapping, which follows
113
+ * common-practice major-key function and is a useful approximation in minor and
114
+ * for borrowed chords. Chord quality then refines it where the sonority settles
115
+ * an ambiguity the root offset cannot:
116
+ *
117
+ * - A chord that *sounds* like a dominant — a major third with a minor seventh,
118
+ * or a diminished-family chord — and is not diatonic to the key has dominant
119
+ * function when its root resolves down a fifth or by a semitone onto a
120
+ * diatonic degree. This is what makes an applied dominant (`A7` in C, which
121
+ * tonicizes ii) and its tritone substitute (`Db7`) read as dominant rather
122
+ * than inheriting the function of the degree they happen to sit on.
123
+ * - The Neapolitan is subdominant.
124
+ * - A major triad on bVI or bVII of a major key is subdominant — the borrowed
125
+ * pop cadence chord, distinct from the bVII7 above, which has a seventh and
126
+ * is a dominant sonority.
113
127
  *
114
128
  * @param chord The chord.
115
129
  * @param key The prevailing key.
116
130
  * @returns The harmonic function.
131
+ * @example
132
+ * ```ts
133
+ * import { functionOf, makeChord, majorKey } from '@libraz/libcantus';
134
+ * functionOf(makeChord(9, 'dom7'), majorKey(0)); // 'dominant' — A7 tonicizes ii
135
+ * functionOf(makeChord(10, 'maj'), majorKey(0)); // 'subdominant' — borrowed bVII
136
+ * ```
117
137
  * @category Functional Harmony
118
138
  */
119
139
  declare function functionOf(chord: Chord, key: KeyScale): HarmonicFunction;
@@ -177,9 +197,10 @@ declare function analyzeChord(chord: Chord, key: KeyScale): ChordAnalysis;
177
197
  /**
178
198
  * The secondary dominant (V7) that tonicizes a scale degree.
179
199
  *
180
- * @param targetDegree 0-based scale degree to tonicize.
200
+ * @param targetDegree 0-based scale degree to tonicize, in 0..6.
181
201
  * @param key The prevailing key.
182
202
  * @returns A dominant-seventh chord a fifth above the target's root.
203
+ * @throws If `targetDegree` is not an integer in 0..6.
183
204
  * @category Functional Harmony
184
205
  */
185
206
  declare function secondaryDominant(targetDegree: number, key: KeyScale): Chord;