@libraz/libcantus 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +2 -2
  2. package/dist/analyze/index.cjs +39 -1947
  3. package/dist/analyze/index.cjs.map +1 -1
  4. package/dist/analyze/index.d.cts +197 -37
  5. package/dist/analyze/index.d.ts +197 -37
  6. package/dist/analyze/index.js +19 -8
  7. package/dist/chunk-36N73KZ3.cjs +246 -0
  8. package/dist/chunk-36N73KZ3.cjs.map +1 -0
  9. package/dist/chunk-4HLLZPXT.cjs +3077 -0
  10. package/dist/chunk-4HLLZPXT.cjs.map +1 -0
  11. package/dist/chunk-7MGZ7D62.cjs +498 -0
  12. package/dist/chunk-7MGZ7D62.cjs.map +1 -0
  13. package/dist/{chunk-4MHA432G.js → chunk-7NKDPF74.js} +403 -180
  14. package/dist/chunk-7NKDPF74.js.map +1 -0
  15. package/dist/chunk-CAN76NCU.cjs +679 -0
  16. package/dist/chunk-CAN76NCU.cjs.map +1 -0
  17. package/dist/{chunk-MIWO425M.js → chunk-D2FAJBYL.js} +101 -34
  18. package/dist/chunk-D2FAJBYL.js.map +1 -0
  19. package/dist/{chunk-SGU7SPLK.js → chunk-EHVEBWM5.js} +1056 -531
  20. package/dist/chunk-EHVEBWM5.js.map +1 -0
  21. package/dist/chunk-FCANXBAT.cjs +30 -0
  22. package/dist/chunk-FCANXBAT.cjs.map +1 -0
  23. package/dist/chunk-FNDWXRRI.cjs +479 -0
  24. package/dist/chunk-FNDWXRRI.cjs.map +1 -0
  25. package/dist/{chunk-IT2O4XFE.js → chunk-GQSGS2N4.js} +43 -24
  26. package/dist/chunk-GQSGS2N4.js.map +1 -0
  27. package/dist/{chunk-62QZKCJQ.js → chunk-HKGYCQ34.js} +10 -6
  28. package/dist/chunk-HKGYCQ34.js.map +1 -0
  29. package/dist/{chunk-AKLYPWIN.js → chunk-I24MV65U.js} +29 -6
  30. package/dist/chunk-I24MV65U.js.map +1 -0
  31. package/dist/{chunk-CHMXELDS.js → chunk-KMHOICIY.js} +311 -181
  32. package/dist/chunk-KMHOICIY.js.map +1 -0
  33. package/dist/chunk-LW53AHQU.cjs +1117 -0
  34. package/dist/chunk-LW53AHQU.cjs.map +1 -0
  35. package/dist/{chunk-2ZWMSJH6.js → chunk-NKIBQ6PT.js} +2 -2
  36. package/dist/chunk-NKIBQ6PT.js.map +1 -0
  37. package/dist/chunk-NP4NA2FB.cjs +106 -0
  38. package/dist/chunk-NP4NA2FB.cjs.map +1 -0
  39. package/dist/chunk-NVE34YGZ.cjs +1449 -0
  40. package/dist/chunk-NVE34YGZ.cjs.map +1 -0
  41. package/dist/{chunk-EH6EM37U.js → chunk-NYYWMV2C.js} +61 -49
  42. package/dist/chunk-NYYWMV2C.js.map +1 -0
  43. package/dist/{chunk-GOAW4VYL.js → chunk-Q2XUP227.js} +243 -117
  44. package/dist/chunk-Q2XUP227.js.map +1 -0
  45. package/dist/chunk-Q6OP4U43.cjs +327 -0
  46. package/dist/chunk-Q6OP4U43.cjs.map +1 -0
  47. package/dist/chunk-RC6GBMCD.cjs +845 -0
  48. package/dist/chunk-RC6GBMCD.cjs.map +1 -0
  49. package/dist/{chunk-C5XYKUVG.js → chunk-RK35RIN7.js} +370 -157
  50. package/dist/chunk-RK35RIN7.js.map +1 -0
  51. package/dist/chunk-RUMU3UA6.cjs +53 -0
  52. package/dist/chunk-RUMU3UA6.cjs.map +1 -0
  53. package/dist/chunk-T64C7ZD7.cjs +1 -0
  54. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  55. package/dist/chunk-XEBG3G53.js +498 -0
  56. package/dist/chunk-XEBG3G53.js.map +1 -0
  57. package/dist/{chunk-I5R7ZBJS.js → chunk-YLEMGEFH.js} +103 -59
  58. package/dist/chunk-YLEMGEFH.js.map +1 -0
  59. package/dist/chunk-ZEJR4TKL.cjs +173 -0
  60. package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
  61. package/dist/core/index.cjs +130 -647
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +301 -39
  64. package/dist/core/index.d.ts +301 -39
  65. package/dist/core/index.js +54 -16
  66. package/dist/{function-DYt4iKsn.d.cts → function-BtiZb9i9.d.ts} +28 -7
  67. package/dist/{function-DVH4Eusr.d.ts → function-DNugAOrt.d.cts} +28 -7
  68. package/dist/generate/index.cjs +28 -4085
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +370 -62
  71. package/dist/generate/index.d.ts +370 -62
  72. package/dist/generate/index.js +23 -9
  73. package/dist/{index-CxqBQtH1.d.cts → index-6VR8-kit.d.ts} +19 -4
  74. package/dist/index-70p168VM.d.ts +192 -0
  75. package/dist/{index-CpOsP1y9.d.cts → index-Be74mcon.d.cts} +18 -17
  76. package/dist/index-BtXT6UQ0.d.cts +192 -0
  77. package/dist/{index-DxvARIP2.d.ts → index-C5UBbQ4G.d.ts} +18 -17
  78. package/dist/{index-Bt4tOUD9.d.ts → index-C83mZI1P.d.ts} +93 -8
  79. package/dist/{index-Bg5dYMmZ.d.ts → index-D6b6na69.d.cts} +14 -11
  80. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  81. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  82. package/dist/index-OG65ocOr.d.cts +272 -0
  83. package/dist/index-OG65ocOr.d.ts +272 -0
  84. package/dist/{index-Jc0Ryi79.d.ts → index-tJhkmQgE.d.cts} +19 -4
  85. package/dist/{index-Dikst44v.d.cts → index-tSQh-xwY.d.ts} +14 -11
  86. package/dist/{index-BNlGP86A.d.cts → index-y_ak9_Hi.d.cts} +93 -8
  87. package/dist/index.cjs +419 -7383
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.d.cts +14 -14
  90. package/dist/index.d.ts +14 -14
  91. package/dist/index.js +111 -27
  92. package/dist/model/index.cjs +8 -2562
  93. package/dist/model/index.cjs.map +1 -1
  94. package/dist/model/index.d.cts +433 -75
  95. package/dist/model/index.d.ts +433 -75
  96. package/dist/model/index.js +6 -7
  97. package/dist/styled-DIRkk9Uz.d.ts +506 -0
  98. package/dist/styled-DJbkJQfj.d.cts +506 -0
  99. package/dist/theory/index.cjs +172 -1822
  100. package/dist/theory/index.cjs.map +1 -1
  101. package/dist/theory/index.d.cts +151 -233
  102. package/dist/theory/index.d.ts +151 -233
  103. package/dist/theory/index.js +30 -7
  104. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  105. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  106. package/package.json +24 -6
  107. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  108. package/dist/chunk-4MHA432G.js.map +0 -1
  109. package/dist/chunk-62QZKCJQ.js.map +0 -1
  110. package/dist/chunk-AKLYPWIN.js.map +0 -1
  111. package/dist/chunk-C5XYKUVG.js.map +0 -1
  112. package/dist/chunk-CHMXELDS.js.map +0 -1
  113. package/dist/chunk-DYWFNC4A.js +0 -152
  114. package/dist/chunk-DYWFNC4A.js.map +0 -1
  115. package/dist/chunk-EH6EM37U.js.map +0 -1
  116. package/dist/chunk-FBQK3ZQN.js +0 -131
  117. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  118. package/dist/chunk-GOAW4VYL.js.map +0 -1
  119. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  120. package/dist/chunk-IT2O4XFE.js.map +0 -1
  121. package/dist/chunk-MIWO425M.js.map +0 -1
  122. package/dist/chunk-SGU7SPLK.js.map +0 -1
  123. package/dist/index-CGMZNCx6.d.cts +0 -140
  124. package/dist/index-CGMZNCx6.d.ts +0 -140
  125. package/dist/index-CnZCo29u.d.cts +0 -108
  126. package/dist/index-JnF4v9bx.d.ts +0 -108
  127. package/dist/styled-DZ7GjShb.d.ts +0 -282
  128. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  129. package/src/analyze/arrange/index.ts +0 -21
  130. package/src/analyze/arrange/internal.ts +0 -127
  131. package/src/analyze/arrange/tension.ts +0 -199
  132. package/src/analyze/arrange/tracks.ts +0 -312
  133. package/src/analyze/detect/index.ts +0 -272
  134. package/src/analyze/functional/borrowed.ts +0 -84
  135. package/src/analyze/functional/cadence.ts +0 -66
  136. package/src/analyze/functional/function.ts +0 -184
  137. package/src/analyze/functional/index.ts +0 -23
  138. package/src/analyze/functional/internal.ts +0 -29
  139. package/src/analyze/functional/roman.ts +0 -402
  140. package/src/analyze/index.ts +0 -49
  141. package/src/analyze/timeline/index.ts +0 -451
  142. package/src/analyze/voice/index.ts +0 -339
  143. package/src/core/event-index/index.ts +0 -104
  144. package/src/core/index.ts +0 -61
  145. package/src/core/interval/index.ts +0 -81
  146. package/src/core/meter/index.ts +0 -302
  147. package/src/core/pitch/index.ts +0 -311
  148. package/src/core/random/index.ts +0 -76
  149. package/src/core/tuning/index.ts +0 -186
  150. package/src/core/types.ts +0 -29
  151. package/src/core/validation/index.ts +0 -119
  152. package/src/generate/bass/index.ts +0 -373
  153. package/src/generate/countermelody/index.ts +0 -470
  154. package/src/generate/drums/beat.ts +0 -266
  155. package/src/generate/drums/euclid.ts +0 -65
  156. package/src/generate/drums/fills.ts +0 -356
  157. package/src/generate/drums/ghost.ts +0 -119
  158. package/src/generate/drums/hihat.ts +0 -285
  159. package/src/generate/drums/hit.ts +0 -31
  160. package/src/generate/drums/index.ts +0 -361
  161. package/src/generate/drums/internal.ts +0 -257
  162. package/src/generate/drums/kick.ts +0 -160
  163. package/src/generate/drums/percussion.ts +0 -121
  164. package/src/generate/drums/rng.ts +0 -7
  165. package/src/generate/drums/swing.ts +0 -71
  166. package/src/generate/groove/index.ts +0 -351
  167. package/src/generate/harmonize/index.ts +0 -451
  168. package/src/generate/index.ts +0 -58
  169. package/src/generate/motif/index.ts +0 -422
  170. package/src/generate/progression/index.ts +0 -362
  171. package/src/generate/reharmony/index.ts +0 -323
  172. package/src/generate/rhythm/index.ts +0 -175
  173. package/src/index.ts +0 -13
  174. package/src/model/chord.ts +0 -442
  175. package/src/model/index.ts +0 -15
  176. package/src/model/interval.ts +0 -82
  177. package/src/model/key.ts +0 -266
  178. package/src/model/note.ts +0 -176
  179. package/src/model/progression.ts +0 -161
  180. package/src/model/shared.ts +0 -12
  181. package/src/theory/chord/index.ts +0 -375
  182. package/src/theory/chordscale/index.ts +0 -374
  183. package/src/theory/counterpoint/index.ts +0 -230
  184. package/src/theory/harmony/index.ts +0 -77
  185. package/src/theory/index.ts +0 -112
  186. package/src/theory/safety/index.ts +0 -488
  187. package/src/theory/scale/degrees.ts +0 -123
  188. package/src/theory/scale/index.ts +0 -28
  189. package/src/theory/scale/key.ts +0 -56
  190. package/src/theory/scale/masks.ts +0 -153
  191. package/src/theory/spelling/index.ts +0 -249
  192. package/src/theory/symbol/index.ts +0 -268
  193. package/src/theory/voicing/index.ts +0 -5
  194. package/src/theory/voicing/internal.ts +0 -168
  195. package/src/theory/voicing/leading.ts +0 -112
  196. package/src/theory/voicing/satb.ts +0 -216
  197. package/src/theory/voicing/styled.ts +0 -199
  198. package/tsconfig.json +0 -23
