@libraz/libcantus 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/README.md +6 -5
  2. package/dist/analyze/index.cjs +41 -1676
  3. package/dist/analyze/index.cjs.map +1 -1
  4. package/dist/analyze/index.d.cts +194 -122
  5. package/dist/analyze/index.d.ts +194 -122
  6. package/dist/analyze/index.js +19 -8
  7. package/dist/chunk-36N73KZ3.cjs +246 -0
  8. package/dist/chunk-36N73KZ3.cjs.map +1 -0
  9. package/dist/chunk-4HLLZPXT.cjs +3077 -0
  10. package/dist/chunk-4HLLZPXT.cjs.map +1 -0
  11. package/dist/chunk-7MGZ7D62.cjs +498 -0
  12. package/dist/chunk-7MGZ7D62.cjs.map +1 -0
  13. package/dist/{chunk-R3S2LUBH.js → chunk-7NKDPF74.js} +660 -305
  14. package/dist/chunk-7NKDPF74.js.map +1 -0
  15. package/dist/chunk-CAN76NCU.cjs +679 -0
  16. package/dist/chunk-CAN76NCU.cjs.map +1 -0
  17. package/dist/{chunk-7Y7WF64D.js → chunk-D2FAJBYL.js} +104 -35
  18. package/dist/chunk-D2FAJBYL.js.map +1 -0
  19. package/dist/{chunk-COKTX2G5.js → chunk-EHVEBWM5.js} +1052 -510
  20. package/dist/chunk-EHVEBWM5.js.map +1 -0
  21. package/dist/chunk-FCANXBAT.cjs +30 -0
  22. package/dist/chunk-FCANXBAT.cjs.map +1 -0
  23. package/dist/chunk-FNDWXRRI.cjs +479 -0
  24. package/dist/chunk-FNDWXRRI.cjs.map +1 -0
  25. package/dist/{chunk-GQKN5KDT.js → chunk-GQSGS2N4.js} +43 -29
  26. package/dist/chunk-GQSGS2N4.js.map +1 -0
  27. package/dist/chunk-HKGYCQ34.js +53 -0
  28. package/dist/chunk-HKGYCQ34.js.map +1 -0
  29. package/dist/chunk-I24MV65U.js +106 -0
  30. package/dist/chunk-I24MV65U.js.map +1 -0
  31. package/dist/{chunk-LN7ZZMD2.js → chunk-KMHOICIY.js} +322 -173
  32. package/dist/chunk-KMHOICIY.js.map +1 -0
  33. package/dist/chunk-LW53AHQU.cjs +1117 -0
  34. package/dist/chunk-LW53AHQU.cjs.map +1 -0
  35. package/dist/chunk-N6IHQUOX.js +1 -0
  36. package/dist/chunk-N6IHQUOX.js.map +1 -0
  37. package/dist/chunk-NKIBQ6PT.js +30 -0
  38. package/dist/chunk-NKIBQ6PT.js.map +1 -0
  39. package/dist/chunk-NP4NA2FB.cjs +106 -0
  40. package/dist/chunk-NP4NA2FB.cjs.map +1 -0
  41. package/dist/chunk-NVE34YGZ.cjs +1449 -0
  42. package/dist/chunk-NVE34YGZ.cjs.map +1 -0
  43. package/dist/{chunk-A4532ZFR.js → chunk-NYYWMV2C.js} +115 -47
  44. package/dist/chunk-NYYWMV2C.js.map +1 -0
  45. package/dist/{chunk-5HTNMQW4.js → chunk-Q2XUP227.js} +310 -118
  46. package/dist/chunk-Q2XUP227.js.map +1 -0
  47. package/dist/chunk-Q6OP4U43.cjs +327 -0
  48. package/dist/chunk-Q6OP4U43.cjs.map +1 -0
  49. package/dist/chunk-RC6GBMCD.cjs +845 -0
  50. package/dist/chunk-RC6GBMCD.cjs.map +1 -0
  51. package/dist/{chunk-XTC3X6MS.js → chunk-RK35RIN7.js} +453 -152
  52. package/dist/chunk-RK35RIN7.js.map +1 -0
  53. package/dist/chunk-RUMU3UA6.cjs +53 -0
  54. package/dist/chunk-RUMU3UA6.cjs.map +1 -0
  55. package/dist/chunk-T64C7ZD7.cjs +1 -0
  56. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  57. package/dist/chunk-XEBG3G53.js +498 -0
  58. package/dist/chunk-XEBG3G53.js.map +1 -0
  59. package/dist/chunk-YLEMGEFH.js +246 -0
  60. package/dist/chunk-YLEMGEFH.js.map +1 -0
  61. package/dist/chunk-ZEJR4TKL.cjs +173 -0
  62. package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
  63. package/dist/core/index.cjs +130 -431
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +331 -17
  66. package/dist/core/index.d.ts +331 -17
  67. package/dist/core/index.js +71 -13
  68. package/dist/{function-2bJW1Zp_.d.ts → function-BtiZb9i9.d.ts} +28 -7
  69. package/dist/{function-r2sh-jOO.d.cts → function-DNugAOrt.d.cts} +28 -7
  70. package/dist/generate/index.cjs +30 -3751
  71. package/dist/generate/index.cjs.map +1 -1
  72. package/dist/generate/index.d.cts +373 -59
  73. package/dist/generate/index.d.ts +373 -59
  74. package/dist/generate/index.js +23 -9
  75. package/dist/{index-DZ1M21do.d.cts → index-6VR8-kit.d.ts} +20 -4
  76. package/dist/index-70p168VM.d.ts +192 -0
  77. package/dist/{index-D_ZTUNKp.d.cts → index-Be74mcon.d.cts} +18 -17
  78. package/dist/index-BtXT6UQ0.d.cts +192 -0
  79. package/dist/{index-BMPxwjwV.d.ts → index-C5UBbQ4G.d.ts} +18 -17
  80. package/dist/{index-BOBXBNwP.d.cts → index-C83mZI1P.d.ts} +93 -8
  81. package/dist/{index-BdeQ1r_p.d.cts → index-D6b6na69.d.cts} +14 -11
  82. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  83. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  84. package/dist/index-OG65ocOr.d.cts +272 -0
  85. package/dist/index-OG65ocOr.d.ts +272 -0
  86. package/dist/{index-BreoJRw0.d.ts → index-tJhkmQgE.d.cts} +20 -4
  87. package/dist/{index-CP3wAfEH.d.ts → index-tSQh-xwY.d.ts} +14 -11
  88. package/dist/{index-CM5W4rcm.d.ts → index-y_ak9_Hi.d.cts} +93 -8
  89. package/dist/index.cjs +419 -6848
  90. package/dist/index.cjs.map +1 -1
  91. package/dist/index.d.cts +14 -13
  92. package/dist/index.d.ts +14 -13
  93. package/dist/index.js +132 -27
  94. package/dist/model/index.cjs +8 -2423
  95. package/dist/model/index.cjs.map +1 -1
  96. package/dist/model/index.d.cts +433 -74
  97. package/dist/model/index.d.ts +433 -74
  98. package/dist/model/index.js +6 -7
  99. package/dist/styled-DIRkk9Uz.d.ts +506 -0
  100. package/dist/styled-DJbkJQfj.d.cts +506 -0
  101. package/dist/theory/index.cjs +172 -1676
  102. package/dist/theory/index.cjs.map +1 -1
  103. package/dist/theory/index.d.cts +151 -233
  104. package/dist/theory/index.d.ts +151 -233
  105. package/dist/theory/index.js +31 -7
  106. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  107. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  108. package/package.json +73 -24
  109. package/dist/chunk-5HTNMQW4.js.map +0 -1
  110. package/dist/chunk-7Y7WF64D.js.map +0 -1
  111. package/dist/chunk-A4532ZFR.js.map +0 -1
  112. package/dist/chunk-BHUT2S5G.js +0 -22
  113. package/dist/chunk-BHUT2S5G.js.map +0 -1
  114. package/dist/chunk-CCBLVAPK.js +0 -42
  115. package/dist/chunk-CCBLVAPK.js.map +0 -1
  116. package/dist/chunk-COKTX2G5.js.map +0 -1
  117. package/dist/chunk-DYWFNC4A.js +0 -152
  118. package/dist/chunk-DYWFNC4A.js.map +0 -1
  119. package/dist/chunk-GQKN5KDT.js.map +0 -1
  120. package/dist/chunk-LN7ZZMD2.js.map +0 -1
  121. package/dist/chunk-MSWFZF7J.js +0 -118
  122. package/dist/chunk-MSWFZF7J.js.map +0 -1
  123. package/dist/chunk-R3S2LUBH.js.map +0 -1
  124. package/dist/chunk-SDM3VJJ5.js +0 -41
  125. package/dist/chunk-SDM3VJJ5.js.map +0 -1
  126. package/dist/chunk-XTC3X6MS.js.map +0 -1
  127. package/dist/chunk-ZXW5YIWK.js +0 -55
  128. package/dist/chunk-ZXW5YIWK.js.map +0 -1
  129. package/dist/index-CGMZNCx6.d.cts +0 -140
  130. package/dist/index-CGMZNCx6.d.ts +0 -140
  131. package/dist/styled-BjXtOFNk.d.cts +0 -282
  132. package/dist/styled-D7BAx3S3.d.ts +0 -282
  133. package/src/analyze/arrange/index.ts +0 -21
  134. package/src/analyze/arrange/internal.ts +0 -125
  135. package/src/analyze/arrange/tension.ts +0 -187
  136. package/src/analyze/arrange/tracks.ts +0 -279
  137. package/src/analyze/detect/index.ts +0 -235
  138. package/src/analyze/functional/borrowed.ts +0 -84
  139. package/src/analyze/functional/cadence.ts +0 -66
  140. package/src/analyze/functional/function.ts +0 -184
  141. package/src/analyze/functional/index.ts +0 -23
  142. package/src/analyze/functional/internal.ts +0 -29
  143. package/src/analyze/functional/roman.ts +0 -393
  144. package/src/analyze/index.ts +0 -49
  145. package/src/analyze/timeline/index.ts +0 -415
  146. package/src/analyze/voice/index.ts +0 -330
  147. package/src/core/index.ts +0 -47
  148. package/src/core/interval/index.ts +0 -81
  149. package/src/core/meter/index.ts +0 -282
  150. package/src/core/pitch/index.ts +0 -311
  151. package/src/core/random/index.ts +0 -53
  152. package/src/core/tuning/index.ts +0 -156
  153. package/src/core/types.ts +0 -29
  154. package/src/generate/bass/index.ts +0 -350
  155. package/src/generate/countermelody/index.ts +0 -379
  156. package/src/generate/drums/beat.ts +0 -266
  157. package/src/generate/drums/euclid.ts +0 -58
  158. package/src/generate/drums/fills.ts +0 -356
  159. package/src/generate/drums/ghost.ts +0 -119
  160. package/src/generate/drums/hihat.ts +0 -285
  161. package/src/generate/drums/hit.ts +0 -31
  162. package/src/generate/drums/index.ts +0 -329
  163. package/src/generate/drums/internal.ts +0 -257
  164. package/src/generate/drums/kick.ts +0 -160
  165. package/src/generate/drums/percussion.ts +0 -121
  166. package/src/generate/drums/rng.ts +0 -7
  167. package/src/generate/drums/swing.ts +0 -71
  168. package/src/generate/groove/index.ts +0 -326
  169. package/src/generate/harmonize/index.ts +0 -440
  170. package/src/generate/index.ts +0 -59
  171. package/src/generate/motif/index.ts +0 -398
  172. package/src/generate/progression/index.ts +0 -370
  173. package/src/generate/reharmony/index.ts +0 -330
  174. package/src/generate/rhythm/index.ts +0 -165
  175. package/src/index.ts +0 -247
  176. package/src/model/chord.ts +0 -423
  177. package/src/model/index.ts +0 -15
  178. package/src/model/interval.ts +0 -82
  179. package/src/model/key.ts +0 -266
  180. package/src/model/note.ts +0 -176
  181. package/src/model/progression.ts +0 -161
  182. package/src/model/shared.ts +0 -12
  183. package/src/theory/chord/index.ts +0 -373
  184. package/src/theory/chordscale/index.ts +0 -374
  185. package/src/theory/counterpoint/index.ts +0 -230
  186. package/src/theory/harmony/index.ts +0 -92
  187. package/src/theory/index.ts +0 -107
  188. package/src/theory/safety/index.ts +0 -419
  189. package/src/theory/scale/degrees.ts +0 -123
  190. package/src/theory/scale/index.ts +0 -28
  191. package/src/theory/scale/key.ts +0 -56
  192. package/src/theory/scale/masks.ts +0 -153
  193. package/src/theory/spelling/index.ts +0 -182
  194. package/src/theory/symbol/index.ts +0 -268
  195. package/src/theory/voicing/index.ts +0 -5
  196. package/src/theory/voicing/internal.ts +0 -168
  197. package/src/theory/voicing/leading.ts +0 -112
  198. package/src/theory/voicing/satb.ts +0 -195
  199. package/src/theory/voicing/styled.ts +0 -176
  200. package/tsconfig.json +0 -23
