@libraz/libcantus 0.9.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +61 -261
  2. package/README_ja.md +115 -0
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +213 -456
  6. package/dist/analyze/index.d.ts +213 -456
  7. package/dist/analyze/index.js +56 -9
  8. package/dist/chunk-3JXCW6DX.cjs +3220 -0
  9. package/dist/chunk-3JXCW6DX.cjs.map +1 -0
  10. package/dist/chunk-4A2RMUCC.js +7647 -0
  11. package/dist/chunk-4A2RMUCC.js.map +1 -0
  12. package/dist/chunk-6XPTBE7Y.cjs +49 -0
  13. package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
  14. package/dist/chunk-AFM2ULJH.cjs +5121 -0
  15. package/dist/chunk-AFM2ULJH.cjs.map +1 -0
  16. package/dist/chunk-E3F4AHAH.cjs +690 -0
  17. package/dist/chunk-E3F4AHAH.cjs.map +1 -0
  18. package/dist/chunk-ELCTZUJJ.js +379 -0
  19. package/dist/chunk-ELCTZUJJ.js.map +1 -0
  20. package/dist/chunk-FVSI36UV.js +690 -0
  21. package/dist/chunk-FVSI36UV.js.map +1 -0
  22. package/dist/chunk-GEDTJHAC.cjs +1 -0
  23. package/dist/chunk-GEDTJHAC.cjs.map +1 -0
  24. package/dist/chunk-GOZG57ER.js +3220 -0
  25. package/dist/chunk-GOZG57ER.js.map +1 -0
  26. package/dist/chunk-H3VORETI.js +2243 -0
  27. package/dist/chunk-H3VORETI.js.map +1 -0
  28. package/dist/chunk-H42UHPYW.js +1577 -0
  29. package/dist/chunk-H42UHPYW.js.map +1 -0
  30. package/dist/chunk-HFPS7JFD.js +1 -0
  31. package/dist/chunk-INHVXP6Z.js +3053 -0
  32. package/dist/chunk-INHVXP6Z.js.map +1 -0
  33. package/dist/chunk-K4ZJUGF4.cjs +379 -0
  34. package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
  35. package/dist/chunk-KNJEKLKV.js +49 -0
  36. package/dist/chunk-KNJEKLKV.js.map +1 -0
  37. package/dist/chunk-M4CWTJWJ.cjs +7647 -0
  38. package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
  39. package/dist/chunk-MXO72PLY.cjs +211 -0
  40. package/dist/chunk-MXO72PLY.cjs.map +1 -0
  41. package/dist/chunk-NLEU4QSE.cjs +360 -0
  42. package/dist/chunk-NLEU4QSE.cjs.map +1 -0
  43. package/dist/chunk-OE6WK2Y2.cjs +1577 -0
  44. package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
  45. package/dist/chunk-Q2WGKJNN.cjs +1064 -0
  46. package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
  47. package/dist/chunk-QYQQ5J6E.js +1 -0
  48. package/dist/chunk-QYQQ5J6E.js.map +1 -0
  49. package/dist/chunk-RHIJ3S4I.cjs +1 -0
  50. package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
  51. package/dist/chunk-TTCLJX6D.js +211 -0
  52. package/dist/chunk-TTCLJX6D.js.map +1 -0
  53. package/dist/chunk-UADYAYAT.cjs +3053 -0
  54. package/dist/chunk-UADYAYAT.cjs.map +1 -0
  55. package/dist/chunk-VOHZB45S.js +5121 -0
  56. package/dist/chunk-VOHZB45S.js.map +1 -0
  57. package/dist/chunk-VTI6FOLB.js +1064 -0
  58. package/dist/chunk-VTI6FOLB.js.map +1 -0
  59. package/dist/chunk-WWURE4XT.cjs +2243 -0
  60. package/dist/chunk-WWURE4XT.cjs.map +1 -0
  61. package/dist/chunk-YH3BIKHH.js +360 -0
  62. package/dist/chunk-YH3BIKHH.js.map +1 -0
  63. package/dist/core/index.cjs +103 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +96 -345
  66. package/dist/core/index.d.ts +96 -345
  67. package/dist/core/index.js +104 -7
  68. package/dist/generate/index.cjs +89 -10
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +437 -1095
  71. package/dist/generate/index.d.ts +437 -1095
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
  75. package/dist/index-B9MNZboQ.d.cts +770 -0
  76. package/dist/index-B9MNZboQ.d.ts +770 -0
  77. package/dist/index-BGFW-8P2.d.cts +2307 -0
  78. package/dist/index-BO6xE9YU.d.cts +1804 -0
  79. package/dist/index-BkGmlyI3.d.cts +470 -0
  80. package/dist/index-CCNJRG-P.d.cts +1095 -0
  81. package/dist/index-CGpRv5sl.d.cts +643 -0
  82. package/dist/index-C_8o--VE.d.cts +1242 -0
  83. package/dist/index-CgHqxdps.d.ts +2307 -0
  84. package/dist/index-Chbst3Jx.d.ts +1343 -0
  85. package/dist/index-Cj49F892.d.ts +470 -0
  86. package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
  87. package/dist/index-D57QRbVn.d.ts +1804 -0
  88. package/dist/index-DInQqT8Q.d.cts +66 -0
  89. package/dist/index-DInQqT8Q.d.ts +66 -0
  90. package/dist/index-Dzr13q3d.d.cts +1343 -0
  91. package/dist/index-G1uwGGWQ.d.ts +643 -0
  92. package/dist/index-szBv3496.d.ts +1242 -0
  93. package/dist/index.cjs +358 -15
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +17 -15
  96. package/dist/index.d.ts +17 -15
  97. package/dist/index.js +407 -64
  98. package/dist/masks-DYbE485J.d.cts +445 -0
  99. package/dist/masks-DYbE485J.d.ts +445 -0
  100. package/dist/model/index.cjs +39 -7
  101. package/dist/model/index.cjs.map +1 -1
  102. package/dist/model/index.d.cts +4685 -243
  103. package/dist/model/index.d.ts +4685 -243
  104. package/dist/model/index.js +40 -8
  105. package/dist/positional-Cptlj-xs.d.ts +287 -0
  106. package/dist/positional-DvH_sd8u.d.cts +287 -0
  107. package/dist/theory/index.cjs +101 -7
  108. package/dist/theory/index.cjs.map +1 -1
  109. package/dist/theory/index.d.cts +513 -173
  110. package/dist/theory/index.d.ts +513 -173
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +16 -11
  113. package/dist/chunk-36N73KZ3.cjs +0 -246
  114. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  115. package/dist/chunk-4HLLZPXT.cjs +0 -3077
  116. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  117. package/dist/chunk-7MGZ7D62.cjs +0 -498
  118. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  119. package/dist/chunk-7NKDPF74.js +0 -3077
  120. package/dist/chunk-7NKDPF74.js.map +0 -1
  121. package/dist/chunk-CAN76NCU.cjs +0 -679
  122. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  123. package/dist/chunk-D2FAJBYL.js +0 -479
  124. package/dist/chunk-D2FAJBYL.js.map +0 -1
  125. package/dist/chunk-EHVEBWM5.js +0 -1449
  126. package/dist/chunk-EHVEBWM5.js.map +0 -1
  127. package/dist/chunk-FCANXBAT.cjs +0 -30
  128. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  129. package/dist/chunk-FNDWXRRI.cjs +0 -479
  130. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  131. package/dist/chunk-GQSGS2N4.js +0 -173
  132. package/dist/chunk-GQSGS2N4.js.map +0 -1
  133. package/dist/chunk-HKGYCQ34.js +0 -53
  134. package/dist/chunk-HKGYCQ34.js.map +0 -1
  135. package/dist/chunk-I24MV65U.js +0 -106
  136. package/dist/chunk-I24MV65U.js.map +0 -1
  137. package/dist/chunk-KMHOICIY.js +0 -679
  138. package/dist/chunk-KMHOICIY.js.map +0 -1
  139. package/dist/chunk-LW53AHQU.cjs +0 -1117
  140. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  141. package/dist/chunk-N6IHQUOX.js +0 -1
  142. package/dist/chunk-NKIBQ6PT.js +0 -30
  143. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  144. package/dist/chunk-NP4NA2FB.cjs +0 -106
  145. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  146. package/dist/chunk-NVE34YGZ.cjs +0 -1449
  147. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  148. package/dist/chunk-NYYWMV2C.js +0 -327
  149. package/dist/chunk-NYYWMV2C.js.map +0 -1
  150. package/dist/chunk-Q2XUP227.js +0 -845
  151. package/dist/chunk-Q2XUP227.js.map +0 -1
  152. package/dist/chunk-Q6OP4U43.cjs +0 -327
  153. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  154. package/dist/chunk-RC6GBMCD.cjs +0 -845
  155. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  156. package/dist/chunk-RK35RIN7.js +0 -1117
  157. package/dist/chunk-RK35RIN7.js.map +0 -1
  158. package/dist/chunk-RUMU3UA6.cjs +0 -53
  159. package/dist/chunk-RUMU3UA6.cjs.map +0 -1
  160. package/dist/chunk-T64C7ZD7.cjs +0 -1
  161. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  162. package/dist/chunk-XEBG3G53.js +0 -498
  163. package/dist/chunk-XEBG3G53.js.map +0 -1
  164. package/dist/chunk-YLEMGEFH.js +0 -246
  165. package/dist/chunk-YLEMGEFH.js.map +0 -1
  166. package/dist/chunk-ZEJR4TKL.cjs +0 -173
  167. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  168. package/dist/function-BtiZb9i9.d.ts +0 -208
  169. package/dist/function-DNugAOrt.d.cts +0 -208
  170. package/dist/index-6VR8-kit.d.ts +0 -143
  171. package/dist/index-70p168VM.d.ts +0 -192
  172. package/dist/index-Be74mcon.d.cts +0 -152
  173. package/dist/index-BtXT6UQ0.d.cts +0 -192
  174. package/dist/index-C5UBbQ4G.d.ts +0 -152
  175. package/dist/index-C83mZI1P.d.ts +0 -242
  176. package/dist/index-DXd9JikN.d.cts +0 -246
  177. package/dist/index-DXd9JikN.d.ts +0 -246
  178. package/dist/index-OG65ocOr.d.cts +0 -272
  179. package/dist/index-OG65ocOr.d.ts +0 -272
  180. package/dist/index-tJhkmQgE.d.cts +0 -143
  181. package/dist/index-y_ak9_Hi.d.cts +0 -242
  182. package/dist/styled-DIRkk9Uz.d.ts +0 -506
  183. package/dist/styled-DJbkJQfj.d.cts +0 -506
  184. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  185. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  186. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -0,0 +1,2307 @@
