@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,11 +1,12 @@
1
- import { T as TimeSignature } from '../index-Cl7mhOFM.js';
2
- import { K as KeyScale, N as NoteEvent } from '../types-MGP29r3t.js';
3
- import { V as VoiceSnapshot, N as NoteSafety, S as SafetyProfile } from '../index-Jc0Ryi79.js';
4
- import { b as ChordTimeline, C as CadenceHit } from '../index-DxvARIP2.js';
5
- export { a as ChordSegment, c as ChordTimelineOptions, d as ChordTimelineResult, e as chordTimelineFromChords, f as chordTimelineFromNotes, g as detectCadences } from '../index-DxvARIP2.js';
6
- import { C as Chord } from '../index-Bt4tOUD9.js';
7
- export { C as ChordMatch, D as DetectChordOptions, K as KeyMatch, d as detectChord, a as detectChordBest, b as detectKey } from '../index-JnF4v9bx.js';
8
- export { B as BorrowedSource, C as Cadence, a as ChordAnalysis, H as HarmonicFunction, b as analyzeChord, c as borrowedSource, d as detectCadence, f as functionOf, i as isBorrowedChord, e as isDiatonic, g as isMinorKey, p as parallelKey, s as secondaryDominant } from '../function-DVH4Eusr.js';
1
+ import { T as TimeSignature } from '../index-DXd9JikN.js';
2
+ import { K as KeyScale, N as NoteEvent } from '../types-BN6_Q9Kl.js';
3
+ import { C as Chord, d as ChordToneRole } from '../index-C83mZI1P.js';
4
+ export { a as ChordQuality, b as ChordSegment, c as ChordSpan, P as PitchSpelling } from '../index-C83mZI1P.js';
5
+ import { V as VoiceSnapshot, N as NoteSafety, S as SafetyProfile } from '../index-6VR8-kit.js';
6
+ import { a as ChordTimeline, C as CadenceHit } from '../index-C5UBbQ4G.js';
7
+ export { b as ChordTimelineOptions, c as ChordTimelineResult, d as chordTimelineFromChords, e as chordTimelineFromNotes, f as detectCadences } from '../index-C5UBbQ4G.js';
8
+ export { C as ChordMatch, D as DetectChordOptions, a as DetectKeyOptions, K as KeyMatch, b as KeyVariant, d as detectChord, c as detectChordBest, e as detectKey, f as detectKeyBest, g as detectKeyFromNotes } from '../index-70p168VM.js';
9
+ export { B as BorrowedSource, C as Cadence, a as ChordAnalysis, H as HarmonicFunction, b as analyzeChord, c as borrowedSource, d as detectCadence, f as functionOf, i as isBorrowedChord, e as isDiatonic, g as isMinorKey, p as parallelKey, s as secondaryDominant } from '../function-BtiZb9i9.js';
9
10
 
10
11
  /**
11
12
  * Roman-numeral parsing and formatting.
@@ -46,30 +47,64 @@ export { B as BorrowedSource, C as Cadence, a as ChordAnalysis, H as HarmonicFun
46
47
  * @category Functional Harmony
47
48
  */
48
49
  declare function romanToChord(text: string, key: KeyScale): Chord;