@@ -1,356 +0,0 @@
1
- import type { HitList } from './hit.js';
2
- import type { DrumStyle, SectionEnergy, SectionType } from './internal.js';
3
- import { EIGHTH, GM, SIXTEENTH } from './internal.js';
4
- import type { DrumRng } from './rng.js';
5
-
6
- /** The thirteen fill archetypes for section transitions. */
7
- export type FillType =
8
- | 'snareRoll'
9
- | 'tomDescend'
10
- | 'tomAscend'
11
- | 'snareTomCombo'
12
- | 'simpleCrash'
13
- | 'linearFill'
14
- | 'ghostToAccent'
15
- | 'bdSnareAlternate'
16
- | 'hiHatChoke'
17
- | 'tomShuffle'
18
- | 'breakdownFill'
19
- | 'flamsAndDrags'
20
- | 'halfTimeFill';
21
-
22
- /** Beat at which a fill begins, widening with section energy. */
23
- export function getFillStartBeat(energy: SectionEnergy): number {
24
- switch (energy) {
25
- case 'low':
26
- return 3;
27
- case 'medium':
28
- return 2;
29
- case 'high':
30
- case 'peak':
31
- return 0;
32
- }
33
- }
34
-
35
- /**
36
- * Pick a fill archetype for a section transition.
37
- *
38
- * The transition-specific archetypes (into a chorus, out of an intro) are
39
- * checked before the generic energy fills so every variation stays reachable;
40
- * with `from === to` the caller effectively asks for a within-section fill.
41
- *
42
- * @param from Section the fill leaves.
43
- * @param to Section the fill leads into.
44
- * @param style Internal drum style.
45
- * @param nextEnergy Energy of the section the fill leads into.
46
- * @param rng Deterministic PRNG.
47
- */
48
- export function selectFillType(
49
- from: SectionType,
50
- to: SectionType,
51
- style: DrumStyle,
52
- nextEnergy: SectionEnergy,
53
- rng: DrumRng,
54
- ): FillType {
55
- if (style === 'sparse') {
56
- return rng.range(0, 1) === 0 ? 'simpleCrash' : 'breakdownFill';
57
- }
58
-
59
- const toChorus = to === 'chorus';
60
- const fromIntro = from === 'intro';
61
- const highEnergy = style === 'rock' || style === 'fourOnFloor';
62
-
63
- // Dropping into a low-energy section: keep the phrase end gentle.
64
- if (nextEnergy === 'low') {
65
- switch (rng.range(0, 2)) {
66
- case 0:
67
- return 'simpleCrash';
68
- case 1:
69
- return 'breakdownFill';
70
- default:
71
- return 'halfTimeFill';
72
- }
73
- }
74
-
75
- // Big lead-ins to a chorus take precedence over the generic energy fills.
76
- if (toChorus) {
77
- const choice = rng.range(0, 7);
78
- if (highEnergy) {
79
- switch (choice) {
80
- case 0:
81
- case 1:
82
- return 'tomDescend';
83
- case 2:
84
- return 'snareRoll';
85
- case 3:
86
- return 'linearFill';
87
- case 4:
88
- return 'bdSnareAlternate';
89
- case 5:
90
- return 'flamsAndDrags';
91
- case 6:
92
- return 'tomShuffle';
93
- default:
94
- return 'ghostToAccent';
95
- }
96
- }
97
- switch (choice) {
98
- case 0:
99
- case 1:
100
- return 'snareTomCombo';
101
- case 2:
102
- return 'tomDescend';
103
- case 3:
104
- return 'ghostToAccent';
105
- case 4:
106
- return 'hiHatChoke';
107
- case 5:
108
- return 'linearFill';
109
- default:
110
- return 'snareRoll';
111
- }
112
- }
113
-
114
- // Leaving the intro: lighter, building character.
115
- if (fromIntro) {
116
- switch (rng.range(0, 5)) {
117
- case 0:
118
- case 1:
119
- return 'snareRoll';
120
- case 2:
121
- return 'simpleCrash';
122
- case 3:
123
- return 'ghostToAccent';
124
- case 4:
125
- return 'breakdownFill';
126
- default:
127
- return 'halfTimeFill';
128
- }
129
- }
130
-
131
- // Generic medium/high/peak fills, split by style energy.
132
- const choice = rng.range(0, 7);
133
- if (highEnergy) {
134
- switch (choice) {
135
- case 0:
136
- return 'tomDescend';
137
- case 1:
138
- return 'snareRoll';
139
- case 2:
140
- return 'tomAscend';
141
- case 3:
142
- return 'snareTomCombo';
143
- case 4:
144
- return 'linearFill';
145
- case 5:
146
- return 'bdSnareAlternate';
147
- case 6:
148
- return 'flamsAndDrags';
149
- default:
150
- return 'tomShuffle';
151
- }
152
- }
153
-
154
- switch (choice) {
155
- case 0:
156
- case 1:
157
- return 'snareRoll';
158
- case 2:
159
- return 'snareTomCombo';
160
- case 3:
161
- return 'ghostToAccent';
162
- case 4:
163
- return 'hiHatChoke';
164
- case 5:
165
- return 'halfTimeFill';
166
- default:
167
- return 'breakdownFill';
168
- }
169
- }
170
-
171
- /**
172
- * Emit a fill for a single beat of the fill bar.
173
- *
174
- * @param track Hit accumulator.
175
- * @param beatTick Absolute beat position of this beat.
176
- * @param beat Beat index within the bar (0-3).
177
- * @param fillType Selected fill archetype.
178
- * @param velocity Base velocity for the beat.
179
- */
180
- export function generateFill(
181
- track: HitList,
182
- beatTick: number,
183
- beat: number,
184
- fillType: FillType,
185
- velocity: number,
186
- ): void {
187
- const fillVel = velocity * 0.9;
188
- const accentVel = velocity * 0.95;
189
- const S = SIXTEENTH;
190
- const E = EIGHTH;
191
-
192
- if (beat === 0) {
193
- track.add(GM.BD, beatTick, E, fillVel);
194
- track.add(GM.SD, beatTick + E, E, fillVel - 5);
195
- return;
196
- }
197
- if (beat === 1) {
198
- track.add(GM.SD, beatTick, E, fillVel);
199
- track.add(GM.TOM_H, beatTick + E, E, fillVel - 3);
200
- return;
201
- }
202
-
203
- switch (fillType) {
204
- case 'snareRoll':
205
- if (beat === 2) {
206
- for (let i = 0; i < 4; i += 1) {
207
- track.add(GM.SD, beatTick + i * S, S, fillVel * (0.6 + 0.1 * i));
208
- }
209
- } else {
210
- for (let i = 0; i < 3; i += 1) {
211
- track.add(GM.SD, beatTick + i * S, S, fillVel * (0.7 + 0.1 * i));
212
- }
213
- track.add(GM.SD, beatTick + 3 * S, S, accentVel);
214
- }
215
- break;
216
-
217
- case 'tomDescend':
218
- if (beat === 2) {
219
- track.add(GM.SD, beatTick, E, fillVel);
220
- track.add(GM.TOM_H, beatTick + E, E, fillVel - 5);
221
- } else {
222
- track.add(GM.TOM_H, beatTick, S, fillVel);
223
- track.add(GM.TOM_M, beatTick + S, S, fillVel - 3);
224
- track.add(GM.TOM_M, beatTick + E, S, fillVel - 5);
225
- track.add(GM.TOM_L, beatTick + E + S, S, accentVel);
226
- }
227
- break;
228
-
229
- case 'tomAscend':
230
- if (beat === 2) {
231
- track.add(GM.SD, beatTick, E, fillVel);
232
- track.add(GM.TOM_L, beatTick + E, E, fillVel - 5);
233
- } else {
234
- track.add(GM.TOM_L, beatTick, S, fillVel);
235
- track.add(GM.TOM_M, beatTick + S, S, fillVel + 3);
236
- track.add(GM.TOM_M, beatTick + E, S, fillVel + 5);
237
- track.add(GM.TOM_H, beatTick + E + S, S, accentVel);
238
- }
239
- break;
240
-
241
- case 'snareTomCombo':
242
- if (beat === 2) {
243
- track.add(GM.SD, beatTick, E, fillVel);
244
- track.add(GM.SD, beatTick + E, S, fillVel - 5);
245
- track.add(GM.TOM_H, beatTick + E + S, S, fillVel);
246
- } else {
247
- track.add(GM.TOM_M, beatTick, S, fillVel);
248
- track.add(GM.SD, beatTick + S, S, fillVel - 3);
249
- track.add(GM.TOM_L, beatTick + E, S, fillVel + 2);
250
- track.add(GM.BD, beatTick + E + S, S, accentVel);
251
- }
252
- break;
253
-
254
- case 'simpleCrash':
255
- if (beat === 3) {
256
- track.add(GM.BD, beatTick + E + S, S, accentVel);
257
- track.add(GM.CRASH, beatTick + E + S, E, accentVel);
258
- }
259
- break;
260
-
261
- case 'linearFill':
262
- if (beat === 2) {
263
- track.add(GM.BD, beatTick, S, fillVel);
264
- track.add(GM.SD, beatTick + S, S, fillVel);
265
- track.add(GM.TOM_H, beatTick + 2 * S, S, fillVel);
266
- track.add(GM.TOM_M, beatTick + 3 * S, S, fillVel);
267
- } else {
268
- track.add(GM.TOM_L, beatTick, S, fillVel + 3);
269
- track.add(GM.SD, beatTick + S, S, fillVel + 5);
270
- track.add(GM.BD, beatTick + 2 * S, S, fillVel + 7);
271
- track.add(GM.SD, beatTick + 3 * S, S, accentVel);
272
- }
273
- break;
274
-
275
- case 'ghostToAccent':
276
- if (beat === 2) {
277
- const ghost = fillVel * 0.4;
278
- track.add(GM.SD, beatTick, S, ghost);
279
- track.add(GM.SD, beatTick + S, S, ghost + 10);
280
- track.add(GM.SD, beatTick + 2 * S, S, ghost + 20);
281
- track.add(GM.SD, beatTick + 3 * S, S, ghost + 30);
282
- } else {
283
- track.add(GM.SD, beatTick, E, fillVel);
284
- track.add(GM.SD, beatTick + E, E, accentVel);
285
- }
286
- break;
287
-
288
- case 'bdSnareAlternate':
289
- if (beat === 2) {
290
- track.add(GM.BD, beatTick, S, fillVel);
291
- track.add(GM.SD, beatTick + S, S, fillVel);
292
- track.add(GM.BD, beatTick + 2 * S, S, fillVel + 3);
293
- track.add(GM.SD, beatTick + 3 * S, S, fillVel + 3);
294
- } else {
295
- track.add(GM.BD, beatTick, S, fillVel + 5);
296
- track.add(GM.SD, beatTick + S, S, fillVel + 5);
297
- track.add(GM.BD, beatTick + 2 * S, S, accentVel);
298
- track.add(GM.SD, beatTick + 3 * S, S, accentVel);
299
- }
300
- break;
301
-
302
- case 'hiHatChoke':
303
- if (beat === 2) {
304
- track.add(GM.OHH, beatTick, E, fillVel);
305
- track.add(GM.OHH, beatTick + E, E, fillVel + 5);
306
- } else {
307
- track.add(GM.OHH, beatTick, S, fillVel + 8);
308
- track.add(GM.CHH, beatTick + S, S, accentVel);
309
- track.add(GM.SD, beatTick + E, E, accentVel);
310
- }
311
- break;
312
-
313
- case 'tomShuffle':
314
- if (beat === 2) {
315
- track.add(GM.TOM_H, beatTick, E, fillVel);
316
- track.add(GM.TOM_M, beatTick + E + S / 2, S, fillVel - 5);
317
- } else {
318
- track.add(GM.TOM_M, beatTick, E, fillVel);
319
- track.add(GM.TOM_L, beatTick + E + S / 2, S, fillVel + 5);
320
- }
321
- break;
322
-
323
- case 'breakdownFill':
324
- if (beat === 3) {
325
- track.add(GM.SD, beatTick + E, S, accentVel);
326
- }
327
- break;
328
-
329
- case 'flamsAndDrags':
330
- if (beat === 2) {
331
- track.add(GM.SD, beatTick - S / 4, S / 4, fillVel * 0.5);
332
- track.add(GM.SD, beatTick, E, fillVel);
333
- track.add(GM.SD, beatTick + E, S / 2, fillVel * 0.6);
334
- track.add(GM.SD, beatTick + E + S / 2, S / 2, fillVel * 0.6);
335
- track.add(GM.SD, beatTick + E + S, E, fillVel);
336
- } else {
337
- track.add(GM.SD, beatTick - S / 4, S / 4, fillVel * 0.5);
338
- track.add(GM.SD, beatTick, 1, accentVel);
339
- }
340
- break;
341
-
342
- case 'halfTimeFill':
343
- if (beat === 2) {
344
- track.add(GM.SD, beatTick, 1, accentVel);
345
- track.add(GM.BD, beatTick, 1, fillVel);
346
- } else if (beat === 3) {
347
- // At low energy the fill spans only beat 3, so this branch keeps the
348
- // phrase end from going silent: a broad half-time backbeat snare with a
349
- // light pickup into the next section.
350
- track.add(GM.SD, beatTick, EIGHTH, accentVel);
351
- track.add(GM.SD, beatTick + E, S, fillVel * 0.6);
352
- track.add(GM.SD, beatTick + E + S, S, accentVel);
353
- }
354
- break;
355
- }
356
- }
@@ -1,119 +0,0 @@
1
- import type { BackingDensity, SectionType } from './internal.js';
2
- import { MoodCategory, sectionIndex } from './internal.js';
3
- import type { DrumRng } from './rng.js';
4
-
5
- /** Ghost-note position within a beat: the "e" (1st 16th) or "a" (3rd 16th). */
6
- export type GhostPosition = 'e' | 'a';
7
-
8
- /** Ghost density level ordinal (0 = none, 3 = heavy). */
9
- type GhostDensityLevel = 0 | 1 | 2 | 3;
10
-
11
- // Ghost density level per [section index][mood category].
12
- const GHOST_DENSITY_TABLE: GhostDensityLevel[][] = [
13
- /* intro */ [0, 1, 1],
14
- /* a */ [0, 1, 2],
15
- /* b */ [1, 2, 2],
16
- /* chorus */ [1, 2, 3],
17
- /* bridge */ [1, 1, 2],
18
- /* interlude */ [0, 1, 1],
19
- /* outro */ [0, 1, 1],
20
- ];
21
-
22
- function levelToProbability(level: GhostDensityLevel): number {
23
- switch (level) {
24
- case 0:
25
- return 0;
26
- case 1:
27
- return 0.15;
28
- case 2:
29
- return 0.3;
30
- case 3:
31
- return 0.45;
32
- }
33
- }
34
-
35
- function clampLevel(level: number): GhostDensityLevel {
36
- return Math.max(0, Math.min(3, level)) as GhostDensityLevel;
37
- }
38
-
39
- function adjustForBpm(level: GhostDensityLevel, bpm: number): GhostDensityLevel {
40
- if (bpm >= 160 && level !== 0) {
41
- return clampLevel(level - 1);
42
- }
43
- if (bpm <= 90 && level !== 3) {
44
- return clampLevel(level + 1);
45
- }
46
- return level;
47
- }
48
-
49
- /** Ghost-note trigger probability for a section, mood, density, and tempo. */
50
- export function getGhostDensity(
51
- mood: MoodCategory,
52
- section: SectionType,
53
- backingDensity: BackingDensity,
54
- bpm: number,
55
- ): number {
56
- const row = GHOST_DENSITY_TABLE[sectionIndex(section)] ?? GHOST_DENSITY_TABLE[1];
57
- const level = adjustForBpm((row?.[mood] ?? 0) as GhostDensityLevel, bpm);
58
-
59
- if (backingDensity === 'thin' && level !== 0) {
60
- return levelToProbability(clampLevel(level - 1));
61
- }
62
- if (backingDensity === 'thick' && level !== 3) {
63
- return levelToProbability(clampLevel(level + 1));
64
- }
65
- return levelToProbability(level);
66
- }
67
-
68
- /** Ghost-note velocity multiplier for a section and metric position. */
69
- export function getGhostVelocity(
70
- section: SectionType,
71
- beatPosition: number,
72
- isAfterSnare: boolean,
73
- ): number {
74
- let base = 0.4;
75
- const even = beatPosition % 2 === 0 ? 0.05 : 0;
76
- switch (section) {
77
- case 'a':
78
- base = 0.35 + even;
79
- break;
80
- case 'chorus':
81
- base = 0.5 + even;
82
- break;
83
- case 'bridge':
84
- base = 0.25 + even;
85
- break;
86
- case 'b':
87
- base = 0.4 + even;
88
- break;
89
- case 'intro':
90
- case 'outro':
91
- base = 0.38;
92
- break;
93
- }
94
- if (isAfterSnare) {
95
- base += 0.1;
96
- }
97
- return Math.max(0.25, Math.min(0.65, base));
98
- }
99
-
100
- /** Probability of a ghost at a specific 16th position (higher near the snare). */
101
- export function getGhostProbabilityAtPosition(beat: number, sixteenthInBeat: number): number {
102
- const nearSnare =
103
- (beat === 0 && sixteenthInBeat === 3) ||
104
- (beat === 1 && sixteenthInBeat === 1) ||
105
- (beat === 2 && sixteenthInBeat === 3) ||
106
- (beat === 3 && sixteenthInBeat === 1);
107
- return nearSnare ? 0.6 : 0.25;
108
- }
109
-
110
- /** Choose which ghost positions a groove favours. */
111
- export function selectGhostPositions(mood: MoodCategory, rng: DrumRng): GhostPosition[] {
112
- if (mood === MoodCategory.Energetic) {
113
- return ['e', 'a'];
114
- }
115
- if (mood === MoodCategory.Calm) {
116
- return rng.prob(0.5) ? ['e'] : [];
117
- }
118
- return ['e'];
119
- }