1
+ import { a as Articulation, g as NoteEvent, d as KeyScale } from './index-B9MNZboQ.cjs';
2
+ import { S as SeedPath, d as PositionalRng, I as InstrumentProfile, f as StringedProfile } from './positional-DvH_sd8u.cjs';
3
+ import { T as TimeSignature } from './index-BkGmlyI3.cjs';
4
+ import { d as ChordQuality, e as ChordSegment, c as ChordLike, K as KeyLike, b as Chord, m as ProfileWeights, g as ChordSpan } from './index-C_8o--VE.cjs';
5
+ import { e as ChordTimeline, B as BorrowedSource, H as HarmonicFunction } from './index-Dzr13q3d.cjs';
6
+ import { H as HarmonyRole } from './index-B4o55NDr.cjs';
7
+
8
+ /**
9
+ * The samplers a generator needs, each addressed by the position it is drawing
10
+ * for rather than by how many draws came before it.
11
+ *
12
+ * Every method is a pure function of the source and the path: asking twice
13
+ * gives the same answer, asking in a different order changes nothing, and a
14
+ * position nobody asks about costs nothing. That is what lets a caller change
15
+ * one parameter — or one bar — without redrawing the rest of the piece.
16
+ *
17
+ * @category Composition
18
+ */
19
+ type Draw = {
20
+ /** The raw draw at a position: a float in [0, 1). */
21
+ at: (...path: SeedPath) => number;
22
+ /**
23
+ * Whether an event of probability `p` happens at a position. Because the
24
+ * underlying draw is fixed by the path, raising `p` only ever turns more
25
+ * positions on.
26
+ */
27
+ prob: (p: number, ...path: SeedPath) => boolean;
28
+ /** An integer in the inclusive range [lo, hi] at a position. */
29
+ range: (lo: number, hi: number, ...path: SeedPath) => number;
30
+ /** A float in [lo, hi) at a position. */
31
+ float: (lo: number, hi: number, ...path: SeedPath) => number;
32
+ };
33
+
34
+ /** Internal drum style selected by the public groove style. */
35
+ type DrumStyle = 'sparse' | 'standard' | 'fourOnFloor' | 'upbeat' | 'rock' | 'synth' | 'trap' | 'latin';
36
+ /** Every groove feel, in declaration order. */
37
+ declare const DRUM_FEELS: readonly ["straight", "swing", "shuffle"];
38
+ /** Groove feel controlling off-beat swing. */
39
+ type Feel = (typeof DRUM_FEELS)[number];
40
+ /**
41
+ * Groove feel (the swing/straight rhythmic character), under the name the
42
+ * public surface uses.
43
+ *
44
+ * @category Composition
45
+ */
46
+ type GrooveFeel = Feel;
47
+ /**
48
+ * Every public groove style, in declaration order. The type is derived from
49
+ * this list so the runtime guard and the type can never disagree.
50
+ */
51
+ declare const GROOVE_STYLES: readonly ["standard", "funk", "shuffle", "bossa", "trap", "halftime", "breakbeat", "house", "synthpop"];
52
+ /**
53
+ * Public groove style identifiers.
54
+ *
55
+ * @category Composition
56
+ */
57
+ type GrooveStyle = (typeof GROOVE_STYLES)[number];
58
+ /** Every public role, in declaration order. */
59
+ declare const DRUM_ROLES: readonly ["full", "ambient", "minimal", "fxOnly"];
60
+ /**
61
+ * Public role identifiers gating which voices are present.
62
+ *
63
+ * @category Composition
64
+ */
65
+ type DrumRole = (typeof DRUM_ROLES)[number];
66
+ /** Every public section, in declaration order. */
67
+ declare const PUBLIC_SECTIONS: readonly ["intro", "verse", "prechorus", "chorus", "bridge", "outro"];
68
+ /**
69
+ * Public section identifiers for {@link generateDrums}.
70
+ *
71
+ * These are the names a caller writes. The generator maps them onto a
72
+ * narrower internal set the pattern tables are keyed by, which answers to
73
+ * `'a'` and `'b'` and refuses `'verse'` and `'prechorus'`. That set is not
74
+ * published: a caller who reached for it by name would be held to a
75
+ * vocabulary that is not this one.
76
+ *
77
+ * @category Composition
78
+ */
79
+ type Section = (typeof PUBLIC_SECTIONS)[number];
80
+
81
+ /**
82
+ * The shared vocabulary model: a remembered figure, plus the conditions under
83
+ * which a player would reach for it.
84
+ *
85
+ * A rule-based generator can build a correct line and still sound characterless,
86
+ * because a player does not synthesise from scales — they assemble figures they
87
+ * already know. Those figures are not derivable, so they are held as data here:
88
+ * one record per figure, carrying its own applicability conditions, so choosing
89
+ * one is a deterministic lookup rather than a branch in a generator.
90
+ *
91
+ * ## Provenance
92
+ *
93
+ * No entry may reproduce a phrase from a particular recorded song. Only figures
94
+ * that belong to nobody are admitted — the common currency of a genre, the
95
+ * things every player of that music already plays. Every entry records which
96
+ * basis it qualifies under in {@link Vocabulary.provenance}, and the field is
97
+ * required so the judgment cannot be skipped while curating.
98
+ */
99
+
100
+ /**
101
+ * Every genre the built-in dictionaries name, in declaration order.
102
+ *
103
+ * The list is open in spirit even though the type is closed: a caller with a
104
+ * genre of its own supplies entries through the generation context rather than
105
+ * waiting for this list to grow.
106
+ *
107
+ * @category Composition
108
+ */
109
+ declare const GENRES: readonly ["pop", "rock", "motown", "funk", "soul", "gospel", "jazz", "blues", "bossa", "samba", "reggae", "country", "hiphop", "house", "dnb"];
110
+ /**
111
+ * The genre a figure belongs to. Genre is what selects material; it never
112
+ * deforms or rejects it.
113
+ *
114
+ * @category Composition
115
+ */
116
+ type Genre = (typeof GENRES)[number];
117
+ /**
118
+ * Every ground on which a figure may enter a dictionary, in declaration order.
119
+ *
120
+ * - `'idiom'`: a genre-defining figure no one owns, played by every player of
121
+ * that music — a backbeat, a walking approach, a two-and-four side stick.
122
+ * - `'construction'`: derived here from the genre's rhythmic grid rather than
123
+ * remembered from any performance.
124
+ * - `'traditional'`: from a folk or traditional repertoire with no author.
125
+ *
126
+ * @category Composition
127
+ */
128
+ declare const PROVENANCE_BASES: readonly ["idiom", "construction", "traditional"];
129
+ /**
130
+ * The ground on which a figure qualifies for a dictionary.
131
+ *
132
+ * @category Composition
133
+ */
134
+ type ProvenanceBasis = (typeof PROVENANCE_BASES)[number];
135
+ /**
136
+ * Why a figure may be published as vocabulary.
137
+ *
138
+ * @category Composition
139
+ */
140
+ type Provenance = {
141
+ /** Which ground the figure qualifies under. */
142
+ basis: ProvenanceBasis;
143
+ /**
144
+ * What the figure is, in the terms a player would use — "the genre's
145
+ * two-and-four side stick", not the name of a record it appears on.
146
+ */
147
+ note: string;
148
+ };
149
+ /**
150
+ * One remembered figure, together with the conditions it fits.
151
+ *
152
+ * The optional conditions are read as "no restriction" when absent: an entry
153
+ * with no `sections` fits every section, one with no `tempoRange` fits every
154
+ * tempo. {@link Vocabulary.difficulty} is on the same 1..5 scale as the
155
+ * playability report, and is compared against the difficulty ceiling: an entry
156
+ * harder than the ceiling is rejected, never simplified in place.
157
+ *
158
+ * @typeParam T - The material this kind of figure is made of: chord-relative
159
+ * degrees for a bass lick, voices on a grid for a drum pattern.
160
+ *
161
+ * @category Composition
162
+ */
163
+ type Vocabulary<T> = {
164
+ /** Stable identifier; a caller entry with the same id replaces a built-in. */
165
+ id: string;
166
+ genre: Genre;
167
+ /** The figure itself. */
168
+ material: T;
169
+ /**
170
+ * How the figure is played, over and above the material's own strokes.
171
+ *
172
+ * These are requirements, not decoration: where a query names the techniques
173
+ * the instrument can produce, an entry asking for one the instrument does not
174
+ * have is not offered. A kit with no flam is never handed flam material.
175
+ */
176
+ articulations: Articulation[];
177
+ /** Chord qualities the figure works over; absent means any chord. */
178
+ fitsOver?: ChordQuality[];
179
+ /** Sections the figure suits; absent means any section. */
180
+ sections?: Section[];
181
+ /** Inclusive tempo band in BPM; absent means any tempo. */
182
+ tempoRange?: [number, number];
183
+ /** Time signature the figure is written in; absent means any. */
184
+ ts?: TimeSignature;
185
+ /** How hard the figure is to play, from 1 to 5. */
186
+ difficulty: number;
187
+ /** Why this figure may be published; see the module documentation. */
188
+ provenance: Provenance;
189
+ };
190
+ /**
191
+ * What a generator knows about the moment it is choosing a figure for.
192
+ *
193
+ * Every field is optional and an absent field asks nothing: a query with no
194
+ * genre matches every genre, exactly as an entry with no `sections` matches
195
+ * every section.
196
+ *
197
+ * @category Composition
198
+ */
199
+ type VocabularyQuery = {
200
+ genre?: Genre;
201
+ section?: Section;
202
+ /** Tempo in BPM, matched against each entry's band. */
203
+ bpm?: number;
204
+ ts?: TimeSignature;
205
+ /** The difficulty ceiling: entries above it are rejected. */
206
+ difficulty?: number;
207
+ /** The chord in force, matched against each entry's `fitsOver`. */
208
+ quality?: ChordQuality;
209
+ /**
210
+ * The techniques the instrument this part is written for can produce, from
211
+ * its profile. An entry asking for one that is not here is not offered;
212
+ * absent asks nothing, which is the programmed case.
213
+ */
214
+ articulations?: readonly Articulation[];
215
+ };
216
+ /**
217
+ * Whether one entry answers a query.
218
+ *
219
+ * @param entry The dictionary entry.
220
+ * @param query What the generator knows about the moment.
221
+ * @returns True when every condition the entry states is met.
222
+ *
223
+ * @category Composition
224
+ */
225
+ declare function fitsQuery<T>(entry: Vocabulary<T>, query: VocabularyQuery): boolean;
226
+ /**
227
+ * Every entry of a dictionary that answers a query, in dictionary order.
228
+ *
229
+ * Order is preserved so a selection made from the result is reproducible: the
230
+ * same dictionary and the same query always offer the same candidates in the
231
+ * same positions.
232
+ *
233
+ * @param dictionary The entries to search.
234
+ * @param query What the generator knows about the moment.
235
+ * @returns The matching entries, in the order they appear in the dictionary.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * import { BASS_LICKS, selectVocabulary } from '@libraz/libcantus';
240
+ * const candidates = selectVocabulary(BASS_LICKS, { genre: 'motown', difficulty: 3 });
241
+ * ```
242
+ *
243
+ * @category Composition
244
+ */
245
+ declare function selectVocabulary<T>(dictionary: readonly Vocabulary<T>[], query: VocabularyQuery): Vocabulary<T>[];
246
+ /**
247
+ * Choose one entry from a dictionary at a position.
248
+ *
249
+ * The choice is addressed by the path, not by how many choices came before it,
250
+ * so the figure at bar 12 does not move because the figure at bar 3 changed.
251
+ *
252
+ * @param dictionary The entries to choose from.
253
+ * @param query What the generator knows about the moment.
254
+ * @param draw The position-addressed sampler.
255
+ * @param path Where the choice is being made.
256
+ * @returns The chosen entry, or undefined when nothing fits.
257
+ *
258
+ * @category Composition
259
+ */
260
+ declare function pickVocabulary<T>(dictionary: readonly Vocabulary<T>[], query: VocabularyQuery, draw: Draw, ...path: readonly (string | number)[]): Vocabulary<T> | undefined;
261
+ /**
262
+ * Merge a caller's dictionary into a built-in one.
263
+ *
264
+ * Caller entries are appended, so every built-in figure stays reachable, except
265
+ * where a caller entry carries the id of a built-in: that one is a replacement
266
+ * and takes the built-in's place, which is how a caller corrects a figure it
267
+ * disagrees with rather than having both offered.
268
+ *
269
+ * @param builtIn The library's own entries.
270
+ * @param supplied The caller's entries, if any.
271
+ * @returns The merged dictionary, built-ins first.
272
+ *
273
+ * @category Composition
274
+ */
275
+ declare function mergeVocabulary<T>(builtIn: readonly Vocabulary<T>[], supplied: readonly Vocabulary<T>[] | undefined): Vocabulary<T>[];
276
+ /**
277
+ * Narrow a mixed dictionary to the entries one part understands.
278
+ *
279
+ * A context carries a single list, because a caller thinks in terms of "the
280
+ * vocabulary of this piece" rather than of one list per generator. Each
281
+ * generator recognises its own material by shape, so an entry meant for the
282
+ * bass is invisible to the drums instead of being mis-read by it.
283
+ *
284
+ * @param entries The mixed dictionary.
285
+ * @param isMaterial Guard recognising one kind of material.
286
+ * @returns The entries whose material the guard accepts.
287
+ *
288
+ * @category Composition
289
+ */
290
+ declare function vocabularyOfKind<T>(entries: readonly Vocabulary<unknown>[], isMaterial: (material: unknown) => material is T): Vocabulary<T>[];
291
+ /**
292
+ * Reject a dictionary entry whose declared fields are outside their domains.
293
+ *
294
+ * Applied to caller-supplied entries at the point they enter a generator: an
295
+ * entry with a difficulty of 40 would otherwise pass every ceiling and be
296
+ * chosen for a beginner's part.
297
+ *
298
+ * @param entry The entry to check.
299
+ * @param label Name used in the error message.
300
+ * @returns The entry, unchanged.
301
+ * @throws If the difficulty is outside 1..5 or the tempo band is inverted.
302
+ *
303
+ * @category Composition
304
+ */
305
+ declare function assertVocabulary<T>(entry: Vocabulary<T>, label?: string): Vocabulary<T>;
306
+
307
+ /**
308
+ * The generation context: one seed, one set of dials, one tempo, shared by
309
+ * every generator.
310
+ *
311
+ * Before it, each generator carried its own idea of "how involved should this
312
+ * be" — a 0..1 density here, a style name there, a boolean somewhere else — and
313
+ * only the drums knew the tempo. A context names those things once, so a piece
314
+ * can be asked for at a given complexity in one place, and every part derives
315
+ * its own randomness from the same project seed.
316
+ */
317
+
318
+ /**
319
+ * How involved the generated music should be, on axes that do not imply one
320
+ * another.
321
+ *
322
+ * The first three ask for more of something and are continuous: moving one of
323
+ * them adds material without disturbing what is already sounding.
324
+ * {@link Complexity.difficulty} is not one of those — it is a ceiling, and it
325
+ * only ever takes candidates away.
326
+ *
327
+ * @category Composition
328
+ */
329
+ type Complexity = {
330
+ /** Subdivision and syncopation, in [0, 1]. */
331
+ rhythmic?: number;
332
+ /** Tension, substitution and passing chords, in [0, 1]. */
333
+ harmonic?: number;
334
+ /** Ghosts, flams and decoration, in [0, 1]. */
335
+ ornament?: number;
336
+ /**
337
+ * The hardest the result may be to play, from 1 (a beginner's part) to 5 (at
338
+ * the limit of the instrument). It is a ceiling rather than a strength:
339
+ * "involved but easy" and "plain but hard" are both real requests, so this
340
+ * filters what the other three proposed instead of scaling it. It governs the
341
+ * timing layer alone — whether the note exists on the instrument, and whether
342
+ * the shape can be held, follow from {@link GenerationContext.instruments} and
343
+ * apply whatever the ceiling says.
344
+ */
345
+ difficulty?: number;
346
+ };
347
+ /**
348
+ * Everything a generator needs that is not about the notes themselves.
349
+ *
350
+ * @category Composition
351
+ */
352
+ type GenerationContext = {
353
+ /**
354
+ * The project seed every part derives its own randomness from. Optional, and
355
+ * zero when left out: a context naming only a tempo is a whole request, and
356
+ * requiring the seed beside it would make the commonest context unwritable
357
+ * now that it is the only place a seed can go.
358
+ */
359
+ seed?: number;
360
+ /** The dials; see {@link Complexity}. */
361
+ complexity?: Complexity;
362
+ /**
363
+ * Draw from this source instead of one derived from `seed`. Each part still
364
+ * addresses its own namespace, so parts do not collide.
365
+ */
366
+ rng?: PositionalRng;
367
+ /**
368
+ * The instruments the parts are written for, keyed by part name — `'bass'`,
369
+ * `'drums'`. Naming one is itself the request that the part be playable on
370
+ * it, so its range and physical limits always apply.
371
+ */
372
+ instruments?: Record<string, InstrumentProfile>;
373
+ /**
374
+ * Tempo in quarter-note beats per minute. Without it no generator can judge
375
+ * whether a passage is reachable in the time available, so a ceiling on
376
+ * difficulty has nothing to measure against.
377
+ */
378
+ bpm?: number;
379
+ /**
380
+ * The algorithm version to generate under; defaults to the version this build
381
+ * produces. See {@link resolveAlgorithmVersion} for what the number promises.
382
+ */
383
+ algorithmVersion?: number;
384
+ /**
385
+ * Figures this piece may draw on, over and above the library's own.
386
+ *
387
+ * Genres are endless, which is the reason the vocabulary is data at all: a
388
+ * caller brings the dictionary for the music it is writing instead of waiting
389
+ * for the library to grow one. Entries for every part travel in one list —
390
+ * each generator recognises its own material by shape — and an entry carrying
391
+ * the id of a built-in replaces it rather than being offered alongside.
392
+ */
393
+ vocabulary?: readonly Vocabulary<unknown>[];
394
+ };
395
+ /**
396
+ * A context, or just the seed for one.
397
+ *
398
+ * A bare number is sugar for `{ seed }`: the common case is a caller who wants
399
+ * reproducible output and nothing else.
400
+ *
401
+ * @category Composition
402
+ */
403
+ type GenerationContextInput = GenerationContext | number;
404
+ /**
405
+ * A context with everything resolved: dials validated, tempo and ceiling
406
+ * settled, and a positional source ready for each part.
407
+ */
408
+ type ResolvedContext = {
409
+ seed: number;
410
+ /** Requested rhythmic density, or undefined when the caller named none. */
411
+ rhythmic: number | undefined;
412
+ /** Requested harmonic richness, or undefined when the caller named none. */
413
+ harmonic: number | undefined;
414
+ /** Requested ornament density, or undefined when the caller named none. */
415
+ ornament: number | undefined;
416
+ /** The difficulty ceiling, or undefined for no ceiling. */
417
+ difficulty: number | undefined;
418
+ /** Tempo in BPM, or undefined when the caller named none. */
419
+ bpm: number | undefined;
420
+ /** The resolved algorithm version. */
421
+ algorithmVersion: number;
422
+ /** The profile written for a part, if one was named for it. */
423
+ instrument: (part: string) => InstrumentProfile | undefined;
424
+ /** The figures the caller brought, validated; empty when it brought none. */
425
+ vocabulary: readonly Vocabulary<unknown>[];
426
+ /** The position-addressed samplers for a part. */
427
+ part: (name: string) => Draw;
428
+ };
429
+ /**
430
+ * Resolve a context (or a bare seed) into the form the generators use.
431
+ *
432
+ * The algorithm version takes part in every derivation, so two versions never
433
+ * share a draw: pinning one is what makes a saved project reopen as the piece
434
+ * it was, and a version this build does not produce is rejected rather than
435
+ * quietly rendered as another.
436
+ *
437
+ * @param input The context, the seed alone, or nothing.
438
+ * @returns The resolved context.
439
+ * @throws If the seed is not a 32-bit integer, a dial is out of range, the
440
+ * tempo is not positive, or the algorithm version is unknown.
441
+ * @example
442
+ * ```ts
443
+ * import { resolveContext } from '@libraz/libcantus';
444
+ * const ctx = resolveContext({ seed: 42, complexity: { rhythmic: 0.8 }, bpm: 96 });
445
+ * ctx.part('drums').prob(0.5, 'ghost', 1, 2); // the same answer however often it is asked
446
+ * ```
447
+ */
448
+ declare function resolveContext(input?: GenerationContextInput): ResolvedContext;
449
+
450
+ /**
451
+ * The transformation rules that deform a remembered figure into the one this
452
+ * bar needs.
453
+ *
454
+ * The division of labour is the whole point of keeping these separate from the
455
+ * dictionary: **genre selects the material, complexity deforms it, difficulty
456
+ * rejects it.** A transform therefore never consults a genre and never consults
457
+ * a ceiling — it takes the figure it is given and reshapes it, and whether the
458
+ * result is playable is decided afterwards, by {@link withinCeiling}.
459
+ *
460
+ * Every figure is a list of onsets on a sixteenth grid, whatever it is made of,
461
+ * so the rules are written once and used by both the bass and the drums.
462
+ */
463
+
464
+ /** Sixteenth steps in one 4/4 bar; the grid every figure is written on. */
465
+ declare const BAR_STEPS = 16;
466
+ /** Sixteenth steps in one quarter-note beat. */
467
+ declare const BEAT_STEPS = 4;
468
+ /** One sixteenth expressed in quarter-note beats. */
469
+ declare const STEP_BEATS: number;
470
+ /**
471
+ * An onset on the shared sixteenth grid.
472
+ *
473
+ * Both kinds of material are lists of these with their own extra fields, so a
474
+ * transform is generic over the record and only ever reads the fields named
475
+ * here.
476
+ *
477
+ * @category Composition
478
+ */
479
+ type GridEvent = {
480
+ /** Sixteenth-note index from the start of the figure. */
481
+ step: number;
482
+ /** Loudness as a factor of the passage's base velocity. */
483
+ velocity: number;
484
+ /**
485
+ * The voice this onset is played on, where the material has more than one.
486
+ * A figure written for a single voice — a bass lick — leaves it out.
487
+ */
488
+ voice?: string;
489
+ /**
490
+ * The limb that plays it, where the material names one. It outranks
491
+ * {@link GridEvent.voice} as the stream an onset belongs to, since what
492
+ * crowds a player is one limb rather than one drum.
493
+ */
494
+ limb?: string;
495
+ };
496
+ /**
497
+ * How strongly a position on the sixteenth grid is felt, from 4 (the downbeat)
498
+ * to 0 (an off sixteenth).
499
+ *
500
+ * This is the ranking the thinning rule works down: the notes a player drops
501
+ * first when asked for something easier are the ones carrying the least of the
502
+ * metre. It is named for the grid it reads because the analysis layer has a
503
+ * metric weight of its own, on another scale and against a time signature; the
504
+ * two answer different questions and are deliberately not the same function.
505
+ *
506
+ * @param step Sixteenth index within the bar.
507
+ * @returns The weight, 0 to 4.
508
+ *
509
+ * @category Composition
510
+ */
511
+ declare function gridMetricWeight(step: number): number;
512
+ /**
513
+ * Drop the notes carrying the least of the metre.
514
+ *
515
+ * The easing direction: at 0 nothing is dropped, and as the amount rises whole
516
+ * ranks of positions go, off sixteenths first and the downbeat last. It is a
517
+ * ranking rather than a sampling, so the same amount always removes the same
518
+ * notes and a caller can explain which ones went.
519
+ *
520
+ * @param events The figure.
521
+ * @param amount How much to thin, in [0, 1].
522
+ * @returns The surviving events, in the order given.
523
+ *
524
+ * @category Composition
525
+ */
526
+ declare function thin<T extends GridEvent>(events: readonly T[], amount: number): T[];
527
+ /**
528
+ * Fill in the gaps of a figure, one note per gap.
529
+ *
530
+ * Doubling is what a player does when asked for more of the same rather than
531
+ * for something different: the added note sits halfway between two the figure
532
+ * already has, takes its predecessor's identity, and is played softer.
533
+ *
534
+ * @param events The figure, which need not be sorted.
535
+ * @param spanSteps Length of the figure in sixteenths.
536
+ * @returns The figure with the midpoints filled, sorted by position.
537
+ *
538
+ * @category Composition
539
+ */
540
+ declare function double<T extends GridEvent>(events: readonly T[], spanSteps?: number): T[];
541
+ /**
542
+ * Stretch a figure to twice its length: everything falls half as often.
543
+ *
544
+ * @param events The figure.
545
+ * @param spanSteps Length of the figure in sixteenths; notes stretched past it
546
+ * are dropped, since the bar is the bar however the figure is felt.
547
+ * @returns The stretched figure.
548
+ *
549
+ * @category Composition
550
+ */
551
+ declare function halfTime<T extends GridEvent>(events: readonly T[], spanSteps?: number): T[];
552
+ /**
553
+ * Compress a figure to half its length and play it twice.
554
+ *
555
+ * The repeat is what makes this the mirror of {@link halfTime} rather than a
556
+ * figure with a hole in it: the bar stays full.
557
+ *
558
+ * @param events The figure.
559
+ * @param spanSteps Length of the figure in sixteenths.
560
+ * @returns The compressed figure, repeated to fill the span.
561
+ *
562
+ * @category Composition
563
+ */
564
+ declare function doubleTime<T extends GridEvent>(events: readonly T[], spanSteps?: number): T[];
565
+ /**
566
+ * Anticipate beats, one sixteenth early.
567
+ *
568
+ * Anticipation is the direction syncopation runs in every genre this library
569
+ * names: the note arrives before the beat it belongs to. It is written as an
570
+ * addition rather than a displacement — the anticipating stroke is softer, and
571
+ * the note it anticipates stays where it was, the way a player ties into the
572
+ * beat rather than abandoning it — because this is one of the dials that only
573
+ * ever add material. Displacing instead would move onsets around under the
574
+ * finger of anyone who puts the dial on a slider, and a saved project reopened
575
+ * one notch along would not contain the piece it was. Which beats are
576
+ * anticipated is fixed by position, so the bar next door is unaffected either
577
+ * way.
578
+ *
579
+ * A position already taken blocks the anticipation only within the same stream
580
+ * — the same limb, else the same voice. A ghost snare sitting where the hi-hat
581
+ * wants to anticipate is another hand playing, not an obstacle, and whether two
582
+ * limbs can actually strike together is the playability layer's question rather
583
+ * than this one's.
584
+ *
585
+ * @param events The figure.
586
+ * @param amount How much syncopation, in [0, 1].
587
+ * @param draw The position-addressed sampler.
588
+ * @param path Where the figure sits, so the same bar always syncopates alike.
589
+ * @returns The figure with its anticipations, sorted by position.
590
+ *
591
+ * @category Composition
592
+ */
593
+ declare function syncopate<T extends GridEvent>(events: readonly T[], amount: number, draw: Draw, ...path: readonly (string | number)[]): T[];
594
+ /**
595
+ * How much decoration a figure keeps.
596
+ *
597
+ * A decorated note — a ghost, a flam, anything the figure marks as ornament —
598
+ * survives on its own draw, so raising the dial only ever brings more of them
599
+ * back and never disturbs the ones already sounding. Notes that are not
600
+ * decoration are untouched.
601
+ *
602
+ * @param events The figure.
603
+ * @param isOrnament Which events count as decoration.
604
+ * @param amount The ornament dial, in [0, 1].
605
+ * @param draw The position-addressed sampler.
606
+ * @param path Where the figure sits.
607
+ * @returns The figure with the surviving decoration.
608
+ *
609
+ * @category Composition
610
+ */
611
+ declare function ornamentBy<T extends GridEvent>(events: readonly T[], isOrnament: (event: T) => boolean, amount: number, draw: Draw, ...path: readonly (string | number)[]): T[];
612
+ /**
613
+ * Everything the complexity dials do to a figure, in one pass.
614
+ *
615
+ * @category Composition
616
+ */
617
+ type DeformOptions = {
618
+ /**
619
+ * Subdivision and syncopation, in [0, 1]. Below the neutral middle the figure
620
+ * is thinned; above it, syncopated. The middle leaves it as the dictionary
621
+ * wrote it, which is what makes the dictionary the thing a reader can check.
622
+ */
623
+ rhythmic?: number;
624
+ /** Ghosts and decoration, in [0, 1]; absent keeps every ornament. */
625
+ ornament?: number;
626
+ /** Which events are decoration, for the ornament dial. */
627
+ isOrnament?: (event: GridEvent) => boolean;
628
+ /**
629
+ * Rate: stretch or compress the figure before the dials are applied. It is
630
+ * named for what it does to the note values, since "feel" elsewhere in the
631
+ * drum surface names the swing a figure is played with — a different
632
+ * question, asked of the same figure.
633
+ */
634
+ rate?: 'straight' | 'half' | 'double';
635
+ /** Length of the figure in sixteenths. */
636
+ spanSteps?: number;
637
+ };
638
+ /**
639
+ * Apply the complexity dials to a figure.
640
+ *
641
+ * This is the one place complexity touches vocabulary: genre has already chosen
642
+ * the material, and the ceiling has not been consulted yet.
643
+ *
644
+ * @param events The figure.
645
+ * @param opts What the dials say.
646
+ * @param draw The position-addressed sampler.
647
+ * @param path Where the figure sits.
648
+ * @returns The deformed figure, sorted by position.
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * import { deform, resolveContext } from '@libraz/libcantus';
653
+ * const figure = [
654
+ * { step: 0, velocity: 1, limb: 'kick' },
655
+ * { step: 4, velocity: 0.9, limb: 'snare' },
656
+ * { step: 8, velocity: 1, limb: 'kick' },
657
+ * { step: 12, velocity: 0.9, limb: 'snare' },
658
+ * ];
659
+ * const draw = resolveContext(7).part('drums');
660
+ * const busier = deform(figure, { rhythmic: 0.9 }, draw, 'bar', 0);
661
+ * // Above the neutral middle the figure is syncopated: onsets are added off
662
+ * // the beat, and the result comes back sorted by position.
663
+ * busier.length; // 7
664
+ * busier.map((event) => event.step); // [0, 3, 4, 7, 8, 11, 12]
665
+ * ```
666
+ *
667
+ * @category Composition
668
+ */
669
+ declare function deform<T extends GridEvent>(events: readonly T[], opts: DeformOptions, draw: Draw, ...path: readonly (string | number)[]): T[];
670
+ /**
671
+ * Whether a figure is inside the difficulty ceiling at this tempo.
672
+ *
673
+ * The ceiling is measured against the closest pair of onsets the figure asks
674
+ * one player for, which is the thing that actually stops a hand — so the pairs
675
+ * are counted within a stream (the limb, else the voice) rather than across the
676
+ * whole kit, because a hi-hat and a kick a sixteenth apart are two limbs and not
677
+ * one hand playing twice. A figure that fails is rejected — never quietly
678
+ * simplified, because a simplified figure is a different figure and the caller
679
+ * asked for this one.
680
+ *
681
+ * @param events The figure.
682
+ * @param bpm Tempo, or undefined when the caller gave none.
683
+ * @param difficulty The ceiling, or undefined for no ceiling.
684
+ * @returns True when the figure is playable at the ceiling, and whenever either
685
+ * the ceiling or the tempo is unknown.
686
+ *
687
+ * @category Composition
688
+ */
689
+ declare function withinCeiling(events: readonly GridEvent[], bpm: number | undefined, difficulty: number | undefined): boolean;
690
+
691
+ /**
692
+ * Deterministic bass-line generation over a chord placement.
693
+ *
694
+ * Given a sequence of chord segments and a key, this builds a monophonic bass
695
+ * line in a chosen low register. Several idiomatic styles are supported, from a
696
+ * single held root per segment to a quarter-note walking line that leads by
697
+ * step into each chord change. All pitches are kept in a narrow bass band and
698
+ * consecutive notes move by small intervals; given a seed the output is fully
699
+ * reproducible.
700
+ *
701
+ * Positions and durations are measured in quarter-note beats, matching the
702
+ * library-wide convention.
703
+ */
704
+
705
+ /**
706
+ * A chord sounding over a half-open beat span `[startBeat, endBeat)`.
707
+ *
708
+ * The same record {@link ChordSegment} names, so a timeline segment can be fed
709
+ * straight to the bass generator.
710
+ *
711
+ * @category Composition
712
+ */
713
+ type BassSegment = ChordSegment;
714
+ /**
715
+ * Every bass-line idiom, in declaration order.
716
+ *
717
+ * @category Composition
718
+ */
719
+ declare const BASS_STYLES: readonly ["root", "rootFifth", "pop", "walking", "arpeggio"];
720
+ /**
721
+ * The bass-line idiom to generate.
722
+ *
723
+ * @category Composition
724
+ */
725
+ type BassStyle = (typeof BASS_STYLES)[number];
726
+ /**
727
+ * Options controlling {@link generateBassLine}.
728
+ *
729
+ * @category Composition
730
+ */
731
+ type BassLineOptions = {
732
+ /**
733
+ * Chord placement to follow; need not be pre-sorted. Each segment's chord may
734
+ * be written as a chord symbol as well as given as chord data.
735
+ */
736
+ segments: readonly (Omit<BassSegment, 'chord'> & {
737
+ chord: ChordLike;
738
+ })[];
739
+ /**
740
+ * Key/scale context, used for diatonic approach tones in `walking`. A key
741
+ * name such as `'C major'` is read as that key.
742
+ */
743
+ key: KeyLike;
744
+ /**
745
+ * Time signature; used for metric accents.
746
+ *
747
+ * @defaultValue `{ numerator: 4, denominator: 4 }`
748
+ */
749
+ ts?: TimeSignature;
750
+ /**
751
+ * Bass-line idiom.
752
+ *
753
+ * @defaultValue `'root'`
754
+ */
755
+ style?: BassStyle;
756
+ /**
757
+ * Target register as a base MIDI octave; roots land around `octave*12+12`.
758
+ * An octave pickup in the `pop` style may drop a note into the octave below
759
+ * that band, which is where such a pickup belongs.
760
+ *
761
+ * @defaultValue 2
762
+ */
763
+ octave?: number;
764
+ /**
765
+ * The instrument the line is written for. Giving one is itself the request
766
+ * that the line be playable on it: `octave` then says where in that
767
+ * instrument to aim rather than which absolute band to use, and where the two
768
+ * disagree the instrument wins — a note below the lowest string comes back an
769
+ * octave up, the way a player would take it. Leave it out for a programmed
770
+ * part, which is generated exactly as before.
771
+ */
772
+ instrument?: StringedProfile;
773
+ /**
774
+ * The generation context. Its `complexity.rhythmic` sets how often the `pop`
775
+ * style takes a weak-beat pickup, its `instruments.bass` names the instrument
776
+ * when `instrument` does not, its `bpm` together with
777
+ * `complexity.difficulty` keeps leaps the hand could not make in time out of
778
+ * the line, and its `seed` fixes every deterministic choice the line makes.
779
+ *
780
+ * @defaultValue `{ seed: 0 }`
781
+ */
782
+ ctx?: GenerationContextInput;
783
+ /**
784
+ * Maximum number of segments, and of notes, {@link generateBassLine} may
785
+ * work through. The line is built segment by segment, so this is the guard
786
+ * against an unbounded caller rather than a limit on any search.
787
+ *
788
+ * @defaultValue 1000000
789
+ */
790
+ budget?: number;
791
+ };
792
+ /**
793
+ * Generate a bass line following a chord placement.
794
+ *
795
+ * Each segment contributes notes in the chosen bass register per the selected
796
+ * style; consecutive notes are kept within roughly a fifth. Every note's
797
+ * duration extends to the next onset (the final note extends to the last
798
+ * segment's end). Given a seed the output is fully reproducible.
799
+ *
800
+ * The `pop` style's pickups are drawn per position, so raising
801
+ * `complexity.rhythmic` adds pickups without moving the notes already written.
802
+ *
803
+ * With an `instrument` the line is written for that instrument: the register
804
+ * band is moved into its range and any note still outside comes back an octave,
805
+ * so every pitch has a fret. Nothing is dropped or replaced — the rhythm and
806
+ * the note count are the same either way.
807
+ *
808
+ * @param opts Segments, key, and generation options.
809
+ * @returns Bass notes sorted by onset, non-overlapping.
810
+ *
811
+ * @example
812
+ * ```ts
813
+ * import { generateBassLine, makeChord, majorKey } from '@libraz/libcantus';
814
+ * const segments = [
815
+ * { startBeat: 0, endBeat: 4, chord: makeChord(0, 'maj') },
816
+ * { startBeat: 4, endBeat: 8, chord: makeChord(7, 'maj') },
817
+ * ];
818
+ * const notes = generateBassLine({ segments, key: majorKey(0), style: 'walking' });
819
+ * ```
820
+ *
821
+ * @category Composition
822
+ */
823
+ declare function generateBassLine(opts: BassLineOptions): NoteEvent[];
824
+
825
+ /**
826
+ * Options controlling {@link generateCounterMelody}.
827
+ *
828
+ * @category Voicing & Counterpoint
829
+ */
830
+ type CounterMelodyOptions = {
831
+ /** The lead line to write against, in ascending onset order. */
832
+ melody: readonly NoteEvent[];
833
+ /**
834
+ * The harmony to write against. A timeline carries its own segment
835
+ * boundaries, so a chord change anywhere — including off the generator's
836
+ * half-beat probe grid — is seen; prefer it over `chordAt`.
837
+ *
838
+ * Exactly one of `timeline` and `chordAt` is required; `timeline` wins when
839
+ * both are given.
840
+ */
841
+ timeline?: ChordTimeline;
842
+ /**
843
+ * Chord sounding at an absolute beat position, or null when none applies.
844
+ *
845
+ * A callback is opaque: the generator cannot see where it changes, so it
846
+ * probes a half-beat grid and otherwise relies on `chordChangeBeats`. A held
847
+ * counter note that spans an unseen change is not checked against the new
848
+ * chord.
849
+ */
850
+ chordAt?: (beat: number) => Chord | null;
851
+ /**
852
+ * Exact chord-change beats the `chordAt` callback does not expose. Ignored
853
+ * when `timeline` is given, since a timeline lists its own boundaries.
854
+ */
855
+ chordChangeBeats?: number[];
856
+ /**
857
+ * Key/scale context for scale-tone decisions. A key name such as `'C major'`
858
+ * is read as that key.
859
+ */
860
+ key: KeyLike;
861
+ /**
862
+ * Meter used for strong-beat decisions.
863
+ *
864
+ * @defaultValue 4/4
865
+ */
866
+ ts?: TimeSignature;
867
+ /**
868
+ * Which side of the melody the counter line occupies.
869
+ *
870
+ * @defaultValue 'below'
871
+ */
872
+ register?: 'above' | 'below';
873
+ /**
874
+ * Onset strategy: `'complement'` moves where the melody holds or rests and
875
+ * reinforces some strong beats; `'follow'` mirrors melody onsets.
876
+ *
877
+ * @defaultValue 'complement'
878
+ */
879
+ rhythm?: 'complement' | 'follow';
880
+ /**
881
+ * Style profile applied to candidate pitches. It sets what is rejected
882
+ * outright and, through {@link PROFILE_WEIGHTS}, what is preferred among the
883
+ * candidates that remain: `'strict'` seeks contrary motion and avoids
884
+ * similar, `'pop'` treats a run of parallel thirds or sixths as the harmony
885
+ * line an arranger would write.
886
+ *
887
+ * @defaultValue 'pop'
888
+ */
889
+ profile?: 'strict' | 'pop';
890
+ /**
891
+ * Per-field overrides of the profile's ranking weights, for a preference the
892
+ * two stock profiles do not express — a line that should leap more freely, or
893
+ * one that should hold a pedal wherever it can.
894
+ */
895
+ weights?: Partial<ProfileWeights>;
896
+ /** Lowest MIDI pitch the counter line may use (default derived from `register`). */
897
+ pitchLow?: number;
898
+ /** Highest MIDI pitch the counter line may use (default derived from `register`). */
899
+ pitchHigh?: number;
900
+ /**
901
+ * The generation context, so the counter line is drawn from the same project
902
+ * source as every other part and no two parts collide.
903
+ *
904
+ * Its `seed` places the `'complement'` onsets and breaks ties between equally
905
+ * good candidate pitches; the same seed always yields the same line. Where the
906
+ * constraints leave only one good answer — which is common with
907
+ * `rhythm: 'follow'`, whose onsets are fixed by the melody — changing the seed
908
+ * changes nothing.
909
+ *
910
+ * @defaultValue `{ seed: 0 }`
911
+ */
912
+ ctx?: GenerationContextInput;
913
+ };
914
+ /**
915
+ * Generate a counter melody against a lead line.
916
+ *
917
+ * Onsets are chosen per `rhythm`: `'follow'` places one counter note on each
918
+ * melody onset; `'complement'` fills melody rests and sustains and reinforces
919
+ * an occasional strong beat, spaced at least a beat apart. Each onset's pitch
920
+ * is picked from the safe pitches in the counter register (chord tones and
921
+ * consonant tensions against the sounding melody note), rejecting candidates
922
+ * that would form a parallel perfect interval with the melody or sit on the
923
+ * wrong side of it, then ranked by the profile's weights — which is where the
924
+ * two styles part company: `'strict'` prefers contrary motion and penalizes
925
+ * similar, `'pop'` rewards a run of parallel thirds or sixths and is content
926
+ * with a pedal. Ties are broken by a seeded perturbation far
927
+ * below any real score difference (see `TIE_BREAK_JITTER`), so the same
928
+ * seed always yields the same line while a different seed can only reshuffle
929
+ * candidates that were already equally good. Notes extend to the next counter
930
+ * onset (the last to the melody's end) at a velocity slightly under the melody.
931
+ *
932
+ * @param opts The lead line, chord context callback, key, and generation knobs.
933
+ * @returns The counter line as note events sorted by onset; `[]` for an empty melody.
934
+ * @example
935
+ * ```ts
936
+ * import { generateCounterMelody, majorKey, parseChordSymbol } from '@libraz/libcantus';
937
+ * const melody = [{ pitch: 72, startBeat: 0, durationBeat: 2 }];
938
+ * const counter = generateCounterMelody({
939
+ * melody,
940
+ * chordAt: () => parseChordSymbol('C'),
941
+ * key: majorKey(0),
942
+ * }); // note events below the melody, in onset order
943
+ * ```
944
+ * @category Voicing & Counterpoint
945
+ */
946
+ declare function generateCounterMelody(opts: CounterMelodyOptions): NoteEvent[];
947
+
948
+ /**
949
+ * The percussion voices the generator emits, as General MIDI note numbers.
950
+ *
951
+ * A {@link DrumHit} carries only its pitch, so splitting a kit across tracks —
952
+ * kick to one, snare to another — means comparing against these numbers. They
953
+ * are published so that comparison is a named constant covered by semver
954
+ * rather than a hard-coded 36.
955
+ *
956
+ * @example
957
+ * ```ts
958
+ * import { DRUM_NOTES, generateDrums } from '@libraz/libcantus';
959
+ * const hits = generateDrums({
960
+ * bars: 1,
961
+ * style: 'standard',
962
+ * section: 'verse',
963
+ * ctx: { bpm: 120, complexity: { rhythmic: 0.5 } },
964
+ * });
965
+ * const kick = hits.filter((hit) => hit.pitch === DRUM_NOTES.kick);
966
+ * ```
967
+ * @category Composition
968
+ */
969
+ declare const DRUM_NOTES: Readonly<{
970
+ readonly kick: 36;
971
+ readonly snare: 38;
972
+ readonly sideStick: 37;
973
+ readonly handClap: 39;
974
+ readonly closedHiHat: 42;
975
+ readonly pedalHiHat: 44;
976
+ readonly openHiHat: 46;
977
+ readonly crash: 49;
978
+ readonly ride: 51;
979
+ readonly tambourine: 54;
980
+ readonly highTom: 50;
981
+ readonly midTom: 47;
982
+ readonly lowTom: 45;
983
+ readonly shaker: 82;
984
+ }>;
985
+ /**
986
+ * A percussion voice name, one key of {@link DRUM_NOTES}.
987
+ *
988
+ * @category Composition
989
+ */
990
+ type DrumVoice = keyof typeof DRUM_NOTES;
991
+ /**
992
+ * The voice a percussion note number names.
993
+ *
994
+ * @param pitch The General MIDI note number.
995
+ * @returns The voice, or undefined for a note this generator never emits.
996
+ * @category Composition
997
+ */
998
+ declare function drumVoiceOf(pitch: number): DrumVoice | undefined;
999
+ /**
1000
+ * A single percussion onset.
1001
+ *
1002
+ * @category Composition
1003
+ */
1004
+ type DrumHit = {
1005
+ /** General MIDI note number; {@link drumVoiceOf} names it. */
1006
+ pitch: number;
1007
+ startBeat: number;
1008
+ durationBeat: number;
1009
+ velocity: number;
1010
+ /**
1011
+ * How the stroke is played, when it is more than a plain hit. A flam is one
1012
+ * onset carrying `'flam'`, not a pair of notes a fraction of a beat apart, so
1013
+ * the grace stroke is the reader's to place — as a second note, a key switch,
1014
+ * or a sample layer — and any pattern can carry one.
1015
+ */
1016
+ articulation?: Articulation;
1017
+ };
1018
+
1019
+ /**
1020
+ * A Euclidean (Bjorklund) kick pattern for {@link generateDrums}.
1021
+ *
1022
+ * @category Composition
1023
+ */
1024
+ type EuclideanKick = {
1025
+ /** Number of kick onsets, clamped to `[0, steps]`. */
1026
+ pulses: number;
1027
+ /**
1028
+ * Total steps in the bar (1..16).
1029
+ *
1030
+ * @defaultValue 16
1031
+ */
1032
+ steps?: number;
1033
+ /**
1034
+ * Steps to rotate the onsets toward later positions.
1035
+ *
1036
+ * @defaultValue 0
1037
+ */
1038
+ rotation?: number;
1039
+ };
1040
+ /**
1041
+ * Options controlling {@link generateDrums}.
1042
+ *
1043
+ * @category Composition
1044
+ */
1045
+ type DrumsOptions = {
1046
+ /**
1047
+ * Number of bars to generate.
1048
+ */
1049
+ bars: number;
1050
+ style: GrooveStyle;
1051
+ section: Section;
1052
+ /**
1053
+ * Time signature.
1054
+ *
1055
+ * Only 4/4 is accepted: every shape this generator writes — the backbeat, the
1056
+ * hi-hat subdivisions, the open-hat and crash beats, the beat a fill starts on
1057
+ * — is written against a four-beat bar, so another meter would come back with
1058
+ * 4/4 accents in a bar of the wrong length and no sign that anything was
1059
+ * wrong. A meter it cannot place is refused rather than mis-placed. Use
1060
+ * {@link generateRhythm} or {@link placeDrumPattern} for other meters.
1061
+ *
1062
+ * @defaultValue `{ numerator: 4, denominator: 4 }`
1063
+ */
1064
+ ts?: TimeSignature;
1065
+ /**
1066
+ * Replace the final bar with a fill.
1067
+ *
1068
+ * A pre-chorus leading into a chorus builds instead: the two-bar lift takes
1069
+ * precedence over the fill, since the buildup already marks the phrase end.
1070
+ * Set `nextSection` to anything but `'chorus'` to get the fill.
1071
+ *
1072
+ * @defaultValue false
1073
+ */
1074
+ fills?: boolean;
1075
+ /**
1076
+ * The swing the groove is played with. Naming one is taken at its word by
1077
+ * every style, including the ones whose own character is straight: a feel the
1078
+ * caller asked for and did not get is worse than one it has to ask for twice.
1079
+ * Left out, the style's own feel applies.
1080
+ */
1081
+ feel?: GrooveFeel;
1082
+ /**
1083
+ * Voicing role, from busiest to sparsest:
1084
+ *
1085
+ * - `'full'`: every voice, backbeat snare at full weight.
1086
+ * - `'ambient'`: side-stick backbeat, ride instead of hi-hat.
1087
+ * - `'minimal'`: side-stick backbeat quieter still, pedal hi-hat, no
1088
+ * auxiliary percussion.
1089
+ * - `'fxOnly'`: no kick, snare, ghost notes, timekeeping hi-hat, or fills —
1090
+ * only the fx and auxiliary voices.
1091
+ *
1092
+ * Roles express orchestration priority and peak dynamics, not a strict hit
1093
+ * count: style-specific ride, foot-hi-hat, and auxiliary patterns may make a
1094
+ * sparser role emit more individual onsets in a particular bar.
1095
+ *
1096
+ * @defaultValue `'full'`
1097
+ */
1098
+ role?: DrumRole;
1099
+ /**
1100
+ * The generation context: the tempo, the complexity dials, and the kit this
1101
+ * part is written for.
1102
+ *
1103
+ * `bpm` is the tempo in quarter-note beats per minute. `complexity.rhythmic`
1104
+ * is how busy the backing is, in [0, 1]; the dial is continuous, so every
1105
+ * value moves the result and raising it only adds onsets — the ones already
1106
+ * sounding stay where they were. `complexity.ornament` sets how many ghost
1107
+ * notes survive, `complexity.difficulty` caps how fast the generator writes,
1108
+ * `instruments.drums` — a percussion profile — restricts the output to voices
1109
+ * that kit actually has, and `seed` fixes every deterministic choice.
1110
+ *
1111
+ * @defaultValue `{ seed: 0, bpm: 120, complexity: { rhythmic: 0.5 } }`
1112
+ */
1113
+ ctx?: GenerationContextInput;
1114
+ /**
1115
+ * Section the final-bar fill leads into. Shapes which fill archetype is
1116
+ * chosen (into-chorus and out-of-intro fills differ from generic ones).
1117
+ * Defaults to `section`, i.e. a within-section fill.
1118
+ */
1119
+ nextSection?: Section;
1120
+ /**
1121
+ * When set, the kick follows this Euclidean rhythm instead of the
1122
+ * style/section pattern, giving direct access to evenly spread onsets.
1123
+ */
1124
+ euclideanKick?: EuclideanKick;
1125
+ /**
1126
+ * Maximum number of onsets {@link generateDrums} may write. Generation is
1127
+ * linear in bar count, so this is the guard against an unbounded caller
1128
+ * rather than a limit on any search.
1129
+ *
1130
+ * @defaultValue 1000000
1131
+ */
1132
+ budget?: number;
1133
+ };
1134
+ /**
1135
+ * Generate a drum performance as a flat list of onsets.
1136
+ *
1137
+ * Every voice (kick, snare, ghost snares, closed/open/foot hi-hats, ride, toms,
1138
+ * crash, and auxiliary percussion) is emitted as a {@link DrumHit} distinguished
1139
+ * by its General MIDI pitch. Groove style selects an internal style and feel;
1140
+ * `complexity.rhythmic` sets the backing-density level; `section` shapes kick,
1141
+ * hi-hat, ghost, and percussion density. 16th-note hi-hats drop to 8ths at or
1142
+ * above 150 BPM. When `fills` is true the final bar is replaced with a fill whose
1143
+ * archetype is shaped by `nextSection`; a fill that would emit nothing on its
1144
+ * beat falls back to the normal groove so the phrase end is never silent.
1145
+ * `euclideanKick` overrides the kick with an evenly-spread Euclidean pattern.
1146
+ * Output is fully determined by the options plus the context's seed.
1147
+ *
1148
+ * The patterns are 4/4 only — backbeats, hi-hat subdivisions, and fills are all
1149
+ * written against a four-beat bar — so beat positions are quarter notes and bar
1150
+ * `n` starts at beat `4n`.
1151
+ *
1152
+ * @param opts Generation options.
1153
+ * @returns Percussion onsets in onset order, ties broken by pitch.
1154
+ *
1155
+ * @example
1156
+ * ```ts
1157
+ * import { generateDrums } from '@libraz/libcantus';
1158
+ * const hits = generateDrums({
1159
+ * bars: 4,
1160
+ * style: 'standard',
1161
+ * section: 'chorus',
1162
+ * fills: true,
1163
+ * ctx: { seed: 0, bpm: 120, complexity: { rhythmic: 0.6 } },
1164
+ * });
1165
+ * // Fully determined by the options plus the context's seed (defaults to 0).
1166
+ * ```
1167
+ *
1168
+ * @category Composition
1169
+ */
1170
+ declare function generateDrums(opts: DrumsOptions): DrumHit[];
1171
+
1172
+ /**
1173
+ * Groove and humanization: nudging quantized note events toward a more human
1174
+ * feel, and capturing/replaying the timing-and-velocity "feel" of a
1175
+ * performance as a reusable groove template.
1176
+ *
1177
+ * Positions and durations are measured in quarter-note beats, matching the
1178
+ * library-wide convention. Randomness, where used, always goes through the
1179
+ * seeded PRNG from the `random` module so results are fully reproducible.
1180
+ */
1181
+
1182
+ /**
1183
+ * Options controlling {@link humanize}.
1184
+ *
1185
+ * @category Rhythm & Meter
1186
+ */
1187
+ type HumanizeOptions = {
1188
+ /**
1189
+ * Time signature used to derive metric accents.
1190
+ *
1191
+ * @defaultValue 4/4
1192
+ */
1193
+ ts?: TimeSignature;
1194
+ /**
1195
+ * Maximum timing jitter in quarter-note beats, applied as ±this value.
1196
+ *
1197
+ * @defaultValue 0.02
1198
+ */
1199
+ timing?: number;
1200
+ /**
1201
+ * Maximum velocity jitter (MIDI units), applied as ±this value.
1202
+ *
1203
+ * @defaultValue 8
1204
+ */
1205
+ velocity?: number;
1206
+ /**
1207
+ * How much louder strong beats get relative to weak ones, in MIDI velocity units.
1208
+ *
1209
+ * @defaultValue 12
1210
+ */
1211
+ accent?: number;
1212
+ /**
1213
+ * Velocity assumed for events with no `velocity` of their own.
1214
+ *
1215
+ * @defaultValue 80
1216
+ */
1217
+ baseVelocity?: number;
1218
+ /**
1219
+ * The generation context, so one project seed covers this call as well as the
1220
+ * generators that wrote the notes. Its `seed` fixes the jitter, so the same
1221
+ * seed always humanizes the same way.
1222
+ *
1223
+ * @defaultValue `{ seed: 0 }`
1224
+ */
1225
+ ctx?: GenerationContextInput;
1226
+ /**
1227
+ * The name this call draws under. Humanizing two parts from one context with
1228
+ * the same name gives notes at the same position and pitch the same jitter,
1229
+ * which is what a doubled line does not want; naming each part keeps them
1230
+ * independent.
1231
+ *
1232
+ * @defaultValue `'humanize'`
1233
+ */
1234
+ part?: string;
1235
+ };
1236
+ /**
1237
+ * Humanize a sequence of note events by nudging their timing and velocity
1238
+ * with small, deterministic (seeded) randomness plus a metric-accent shape:
1239
+ * events on strong beats (see {@link metricWeight}) come out louder on
1240
+ * average than events on weak ones.
1241
+ *
1242
+ * Each returned event is a copy; `startBeat` is jittered within
1243
+ * `[-timing, +timing]` beats — not floored at 0, because a pickup sounds before
1244
+ * the downbeat and flooring it there would play two upbeats as one — and
1245
+ * `velocity` is the event's own velocity (or `baseVelocity` if it has none) plus an
1246
+ * accent term scaled by the event's metric weight plus a jitter within
1247
+ * `[-velocity, +velocity]`, clamped to `[1, 127]` and rounded. The metric
1248
+ * weight is computed from each event's original (pre-jitter) `startBeat`.
1249
+ * `pitch` and `durationBeat` pass through unchanged, and the output keeps the
1250
+ * input order.
1251
+ *
1252
+ * The jitter is drawn from the generation context, addressed by the event's own
1253
+ * onset and pitch rather than by its position in the array: an event added or
1254
+ * removed leaves every other event's jitter exactly where it was, and a part
1255
+ * humanized under its own name is independent of the others sharing the seed.
1256
+ *
1257
+ * @param events The events to humanize.
1258
+ * @param opts Humanization options.
1259
+ * @returns Humanized copies of `events`, in input order.
1260
+ * @example
1261
+ * ```ts
1262
+ * import { humanize } from '@libraz/libcantus';
1263
+ * const events = [{ pitch: 60, startBeat: 0, durationBeat: 1, velocity: 80 }];
1264
+ * humanize(events, { ctx: 1, timing: 0.03 }); // copies with jittered timing and accented velocity
1265
+ * humanize(events, { ctx: { seed: 42 }, part: 'lead' }); // under a project context
1266
+ * ```
1267
+ * Notes with a zero or negative duration never sound and are dropped, so the
1268
+ * result can be shorter than the input.
1269
+ *
1270
+ * @category Rhythm & Meter
1271
+ */
1272
+ declare function humanize(events: readonly NoteEvent[], opts?: HumanizeOptions): NoteEvent[];
1273
+ /**
1274
+ * The recorded feel at a single grid slot: an average timing deviation and velocity.
1275
+ *
1276
+ * @category Rhythm & Meter
1277
+ */
1278
+ type GrooveSlot = {
1279
+ /** Average `actualStartBeat - quantizedBeat` (in quarter-note beats) for events landing on this slot. */
1280
+ timingOffset: number;
1281
+ /**
1282
+ * Average velocity of events landing on this slot, or `null` if no event
1283
+ * with a velocity did. `null` (not 0) is the "unrecorded" sentinel so that a
1284
+ * genuine velocity of 0 is preserved and applied like any other value.
1285
+ */
1286
+ velocity: number | null;
1287
+ };
1288
+ /**
1289
+ * A per-bar grid of timing and velocity deviations captured from a performance.
1290
+ *
1291
+ * @category Rhythm & Meter
1292
+ */
1293
+ type GrooveTemplate = {
1294
+ /** Grid resolution: equal grid steps per quarter-note beat. */
1295
+ subdivision: number;
1296
+ /** Number of grid slots per bar (`beatsPerBar(ts) * subdivision`). */
1297
+ slotsPerBar: number;
1298
+ /** One entry per grid slot, in slot-index order (slot 0 is the downbeat). */
1299
+ slots: GrooveSlot[];
1300
+ /**
1301
+ * The time signature the template was extracted under. {@link applyGrooveTemplate}
1302
+ * requires the apply-time meter to match this so the per-bar grid lines up
1303
+ * instead of silently drifting. Optional for backward compatibility with
1304
+ * templates constructed without it (in which case no check is performed).
1305
+ */
1306
+ ts?: TimeSignature;
1307
+ };
1308
+ /**
1309
+ * Extract a groove template from a set of note events: for each grid slot,
1310
+ * the average timing deviation from the quantized grid and the average
1311
+ * velocity of events landing on it. Slots with no velocity-carrying event
1312
+ * record `velocity: null` (the "unrecorded" sentinel); empty slots default to
1313
+ * `{ timingOffset: 0, velocity: null }`. The time signature is stored on the
1314
+ * template so {@link applyGrooveTemplate} can verify the meter matches.
1315
+ *
1316
+ * @param events The (typically "groovy", human-played) events to analyze.
1317
+ * @param ts The time signature, used to compute the bar length.
1318
+ * @param subdivision Grid resolution: equal grid steps per quarter-note beat.
1319
+ * Defaults to a sixteenth-note grid, which is where the timing deviations a
1320
+ * template records — swing, push, drag — are audible.
1321
+ * @returns The extracted groove template.
1322
+ * @category Rhythm & Meter
1323
+ */
1324
+ declare function extractGrooveTemplate(events: readonly NoteEvent[], ts: TimeSignature, subdivision?: number): GrooveTemplate;
1325
+ /**
1326
+ * Apply a groove template to a set of (typically quantized) note events: each
1327
+ * event is snapped to its grid slot and then offset by that slot's recorded
1328
+ * timing deviation, pushing stiff timing toward the template's feel.
1329
+ * Velocity is set to the slot's recorded average when it recorded one (a
1330
+ * non-`null` velocity, including a genuine 0); otherwise the event's own
1331
+ * velocity is left untouched.
1332
+ * Non-positive-duration input artefacts are omitted, so output positions do
1333
+ * not retain a one-to-one correspondence with the input array.
1334
+ *
1335
+ * If the template carries a time signature (see {@link GrooveTemplate.ts}) it
1336
+ * must match `ts`, since a differing bar length would misalign the per-bar
1337
+ * grid and drift the feel; a mismatch throws rather than corrupting timing
1338
+ * silently.
1339
+ *
1340
+ * @param events The events to reshape.
1341
+ * @param template The groove template, from {@link extractGrooveTemplate}.
1342
+ * @param ts The time signature, used to compute the bar length.
1343
+ * @returns Reshaped copies of `events`, in input order.
1344
+ * @throws If `template.ts` is set and does not match `ts`.
1345
+ * @example
1346
+ * ```ts
1347
+ * import { extractGrooveTemplate, applyGrooveTemplate, parseTimeSignature } from '@libraz/libcantus';
1348
+ * const ts = parseTimeSignature('4/4');
1349
+ * // Learn the feel from a loosely played performance...
1350
+ * const groovy = [
1351
+ * { pitch: 36, startBeat: 0.02, durationBeat: 1, velocity: 100 },
1352
+ * { pitch: 38, startBeat: 1.06, durationBeat: 1, velocity: 70 },
1353
+ * ];
1354
+ * const template = extractGrooveTemplate(groovy, ts, 4);
1355
+ * // ...then impose it on stiff, quantized events.
1356
+ * const quantized = [{ pitch: 36, startBeat: 0, durationBeat: 1, velocity: 80 }];
1357
+ * applyGrooveTemplate(quantized, template, ts); // events nudged toward the captured feel
1358
+ * ```
1359
+ * @category Rhythm & Meter
1360
+ */
1361
+ declare function applyGrooveTemplate(events: readonly NoteEvent[], template: GrooveTemplate, ts: TimeSignature): NoteEvent[];
1362
+
1363
+ /**
1364
+ * Broad production style a progression preset suits.
1365
+ *
1366
+ * @category Composition
1367
+ */
1368
+ type ProgStyle = 'minimal' | 'dance' | 'idol' | 'rock';
1369
+ /**
1370
+ * Harmonic-function role of a progression as a whole.
1371
+ *
1372
+ * @category Composition
1373
+ */
1374
+ type ProgFunction = 'loop' | 'tensionBuild' | 'cadenceStrong' | 'stable';
1375
+ /**
1376
+ * A named chord-progression preset expressed in scale degrees.
1377
+ *
1378
+ * @category Composition
1379
+ */
1380
+ type ProgressionPreset = {
1381
+ id: string;
1382
+ name: string;
1383
+ /**
1384
+ * The chord roots, in order, as {@link ProgressionDegree} codes: 0..6 for the
1385
+ * key's own scale degrees and {@link BORROWED_DEGREES} for the borrowed ones.
1386
+ */
1387
+ degrees: ProgressionDegree[];
1388
+ functional: ProgFunction;
1389
+ styles: ProgStyle[];
1390
+ };
1391
+ /**
1392
+ * The borrowed (non-diatonic) chord roots a preset can name, as their semitone
1393
+ * code for a borrowed degree. These are stable identifiers, not semitone
1394
+ * offsets: their pitch-class offsets are resolved internally.
1395
+ *
1396
+ * Scale degrees 1..7 address the key's own chords; these codes continue the
1397
+ * numbering for the chromatic chords a pop progression borrows, so a preset is
1398
+ * one flat list of degree codes.
1399
+ *
1400
+ * @example
1401
+ * ```ts
1402
+ * import { BORROWED_DEGREES, generateProgression, majorKey } from '@libraz/libcantus';
1403
+ * generateProgression({
1404
+ * key: majorKey(0),
1405
+ * style: 'rock',
1406
+ * bars: 4,
1407
+ * preset: { degrees: [1, BORROWED_DEGREES.bVII, 4, 1] },
1408
+ * });
1409
+ * ```
1410
+ * @category Composition
1411
+ */
1412
+ declare const BORROWED_DEGREES: Readonly<{
1413
+ /** Flat submediant: bVI. */
1414
+ readonly bVI: 8;
1415
+ /** Flat subtonic: bVII. */
1416
+ readonly bVII: 10;
1417
+ /** Flat mediant: bIII. */
1418
+ readonly bIII: 11;
1419
+ /** Minor subdominant borrowed from the parallel minor: iv. */
1420
+ readonly iv: 12;
1421
+ /** Neapolitan: bII. */
1422
+ readonly bII: 13;
1423
+ /** Sharp subdominant, the diminished #IV. */
1424
+ readonly sharpIV: 14;
1425
+ }>;
1426
+ /**
1427
+ * A chord root in a preset: a scale degree 1..7, or one of
1428
+ * {@link BORROWED_DEGREES}.
1429
+ *
1430
+ * @category Composition
1431
+ */
1432
+ type ProgressionDegree = number;
1433
+ /**
1434
+ * Options controlling {@link generateProgression}.
1435
+ *
1436
+ * @category Composition
1437
+ */
1438
+ type ProgressionOptions = {
1439
+ /**
1440
+ * The key the degrees are resolved against; a key name such as `'C major'` is
1441
+ * read as that key.
1442
+ */
1443
+ key: KeyLike;
1444
+ /**
1445
+ * Which pool of built-in presets to choose from. Ignored when `preset` names
1446
+ * the progression outright.
1447
+ */
1448
+ style: ProgStyle;
1449
+ bars: number;
1450
+ /** Pick a specific built-in preset by id instead of choosing one by style. */
1451
+ presetId?: string;
1452
+ /**
1453
+ * Use this progression rather than a built-in one. Only `degrees` is
1454
+ * required; see {@link BORROWED_DEGREES} for the non-diatonic codes.
1455
+ */
1456
+ preset?: Partial<ProgressionPreset> & {
1457
+ degrees: ProgressionDegree[];
1458
+ };
1459
+ ext?: ChordQuality | 'auto';
1460
+ /**
1461
+ * The generation context.
1462
+ *
1463
+ * Its `complexity.harmonic` sets how much of the progression is replaced by
1464
+ * the secondary dominant of what follows: 0 — the default — leaves it alone,
1465
+ * 0.5 takes about half of what the voice-leading rules allow, and 1 takes
1466
+ * every one of them. Its `seed` fixes both the preset choice and which chords
1467
+ * are replaced, so the same seed always yields the same progression.
1468
+ *
1469
+ * @defaultValue `{ seed: 0 }`
1470
+ */
1471
+ ctx?: GenerationContextInput;
1472
+ };
1473
+ /**
1474
+ * All built-in progression presets.
1475
+ *
1476
+ * @category Composition
1477
+ */
1478
+ declare function progressions(): ProgressionPreset[];
1479
+ /**
1480
+ * Presets whose style list includes the given style.
1481
+ *
1482
+ * @category Composition
1483
+ */
1484
+ declare function progressionsByStyle(style: ProgStyle): ProgressionPreset[];
1485
+ /**
1486
+ * The preset {@link generateProgression} would choose for a style and seed.
1487
+ *
1488
+ * The choice is otherwise invisible: the generator returns chords, not the
1489
+ * preset it drew them from, so a caller who wants to show or reproduce it has
1490
+ * no way to name it.
1491
+ *
1492
+ * @param style The style pool to choose from.
1493
+ * @param seed The same seed the generator would be given.
1494
+ * @returns The preset that seed selects.
1495
+ * @throws If no preset claims the style.
1496
+ * @example
1497
+ * ```ts
1498
+ * import { pickProgressionPreset } from '@libraz/libcantus';
1499
+ * pickProgressionPreset('dance', 3).name;
1500
+ * ```
1501
+ * @category Composition
1502
+ */
1503
+ declare function pickProgressionPreset(style: ProgStyle, seed?: number): ProgressionPreset;
1504
+ /**
1505
+ * Generate a chord progression laid out one chord per bar.
1506
+ *
1507
+ * A preset is chosen by `presetId` when given, otherwise deterministically from
1508
+ * the presets matching `style`, seeded by the context's `seed`. An unknown
1509
+ * `presetId`, or a `style` no preset claims, is a caller error and throws rather
1510
+ * than silently falling back to a random preset. The preset's degrees cycle to
1511
+ * fill `bars`; each bar is four beats, so `startBeat` is `barIndex * 4`. Chord
1512
+ * roots come from the key's diatonic scale-degree mapping. When `ext` is
1513
+ * omitted or `'auto'`, each chord takes its diatonic triad quality; otherwise
1514
+ * `ext` is forced on every chord — except a reharmonized chord, which is a
1515
+ * secondary dominant and is therefore always a `dom7`. With
1516
+ * `complexity.harmonic` above 0, some chords are deterministically replaced with
1517
+ * the secondary dominant (V7) of the following chord, flagged with
1518
+ * `secondaryDominant`.
1519
+ *
1520
+ * @param opts Generation options.
1521
+ * @returns One chord per bar in timeline order.
1522
+ * @throws If `presetId` matches no built-in preset, or `style` matches none.
1523
+ *
1524
+ * @example
1525
+ * ```ts
1526
+ * import { generateProgression, majorKey } from '@libraz/libcantus';
1527
+ * const chords = generateProgression({ key: majorKey(0), style: 'dance', bars: 4 });
1528
+ * // Deterministic for a given seed (defaults to 0); one ChordSpan per bar.
1529
+ * const richer = generateProgression({
1530
+ * key: majorKey(0),
1531
+ * style: 'dance',
1532
+ * bars: 4,
1533
+ * ctx: { seed: 3, complexity: { harmonic: 0.5 } },
1534
+ * });
1535
+ * // Some chords are now the secondary dominant of the chord that follows.
1536
+ * ```
1537
+ *
1538
+ * @category Composition
1539
+ */
1540
+ declare function generateProgression(opts: ProgressionOptions): ChordSpan[];
1541
+
1542
+ /**
1543
+ * A melody note supplied to the harmonizer: a {@link NoteEvent}.
1544
+ *
1545
+ * @category Reharmonization
1546
+ */
1547
+ type MelodyNote = NoteEvent;
1548
+ /**
1549
+ * Options controlling {@link harmonizeMelody}.
1550
+ *
1551
+ * @category Reharmonization
1552
+ */
1553
+ type HarmonizePlacement = {
1554
+ /**
1555
+ * Move the melody into the key it is harmonized in. The search reports the
1556
+ * shift it chose in `transposeSemitones`; transposing the melody by that many
1557
+ * semitones puts it in the returned `key`, so melody and harmony always agree.
1558
+ *
1559
+ * Use it for a melody notated in one key that has to be sung or played in
1560
+ * another. It never changes register on its own — that is `octaveSearch`.
1561
+ *
1562
+ * @defaultValue false
1563
+ */
1564
+ transposeSearch: boolean;
1565
+ /**
1566
+ * Search octave placements as well, so a melody written too high or too low
1567
+ * is moved into a comfortable register. Octaves leave every pitch class where
1568
+ * it was, so this changes neither the key nor the chords.
1569
+ *
1570
+ * @defaultValue false
1571
+ */
1572
+ octaveSearch: boolean;
1573
+ };
1574
+ /**
1575
+ * Options controlling {@link harmonizeMelody}.
1576
+ *
1577
+ * Only `melody` is required; every other knob has the default a first call
1578
+ * wants.
1579
+ *
1580
+ * @category Reharmonization
1581
+ */
1582
+ type HarmonizeOptions = {
1583
+ /** The melody to harmonize, in ascending onset order. */
1584
+ melody: readonly MelodyNote[];
1585
+ /**
1586
+ * The key to harmonize in, or `'infer'` to estimate it from the melody's
1587
+ * pitch-class weighting — which is what a caller who has only a melody
1588
+ * wants, and so the default. A key name such as `'C major'` is read as that
1589
+ * key; `'infer'` names no key and always asks for the estimate.
1590
+ *
1591
+ * @defaultValue `'infer'`
1592
+ */
1593
+ key?: KeyLike | 'infer';
1594
+ /**
1595
+ * Length of each chord slot in beats. Harmonization places chords on a fixed
1596
+ * grid of this length — unlike {@link chordTimelineFromNotes}, which searches
1597
+ * for the boundaries an existing piece already implies. Any positive value is
1598
+ * honoured; a value fine enough to make the search explode is rejected by the
1599
+ * generation budget rather than rounded up.
1600
+ *
1601
+ * Left out, it is read from `ts`: half a bar where the half falls on a pulse,
1602
+ * and the whole bar where it does not, so a waltz changes chord on its
1603
+ * downbeats rather than across them. In 4/4 that is one chord per half bar.
1604
+ *
1605
+ * @defaultValue half a bar of `ts` — 2 in 4/4
1606
+ */
1607
+ harmonicRhythm?: number;
1608
+ /**
1609
+ * Time signature the melody is barred in. It weights the metric accents the
1610
+ * chords are chosen against and sets the default chord grid, so a waltz or a
1611
+ * jig is harmonized on its own beats rather than on a 4/4 reading of them.
1612
+ *
1613
+ * @defaultValue `4/4`
1614
+ */
1615
+ ts?: TimeSignature;
1616
+ /**
1617
+ * How far beyond the key's own triads the chord vocabulary reaches:
1618
+ * `'diatonic'` uses only them, `'secondaryDominant'` adds the dominants that
1619
+ * tonicize each degree, and `'borrowed'` adds the parallel mode's chords too.
1620
+ *
1621
+ * Sugar for `ctx: { complexity: { harmonic } }` at the three dial positions
1622
+ * these names have always stood for; a context names how far the vocabulary
1623
+ * reaches instead of which of three steps it stops at, and wins where both
1624
+ * are given.
1625
+ *
1626
+ * @defaultValue `'diatonic'`
1627
+ */
1628
+ reharmonize?: 'diatonic' | 'secondaryDominant' | 'borrowed';
1629
+ /**
1630
+ * Whether to search transpositions and octave placements for the melody.
1631
+ * Both are off by default, so the melody is harmonized where it was written.
1632
+ *
1633
+ * @defaultValue both false
1634
+ */
1635
+ placement?: HarmonizePlacement;
1636
+ /**
1637
+ * Beats at which the melody's phrases end, so a longer line closes at each of
1638
+ * them instead of only at its end.
1639
+ *
1640
+ * Naming a beat asks for a close there, and three things follow from it. The
1641
+ * beat divides the chord grid, so the slot the phrase closes in ends where the
1642
+ * phrase does rather than running on into the next one. The harmony moves into
1643
+ * that slot — the chord under a named close is never the chord that was
1644
+ * already sounding, which is what makes the close audible as one. And the note
1645
+ * the phrase comes to rest on is read as a structural tone rather than as an
1646
+ * ornament of the next phrase's first note, so a phrase resting on the tonic
1647
+ * is harmonized by the tonic.
1648
+ *
1649
+ * Which cadence that close forms is still the melody's to decide: a phrase
1650
+ * coming to rest on the tonic over an approach that can carry the dominant
1651
+ * cadences authentically, and one whose approach cannot is harmonized by what
1652
+ * it sounds.
1653
+ *
1654
+ * The melody always closes where it ends, whatever this says; these are the
1655
+ * closes *inside* it. `phrasesFromTimeline` finds the phrases of a line
1656
+ * already labelled with chords, and its `endBeat` values are what this
1657
+ * expects, so a caller harmonizes a whole piece in one call rather than
1658
+ * harmonizing each phrase and joining the results.
1659
+ *
1660
+ * A boundary landing exactly on a chord-slot boundary closes the slot before
1661
+ * it — the beat a phrase ends on is where the next phrase begins.
1662
+ *
1663
+ * @defaultValue none — one close, at the end
1664
+ */
1665
+ phraseEnds?: readonly number[];
1666
+ /**
1667
+ * The generation context. Its `complexity.harmonic` is how far the chord
1668
+ * vocabulary reaches beyond the key's own triads — 0 uses them alone, 0.5 has
1669
+ * every secondary dominant, 1 the parallel mode's chords as well — and its
1670
+ * `seed` drives the deterministic tie-break perturbation.
1671
+ *
1672
+ * @defaultValue `{ seed: 0 }`
1673
+ */
1674
+ ctx?: GenerationContextInput;
1675
+ };
1676
+ /**
1677
+ * The chosen transpose, key, chord path, and per-note roles.
1678
+ *
1679
+ * @category Reharmonization
1680
+ */
1681
+ type HarmonizeResult = {
1682
+ /**
1683
+ * How far the melody was moved, in semitones. Transposing the melody by this
1684
+ * much puts it in `key`, which is the key the chords are in.
1685
+ */
1686
+ transposeSemitones: number;
1687
+ key: KeyScale;
1688
+ /** One span per chord change, each starting on a harmonic-rhythm boundary. */
1689
+ chords: ChordSpan[];
1690
+ /** Each melody note's role in the chord sounding under it, once chosen. */
1691
+ melodyRoles: {
1692
+ noteIndex: number;
1693
+ role: HarmonyRole;
1694
+ }[];
1695
+ };
1696
+ /**
1697
+ * Harmonize a melody with a min-cost chord progression.
1698
+ *
1699
+ * The melody's ornaments are classified first, from the melody and the metre
1700
+ * alone (see {@link classifyMelodyTones}); the chords then have to explain the
1701
+ * structural tones only, so a passing tone no longer buys itself a chord. The
1702
+ * melody is segmented by `harmonicRhythm`; each segment is scored against the
1703
+ * candidate chords the harmonic dial opens — the key's own triads, then the
1704
+ * secondary dominants, then the parallel mode's chords, one at a time as
1705
+ * `ctx.complexity.harmonic` rises from 0 to 1 — by the fit of those structural
1706
+ * tones, and a Viterbi search picks the lowest-cost path using a
1707
+ * functional-flow transition cost and a cadence bonus at the end of the melody
1708
+ * it is given — one cadence, at the close, unless `phraseEnds` names the closes
1709
+ * inside a longer line, and then one at each of them as well. Runs of the same
1710
+ * chord are reported once, so the chord count follows the harmony rather than
1711
+ * the grid.
1712
+ *
1713
+ * With `placement.transposeSearch` the melody is moved into the key it is
1714
+ * harmonized in, and `transposeSemitones` reports how far; with
1715
+ * `placement.octaveSearch` it is moved by octaves into a comfortable register,
1716
+ * which leaves the key and the chords untouched. Both may be set, and the
1717
+ * reported shift is their sum.
1718
+ *
1719
+ * The `seed` drives a deterministic tie-break only: it perturbs candidates by a
1720
+ * magnitude far below any real cost difference (see `TIE_BREAK_JITTER`),
1721
+ * so it can decide between chords or paths of otherwise-equal cost but never
1722
+ * overrides melody fit or functional flow. For a well-determined melody the
1723
+ * result is identical across seeds; the same seed always yields the same result.
1724
+ *
1725
+ * @param opts The melody and, optionally, the generation context, key, harmonic
1726
+ * rhythm, reharmonization strength, placement search, meter, and seed. Only
1727
+ * `melody` is required.
1728
+ * @returns The chosen transpose, key, chord path, and per-note roles.
1729
+ * @example
1730
+ * ```ts
1731
+ * import { harmonizeMelody } from '@libraz/libcantus';
1732
+ * const result = harmonizeMelody({
1733
+ * melody: [
1734
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
1735
+ * { pitch: 64, startBeat: 1, durationBeat: 1 },
1736
+ * ],
1737
+ * });
1738
+ * result.chords; // one ChordSpan per chord change, on the harmonic-rhythm grid
1739
+ * ```
1740
+ * @category Reharmonization
1741
+ */
1742
+ declare function harmonizeMelody(opts: HarmonizeOptions): HarmonizeResult;
1743
+
1744
+ /**
1745
+ * A transformation applicable to a motif cell.
1746
+ *
1747
+ * @category Composition
1748
+ */
1749
+ type MotifTransform = 'transposeDiatonic' | 'transposeChromatic' | 'invert' | 'retrograde' | 'augment' | 'diminish' | 'sequence';
1750
+ /**
1751
+ * A single note within a motif cell.
1752
+ *
1753
+ * @category Composition
1754
+ */
1755
+ type MotifNote = {
1756
+ pitch: number;
1757
+ startBeat: number;
1758
+ durationBeat: number;
1759
+ };
1760
+ /**
1761
+ * A short melodic cell.
1762
+ *
1763
+ * @category Composition
1764
+ */
1765
+ type MotifCell = {
1766
+ notes: MotifNote[];
1767
+ };
1768
+ /**
1769
+ * Melodic contour shape for {@link generateMotif}.
1770
+ *
1771
+ * @category Composition
1772
+ */
1773
+ type MotifContour = 'arch' | 'ascending' | 'descending' | 'wave';
1774
+ /**
1775
+ * Options controlling {@link generateMotif}.
1776
+ *
1777
+ * @category Composition
1778
+ */
1779
+ type MotifOptions = {
1780
+ /** The key the line is written in; a key name such as `'C major'` is read as that key. */
1781
+ key: KeyLike;
1782
+ /**
1783
+ * Chord the downbeats are pulled to, if any; it may be written as a chord
1784
+ * symbol as well as given as chord data.
1785
+ */
1786
+ chord?: ChordLike | null;
1787
+ /** Length of the motif in bars of `ts`. */
1788
+ bars: number;
1789
+ /**
1790
+ * Meter the bars are counted in. It sets the bar length, so a motif shares a
1791
+ * bar grid with the other generators instead of assuming four beats.
1792
+ *
1793
+ * @defaultValue 4/4
1794
+ */
1795
+ ts?: TimeSignature;
1796
+ /**
1797
+ * Melodic contour shape the line follows.
1798
+ *
1799
+ * @defaultValue `'arch'`
1800
+ */
1801
+ contour?: MotifContour;
1802
+ /**
1803
+ * Probability in [0, 1] that a note is nudged by a single diatonic step. The
1804
+ * nudge direction is balanced (up or down with equal odds), so it adds
1805
+ * variety without biasing the line off its contour. Default 0, which
1806
+ * reproduces the requested contour exactly (no drift, tails return to tonic).
1807
+ *
1808
+ * Sugar for `ctx: { complexity: { ornament } }` — a nudge off the contour is
1809
+ * decoration rather than rhythm; the context wins where both are given. Being
1810
+ * the same dial, it takes the same values: one outside [0, 1] is rejected here
1811
+ * exactly as it is there, rather than being folded to the nearest end.
1812
+ *
1813
+ * @defaultValue 0
1814
+ * @throws If it is not a finite number in [0, 1].
1815
+ */
1816
+ jitter?: number;
1817
+ /**
1818
+ * The generation context. Its `complexity.ornament` is this generator's
1819
+ * jitter, and its `seed` fixes which notes are nudged.
1820
+ *
1821
+ * @defaultValue `{ seed: 0 }`
1822
+ */
1823
+ ctx?: GenerationContextInput;
1824
+ };
1825
+ /**
1826
+ * Generate a short seed motif over `bars`.
1827
+ *
1828
+ * The melody follows the requested contour in diatonic steps from the tonic,
1829
+ * snapped to the key. When a chord is supplied, notes landing on bar downbeats
1830
+ * are pulled to the nearest chord tone. An optional `jitter` adds balanced
1831
+ * per-note variation without biasing the contour; it is off by default, so the
1832
+ * contour is reproduced exactly. Output is deterministic for a given seed.
1833
+ *
1834
+ * @param opts Key, optional chord, length, contour, jitter, and context.
1835
+ * @returns The generated motif cell.
1836
+ *
1837
+ * @example
1838
+ * ```ts
1839
+ * import { generateMotif, majorKey } from '@libraz/libcantus';
1840
+ * const cell = generateMotif({ key: majorKey(0), bars: 2, contour: 'arch' });
1841
+ * // cell.notes is a deterministic MotifCell (seed defaults to 0)
1842
+ * ```
1843
+ *
1844
+ * @category Composition
1845
+ */
1846
+ declare function generateMotif(opts: MotifOptions): MotifCell;
1847
+ /**
1848
+ * Turn a motif cell into note events.
1849
+ *
1850
+ * A {@link MotifNote} already carries the {@link NoteEvent} fields, but a cell
1851
+ * wraps them in an object; this unwraps it, so a motif can be humanized,
1852
+ * grooved, or written out like any other line.
1853
+ *
1854
+ * @param cell The motif to convert.
1855
+ * @returns The cell's notes, copied, in the order they are stored.
1856
+ * @example
1857
+ * ```ts
1858
+ * import { generateMotif, humanize, majorKey, motifToNoteEvents } from '@libraz/libcantus';
1859
+ * humanize(motifToNoteEvents(generateMotif({ key: majorKey(0), bars: 2 })));
1860
+ * ```
1861
+ * @category Composition
1862
+ */
1863
+ declare function motifToNoteEvents(cell: MotifCell): NoteEvent[];
1864
+ /**
1865
+ * Apply a transformation to a motif cell.
1866
+ *
1867
+ * `invert` reflects pitches about the first note (chromatic, self-inverse);
1868
+ * `retrograde` mirrors onsets about the cell span, preserving rests (self-inverse);
1869
+ * `augment`/`diminish` scale time by `amount ?? 2` and its reciprocal;
1870
+ * `transposeChromatic` adds `amount` semitones; `transposeDiatonic` shifts by
1871
+ * `amount` scale degrees when a `key` is given, or — with no `key` — falls back
1872
+ * to a chromatic shift of `amount` semitones; `sequence` appends a shifted copy
1873
+ * (by `amount` diatonic degrees with a `key`, or by `amount` semitones without
1874
+ * one, since it delegates to `transposeDiatonic`).
1875
+ *
1876
+ * @param cell The cell to transform.
1877
+ * @param t The transformation.
1878
+ * @param amount Optional parameter (semitones, degrees, or time factor).
1879
+ * @param key Key context for the diatonic transforms, as a key name such as
1880
+ * `'C major'` or as a key/scale; without it, `transposeDiatonic` and
1881
+ * `sequence` shift chromatically by semitones.
1882
+ * @returns The transformed cell.
1883
+ * @throws If a transformed pitch would fall outside the MIDI range 0..127.
1884
+ *
1885
+ * @example
1886
+ * ```ts
1887
+ * import { generateMotif, transformMotif, majorKey } from '@libraz/libcantus';
1888
+ * const cell = generateMotif({ key: majorKey(0), bars: 1 });
1889
+ * const inverted = transformMotif(cell, 'invert');
1890
+ * ```
1891
+ *
1892
+ * @category Composition
1893
+ */
1894
+ declare function transformMotif(cell: MotifCell, t: MotifTransform, amount?: number, key?: KeyLike): MotifCell;
1895
+ /**
1896
+ * Lay a motif across `bars` and snap it to a chord timeline.
1897
+ *
1898
+ * The cell is tiled back-to-back to fill the requested span. Notes that carry
1899
+ * structural weight — the head of each tile and every bar line — are pulled to
1900
+ * the nearest chord tone of the segment sounding at that onset, so the
1901
+ * developed line spells the underlying harmony. The notes between them are kept
1902
+ * in the key as passing and neighbour tones, and two pitches that differ in the
1903
+ * cell still differ in the development, so the development is still recognizably
1904
+ * the motif rather than a chord arpeggiated over the cell's rhythm.
1905
+ *
1906
+ * @param cell The source motif.
1907
+ * @param timeline Chord segments to snap against.
1908
+ * @param key Key context, as a key name such as `'C major'` or as a key/scale;
1909
+ * the notes off the structural positions are kept in it.
1910
+ * @param bars Number of bars to fill.
1911
+ * @param ts Meter the bars are counted in; defaults to 4/4.
1912
+ * @returns The developed, harmony-aware cell.
1913
+ *
1914
+ * @example
1915
+ * ```ts
1916
+ * import { chordTimelineFromChords, developMotif, generateMotif, majorKey } from '@libraz/libcantus';
1917
+ * const key = majorKey(0);
1918
+ * const timeline = chordTimelineFromChords([{ rootPc: 0, quality: 'maj', startBeat: 0 }], 8);
1919
+ * const developed = developMotif(generateMotif({ key, bars: 1 }), timeline, key, 2);
1920
+ * developed.notes.map((n) => n.pitch); // [60, 62, 60, 60, 62, 60] — the C-D-C cell, twice
1921
+ * ```
1922
+ *
1923
+ * @category Composition
1924
+ */
1925
+ declare function developMotif(cell: MotifCell, timeline: ChordTimeline, key: KeyLike, bars: number, ts?: TimeSignature): MotifCell;
1926
+
1927
+ /**
1928
+ * Ornaments applied to material that already exists.
1929
+ *
1930
+ * Decoration is not a property of generation: this layer takes notes and gives
1931
+ * notes back, the way {@link humanize} does, so a line from any source — a
1932
+ * generator, a MIDI import, a caller's own array — can be decorated without
1933
+ * being generated again by whichever generator happens to write ghost notes.
1934
+ *
1935
+ * The decoration is carried as an {@link Articulation}, not baked into extra
1936
+ * onsets: a flam is one note marked `'flam'`, so the grace stroke stays the
1937
+ * reader's to voice and nothing downstream has to guess which of two onsets was
1938
+ * the ornament.
1939
+ */
1940
+
1941
+ /**
1942
+ * Every ornament idiom, in declaration order.
1943
+ *
1944
+ * @category Composition
1945
+ */
1946
+ declare const ORNAMENT_STYLES: readonly ["ghost", "flam", "drag", "slide", "accent"];
1947
+ /**
1948
+ * The kind of ornament to apply.
1949
+ *
1950
+ * - `'ghost'`: soften weak-position notes into ghosted strokes.
1951
+ * - `'flam'`: mark accented notes as flammed — the strong positions `'accent'`
1952
+ * also takes, voiced as a grace stroke instead of a velocity lift.
1953
+ * - `'drag'`: mark notes leading into a strong position as dragged. A weak-position
1954
+ * note qualifies only when the next onset in the material falls on a strong one.
1955
+ * - `'slide'`: mark a note reached by a step or a leap as slid into.
1956
+ * - `'accent'`: mark strong-position notes as accented, and lift their velocity.
1957
+ *
1958
+ * @category Composition
1959
+ */
1960
+ type OrnamentStyle = (typeof ORNAMENT_STYLES)[number];
1961
+ /**
1962
+ * Options controlling {@link ornament}.
1963
+ *
1964
+ * @category Composition
1965
+ */
1966
+ type OrnamentOptions = {
1967
+ /**
1968
+ * The ornament to apply.
1969
+ *
1970
+ * @defaultValue `'ghost'`
1971
+ */
1972
+ style?: OrnamentStyle;
1973
+ /**
1974
+ * How much of the eligible material is decorated, in [0, 1]. Raising it only
1975
+ * ornaments more notes; the notes already ornamented keep the ornament they
1976
+ * had. Defaults to `complexity.ornament` from the context, or 0.5.
1977
+ */
1978
+ amount?: number;
1979
+ /**
1980
+ * Time signature, used to tell strong positions from weak ones.
1981
+ *
1982
+ * @defaultValue 4/4
1983
+ */
1984
+ ts?: TimeSignature;
1985
+ /**
1986
+ * The generation context. Its `complexity.ornament` sets the amount, its
1987
+ * `complexity.difficulty` — with a tempo — keeps ornaments off passages too
1988
+ * fast to play them in, and its `seed` fixes which notes are decorated.
1989
+ *
1990
+ * @defaultValue `{ seed: 0 }`
1991
+ */
1992
+ ctx?: GenerationContextInput;
1993
+ };
1994
+ /**
1995
+ * Decorate existing notes with an ornament.
1996
+ *
1997
+ * Which notes are eligible follows from the style and the meter — ghosts fall on
1998
+ * weak positions, drags on the weak position before a strong one, flams and
1999
+ * accents on strong ones, slides on notes reached by a leap — and `amount`
2000
+ * decides how many of those eligible notes are taken. The choice is addressed by
2001
+ * each note's own position, so raising `amount` adds ornaments without moving the
2002
+ * ones already there, and no onset is ever added or shifted: a decorated note is
2003
+ * the same note carrying an {@link Articulation}. A note that already carries one
2004
+ * is left alone, so ornament passes can be layered.
2005
+ *
2006
+ * With a difficulty ceiling and a tempo in the context, a passage whose strokes
2007
+ * come too fast for that ceiling is left plain — the ornament is exactly the
2008
+ * kind of candidate a ceiling exists to reject.
2009
+ *
2010
+ * @param notes The material to decorate.
2011
+ * @param opts Style, amount, meter, and context.
2012
+ * @returns Copies of the sounding notes, in input order, with ornaments applied.
2013
+ * @example
2014
+ * ```ts
2015
+ * import { generateBassLine, majorKey, makeChord, ornament } from '@libraz/libcantus';
2016
+ * const line = generateBassLine({
2017
+ * segments: [{ startBeat: 0, endBeat: 4, chord: makeChord(0, 'maj') }],
2018
+ * key: majorKey(0),
2019
+ * style: 'pop',
2020
+ * });
2021
+ * ornament(line, { style: 'ghost', amount: 0.4, ctx: 3 });
2022
+ * ```
2023
+ * Notes with a zero or negative duration never sound and are dropped, so the
2024
+ * result can be shorter than the input.
2025
+ *
2026
+ * @category Composition
2027
+ */
2028
+ declare function ornament(notes: readonly NoteEvent[], opts?: OrnamentOptions): NoteEvent[];
2029
+
2030
+ /**
2031
+ * Reharmonization: chord substitution, modal-interchange palettes, and negative
2032
+ * harmony. These transforms take an existing chord (or a key) and propose
2033
+ * alternative harmonies that preserve a chosen relationship — a shared function,
2034
+ * a common tone, or a reflection across the key axis.
2035
+ *
2036
+ * Roots are pitch classes (0..11); no spelled key signature is required. Every
2037
+ * proposed chord nevertheless carries the spelling hints of the key it was asked
2038
+ * for, so a flat-side key names its chromatic chords with flats (the tritone
2039
+ * substitute of G7 in C major is Db7, not C#7).
2040
+ */
2041
+
2042
+ /**
2043
+ * The kind of substitution relationship a candidate realizes.
2044
+ *
2045
+ * An applied dominant is not among them: which dominant applies is decided by
2046
+ * the harmony that follows, and {@link substituteChord} is given one chord and a
2047
+ * key. Build one with `secondaryDominantOf` where the target is known, or let
2048
+ * `harmonizeMelody` open its vocabulary with `reharmonize: 'secondaryDominant'`
2049
+ * where the progression is being chosen as a whole.
2050
+ *
2051
+ * @category Reharmonization
2052
+ */
2053
+ type SubstitutionType = 'tritone' | 'relative' | 'borrowed' | 'chromaticMediant';
2054
+ /**
2055
+ * A proposed chord substitution with its relationship, numeral, and function.
2056
+ *
2057
+ * @category Reharmonization
2058
+ */
2059
+ type Substitution = {
2060
+ chord: Chord;
2061
+ type: SubstitutionType;
2062
+ roman: string;
2063
+ function: HarmonicFunction;
2064
+ };
2065
+ /**
2066
+ * Options controlling which substitutions {@link substituteChord} returns.
2067
+ *
2068
+ * @category Reharmonization
2069
+ */
2070
+ type SubstituteOptions = {
2071
+ /**
2072
+ * Melody pitch classes that must remain chord tones. When given, only
2073
+ * substitutions whose pitch classes contain every listed pitch class are
2074
+ * kept, so the melody stays consonant against the new harmony.
2075
+ */
2076
+ melodyPcs?: number[];
2077
+ };
2078
+ /**
2079
+ * Propose substitutions for a chord within a key.
2080
+ *
2081
+ * Candidates are generated by several relationships and deduplicated against the
2082
+ * original chord and each other:
2083
+ *
2084
+ * - `tritone`: for a dominant-type chord, the dominant seventh a tritone away.
2085
+ * - `relative`: a diatonic triad a third away that shares two common tones.
2086
+ * - `borrowed`: a triad from the parallel mode with the same harmonic function.
2087
+ * - `chromaticMediant`: a major/minor triad a third away sharing one common
2088
+ * tone.
2089
+ *
2090
+ * These four are the whole vocabulary. An applied dominant is not proposed
2091
+ * because the chord it applies to is the one that follows, which a call about a
2092
+ * single chord cannot see; `secondaryDominantOf` builds one from a named target.
2093
+ *
2094
+ * Each result carries its Roman numeral and harmonic function in `key`. When
2095
+ * `opts.melodyPcs` is given, only substitutions whose pitch classes contain all
2096
+ * of those pitch classes are returned.
2097
+ *
2098
+ * Every substitution is spelled the way `key` writes it, as `rootSpelling` and
2099
+ * `bassSpelling` hints, so a flat-side key keeps flat names: in C major the
2100
+ * tritone substitute of G7 formats as `Db7` and its chromatic mediants as `Bb`
2101
+ * and `Eb`. Where the substitution's function and the bare pitch class disagree,
2102
+ * the function decides — a tritone substitute is a bII7 and so is written on the
2103
+ * flattened supertonic of the chord it resolves to, and a borrowed chord takes
2104
+ * the spelling of the mode it is borrowed from.
2105
+ *
2106
+ * @param chord The chord to reharmonize, as a chord symbol or as chord data.
2107
+ * @param key The prevailing key, as a key name or as a key/scale.
2108
+ * @param opts Optional melody-preservation constraint.
2109
+ * @returns The deduplicated substitution candidates.
2110
+ * @example
2111
+ * ```ts
2112
+ * import { substituteChord, makeChord, majorKey, formatChordSymbol } from '@libraz/libcantus';
2113
+ * const subs = substituteChord(makeChord(7, 'dom7'), majorKey(0));
2114
+ * subs
2115
+ * .filter((sub) => sub.type === 'tritone')
2116
+ * .map((sub) => formatChordSymbol(sub.chord)); // ['Db7'] — the bII7 of C major, not 'C#7'
2117
+ * ```
2118
+ * @category Reharmonization
2119
+ */
2120
+ declare function substituteChord(chord: ChordLike, key: KeyLike, opts?: SubstituteOptions): Substitution[];
2121
+ /**
2122
+ * A borrowed chord in the modal-interchange palette of a key.
2123
+ *
2124
+ * @category Reharmonization
2125
+ */
2126
+ type BorrowedChord = {
2127
+ chord: Chord;
2128
+ roman: string;
2129
+ source: BorrowedSource;
2130
+ };
2131
+ /**
2132
+ * The modal-interchange palette of a key.
2133
+ *
2134
+ * Lists the diatonic triads of the parallel key that are not diatonic to
2135
+ * `key` (the classic borrowed chords such as `iv`, `bVI`, and `bVII` in a major
2136
+ * key), plus the Neapolitan (a major triad on the flat second degree). Each
2137
+ * carries its Roman numeral and {@link borrowedSource} relative to `key`.
2138
+ *
2139
+ * Every chord is spelled as the mode it is borrowed from writes it, recorded as
2140
+ * a `rootSpelling` hint: C major borrows `Fm`, `Ab` and `Bb` from C minor and
2141
+ * takes `Db` as its Neapolitan, while a minor key borrows in the other direction
2142
+ * (A minor borrows `A`, `D` and `F#m` from A major, with `Bb` as its
2143
+ * Neapolitan). The major dominant of a minor key — `E` in A minor — is not among
2144
+ * them: raising the seventh degree is an alteration inside the key rather than a
2145
+ * chord taken from the parallel mode.
2146
+ *
2147
+ * @param key The prevailing key, as a key name or as a key/scale.
2148
+ * @returns The borrowed-chord palette.
2149
+ * @example
2150
+ * ```ts
2151
+ * import { modalInterchangePalette, majorKey, formatChordSymbol } from '@libraz/libcantus';
2152
+ * const palette = modalInterchangePalette(majorKey(0));
2153
+ * palette.map((borrowed) => formatChordSymbol(borrowed.chord));
2154
+ * // ['Cm', 'Ddim', 'Eb', 'Fm', 'Gm', 'Ab', 'Bb', 'Db'] — flat-side, not 'D#'/'G#'/'A#'
2155
+ * ```
2156
+ * @category Reharmonization
2157
+ */
2158
+ declare function modalInterchangePalette(key: KeyLike): BorrowedChord[];
2159
+ /**
2160
+ * Reflect a chord across the tonic–dominant axis of a key (negative harmony).
2161
+ *
2162
+ * Every pitch class `p` maps to `(2*T + 7 - p) mod 12`, where `T` is the key
2163
+ * tonic — the reflection that swaps the tonic with the dominant. The mirrored
2164
+ * pitch classes (including the bass, if set) are reassembled into a chord by
2165
+ * {@link detectChordBest}; when no chord is recognized, the mirrored root and
2166
+ * the original quality are used as a fallback.
2167
+ *
2168
+ * The result is spelled the way `key` writes it, as `rootSpelling` and
2169
+ * `bassSpelling` hints, so the reflection of a chord in a flat-side key keeps
2170
+ * flat names: in Eb major the mirror of Bb formats as `Abm`, not `G#m`.
2171
+ *
2172
+ * @param chord The chord to reflect, as a chord symbol or as chord data.
2173
+ * @param key The prevailing key, as a key name or as a key/scale.
2174
+ * @returns The negative-harmony counterpart of the chord.
2175
+ * @example
2176
+ * ```ts
2177
+ * import { negativeHarmonyMirror, makeChord, majorKey } from '@libraz/libcantus';
2178
+ * negativeHarmonyMirror(makeChord(7, 'maj'), majorKey(0));
2179
+ * // G major reflected across C's axis => F minor (rootPc 5, quality 'min')
2180
+ * ```
2181
+ * @category Reharmonization
2182
+ */
2183
+ declare function negativeHarmonyMirror(chord: ChordLike, key: KeyLike): Chord;
2184
+
2185
+ /**
2186
+ * Deterministic rhythm generation over a metric grid.
2187
+ *
2188
+ * Builds a fixed-resolution grid across one or more bars of a time signature
2189
+ * and decides, per grid slot, whether it becomes a note onset. Onset
2190
+ * probability follows the metric-accent hierarchy from the `meter` module, so
2191
+ * strong positions (downbeats, secondary pulses) are favored over weak
2192
+ * off-pulse subdivisions. Given a seed the output is fully reproducible.
2193
+ *
2194
+ * Positions and durations are measured in quarter-note beats, matching the
2195
+ * library-wide convention.
2196
+ */
2197
+
2198
+ /**
2199
+ * A single rhythmic note: an onset position and how long it sounds.
2200
+ *
2201
+ * @category Rhythm & Meter
2202
+ */
2203
+ type RhythmEvent = {
2204
+ /** Onset position in quarter-note beats, absolute from the start of the span. */
2205
+ position: number;
2206
+ /** Duration in quarter-note beats, extending to the next onset. */
2207
+ duration: number;
2208
+ };
2209
+ /**
2210
+ * Turn rhythm events into note events at a fixed pitch.
2211
+ *
2212
+ * {@link RhythmEvent} names its fields `position`/`duration` because a rhythm
2213
+ * has no pitch; every other generator in the library speaks {@link NoteEvent}.
2214
+ * This is the bridge, so a generated rhythm can be humanized, grooved, or
2215
+ * written out like any other line.
2216
+ *
2217
+ * @param events The rhythm events to convert.
2218
+ * @param pitch The MIDI pitch to give every note.
2219
+ * @param velocity Velocity for every note; defaults to a mezzo-forte 96.
2220
+ * @returns The note events, in the order given.
2221
+ * @example
2222
+ * ```ts
2223
+ * import { generateRhythm, humanize, rhythmToNoteEvents } from '@libraz/libcantus';
2224
+ * const rhythm = generateRhythm({ numerator: 4, denominator: 4 }, { bars: 2 });
2225
+ * humanize(rhythmToNoteEvents(rhythm, 38));
2226
+ * ```
2227
+ * @category Rhythm & Meter
2228
+ */
2229
+ declare function rhythmToNoteEvents(events: RhythmEvent[], pitch: number, velocity?: number): NoteEvent[];
2230
+ /**
2231
+ * Options controlling {@link generateRhythm}.
2232
+ *
2233
+ * @category Rhythm & Meter
2234
+ */
2235
+ type RhythmOptions = {
2236
+ /**
2237
+ * Number of bars to generate.
2238
+ *
2239
+ * @defaultValue 1
2240
+ */
2241
+ bars?: number;
2242
+ /**
2243
+ * Grid resolution as the number of equal grid steps per quarter-note beat.
2244
+ * The default of 2 yields an eighth-note grid; 4 yields a sixteenth-note
2245
+ * grid, 3 an eighth-note triplet grid, and so on.
2246
+ *
2247
+ * @defaultValue 2
2248
+ */
2249
+ subdivision?: number;
2250
+ /**
2251
+ * The generation context.
2252
+ *
2253
+ * Its `complexity.rhythmic` is this generator's overall onset density: it
2254
+ * scales the per-slot onset probability, so higher values fill more grid
2255
+ * slots. A value outside [0, 1] is rejected. Its `seed` fixes which slots are
2256
+ * taken, so the same seed always yields the same pattern.
2257
+ *
2258
+ * @defaultValue `{ seed: 0, complexity: { rhythmic: 0.5 } }`
2259
+ */
2260
+ ctx?: GenerationContextInput;
2261
+ };
2262
+ /**
2263
+ * Map a metric weight (0–3 from {@link metricWeight}) to a base onset
2264
+ * probability before density scaling. Strong positions are much more likely to
2265
+ * carry an onset than weak ones; the curve is intentionally small and explicit
2266
+ * so its behavior is easy to inspect and test.
2267
+ *
2268
+ * @param weight The metric weight, 0 (off-pulse) to 3 (downbeat).
2269
+ * @returns A base onset probability in [0, 1].
2270
+ * @category Rhythm & Meter
2271
+ */
2272
+ declare function onsetWeightCurve(weight: number): number;
2273
+ /**
2274
+ * Generate a deterministic rhythmic pattern over `bars` bars of a time
2275
+ * signature. A grid is built at the chosen subdivision; each slot becomes an
2276
+ * onset when the draw belonging to that slot falls under its metric weight
2277
+ * scaled by the density dial. Because the draw belongs to the slot rather than
2278
+ * to the order the slots were visited, raising the dial only adds onsets:
2279
+ * everything already sounding stays where it was. Every bar's downbeat is always
2280
+ * an onset, so a pattern of any length has a reliable pulse independent of the
2281
+ * seed. Each event's duration extends to the next onset, and the last event
2282
+ * extends to the end of the span.
2283
+ *
2284
+ * @param ts The time signature.
2285
+ * @param opts Generation options.
2286
+ * @returns Onset events sorted by position, non-overlapping, covering the span.
2287
+ * @example
2288
+ * ```ts
2289
+ * import { parseTimeSignature, generateRhythm } from '@libraz/libcantus';
2290
+ * const ts = parseTimeSignature('4/4');
2291
+ * generateRhythm(ts, { ctx: { seed: 42, complexity: { rhythmic: 0.6 } } });
2292
+ * // onset events over one bar
2293
+ * ```
2294
+ * @category Rhythm & Meter
2295
+ */
2296
+ declare function generateRhythm(ts: TimeSignature, opts?: RhythmOptions): RhythmEvent[];
2297
+ /**
2298
+ * Onset density of a generated pattern: the mean number of onsets per bar.
2299
+ *
2300
+ * @param events Events from {@link generateRhythm}.
2301
+ * @param ts The time signature.
2302
+ * @returns The onset count divided by the number of bars the events span.
2303
+ * @category Rhythm & Meter
2304
+ */
2305
+ declare function rhythmDensity(events: RhythmEvent[], ts: TimeSignature): number;
2306
+
2307
+ export { type Section as $, type MotifCell as A, BAR_STEPS as B, type Complexity as C, DRUM_NOTES as D, type EuclideanKick as E, type MotifContour as F, GENRES as G, type HarmonizeOptions as H, type MotifNote as I, type MotifOptions as J, type MotifTransform as K, type OrnamentOptions as L, type MelodyNote as M, type OrnamentStyle as N, ORNAMENT_STYLES as O, PROVENANCE_BASES as P, type ProgFunction as Q, type ProgStyle as R, type ProgressionDegree as S, type ProgressionOptions as T, type ProgressionPreset as U, type Provenance as V, type ProvenanceBasis as W, type ResolvedContext as X, type RhythmEvent as Y, type RhythmOptions as Z, STEP_BEATS as _, BASS_STYLES as a, type SubstituteOptions as a0, type Substitution as a1, type SubstitutionType as a2, type Vocabulary as a3, type VocabularyQuery as a4, applyGrooveTemplate as a5, assertVocabulary as a6, deform as a7, developMotif as a8, double as a9, rhythmDensity as aA, rhythmToNoteEvents as aB, selectVocabulary as aC, substituteChord as aD, syncopate as aE, thin as aF, transformMotif as aG, vocabularyOfKind as aH, withinCeiling as aI, doubleTime as aa, drumVoiceOf as ab, extractGrooveTemplate as ac, fitsQuery as ad, generateBassLine as ae, generateCounterMelody as af, generateDrums as ag, generateMotif as ah, generateProgression as ai, generateRhythm as aj, gridMetricWeight as ak, halfTime as al, harmonizeMelody as am, humanize as an, mergeVocabulary as ao, modalInterchangePalette as ap, motifToNoteEvents as aq, negativeHarmonyMirror as ar, onsetWeightCurve as as, ornament as at, ornamentBy as au, pickProgressionPreset as av, pickVocabulary as aw, progressions as ax, progressionsByStyle as ay, resolveContext as az, BEAT_STEPS as b, BORROWED_DEGREES as c, type BassLineOptions as d, type BassSegment as e, type BassStyle as f, type BorrowedChord as g, type CounterMelodyOptions as h, type DeformOptions as i, type Draw as j, type DrumHit as k, type DrumRole as l, type DrumStyle as m, type DrumVoice as n, type DrumsOptions as o, type GenerationContext as p, type GenerationContextInput as q, type Genre as r, type GridEvent as s, type GrooveFeel as t, type GrooveSlot as u, type GrooveStyle as v, type GrooveTemplate as w, type HarmonizePlacement as x, type HarmonizeResult as y, type HumanizeOptions as z };