50
+ /**
51
+ * Options for {@link chordToRoman}.
52
+ *
53
+ * @category Functional Harmony
54
+ */
55
+ type ChordToRomanOptions = {
56
+ /**
57
+ * Render a tonicizing chord as an applied numeral (`V7/V`, `viio7/ii`)
58
+ * instead of naming its root against the home key (`II7`, `#ivo7`).
59
+ *
60
+ * Off by default: naming the root is always a correct spelling, whereas
61
+ * whether a chromatic dominant is genuinely applied is a reading only the
62
+ * caller can make. Turning it on makes {@link chordToRoman} the exact inverse
63
+ * of {@link romanToChord} for the chords {@link secondaryDominant} builds.
64
+ */
65
+ applied?: boolean;
66
+ };
49
67
  /**
50
68
  * Render a chord as a Roman numeral relative to a key.
51
69
  *
52
70
  * Diatonic roots take their scale-degree numeral directly, so numerals are
53
71
  * correct in both major and minor keys (and any custom scale). The quality
54
72
  * selects the case and suffix; chromatic roots receive a flat/sharp spelling by
55
- * convention. When the chord carries a `bassPc` on a chord tone, a figured-bass
56
- * inversion (`6`, `64`, `65`, `43`, `42`) is emitted for plain triads and true
57
- * seventh chords; added-tone and extended qualities have no lossless figure and
58
- * render in root position instead (the bass is dropped, pitch classes are
59
- * preserved).
73
+ * convention. A key that is not heptatonic has no numeral of its own for every
74
+ * degree, so its roots are named against its parallel major.
75
+ *
76
+ * When the chord carries a `bassPc` on a chord tone, a figured-bass inversion
77
+ * (`6`, `64`, `65`, `43`, `42`) is emitted for plain triads and true seventh
78
+ * chords; added-tone and extended qualities have no lossless figure and render
79
+ * in root position instead (the bass is dropped, pitch classes are preserved).
80
+ *
81
+ * A `bassPc` that is *not* a chord tone — a pedal or passing bass such as
82
+ * `C/D` — is likewise dropped, and the numeral names the upper chord alone.
83
+ * Roman-numeral notation spends its slash on applied chords (`V7/V`), so there
84
+ * is no unambiguous way to write such a bass; a caller that must keep it should
85
+ * read `chord.bassPc` alongside the numeral.
60
86
  *
61
87
  * @param chord The chord to name.
62
88
  * @param key The prevailing key.
89
+ * @param opts `applied` renders tonicizing chords as `V7/V`-style numerals.
63
90
  * @returns The Roman numeral string.
64
91
  * @example
65
92
  * ```ts
66
93
  * import { chordToRoman, makeChord, majorKey } from '@libraz/libcantus';
67
94
  * chordToRoman(makeChord(7, 'dom7'), majorKey(0)); // => 'V7' (G7 in C major)
95
+ * chordToRoman(makeChord(2, 'dom7'), majorKey(0), { applied: true }); // => 'V7/V'
68
96
  * ```
69
97
  * @category Functional Harmony
70
98
  */
71
- declare function chordToRoman(chord: Chord, key: KeyScale): string;
99
+ declare function chordToRoman(chord: Chord, key: KeyScale, opts?: ChordToRomanOptions): string;
72
100
 
101
+ /**
102
+ * A suspension figure, named by the interval above the bass and the interval it
103
+ * resolves to.
104
+ *
105
+ * @category Arrangement & Analysis
106
+ */
107
+ type SuspensionFigure = 'sus4-3' | 'sus6-5' | 'sus7-6' | 'sus9-8' | 'sus2-3';
73
108
  /**
74
109
  * A theory annotation attached to a note.
75
110
  *
@@ -77,7 +112,7 @@ declare function chordToRoman(chord: Chord, key: KeyScale): string;
77
112
  */
