@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,257 +0,0 @@
1
- /** General MIDI percussion note numbers used across the drum engine. */
2
- export const GM = {
3
- BD: 36,
4
- SD: 38,
5
- SIDESTICK: 37,
6
- HANDCLAP: 39,
7
- CHH: 42,
8
- FHH: 44,
9
- OHH: 46,
10
- CRASH: 49,
11
- RIDE: 51,
12
- TAMBOURINE: 54,
13
- TOM_H: 50,
14
- TOM_M: 47,
15
- TOM_L: 45,
16
- SHAKER: 82,
17
- } as const;
18
-
19
- /** One beat and one sixteenth expressed as fractions of a beat. */
20
- export const EIGHTH = 0.5;
21
- export const SIXTEENTH = 0.25;
22
-
23
- /** Internal drum style selected by the public groove style. */
24
- export type DrumStyle =
25
- | 'sparse'
26
- | 'standard'
27
- | 'fourOnFloor'
28
- | 'upbeat'
29
- | 'rock'
30
- | 'synth'
31
- | 'trap'
32
- | 'latin';
33
-
34
- /** Groove feel controlling off-beat swing. */
35
- export type Feel = 'straight' | 'swing' | 'shuffle';
36
-
37
- /** Reduced section set the engine reasons about. */
38
- export type SectionType = 'intro' | 'a' | 'b' | 'chorus' | 'bridge' | 'outro';
39
-
40
- /** Backing density bucket derived from the public density knob. */
41
- export type BackingDensity = 'thin' | 'normal' | 'thick';
42
-
43
- /** Section energy level, used to shape fills. */
44
- export type SectionEnergy = 'low' | 'medium' | 'high' | 'peak';
45
-
46
- /** Ghost-note mood category for the density table. */
47
- export enum MoodCategory {
48
- Calm = 0,
49
- Standard = 1,
50
- Energetic = 2,
51
- }
52
-
53
- /** Percussion mood category for the activation table. */
54
- export enum PercMoodCategory {
55
- Calm = 0,
56
- Standard = 1,
57
- Energetic = 2,
58
- Idol = 3,
59
- RockDark = 4,
60
- }
61
-
62
- /** Public groove style identifiers. */
63
- export type GrooveStyle =
64
- | 'standard'
65
- | 'funk'
66
- | 'shuffle'
67
- | 'bossa'
68
- | 'trap'
69
- | 'halftime'
70
- | 'breakbeat'
71
- | 'house'
72
- | 'synthpop';
73
-
74
- /** Public role identifiers gating which voices are present. */
75
- export type DrumRole = 'full' | 'ambient' | 'minimal' | 'fxOnly';
76
-
77
- /** Public section identifiers. */
78
- export type PublicSection = 'intro' | 'verse' | 'prechorus' | 'chorus' | 'bridge' | 'outro';
79
-
80
- /** Resolved internal parameters for a public groove style. */
81
- export type StyleMapping = {
82
- style: DrumStyle;
83
- feel: Feel;
84
- ghostBoost: boolean;
85
- snareBeat3: boolean;
86
- };
87
-
88
- const STYLE_MAP: Record<GrooveStyle, StyleMapping> = {
89
- // Standard pop pulse: kick on beats 1 and 3, backbeat snare on 2 and 4.
90
- standard: { style: 'standard', feel: 'straight', ghostBoost: false, snareBeat3: false },
91
- funk: { style: 'upbeat', feel: 'straight', ghostBoost: true, snareBeat3: false },
92
- shuffle: { style: 'standard', feel: 'shuffle', ghostBoost: false, snareBeat3: false },
93
- bossa: { style: 'latin', feel: 'straight', ghostBoost: false, snareBeat3: false },
94
- trap: { style: 'trap', feel: 'straight', ghostBoost: false, snareBeat3: true },
95
- halftime: { style: 'sparse', feel: 'straight', ghostBoost: false, snareBeat3: true },
96
- breakbeat: { style: 'rock', feel: 'straight', ghostBoost: false, snareBeat3: false },
97
- // Four-on-the-floor dance kick with idol-style auxiliary percussion.
98
- house: { style: 'fourOnFloor', feel: 'straight', ghostBoost: false, snareBeat3: false },
99
- // Synth-pop pulse with denser 16th hi-hats and busy kick syncopation.
100
- synthpop: { style: 'synth', feel: 'straight', ghostBoost: true, snareBeat3: false },
101
- };
102
-
103
- /** Resolve the internal style/feel parameters for a public groove style. */
104
- export function mapStyle(style: GrooveStyle): StyleMapping {
105
- return STYLE_MAP[style];
106
- }
107
-
108
- /** Map a public section to the internal section type. */
109
- export function mapSection(section: PublicSection): SectionType {
110
- switch (section) {
111
- case 'intro':
112
- return 'intro';
113
- case 'verse':
114
- return 'a';
115
- case 'prechorus':
116
- return 'b';
117
- case 'chorus':
118
- return 'chorus';
119
- case 'bridge':
120
- return 'bridge';
121
- case 'outro':
122
- return 'outro';
123
- }
124
- }
125
-
126
- /** Bucket the 0..1 density knob into a backing-density level. */
127
- export function mapDensity(density: number): BackingDensity {
128
- if (density < 0.34) {
129
- return 'thin';
130
- }
131
- if (density < 0.67) {
132
- return 'normal';
133
- }
134
- return 'thick';
135
- }
136
-
137
- /** Swing amount (0..1) implied by a groove feel. */
138
- export function feelSwingAmount(feel: Feel): number {
139
- if (feel === 'swing') {
140
- return 0.33;
141
- }
142
- if (feel === 'shuffle') {
143
- return 0.5;
144
- }
145
- return 0;
146
- }
147
-
148
- /** Ghost-note mood category derived from the internal style. */
149
- export function ghostMoodCategory(style: DrumStyle): MoodCategory {
150
- if (style === 'sparse') {
151
- return MoodCategory.Calm;
152
- }
153
- if (style === 'trap' || style === 'upbeat') {
154
- return MoodCategory.Energetic;
155
- }
156
- return MoodCategory.Standard;
157
- }
158
-
159
- /** Percussion mood category derived from the internal style. */
160
- export function percMoodCategory(style: DrumStyle): PercMoodCategory {
161
- switch (style) {
162
- case 'sparse':
163
- return PercMoodCategory.Calm;
164
- case 'upbeat':
165
- case 'latin':
166
- return PercMoodCategory.Energetic;
167
- case 'fourOnFloor':
168
- return PercMoodCategory.Idol;
169
- case 'trap':
170
- case 'rock':
171
- return PercMoodCategory.RockDark;
172
- default:
173
- return PercMoodCategory.Standard;
174
- }
175
- }
176
-
177
- /** Zero-based section index shared by the ghost and percussion tables. */
178
- export function sectionIndex(section: SectionType): number {
179
- switch (section) {
180
- case 'intro':
181
- return 0;
182
- case 'a':
183
- return 1;
184
- case 'b':
185
- return 2;
186
- case 'chorus':
187
- return 3;
188
- case 'bridge':
189
- return 4;
190
- case 'outro':
191
- return 6;
192
- }
193
- }
194
-
195
- /** Section energy used to size fills. */
196
- export function sectionEnergy(section: SectionType): SectionEnergy {
197
- switch (section) {
198
- case 'intro':
199
- case 'outro':
200
- return 'low';
201
- case 'a':
202
- case 'bridge':
203
- return 'medium';
204
- case 'b':
205
- return 'high';
206
- case 'chorus':
207
- return 'peak';
208
- }
209
- }
210
-
211
- /** Section velocity multiplier applied to the base drum velocity. */
212
- export function sectionVelocityMultiplier(section: SectionType): number {
213
- switch (section) {
214
- case 'intro':
215
- case 'outro':
216
- return 0.8;
217
- case 'a':
218
- return 0.9;
219
- case 'b':
220
- return 1.0;
221
- case 'chorus':
222
- return 1.1;
223
- case 'bridge':
224
- return 0.85;
225
- }
226
- }
227
-
228
- /** Section density multiplier before backing-density scaling. */
229
- export function sectionDensityMultiplier(section: SectionType): number {
230
- switch (section) {
231
- case 'intro':
232
- return 0.5;
233
- case 'a':
234
- return 0.7;
235
- case 'b':
236
- return 0.85;
237
- case 'chorus':
238
- return 1.0;
239
- case 'bridge':
240
- return 0.6;
241
- case 'outro':
242
- return 0.6;
243
- }
244
- }
245
-
246
- /** Base drum velocity for a section beat (downbeats accented). */
247
- export function calculateVelocity(section: SectionType, beat: number): number {
248
- const base = 80;
249
- const beatAdj = beat === 0 ? 10 : beat === 2 ? 5 : 0;
250
- const velocity = (base + beatAdj) * sectionVelocityMultiplier(section);
251
- return clampVel(velocity);
252
- }
253
-
254
- /** Round and clamp a velocity into the valid MIDI range. */
255
- export function clampVel(velocity: number): number {
256
- return Math.max(1, Math.min(127, Math.round(velocity)));
257
- }
@@ -1,160 +0,0 @@
1
- import { hasHit } from './euclid.js';
2
- import type { DrumStyle, SectionType } from './internal.js';
3
- import type { DrumRng } from './rng.js';
4
-
5
- /** Kick presence flags across the eight 8th-note slots of a bar. */
6
- export type KickPattern = {
7
- beat1: boolean;
8
- beat1and: boolean;
9
- beat2: boolean;
10
- beat2and: boolean;
11
- beat3: boolean;
12
- beat3and: boolean;
13
- beat4: boolean;
14
- beat4and: boolean;
15
- };
16
-
17
- const EMPTY: KickPattern = {
18
- beat1: false,
19
- beat1and: false,
20
- beat2: false,
21
- beat2and: false,
22
- beat3: false,
23
- beat3and: false,
24
- beat4: false,
25
- beat4and: false,
26
- };
27
-
28
- /**
29
- * Whether a pre-chorus bar sits in the two-bar lift into the chorus.
30
- *
31
- * @param section Current section.
32
- * @param bar Bar index within the section.
33
- * @param sectionBars Total bars in the section.
34
- * @param nextIsChorus Whether the following section is a chorus.
35
- */
36
- export function isInPreChorusLift(
37
- section: SectionType,
38
- bar: number,
39
- sectionBars: number,
40
- nextIsChorus: boolean,
41
- ): boolean {
42
- if (section !== 'b' || !nextIsChorus || sectionBars < 3) {
43
- return false;
44
- }
45
- return bar >= sectionBars - 2;
46
- }
47
-
48
- /** Convert a 16-step euclidean bitmask into an 8th-slot kick pattern. */
49
- export function euclideanToKickPattern(mask: number): KickPattern {
50
- const slot = (step: number) => hasHit(mask, step) || hasHit(mask, (step + 15) % 16);
51
- return {
52
- beat1: slot(0),
53
- beat1and: slot(2),
54
- beat2: slot(4),
55
- beat2and: slot(6),
56
- beat3: slot(8),
57
- beat3and: slot(10),
58
- beat4: slot(12),
59
- beat4and: slot(14),
60
- };
61
- }
62
-
63
- /**
64
- * Build a section- and style-aware kick pattern.
65
- *
66
- * @param section Current section.
67
- * @param style Internal drum style.
68
- * @param bar Bar index (drives variation for a few styles).
69
- * @param rng Deterministic PRNG for probabilistic accents.
70
- */
71
- export function getKickPattern(
72
- section: SectionType,
73
- style: DrumStyle,
74
- bar: number,
75
- rng: DrumRng,
76
- ): KickPattern {
77
- const p: KickPattern = { ...EMPTY };
78
-
79
- if (section === 'outro') {
80
- p.beat1 = true;
81
- p.beat3 = true;
82
- return p;
83
- }
84
-
85
- switch (style) {
86
- case 'sparse':
87
- p.beat1 = true;
88
- if (section === 'chorus' && bar % 2 === 1) {
89
- p.beat3 = true;
90
- }
91
- break;
92
-
93
- case 'fourOnFloor':
94
- p.beat1 = p.beat2 = p.beat3 = p.beat4 = true;
95
- if (section === 'chorus' && rng.prob(0.2)) {
96
- p.beat2and = true;
97
- }
98
- break;
99
-
100
- case 'upbeat':
101
- p.beat1 = true;
102
- p.beat3 = true;
103
- if (section === 'b' || section === 'chorus') {
104
- p.beat2and = rng.prob(0.7);
105
- }
106
- if (section === 'chorus') {
107
- p.beat4and = rng.prob(0.6);
108
- }
109
- break;
110
-
111
- case 'rock':
112
- p.beat1 = true;
113
- p.beat3 = true;
114
- if (section === 'chorus') {
115
- p.beat2and = rng.prob(0.65);
116
- p.beat4and = rng.prob(0.4);
117
- } else if (section === 'b') {
118
- p.beat2and = rng.prob(0.3);
119
- }
120
- break;
121
-
122
- case 'synth':
123
- p.beat1 = true;
124
- p.beat3 = true;
125
- if (section === 'b' || section === 'chorus') {
126
- p.beat2and = rng.prob(0.75);
127
- }
128
- if (section === 'chorus') {
129
- p.beat4and = rng.prob(0.65);
130
- }
131
- break;
132
-
133
- case 'trap':
134
- p.beat1 = true;
135
- p.beat2and = rng.prob(0.8);
136
- p.beat3 = rng.prob(0.3);
137
- p.beat4and = rng.prob(0.7);
138
- break;
139
-
140
- case 'latin':
141
- p.beat1 = true;
142
- p.beat2and = true;
143
- p.beat3 = true;
144
- p.beat4and = rng.prob(0.5);
145
- break;
146
-
147
- default:
148
- p.beat1 = true;
149
- p.beat3 = true;
150
- if (section === 'b') {
151
- p.beat2and = rng.prob(0.5);
152
- } else if (section === 'chorus') {
153
- p.beat2and = rng.prob(0.55);
154
- p.beat4and = rng.prob(0.35);
155
- }
156
- break;
157
- }
158
-
159
- return p;
160
- }
@@ -1,121 +0,0 @@
1
- import { HH_16TH_BPM_THRESHOLD } from './hihat.js';
2
- import type { HitList } from './hit.js';
3
- import type { DrumRole, SectionType } from './internal.js';
4
- import { EIGHTH, GM, PercMoodCategory, SIXTEENTH, sectionIndex } from './internal.js';
5
- import type { DrumRng } from './rng.js';
6
-
7
- /** Enabled auxiliary percussion voices for a section. */
8
- export type PercussionConfig = {
9
- tambourine: boolean;
10
- shaker: boolean;
11
- handclap: boolean;
12
- shaker16th: boolean;
13
- };
14
-
15
- type PercActivation = [tambourine: boolean, shaker: boolean, handclap: boolean];
16
-
17
- // Percussion activation per [mood category][section index]. Columns:
18
- // intro, a, b, chorus, bridge, interlude, outro, chant, mixbreak.
19
- const F: PercActivation = [false, false, false];
20
- const PERC_TABLE: PercActivation[][] = [
21
- /* calm */ [F, F, F, F, F, F, F, F, F],
22
- /* std */ [F, F, [false, true, false], [true, false, true], F, F, F, F, [true, false, true]],
23
- /* ener */ [
24
- F,
25
- [false, true, false],
26
- [false, true, false],
27
- [true, true, true],
28
- F,
29
- F,
30
- F,
31
- F,
32
- [true, true, true],
33
- ],
34
- /* idol */ [
35
- F,
36
- [false, true, false],
37
- [false, true, false],
38
- [true, true, true],
39
- F,
40
- F,
41
- F,
42
- F,
43
- [true, true, true],
44
- ],
45
- /* rock */ [F, F, F, [false, false, true], F, F, F, F, [false, false, true]],
46
- ];
47
-
48
- /** Resolve which auxiliary percussion voices play for a section. */
49
- export function getPercussionConfig(
50
- mood: PercMoodCategory,
51
- section: SectionType,
52
- ): PercussionConfig {
53
- const row = PERC_TABLE[mood] ?? PERC_TABLE[PercMoodCategory.Standard];
54
- const act = row?.[sectionIndex(section)] ?? F;
55
- return {
56
- tambourine: act[0],
57
- shaker: act[1],
58
- handclap: act[2],
59
- shaker16th: act[1],
60
- };
61
- }
62
-
63
- /** Emit one bar of auxiliary percussion (tambourine/shaker/handclap). */
64
- export function generateAuxPercussionForBar(
65
- track: HitList,
66
- barStart: number,
67
- config: PercussionConfig,
68
- role: DrumRole,
69
- densityMult: number,
70
- rng: DrumRng,
71
- bpm: number,
72
- ): void {
73
- if (role === 'minimal' || role === 'fxOnly') {
74
- return;
75
- }
76
-
77
- if (config.tambourine) {
78
- for (let beat = 1; beat <= 3; beat += 2) {
79
- const raw = 70 * densityMult * rng.float(0.9, 1.1);
80
- track.add(GM.TAMBOURINE, barStart + beat, EIGHTH, Math.max(40, Math.min(90, raw)));
81
- }
82
- }
83
-
84
- if (config.shaker) {
85
- const use16th = config.shaker16th && bpm < HH_16TH_BPM_THRESHOLD;
86
- if (use16th) {
87
- const velCurve = [0.75, 0.45, 0.6, 0.45];
88
- for (let beat = 0; beat < 4; beat += 1) {
89
- for (let sub = 0; sub < 4; sub += 1) {
90
- const raw = 80 * (velCurve[sub] ?? 0.5) * densityMult * rng.float(0.9, 1.1);
91
- track.add(
92
- GM.SHAKER,
93
- barStart + beat + sub * SIXTEENTH,
94
- SIXTEENTH,
95
- Math.max(25, Math.min(85, raw)),
96
- );
97
- }
98
- }
99
- } else {
100
- const velCurve = [0.75, 0.55];
101
- for (let beat = 0; beat < 4; beat += 1) {
102
- for (let sub = 0; sub < 2; sub += 1) {
103
- const raw = 80 * (velCurve[sub] ?? 0.6) * densityMult * rng.float(0.9, 1.1);
104
- track.add(
105
- GM.SHAKER,
106
- barStart + beat + sub * EIGHTH,
107
- EIGHTH,
108
- Math.max(25, Math.min(85, raw)),
109
- );
110
- }
111
- }
112
- }
113
- }
114
-
115
- if (config.handclap) {
116
- for (let beat = 1; beat <= 3; beat += 2) {
117
- const raw = 85 * densityMult * rng.float(0.9, 1.1);
118
- track.add(GM.HANDCLAP, barStart + beat, EIGHTH, Math.max(50, Math.min(100, raw)));
119
- }
120
- }
121
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * The drum engine's PRNG, re-exported from the shared {@link ../random} module.
3
- * `DrumRng` is retained as the drum-facing name of the shared {@link Rng} type.
4
- */
5
-
6
- export type { Rng as DrumRng } from '../../core/random/index.js';
7
- export { createRng } from '../../core/random/index.js';
@@ -1,71 +0,0 @@
1
- import type { Feel } from './internal.js';
2
-
3
- const SIXTEENTH = 0.25;
4
- const EIGHTH = 0.5;
5
- const HALF_16TH = 0.125;
6
- /** Delay of an off-beat 16th at full swing (triplet minus straight), in beats. */
7
- const DELTA_16 = 1 / 3 - 1 / 4; // 0.08333...
8
- /** Delay of an off-beat 8th at full swing, in beats. */
9
- const DELTA_8 = (2 / 3 - 1 / 2) / 2 + (2 / 3 - 1 / 2) / 2; // 0.16667...
10
-
11
- /** Grid resolution for swing quantization. */
12
- export type SwingResolution = 'eighth' | 'sixteenth';
13
-
14
- /** Effective swing after applying the groove feel to a base swing amount. */
15
- export function effectiveSwing(feel: Feel, swingAmount: number): number {
16
- if (feel === 'straight') {
17
- return 0;
18
- }
19
- if (feel === 'shuffle') {
20
- return Math.min(1, swingAmount * 1.5);
21
- }
22
- return swingAmount;
23
- }
24
-
25
- /**
26
- * Push an off-beat position toward its triplet placement.
27
- *
28
- * On-beat positions are returned unchanged; off-beat 8ths and 16ths are delayed
29
- * proportionally to the swing amount.
30
- *
31
- * @param startBeat Absolute position in beats.
32
- * @param swing Swing amount in [0, 1] (already feel-adjusted).
33
- * @param resolution Whether to swing the 16th or only the 8th grid.
34
- * @returns The swung position in beats.
35
- */
36
- export function quantizeSwing(
37
- startBeat: number,
38
- swing: number,
39
- resolution: SwingResolution,
40
- ): number {
41
- const clamped = Math.max(0, Math.min(1, swing));
42
- if (clamped <= 0) {
43
- return startBeat;
44
- }
45
- const base = Math.floor(startBeat);
46
- const offset = startBeat - base;
47
-
48
- if (resolution === 'sixteenth') {
49
- if (offset >= HALF_16TH && offset < SIXTEENTH + HALF_16TH) {
50
- return base + SIXTEENTH + DELTA_16 * clamped;
51
- }
52
- if (offset >= SIXTEENTH + HALF_16TH && offset < EIGHTH + HALF_16TH) {
53
- return base + EIGHTH + DELTA_8 * clamped;
54
- }
55
- if (offset >= EIGHTH + HALF_16TH && offset < 3 * SIXTEENTH + HALF_16TH) {
56
- // The "a" 16th is the midpoint of the compressed second half of the beat
57
- // (between the swung "and" and the next downbeat), mirroring the "e" 16th
58
- // in the first half. Because DELTA_8 = 2 * DELTA_16, that midpoint reduces
59
- // to a single DELTA_16 delay off the straight position, so the "a" never
60
- // overshoots the downbeat and needs no clamp. At the default shuffle feel
61
- // (swing 0.75) this lands near 0.8125 rather than the doubly-swung 0.9375.
62
- return base + 3 * SIXTEENTH + DELTA_16 * clamped;
63
- }
64
- return startBeat;
65
- }
66
-
67
- if (offset >= SIXTEENTH && offset < 3 * SIXTEENTH) {
68
- return base + EIGHTH + DELTA_8 * clamped;
69
- }
70
- return startBeat;
71
- }