@@ -1,40 +1,154 @@
1
- import { N as NoteEvent } from '../types-MGP29r3t.js';
2
- export { K as KeyScale } from '../types-MGP29r3t.js';
3
- import { T as TimeSignature } from '../index-Cl7mhOFM.js';
4
- export { B as BarPosition, 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';
5
- 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';
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';
6
6
 
7
- /** A validated note retaining its position in the caller's original array. */
7
+ /**
8
+ * The library's error taxonomy.
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
+ */
8
93
  type IndexedNoteEvent = {
9
94
  note: NoteEvent;
10
95
  originalIndex: number;
11
96
  endBeat: number;
12
97
  };
13
- /** Binary-searchable, stable-sorted index over note onsets and active spans. */
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
+ */
14
113
  type NoteEventIndex = {
15
114
  notes: IndexedNoteEvent[];
16
- /** Latest-onset note sounding at `beat`, with later input order winning ties. */
115
+ /**
116
+ * Latest-onset note sounding at `beat`. Simultaneous onsets are resolved by
117
+ * the index's {@link OnsetTieBreak}.
118
+ */
17
119
  at: (beat: number) => IndexedNoteEvent | undefined;
18
- /** Whether one or more notes attack at `beat`. */
120
+ /** Whether one or more sounding notes attack at `beat`. */
19
121
  attacksAt: (beat: number) => boolean;
20
- /** Unique attack beats strictly inside `(startBeat, endBeat)`. */
122
+ /** Unique sounding-note attack beats strictly inside `(startBeat, endBeat)`. */
21
123
  onsetsBetween: (startBeat: number, endBeat: number) => number[];
22
124
  };
23
125
  /**
24
126
  * Validate and stable-sort note events once, then expose logarithmic onset and
25
127
  * active-note lookups. Non-positive-duration notes may be retained for callers
26
128
  * that intentionally filter them later, but never count as sounding.
129
+ *
130
+ * @category Core
27
131
  */
28
- declare function createNoteEventIndex(events: NoteEvent[], options?: {
132
+ declare function createNoteEventIndex(events: readonly NoteEvent[], options?: {
29
133
  allowNonPositiveDuration?: boolean;
134
+ tieBreak?: OnsetTieBreak;
135
+ budget?: number;
136
+ /** What the events are, for the validation error message. */
137
+ name?: string;
30
138
  }): NoteEventIndex;
31
139
 
32
140
  /**
33
- * Counterpoint classification of a harmonic interval.
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.
34
147
  *
148
+ * @see {@link IntervalQualityLabel}
35
149
  * @category Pitch & Intervals
36
150
  */
37
- declare enum IntervalQuality {
151
+ declare enum ConsonanceClass {
38
152
  PerfectConsonance = 0,
39
153
  ImperfectConsonance = 1,
40
154
  Dissonance = 2
@@ -52,16 +166,22 @@ declare enum IntervalQuality {
52
166
  * @returns The counterpoint quality of the interval.
53
167
  * @example
54
168
  * ```ts
55
- * import { classifyInterval, IntervalQuality } from '@libraz/libcantus';
56
- * classifyInterval(7); // IntervalQuality.PerfectConsonance
57
- * classifyInterval(5); // IntervalQuality.Dissonance (two-voice)
58
- * 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
59
173
  * ```
60
174
  * @category Pitch & Intervals
61
175
  */
62
- declare function classifyInterval(semitones: number, twoVoice?: boolean): IntervalQuality;
176
+ declare function classifyInterval(semitones: number, twoVoice?: boolean): ConsonanceClass;
63
177
  /**
64
- * 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.
65
185
  *
66
186
  * @param semitones Interval size in semitones (may be negative or compound).
67
187
  * @returns True for the unison/octave (0) and the perfect fifth (7) mod 12.
@@ -136,11 +256,11 @@ declare function createRng(seed: number): Rng;
136
256
  */
137
257
  type Tuning = {
138
258
  /** Step index (MIDI number when `divisions` is 12) whose frequency is `refFreq`. */
139
- refStep: number;
259
+ readonly refStep: number;
140
260
  /** Frequency in Hz of `refStep`. */
141
- refFreq: number;
261
+ readonly refFreq: number;
142
262
  /** Equal divisions of the octave (12 standard; e.g. 19 or 31 for microtonal). */
143
- divisions: number;
263
+ readonly divisions: number;
144
264
  };
145
265
  /**
146
266
  * Standard twelve-tone equal temperament, A4 (MIDI 69) = 440 Hz.
@@ -189,6 +309,67 @@ declare function frequencyOf(step: number, tuning?: Tuning): number;
189
309
  * @category Pitch & Intervals
190
310
  */
191
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;
192
373
  /**
193
374
  * Interval in cents between two frequencies.
194
375
  *
@@ -223,7 +404,7 @@ declare function ratioToCents(numerator: number, denominator: number): number;
223
404
  *
224
405
  * @category Pitch & Intervals
225
406
  */
226
- declare const JUST_RATIOS: Record<number, [number, number]>;
407
+ declare const JUST_RATIOS: Readonly<Record<number, readonly [number, number]>>;
227
408
  /**
228
409
  * Cents by which a five-limit just interval departs from its 12-TET tempering
229
410
  * (positive means the just interval is wider).
@@ -235,28 +416,109 @@ declare const JUST_RATIOS: Record<number, [number, number]>;
235
416
  */
236
417
  declare function justDeviationCents(semitoneClass: number): number;
237
418
 
238
- /** Default upper bound for synchronous event/window/candidate generation. */
419
+ /**
420
+ * Default upper bound for synchronous event/window/candidate generation.
421
+ *
422
+ * @category Core
423
+ */
239
424
  declare const DEFAULT_GENERATION_BUDGET = 1000000;
240
- /** Require a finite JavaScript number and return it unchanged. */
425
+ /**
426
+ * Require a finite JavaScript number and return it unchanged.
427
+ *
428
+ * @category Core
429
+ */
241
430
  declare function assertFiniteNumber(value: number, name: string): number;
242
- /** Require an integer in the inclusive range `[min, max]`. */
431
+ /**
432
+ * Require an integer in the inclusive range `[min, max]`.
433
+ *
434
+ * @category Core
435
+ */
243
436
  declare function assertInteger(value: number, name: string, min?: number, max?: number): number;
244
- /** Require a positive safe integer, optionally capped by `max`. */
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
+ */
245
477
  declare function assertPositiveInt(value: number, name: string, max?: number): number;
246
- /** Require a finite number in the inclusive range `[min, max]`. */
478
+ /**
479
+ * Require a finite number in the inclusive range `[min, max]`.
480
+ *
481
+ * @category Core
482
+ */
247
483
  declare function assertRange(value: number, min: number, max: number, name: string): number;
248
- /** Reject work estimates that would exceed a synchronous generation budget. */
249
- declare function assertGenerationBudget(estimated: number, name: string, limit?: number): number;
250
- /** Validate a time signature, including additive grouping, before any early return. */
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
+ */
251
495
  declare function assertTimeSignature(ts: TimeSignature, name?: string): TimeSignature;
252
- /** Validate the finite fields of one timeline note event. */
253
- declare function assertNoteEvent(event: NoteEvent, name?: string, options?: {
254
- allowNonPositiveDuration?: boolean;
255
- }): NoteEvent;
256
- /** Validate an event array and its allocation budget without copying it. */
257
- declare function assertNoteEvents(events: NoteEvent[], name?: string, options?: {
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
+ */
258
507
  allowNonPositiveDuration?: boolean;
508
+ /** Upper bound on the event count; defaults to the generation budget. */
259
509
  budget?: number;
260
- }): NoteEvent[];
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[];
261
523
 
262
- export { DEFAULT_GENERATION_BUDGET, type IndexedNoteEvent, IntervalQuality, JUST_RATIOS, NoteEvent, type NoteEventIndex, type Rng, TWELVE_TET, TimeSignature, type Tuning, assertFiniteNumber, assertGenerationBudget, assertInteger, assertNoteEvent, assertNoteEvents, assertPositiveInt, assertRange, assertTimeSignature, centsBetweenFreq, centsOfSteps, classifyInterval, createNoteEventIndex, createRng, edo, frequencyOf, isConsonantInterval, isPerfectInterval, justDeviationCents, nearestStep, ratioToCents };
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,92 +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-AKLYPWIN.js";
12
+ ratioToCents,
13
+ stepOf,
14
+ stepsOfCents
15
+ } from "../chunk-I24MV65U.js";
12
16
  import {
13
17
  createRng
14
- } from "../chunk-62QZKCJQ.js";
18
+ } from "../chunk-HKGYCQ34.js";
15
19
  import {
16
20
  barPositionToBeat,
21
+ barPositionToPulse,
17
22
  beatToBarPosition,
18
23
  beatsPerBar,
19
24
  createNoteEventIndex,
25
+ formatBarPosition,
20
26
  formatTimeSignature,
21
27
  isCompound,
22
28
  isStrongBeat,
23
29
  metricWeight,
24
30
  parseTimeSignature,
31
+ pulseBeats,
25
32
  pulsesPerBar,
26
33
  tuplet
27
- } from "../chunk-I5R7ZBJS.js";
28
- import {
29
- formatNote,
30
- midiToNote,
31
- noteToMidi,
32
- noteToPitchClass,
33
- parseNote,
34
- spelledInterval
35
- } from "../chunk-DYWFNC4A.js";
34
+ } from "../chunk-YLEMGEFH.js";
36
35
  import {
36
+ BudgetExceededError,
37
+ ConsonanceClass,
37
38
  DEFAULT_GENERATION_BUDGET,
38
- IntervalQuality,
39
+ InvalidInputError,
40
+ NoSolutionError,
39
41
  assertFiniteNumber,
40
42
  assertGenerationBudget,
41
43
  assertInteger,
42
44
  assertNoteEvent,
43
45
  assertNoteEvents,
46
+ assertOneOf,
44
47
  assertPositiveInt,
45
48
  assertRange,
46
49
  assertTimeSignature,
47
50
  classifyInterval,
51
+ diatonicLetterOf,
52
+ dropSilentNotes,
53
+ formatNote,
54
+ intervalSemitones,
48
55
  isConsonantInterval,
49
- isPerfectInterval
50
- } from "../chunk-FBQK3ZQN.js";
56
+ isLibcantusError,
57
+ isPerfectInterval,
58
+ midiToNote,
59
+ naturalPitchClassOf,
60
+ noteToMidi,
61
+ noteToPitchClass,
62
+ parseInterval,
63
+ parseNote,
64
+ pitchClassOf,
65
+ spelledInterval,
66
+ transposeByInterval,
67
+ transposeNote
68
+ } from "../chunk-XEBG3G53.js";
51
69
  export {
70
+ BudgetExceededError,
71
+ ConsonanceClass,
52
72
  DEFAULT_GENERATION_BUDGET,
53
- IntervalQuality,
73
+ InvalidInputError,
54
74
  JUST_RATIOS,
75
+ NoSolutionError,
55
76
  TWELVE_TET,
56
77
  assertFiniteNumber,
57
78
  assertGenerationBudget,
58
79
  assertInteger,
59
80
  assertNoteEvent,
60
81
  assertNoteEvents,
82
+ assertOneOf,
61
83
  assertPositiveInt,
62
84
  assertRange,
63
85
  assertTimeSignature,
64
86
  barPositionToBeat,
87
+ barPositionToPulse,
65
88
  beatToBarPosition,
66
89
  beatsPerBar,
67
90
  centsBetweenFreq,
91
+ centsFromNearestStep,
68
92
  centsOfSteps,
93
+ centsToRatio,
69
94
  classifyInterval,
70
95
  createNoteEventIndex,
71
96
  createRng,
97
+ diatonicLetterOf,
98
+ dropSilentNotes,
72
99
  edo,
100
+ formatBarPosition,
73
101
  formatNote,
74
102
  formatTimeSignature,
75
103
  frequencyOf,
104
+ intervalSemitones,
76
105
  isCompound,
77
106
  isConsonantInterval,
107
+ isLibcantusError,
78
108
  isPerfectInterval,
79
109
  isStrongBeat,
80
110
  justDeviationCents,
81
111
  metricWeight,
82
112
  midiToNote,
113
+ naturalPitchClassOf,
83
114
  nearestStep,
84
115
  noteToMidi,
85
116
  noteToPitchClass,
117
+ parseInterval,
86
118
  parseNote,
87
119
  parseTimeSignature,
120
+ pitchClassOf,
121
+ pulseBeats,
88
122
  pulsesPerBar,
89
123
  ratioToCents,
90
124
  spelledInterval,
125
+ stepOf,
126
+ stepsOfCents,
127
+ transposeByInterval,
128
+ transposeNote,
91
129
  tuplet
92
130
  };
93
131
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
- import { K as KeyScale } from './types-MGP29r3t.cjs';
2
- import { C as Chord } from './index-BNlGP86A.cjs';
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-BNlGP86A.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;