78
113
  type TheoryLabel = {
79
114
  kind: 'chordTone';
80
- role: 'root' | 'third' | 'fifth' | 'sixth' | 'seventh';
115
+ role: ChordToneRole;
81
116
  } | {
82
117
  kind: 'tension';
83
118
  degree: 9 | 11 | 13;
@@ -89,7 +124,7 @@ type TheoryLabel = {
89
124
  kind: 'neighbor';
90
125
  } | {
91
126
  kind: 'suspension';
92
- type: 'sus4-3' | 'sus7-6' | 'sus9-8' | 'sus2-3';
127
+ type: SuspensionFigure;
93
128
  resolveTo: number;
94
129
  } | {
95
130
  kind: 'anticipation';
@@ -105,21 +140,60 @@ type TheoryLabel = {
105
140
  /**
106
141
  * A note with its theory labels and a short rationale.
107
142
  *
143
+ * The labels describe the note against the chord sounding at its **onset**;
144
+ * they are not re-derived for the later chords a long note sustains across.
145
+ * `analyzeArrangement` does re-check every crossed chord change, and reports
146
+ * what it finds as conflicts, so a note labelled a chord tone here can still
147
+ * appear in `conflicts` at a later beat.
148
+ *
149
+ * The identity fields make an annotation mappable back to the caller's input:
150
+ * `originalIndex` is the note's position in the array that was passed in, and
151
+ * `trackIndex` is set by {@link analyzeArrangement}.
152
+ *
108
153
  * @category Arrangement & Analysis
109
154
  */
110
155
  type AnalyzedNote = {
111
156
  noteId: number;
157
+ /** The note's index in the caller's own array, when the caller supplied one. */
158
+ originalIndex?: number;
159
+ /** The note's track, set by `analyzeArrangement`. */
160
+ trackIndex?: number;
161
+ pitch: number;
162
+ startBeat: number;
163
+ durationBeat: number;
112
164
  labels: TheoryLabel[];
113
165
  rationale?: string;
114
166
  };
115
167
  /**
116
- * A single note in a monophonic voice: a {@link NoteEvent} with a stable id.
168
+ * A single note in a monophonic voice: a {@link NoteEvent} with a stable id and,
169
+ * optionally, its index in the caller's original array.
117
170
  *
118
171
  * @category Arrangement & Analysis
119
172
  */
120
173
  type VoiceNote = NoteEvent & {
174
+ id?: number;
175
+ originalIndex?: number;
176
+ };
177
+ /**
178
+ * A voice note whose id has been assigned, as {@link toVoiceNotes} returns and
179
+ * {@link analyzeArrangement} works in.
180
+ *
181
+ * @category Arrangement & Analysis
182
+ */
183
+ type IdentifiedVoiceNote = VoiceNote & {
121
184
  id: number;
122
185
  };
186
+ /**
187
+ * Give plain note events the ids {@link analyzeVoice} reports back.
188
+ *
189
+ * The id is only an identity handle, so the array position serves; this exists
190
+ * so the mapping is a named call rather than a `map` every caller writes.
191
+ *
192
+ * @param events The note events, in time order.
193
+ * @returns The same notes as voice notes, each carrying its index as its id.
194
+ * @category Arrangement & Analysis
195
+ */
196
+ declare function toVoiceNotes(events: readonly NoteEvent[]): IdentifiedVoiceNote[];
123
197
  /**
124
198
  * Label every note of a voice with its theory roles.
125
199
  *
@@ -136,26 +210,28 @@ type VoiceNote = NoteEvent & {
136
210
  * escape) — only harmonic ones. Callers with truly polyphonic material should
137
211
  * split it into monophonic sub-voices first, as `analyzeArrangement` does.
138
212
  *
139
- * @param voice The monophonic voice, in time order.
213
+ * @param voice The monophonic voice, in time order. A note without an `id` is
214
+ * identified by its position, so plain note events can be passed straight in.
140
215
  * @param chordAtBeat Chord sounding at a given beat, or null.
141
216
  * @param key Key context for leading-tone detection.
142
- * @param otherVoicesAtBeat Other sounding voices at a given beat.
217
+ * @param otherVoicesAtBeat Other sounding voices at a given beat; defaults to
218
+ * none, which is the whole story for a solo line.
143
219
  * @returns One annotation per input note.
144
220
  * @example
145
221
  * ```ts
146
222
  * import { analyzeVoice, makeChord, majorKey } from '@libraz/libcantus';
147
223
  * const voice = [
148
- * { id: 0, pitch: 60, startBeat: 0, durationBeat: 1 },
149
- * { id: 1, pitch: 62, startBeat: 1, durationBeat: 1 },
150
- * { id: 2, pitch: 64, startBeat: 2, durationBeat: 1 },
224
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
225
+ * { pitch: 62, startBeat: 1, durationBeat: 1 },
226
+ * { pitch: 64, startBeat: 2, durationBeat: 1 },
151
227
  * ];
152
228
  * const cMajor = makeChord(0, 'maj');
153
- * const labels = analyzeVoice(voice, () => cMajor, majorKey(0), () => []);
229
+ * const labels = analyzeVoice(voice, () => cMajor, majorKey(0));
154
230
  * labels; // one AnalyzedNote per input note, in the same order
155
231
  * ```
156
232
  * @category Arrangement & Analysis
157
233
  */
158
- declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) => Chord | null, key: KeyScale, otherVoicesAtBeat: (beat: number) => VoiceSnapshot[]): AnalyzedNote[];
234
+ declare function analyzeVoice(voice: readonly VoiceNote[], chordAtBeat: (beat: number) => Chord | null, key: KeyScale, otherVoicesAtBeat?: (beat: number) => VoiceSnapshot[]): AnalyzedNote[];
159
235
 
160
236
  /**
161
237
  * Arrangement track-role analysis: a single harmony is inferred from every
@@ -167,9 +243,18 @@ declare function analyzeVoice(voice: VoiceNote[], chordAtBeat: (beat: number) =>
167
243
  /**
168
244
  * The musical role a track plays in the arrangement.
169
245
  *
246
+ * Only `drums` changes the analysis: its pitches select instruments rather than
247
+ * naming harmony, so such a track is excluded from chord and key inference and
248
+ * from every voice-leading comparison, and is reported back with no
249
+ * annotations. `harmony` is meaningful through
250
+ * {@link ArrangementOptions.harmonyTracks}, which names the tracks the chords
251
+ * are inferred from. The rest are labels carried through to
252
+ * {@link TrackAnalysis.role} for the caller's own use; nothing is inferred, so
253
+ * a track left unlabelled reports `'other'` rather than a guess.
254
+ *
170
255
  * @category Arrangement & Analysis
171
256
  */
172
- type TrackRole = 'melody' | 'harmony' | 'bass' | 'other';
257
+ type TrackRole = 'melody' | 'harmony' | 'bass' | 'drums' | 'other';
173
258
  /**
174
259
  * One input track: its notes plus optional name and role.
175
260
  *
@@ -178,7 +263,7 @@ type TrackRole = 'melody' | 'harmony' | 'bass' | 'other';
178
263
  type ArrangementTrack = {
179
264
  name?: string;
180
265
  role?: TrackRole;
181
- notes: NoteEvent[];
266
+ notes: readonly NoteEvent[];
182
267
  };
183
268
  /**
184
269
  * A track after analysis: its resolved name, role, and per-note annotations.
@@ -199,10 +284,22 @@ type TrackAnalysis = {
199
284
  type Conflict = {
200
285
  beat: number;
201
286
  trackName: string;
287
+ /** The clashing track's index in the caller's input array. */
288
+ trackIndex: number;
289
+ /** The note's index in that track's own note array. */
290
+ originalIndex?: number;
291
+ /** The id the note carries in {@link TrackAnalysis.notes}. */
292
+ noteId: number;
202
293
  pitch: number;
294
+ /**
295
+ * The note's theory labels at its onset, so a caller can tell a genuine clash
296
+ * from a passing tone, a neighbour, or a prepared suspension — all of which
297
+ * are ordinary non-chord tones that still evaluate as unsafe.
298
+ */
299
+ labels: TheoryLabel[];
203
300
  /** The clash severity: {@link NoteSafety.Warning} or {@link NoteSafety.Dissonant}. */
204
301
  safety: NoteSafety;
205
- /** {@link import('../safety/index.js').SafetyResult.reasons} bitmask. */
302
+ /** {@link SafetyResult.reasons} bitmask. */
206
303
  reasons: number;
207
304
  /** Preferred stepwise resolution, when the safety evaluator provides one. */
208
305
  resolveTo?: number;
@@ -232,6 +329,23 @@ type ArrangementAnalysis = {
232
329
  type ArrangementOptions = {
233
330
  /** Key context; inferred from the pooled notes when omitted. */
234
331
  key?: KeyScale;
332
+ /**
333
+ * A chord timeline to analyse against, instead of inferring one from the
334
+ * notes. Supply it to analyse a hand-written progression, or to reuse the
335
+ * timeline of an earlier call rather than re-running chord inference.
336
+ */
337
+ timeline?: ChordTimeline;
338
+ /**
339
+ * Indices of the tracks the harmony is inferred from. Defaults to every
340
+ * pitched track. Percussion tracks are excluded either way.
341
+ */
342
+ harmonyTracks?: number[];
343
+ /**
344
+ * Lowest severity reported in `conflicts`.
345
+ *
346
+ * @defaultValue {@link NoteSafety.Warning}
347
+ */
348
+ minSeverity?: NoteSafety;
235
349
  /**
236
350
  * Time signature; defaults to 4/4.
237
351
  *
@@ -250,16 +364,31 @@ type ArrangementOptions = {
250
364
  * @defaultValue `'pop'`
251
365
  */
252
366
  profile?: SafetyProfile;
367
+ /**
368
+ * Upper bound on the work this call may do — note counts, windows, and
369
+ * candidate counts are each checked against it before anything is allocated.
370
+ *
371
+ * Raise it to analyse a piece larger than the default allows; the default is
372
+ * {@link DEFAULT_GENERATION_BUDGET}, chosen so a runaway input fails fast
373
+ * rather than blocking the thread.
374
+ *
375
+ * @defaultValue {@link DEFAULT_GENERATION_BUDGET}
376
+ */
377
+ budget?: number;
253
378
  };
254
379
  /**
255
380
  * Analyse a whole arrangement against a single inferred harmony.
256
381
  *
257
- * The chord timeline and key are inferred from the pooled notes of every track
258
- * (see {@link chordTimelineFromNotes}); pooling all voices is robust even when
259
- * roles are absent or a track doubles the harmony, so it is preferred over
260
- * deriving the harmony from a subset. Zero- and negative-length notes never
261
- * sound, so they are dropped at ingest and appear in neither the annotations
262
- * nor the conflicts.
382
+ * The chord timeline and key are inferred from the pooled notes of every
383
+ * pitched track (see {@link chordTimelineFromNotes}); pooling all voices is
384
+ * robust even when roles are absent or a track doubles the harmony, so it is
385
+ * preferred over deriving the harmony from a subset. A track marked
386
+ * `role: 'drums'` is excluded — its pitches name instruments, not harmony, and
387
+ * pooling them would corrupt the key, the timeline and every reading built on
388
+ * them. Pass `harmonyTracks` to restrict inference further, or `timeline` to
389
+ * analyse against a progression you already have. Zero- and negative-length
390
+ * notes never sound, so they are dropped at ingest and appear in neither the
391
+ * annotations nor the conflicts.
263
392
  *
264
393
  * Voice analysis assumes one monophonic voice at a time, so each track is
265
394
  * first partitioned into monophonic sub-voices (a polyphonic block-chord track
@@ -272,6 +401,13 @@ type ArrangementOptions = {
272
401
  * evaluations that are not {@link NoteSafety.Safe} become conflicts, one per
273
402
  * clashing beat, sorted worst severity first and then by beat.
274
403
  *
404
+ * Every ordinary non-chord tone — a passing note, a neighbour, a prepared
405
+ * suspension — is by definition unsafe against the chord under it, so the
406
+ * conflict list is a report, not a fault list. Each conflict carries the note's
407
+ * `labels` and its `trackIndex`/`originalIndex`, so a caller can tell those
408
+ * apart and map a conflict back to the note it passed in; `minSeverity` narrows
409
+ * the list to outright dissonance.
410
+ *
275
411
  * @param tracks The tracks to analyse.
276
412
  * @param opts Analysis options; see {@link ArrangementOptions}.
277
413
  * @returns The inferred harmony, per-track annotations, cadences, and conflicts.
@@ -311,12 +447,12 @@ type TensionPoint = {
311
447
  *
312
448
  * At each sample beat the tension combines three normalized terms:
313
449
  * - the sounding chord's harmonic function (dominant `1`, subdominant `0.5`,
314
- * tonic or no chord `0`), weighted {@link FUNCTION_WEIGHT};
450
+ * tonic or no chord `0`), weighted `FUNCTION_WEIGHT`;
315
451
  * - the dissonance of the sounding notes — the larger of the share of sounding
316
452
  * pitches that are not chord tones and the share that {@link evaluateSafety}
317
- * rates {@link NoteSafety.Dissonant} — weighted {@link DISSONANCE_WEIGHT};
453
+ * rates {@link NoteSafety.Dissonant} — weighted `DISSONANCE_WEIGHT`;
318
454
  * - the registral span of the sounding notes, saturating at
319
- * {@link SPAN_SATURATION} semitones, weighted {@link SPAN_WEIGHT}.
455
+ * `SPAN_SATURATION` semitones, weighted `SPAN_WEIGHT`.
320
456
  *
321
457
  * The weighted sum is clamped to [0, 1]. The harmony is inferred from all tracks
322
458
  * pooled together, so the result is deterministic and self-contained.
@@ -337,5 +473,29 @@ type TensionPoint = {
337
473
  declare function tensionCurve(tracks: ArrangementTrack[], opts?: ArrangementOptions & {
338
474
  step?: number;
339
475
  }): TensionPoint[];
476
+ /**
477
+ * Sample the tension of an arrangement already analysed by
478
+ * {@link analyzeArrangement}, reusing its harmony instead of inferring it again.
479
+ *
480
+ * `analyzeArrangement` followed by `tensionCurve` runs chord and key inference
481
+ * twice over the same notes. This takes the analysis it already produced, so
482
+ * the curve and the annotations describe the same harmony by construction.
483
+ *
484
+ * @param tracks The same tracks that were analysed.
485
+ * @param analysis The result of {@link analyzeArrangement} for those tracks.
486
+ * @param opts Analysis options plus an optional `step` (default one beat).
487
+ * @returns One {@link TensionPoint} per sampled beat, in beat order.
488
+ * @example
489
+ * ```ts
490
+ * import { analyzeArrangement, tensionCurveFrom } from '@libraz/libcantus';
491
+ * const tracks = [{ notes: [{ pitch: 60, startBeat: 0, durationBeat: 4 }] }];
492
+ * const analysis = analyzeArrangement(tracks);
493
+ * tensionCurveFrom(tracks, analysis, { step: 1 });
494
+ * ```
495
+ * @category Arrangement & Analysis
496
+ */
497
+ declare function tensionCurveFrom(tracks: ArrangementTrack[], analysis: ArrangementAnalysis, opts?: ArrangementOptions & {
498
+ step?: number;
499
+ }): TensionPoint[];
340
500
 
341
- export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, CadenceHit, ChordTimeline, type Conflict, type TensionPoint, type TheoryLabel, type TrackAnalysis, type TrackRole, type VoiceNote, analyzeArrangement, analyzeVoice, chordToRoman, romanToChord, tensionCurve };
501
+ export { type AnalyzedNote, type ArrangementAnalysis, type ArrangementOptions, type ArrangementTrack, CadenceHit, Chord as ChordData, ChordTimeline, type ChordToRomanOptions, ChordToneRole, type Conflict, type IdentifiedVoiceNote, KeyScale, NoteEvent, NoteSafety, SafetyProfile, type SuspensionFigure, type TensionPoint, type TheoryLabel, TimeSignature, type TrackAnalysis, type TrackRole, type VoiceNote, VoiceSnapshot, analyzeArrangement, analyzeVoice, chordToRoman, romanToChord, tensionCurve, tensionCurveFrom, toVoiceNotes };
@@ -4,9 +4,11 @@ import {
4
4
  chordTimelineFromChords,
5
5
  chordTimelineFromNotes,
6
6
  detectCadences,
7
- tensionCurve
8
- } from "../chunk-GOAW4VYL.js";
9
- import "../chunk-I5R7ZBJS.js";
7
+ tensionCurve,
8
+ tensionCurveFrom,
9
+ toVoiceNotes
10
+ } from "../chunk-Q2XUP227.js";
11
+ import "../chunk-YLEMGEFH.js";
10
12
  import {
11
13
  analyzeChord,
12
14
  borrowedSource,
@@ -15,6 +17,8 @@ import {
15
17
  detectChord,
16
18
  detectChordBest,
17
19
  detectKey,
20
+ detectKeyBest,
21
+ detectKeyFromNotes,
18
22
  functionOf,
19
23
  isBorrowedChord,
20
24
  isDiatonic,
@@ -22,11 +26,14 @@ import {
22
26
  parallelKey,
23
27
  romanToChord,
24
28
  secondaryDominant
25
- } from "../chunk-CHMXELDS.js";
26
- import "../chunk-EH6EM37U.js";
27
- import "../chunk-MIWO425M.js";
28
- import "../chunk-FBQK3ZQN.js";
29
+ } from "../chunk-KMHOICIY.js";
30
+ import {
31
+ NoteSafety
32
+ } from "../chunk-NYYWMV2C.js";
33
+ import "../chunk-D2FAJBYL.js";
34
+ import "../chunk-XEBG3G53.js";
29
35
  export {
36
+ NoteSafety,
30
37
  analyzeArrangement,
31
38
  analyzeChord,
32
39
  analyzeVoice,
@@ -39,6 +46,8 @@ export {
39
46
  detectChord,
40
47
  detectChordBest,
41
48
  detectKey,
49
+ detectKeyBest,
50
+ detectKeyFromNotes,
42
51
  functionOf,
43
52
  isBorrowedChord,
44
53
  isDiatonic,
@@ -46,6 +55,8 @@ export {
46
55
  parallelKey,
47
56
  romanToChord,
48
57
  secondaryDominant,
49
- tensionCurve
58
+ tensionCurve,
59
+ tensionCurveFrom,
60
+ toVoiceNotes
50
61
  };
51
62
  //# sourceMappingURL=index.js.map