@libraz/libcantus 0.9.3 → 0.9.5

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 (201) hide show
  1. package/README.md +29 -20
  2. package/README_ja.md +268 -0
  3. package/dist/analyze/index.cjs +39 -1947
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +161 -91
  6. package/dist/analyze/index.d.ts +161 -91
  7. package/dist/analyze/index.js +19 -8
  8. package/dist/chunk-2A2QMA74.cjs +261 -0
  9. package/dist/chunk-2A2QMA74.cjs.map +1 -0
  10. package/dist/chunk-2PTFGVD7.js +261 -0
  11. package/dist/chunk-2PTFGVD7.js.map +1 -0
  12. package/dist/chunk-357ZDPVU.cjs +162 -0
  13. package/dist/chunk-357ZDPVU.cjs.map +1 -0
  14. package/dist/chunk-3KX76RLG.js +1548 -0
  15. package/dist/chunk-3KX76RLG.js.map +1 -0
  16. package/dist/chunk-4VZ5MZAM.cjs +889 -0
  17. package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
  18. package/dist/{chunk-C5XYKUVG.js → chunk-7EONYVI5.js} +471 -171
  19. package/dist/chunk-7EONYVI5.js.map +1 -0
  20. package/dist/{chunk-IT2O4XFE.js → chunk-7JEP3MFY.js} +42 -34
  21. package/dist/chunk-7JEP3MFY.js.map +1 -0
  22. package/dist/{chunk-4MHA432G.js → chunk-AID75RN3.js} +572 -250
  23. package/dist/chunk-AID75RN3.js.map +1 -0
  24. package/dist/chunk-BSLTLGM3.cjs +497 -0
  25. package/dist/chunk-BSLTLGM3.cjs.map +1 -0
  26. package/dist/chunk-CU6VG2YM.cjs +109 -0
  27. package/dist/chunk-CU6VG2YM.cjs.map +1 -0
  28. package/dist/{chunk-EH6EM37U.js → chunk-DZMHIGI4.js} +81 -59
  29. package/dist/chunk-DZMHIGI4.js.map +1 -0
  30. package/dist/{chunk-AKLYPWIN.js → chunk-EDOTEABK.js} +45 -19
  31. package/dist/chunk-EDOTEABK.js.map +1 -0
  32. package/dist/{chunk-2ZWMSJH6.js → chunk-HN2OEUN2.js} +7 -4
  33. package/dist/chunk-HN2OEUN2.js.map +1 -0
  34. package/dist/{chunk-MIWO425M.js → chunk-KBTTEX7Q.js} +128 -43
  35. package/dist/chunk-KBTTEX7Q.js.map +1 -0
  36. package/dist/chunk-KQN2ZYND.cjs +724 -0
  37. package/dist/chunk-KQN2ZYND.cjs.map +1 -0
  38. package/dist/chunk-MPTVJIVG.cjs +3176 -0
  39. package/dist/chunk-MPTVJIVG.cjs.map +1 -0
  40. package/dist/chunk-MTMZTMWM.cjs +53 -0
  41. package/dist/chunk-MTMZTMWM.cjs.map +1 -0
  42. package/dist/{chunk-CHMXELDS.js → chunk-NNB67XA2.js} +372 -197
  43. package/dist/chunk-NNB67XA2.js.map +1 -0
  44. package/dist/chunk-OL3IHHSJ.cjs +1204 -0
  45. package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
  46. package/dist/chunk-PI3UXRWK.cjs +528 -0
  47. package/dist/chunk-PI3UXRWK.cjs.map +1 -0
  48. package/dist/chunk-R2VVFAIN.js +528 -0
  49. package/dist/chunk-R2VVFAIN.js.map +1 -0
  50. package/dist/chunk-T64C7ZD7.cjs +1 -0
  51. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  52. package/dist/chunk-WFDXOLUB.cjs +337 -0
  53. package/dist/chunk-WFDXOLUB.cjs.map +1 -0
  54. package/dist/chunk-WTAN4ACG.cjs +1548 -0
  55. package/dist/chunk-WTAN4ACG.cjs.map +1 -0
  56. package/dist/chunk-YFJXS3AU.cjs +33 -0
  57. package/dist/chunk-YFJXS3AU.cjs.map +1 -0
  58. package/dist/{chunk-GOAW4VYL.js → chunk-YUE6TJ4M.js} +315 -145
  59. package/dist/chunk-YUE6TJ4M.js.map +1 -0
  60. package/dist/{chunk-62QZKCJQ.js → chunk-ZGLJM4A4.js} +10 -6
  61. package/dist/chunk-ZGLJM4A4.js.map +1 -0
  62. package/dist/core/index.cjs +140 -647
  63. package/dist/core/index.cjs.map +1 -1
  64. package/dist/core/index.d.cts +313 -41
  65. package/dist/core/index.d.ts +313 -41
  66. package/dist/core/index.js +64 -16
  67. package/dist/{function-DVH4Eusr.d.ts → function-CxixTB02.d.cts} +122 -10
  68. package/dist/{function-DYt4iKsn.d.cts → function-DTCt675z.d.ts} +122 -10
  69. package/dist/generate/index.cjs +28 -4085
  70. package/dist/generate/index.cjs.map +1 -1
  71. package/dist/generate/index.d.cts +374 -66
  72. package/dist/generate/index.d.ts +374 -66
  73. package/dist/generate/index.js +23 -9
  74. package/dist/{index-Jc0Ryi79.d.ts → index-B2Ulxxpo.d.cts} +21 -5
  75. package/dist/{index-CpOsP1y9.d.cts → index-BkYJ0xMq.d.cts} +18 -17
  76. package/dist/{index-CxqBQtH1.d.cts → index-BuaB-ypt.d.ts} +21 -5
  77. package/dist/index-CAN7v-kw.d.ts +199 -0
  78. package/dist/{index-Bt4tOUD9.d.ts → index-COw1Ky9g.d.ts} +93 -8
  79. package/dist/{index-Dikst44v.d.cts → index-Cfx8xgH0.d.ts} +14 -11
  80. package/dist/{index-DxvARIP2.d.ts → index-DRtIhlJj.d.ts} +18 -17
  81. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  82. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  83. package/dist/index-alrRninU.d.cts +199 -0
  84. package/dist/{index-BNlGP86A.d.cts → index-oTQbYYIs.d.cts} +93 -8
  85. package/dist/index-tR9jTn26.d.cts +275 -0
  86. package/dist/index-tR9jTn26.d.ts +275 -0
  87. package/dist/{index-Bg5dYMmZ.d.ts → index-x2vcsduE.d.cts} +14 -11
  88. package/dist/index.cjs +429 -7383
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +15 -15
  91. package/dist/index.d.ts +15 -15
  92. package/dist/index.js +121 -27
  93. package/dist/model/index.cjs +8 -2562
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +471 -81
  96. package/dist/model/index.d.ts +471 -81
  97. package/dist/model/index.js +6 -7
  98. package/dist/styled-Bl0ZK28h.d.ts +543 -0
  99. package/dist/styled-Crv6SA4S.d.cts +543 -0
  100. package/dist/theory/index.cjs +172 -1822
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +151 -233
  103. package/dist/theory/index.d.ts +151 -233
  104. package/dist/theory/index.js +30 -7
  105. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  106. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  107. package/package.json +34 -13
  108. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  109. package/dist/chunk-4MHA432G.js.map +0 -1
  110. package/dist/chunk-62QZKCJQ.js.map +0 -1
  111. package/dist/chunk-AKLYPWIN.js.map +0 -1
  112. package/dist/chunk-C5XYKUVG.js.map +0 -1
  113. package/dist/chunk-CHMXELDS.js.map +0 -1
  114. package/dist/chunk-DYWFNC4A.js +0 -152
  115. package/dist/chunk-DYWFNC4A.js.map +0 -1
  116. package/dist/chunk-EH6EM37U.js.map +0 -1
  117. package/dist/chunk-FBQK3ZQN.js +0 -131
  118. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  119. package/dist/chunk-GOAW4VYL.js.map +0 -1
  120. package/dist/chunk-I5R7ZBJS.js +0 -202
  121. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  122. package/dist/chunk-IT2O4XFE.js.map +0 -1
  123. package/dist/chunk-MIWO425M.js.map +0 -1
  124. package/dist/chunk-SGU7SPLK.js +0 -924
  125. package/dist/chunk-SGU7SPLK.js.map +0 -1
  126. package/dist/index-CGMZNCx6.d.cts +0 -140
  127. package/dist/index-CGMZNCx6.d.ts +0 -140
  128. package/dist/index-CnZCo29u.d.cts +0 -108
  129. package/dist/index-JnF4v9bx.d.ts +0 -108
  130. package/dist/styled-DZ7GjShb.d.ts +0 -282
  131. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  132. package/src/analyze/arrange/index.ts +0 -21
  133. package/src/analyze/arrange/internal.ts +0 -127
  134. package/src/analyze/arrange/tension.ts +0 -199
  135. package/src/analyze/arrange/tracks.ts +0 -312
  136. package/src/analyze/detect/index.ts +0 -272
  137. package/src/analyze/functional/borrowed.ts +0 -84
  138. package/src/analyze/functional/cadence.ts +0 -66
  139. package/src/analyze/functional/function.ts +0 -184
  140. package/src/analyze/functional/index.ts +0 -23
  141. package/src/analyze/functional/internal.ts +0 -29
  142. package/src/analyze/functional/roman.ts +0 -402
  143. package/src/analyze/index.ts +0 -49
  144. package/src/analyze/timeline/index.ts +0 -451
  145. package/src/analyze/voice/index.ts +0 -339
  146. package/src/core/event-index/index.ts +0 -104
  147. package/src/core/index.ts +0 -61
  148. package/src/core/interval/index.ts +0 -81
  149. package/src/core/meter/index.ts +0 -302
  150. package/src/core/pitch/index.ts +0 -311
  151. package/src/core/random/index.ts +0 -76
  152. package/src/core/tuning/index.ts +0 -186
  153. package/src/core/types.ts +0 -29
  154. package/src/core/validation/index.ts +0 -119
  155. package/src/generate/bass/index.ts +0 -373
  156. package/src/generate/countermelody/index.ts +0 -470
  157. package/src/generate/drums/beat.ts +0 -266
  158. package/src/generate/drums/euclid.ts +0 -65
  159. package/src/generate/drums/fills.ts +0 -356
  160. package/src/generate/drums/ghost.ts +0 -119
  161. package/src/generate/drums/hihat.ts +0 -285
  162. package/src/generate/drums/hit.ts +0 -31
  163. package/src/generate/drums/index.ts +0 -361
  164. package/src/generate/drums/internal.ts +0 -257
  165. package/src/generate/drums/kick.ts +0 -160
  166. package/src/generate/drums/percussion.ts +0 -121
  167. package/src/generate/drums/rng.ts +0 -7
  168. package/src/generate/drums/swing.ts +0 -71
  169. package/src/generate/groove/index.ts +0 -351
  170. package/src/generate/harmonize/index.ts +0 -451
  171. package/src/generate/index.ts +0 -58
  172. package/src/generate/motif/index.ts +0 -422
  173. package/src/generate/progression/index.ts +0 -362
  174. package/src/generate/reharmony/index.ts +0 -323
  175. package/src/generate/rhythm/index.ts +0 -175
  176. package/src/index.ts +0 -13
  177. package/src/model/chord.ts +0 -442
  178. package/src/model/index.ts +0 -15
  179. package/src/model/interval.ts +0 -82
  180. package/src/model/key.ts +0 -266
  181. package/src/model/note.ts +0 -176
  182. package/src/model/progression.ts +0 -161
  183. package/src/model/shared.ts +0 -12
  184. package/src/theory/chord/index.ts +0 -375
  185. package/src/theory/chordscale/index.ts +0 -374
  186. package/src/theory/counterpoint/index.ts +0 -230
  187. package/src/theory/harmony/index.ts +0 -77
  188. package/src/theory/index.ts +0 -112
  189. package/src/theory/safety/index.ts +0 -488
  190. package/src/theory/scale/degrees.ts +0 -123
  191. package/src/theory/scale/index.ts +0 -28
  192. package/src/theory/scale/key.ts +0 -56
  193. package/src/theory/scale/masks.ts +0 -153
  194. package/src/theory/spelling/index.ts +0 -249
  195. package/src/theory/symbol/index.ts +0 -268
  196. package/src/theory/voicing/index.ts +0 -5
  197. package/src/theory/voicing/internal.ts +0 -168
  198. package/src/theory/voicing/leading.ts +0 -112
  199. package/src/theory/voicing/satb.ts +0 -216
  200. package/src/theory/voicing/styled.ts +0 -199
  201. package/tsconfig.json +0 -23
@@ -1,266 +0,0 @@
1
- import {
2
- getGhostDensity,
3
- getGhostProbabilityAtPosition,
4
- getGhostVelocity,
5
- selectGhostPositions,
6
- } from './ghost.js';
7
- import {
8
- footHiHatVelocity,
9
- type HiHatLevel,
10
- hiHatNote,
11
- hiHatTypeVelocityMultiplier,
12
- hiHatVelocityMultiplier,
13
- OHH_VEL_BOOST,
14
- roleHiHatInstrument,
15
- sectionHiHatType,
16
- shouldAddOpenHHAccent,
17
- shouldPlayHiHat,
18
- shouldUseBridgeCrossStick,
19
- } from './hihat.js';
20
- import type { HitList } from './hit.js';
21
- import type { BackingDensity, DrumRole, DrumStyle, Feel, SectionType } from './internal.js';
22
- import { EIGHTH, GM, type MoodCategory, SIXTEENTH } from './internal.js';
23
- import type { KickPattern } from './kick.js';
24
- import type { DrumRng } from './rng.js';
25
- import { effectiveSwing, quantizeSwing } from './swing.js';
26
-
27
- /** Per-section drum state shared across the bars of a section. */
28
- export type SectionCtx = {
29
- style: DrumStyle;
30
- feel: Feel;
31
- densityMult: number;
32
- backingDensity: BackingDensity;
33
- hhLevel: HiHatLevel;
34
- useGhostNotes: boolean;
35
- ghostBoost: boolean;
36
- useRide: boolean;
37
- useFootHh: boolean;
38
- role: DrumRole;
39
- ghostMood: MoodCategory;
40
- snareBeat3: boolean;
41
- };
42
-
43
- /** Per-beat context passed to every beat processor. */
44
- export type BeatCtx = {
45
- track: HitList;
46
- beatTick: number;
47
- beat: number;
48
- velocity: number;
49
- section: SectionType;
50
- bpm: number;
51
- bar: number;
52
- sectionBars: number;
53
- inPrechorusLift: boolean;
54
- swingAmount: number;
55
- barHasOpenHh: boolean;
56
- openHhBeat: number;
57
- rng: DrumRng;
58
- };
59
-
60
- function swing16(tick: number, feel: Feel, swingAmount: number): number {
61
- return quantizeSwing(tick, effectiveSwing(feel, swingAmount), 'sixteenth');
62
- }
63
-
64
- function hatSwingFactor(style: DrumStyle): number {
65
- if (style === 'trap') {
66
- return 0;
67
- }
68
- if (style === 'latin') {
69
- return 0.35;
70
- }
71
- return 0.5;
72
- }
73
-
74
- /** Emit the kick for one beat. */
75
- export function generateKickForBeat(ctx: BeatCtx, sec: SectionCtx, kick: KickPattern): void {
76
- if (ctx.inPrechorusLift) {
77
- return;
78
- }
79
- const on = [kick.beat1, kick.beat2, kick.beat3, kick.beat4][ctx.beat] ?? false;
80
- const and = [kick.beat1and, kick.beat2and, kick.beat3and, kick.beat4and][ctx.beat] ?? false;
81
-
82
- if (on) {
83
- ctx.track.add(GM.BD, ctx.beatTick, EIGHTH, ctx.velocity);
84
- }
85
- if (and) {
86
- const andTick = swing16(ctx.beatTick + EIGHTH, sec.feel, ctx.swingAmount);
87
- ctx.track.add(GM.BD, andTick, EIGHTH, ctx.velocity * 0.85);
88
- }
89
- }
90
-
91
- /** Emit the backbeat snare (or side-stick) for one beat. */
92
- export function generateSnareForBeat(ctx: BeatCtx, sec: SectionCtx, isIntroFirst: boolean): void {
93
- if (ctx.inPrechorusLift) {
94
- return;
95
- }
96
- const snareOn = sec.snareBeat3 ? ctx.beat === 2 : ctx.beat === 1 || ctx.beat === 3;
97
- if (!snareOn || isIntroFirst) {
98
- return;
99
- }
100
- if (sec.useRide && shouldUseBridgeCrossStick(ctx.section, ctx.beat)) {
101
- return;
102
- }
103
-
104
- const backbeatVel = Math.min(127, ctx.velocity + 16);
105
- const promoteSparseChorus =
106
- sec.style === 'sparse' && ctx.section === 'chorus' && sec.role === 'full';
107
-
108
- if (promoteSparseChorus) {
109
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, backbeatVel);
110
- } else if (sec.style === 'sparse' || sec.role === 'ambient') {
111
- if (sec.role !== 'fxOnly' && sec.role !== 'minimal') {
112
- ctx.track.add(GM.SIDESTICK, ctx.beatTick, EIGHTH, ctx.velocity * 0.8);
113
- }
114
- } else {
115
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, backbeatVel);
116
- }
117
- }
118
-
119
- /** Emit ghost snares at the "e"/"a" 16ths of beats 1 and 3. */
120
- export function generateGhostNotesForBeat(ctx: BeatCtx, sec: SectionCtx): void {
121
- if (ctx.beat !== 0 && ctx.beat !== 2) {
122
- return;
123
- }
124
- const positions = selectGhostPositions(sec.ghostMood, ctx.rng);
125
- let ghostProb = getGhostDensity(sec.ghostMood, ctx.section, sec.backingDensity, ctx.bpm);
126
- if (sec.ghostBoost) {
127
- ghostProb = Math.min(1, ghostProb * 1.4);
128
- }
129
-
130
- for (const pos of positions) {
131
- const sixteenthInBeat = pos === 'e' ? 1 : 3;
132
- const posProb = getGhostProbabilityAtPosition(ctx.beat, sixteenthInBeat);
133
- if (!ctx.rng.prob(ghostProb * posProb)) {
134
- continue;
135
- }
136
- const variation = ctx.rng.float(0.85, 1.15);
137
- const ghostBase = getGhostVelocity(ctx.section, ctx.beat / 2, false);
138
- let ghostVel = ctx.velocity * ghostBase * variation;
139
- if (pos === 'a') {
140
- ghostVel *= 0.9;
141
- }
142
- const offset = pos === 'e' ? SIXTEENTH : 3 * SIXTEENTH;
143
- const tick = swing16(ctx.beatTick + offset, sec.feel, ctx.swingAmount);
144
- ctx.track.add(GM.SD, tick, SIXTEENTH, ghostVel);
145
- }
146
- }
147
-
148
- /** Emit the pre-chorus lift buildup for one beat. */
149
- export function generatePreChorusBuildup(
150
- ctx: BeatCtx,
151
- sec: SectionCtx,
152
- isSectionLastBar: boolean,
153
- ): void {
154
- if (sec.style === 'sparse') {
155
- if (isSectionLastBar && ctx.beat === 3) {
156
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, Math.max(45, ctx.velocity * 0.75));
157
- ctx.track.add(GM.CRASH, ctx.beatTick + EIGHTH + SIXTEENTH, SIXTEENTH, ctx.velocity * 0.9);
158
- }
159
- return;
160
- }
161
- const barsInLift = 2;
162
- const barInLift = ctx.bar - (ctx.sectionBars - barsInLift);
163
- const progress = (barInLift * 4 + ctx.beat) / (barsInLift * 4);
164
- const buildupVel = ctx.velocity * (0.5 + 0.5 * progress);
165
- ctx.track.add(GM.SD, ctx.beatTick, EIGHTH, buildupVel);
166
- ctx.track.add(GM.SD, ctx.beatTick + EIGHTH, EIGHTH, buildupVel * 0.85);
167
- if (isSectionLastBar && ctx.beat === 3) {
168
- ctx.track.add(GM.CRASH, ctx.beatTick + EIGHTH + SIXTEENTH, SIXTEENTH, ctx.velocity * 1.1);
169
- }
170
- }
171
-
172
- /** Emit the timekeeping hi-hat (or ride/foot) for one beat. */
173
- export function generateHiHatForBeat(ctx: BeatCtx, sec: SectionCtx): void {
174
- if (!shouldPlayHiHat(sec.role)) {
175
- if (sec.useFootHh && (ctx.beat === 0 || ctx.beat === 2)) {
176
- ctx.track.add(GM.FHH, ctx.beatTick, EIGHTH, footHiHatVelocity(ctx.rng));
177
- }
178
- return;
179
- }
180
-
181
- const crossStick = sec.useRide && shouldUseBridgeCrossStick(ctx.section, ctx.beat);
182
- const hhInstrument = crossStick ? GM.SIDESTICK : roleHiHatInstrument(sec.role, sec.useRide);
183
- const hhType = sectionHiHatType(ctx.section, sec.role);
184
- const typeMult = hiHatTypeVelocityMultiplier(hhType);
185
- const isDynamicOpen = ctx.barHasOpenHh && ctx.beat === ctx.openHhBeat;
186
- const dm = sec.densityMult;
187
-
188
- if (sec.hhLevel === 'quarter') {
189
- const introRest = ctx.section === 'intro' && ctx.beat !== 0;
190
- if (!introRest) {
191
- if (isDynamicOpen) {
192
- ctx.track.add(
193
- GM.OHH,
194
- ctx.beatTick,
195
- EIGHTH,
196
- ctx.velocity * dm * 0.75 * typeMult + OHH_VEL_BOOST,
197
- );
198
- } else {
199
- ctx.track.add(
200
- hhInstrument,
201
- ctx.beatTick,
202
- EIGHTH,
203
- Math.max(20, ctx.velocity * dm * 0.75 * typeMult),
204
- );
205
- }
206
- } else if (sec.useFootHh) {
207
- ctx.track.add(GM.FHH, ctx.beatTick, EIGHTH, footHiHatVelocity(ctx.rng));
208
- }
209
- return;
210
- }
211
-
212
- if (sec.hhLevel === 'eighth') {
213
- for (let eighth = 0; eighth < 2; eighth += 1) {
214
- let hhTick = ctx.beatTick + eighth * EIGHTH;
215
- if (eighth === 1) {
216
- hhTick = swing16(hhTick, sec.feel, ctx.swingAmount);
217
- }
218
- if (ctx.section === 'intro' && eighth === 1) {
219
- if (sec.useFootHh && ctx.beat % 2 === 0) {
220
- ctx.track.add(GM.FHH, hhTick, EIGHTH, footHiHatVelocity(ctx.rng));
221
- }
222
- continue;
223
- }
224
- const hhVel = Math.max(20, ctx.velocity * dm * typeMult * (eighth === 0 ? 0.9 : 0.65));
225
- if (isDynamicOpen && eighth === 0) {
226
- ctx.track.add(GM.OHH, hhTick, EIGHTH, hhVel + OHH_VEL_BOOST);
227
- continue;
228
- }
229
- let useOpen = false;
230
- if (sec.style === 'fourOnFloor' && eighth === 1) {
231
- const openProb = Math.max(0.15, Math.min(0.8, 45 / ctx.bpm));
232
- useOpen = (ctx.beat === 1 || ctx.beat === 3) && ctx.rng.prob(openProb);
233
- } else if (eighth === 0) {
234
- useOpen = shouldAddOpenHHAccent(ctx.section, ctx.beat, ctx.bar, ctx.rng);
235
- }
236
- if (useOpen) {
237
- ctx.track.add(hiHatNote('open'), hhTick, EIGHTH, Math.max(20, hhVel * 1.1));
238
- } else {
239
- ctx.track.add(hhInstrument, hhTick, EIGHTH / 2, hhVel);
240
- }
241
- }
242
- return;
243
- }
244
-
245
- for (let sixteenth = 0; sixteenth < 4; sixteenth += 1) {
246
- let hhTick = ctx.beatTick + sixteenth * SIXTEENTH;
247
- if (sixteenth === 1 || sixteenth === 3) {
248
- const swung = effectiveSwing(sec.feel, ctx.swingAmount) * hatSwingFactor(sec.style);
249
- hhTick = quantizeSwing(hhTick, swung, 'sixteenth');
250
- }
251
- const metricVel = hiHatVelocityMultiplier(sixteenth, ctx.rng);
252
- const hhVel = Math.max(20, ctx.velocity * dm * typeMult * metricVel);
253
- if (isDynamicOpen && sixteenth === 0) {
254
- ctx.track.add(GM.OHH, hhTick, SIXTEENTH, hhVel + OHH_VEL_BOOST);
255
- continue;
256
- }
257
- if (ctx.beat === 3 && sixteenth === 3) {
258
- const openProb = Math.max(0.1, Math.min(0.4, 30 / ctx.bpm));
259
- if (ctx.rng.prob(openProb)) {
260
- ctx.track.add(GM.OHH, hhTick, SIXTEENTH, Math.max(20, hhVel * 1.2));
261
- continue;
262
- }
263
- }
264
- ctx.track.add(hhInstrument, hhTick, SIXTEENTH / 2, hhVel);
265
- }
266
- }
@@ -1,65 +0,0 @@
1
- /**
2
- * Generate a euclidean onset pattern (Bjorklund's algorithm), distributing
3
- * `pulses` as evenly as possible across `steps`, then rotating.
4
- *
5
- * @param pulses Number of onsets, clamped to [0, steps].
6
- * @param steps Total steps (>= 1).
7
- * @param rotation Steps to rotate onsets toward later positions.
8
- * @returns One boolean per step; true marks an onset.
9
- */
10
- import {
11
- assertGenerationBudget,
12
- assertInteger,
13
- assertPositiveInt,
14
- } from '../../core/validation/index.js';
15
-
16
- export function euclideanRhythm(pulses: number, steps: number, rotation = 0): boolean[] {
17
- assertPositiveInt(steps, 'euclidean steps');
18
- assertInteger(pulses, 'euclidean pulses');
19
- assertInteger(rotation, 'euclidean rotation');
20
- assertGenerationBudget(steps, 'euclidean pattern steps');
21
- const p = Math.max(0, Math.min(pulses, steps));
22
- const base = bjorklund(p, steps);
23
- const shift = ((rotation % steps) + steps) % steps;
24
- if (shift === 0) {
25
- return base;
26
- }
27
- return base.map((_, i) => base[(i - shift + steps) % steps] ?? false);
28
- }
29
-
30
- /** True when the 16-step bitmask has an onset at `step`. */
31
- export function hasHit(mask: number, step: number): boolean {
32
- return ((mask >> step) & 1) === 1;
33
- }
34
-
35
- /** Convert a euclidean boolean pattern to a 16-step bitmask (LSB = step 0). */
36
- export function patternToMask(pattern: boolean[]): number {
37
- let mask = 0;
38
- for (let i = 0; i < pattern.length; i += 1) {
39
- if (pattern[i]) {
40
- mask |= 1 << i;
41
- }
42
- }
43
- return mask;
44
- }
45
-
46
- function bjorklund(pulses: number, steps: number): boolean[] {
47
- if (pulses === 0) {
48
- return Array.from({ length: steps }, () => false);
49
- }
50
- if (pulses >= steps) {
51
- return Array.from({ length: steps }, () => true);
52
- }
53
- let head: boolean[][] = Array.from({ length: pulses }, () => [true]);
54
- let remainder: boolean[][] = Array.from({ length: steps - pulses }, () => [false]);
55
- while (remainder.length > 1) {
56
- const count = Math.min(head.length, remainder.length);
57
- const next: boolean[][] = [];
58
- for (let i = 0; i < count; i += 1) {
59
- next.push([...(head[i] ?? []), ...(remainder[i] ?? [])]);
60
- }
61
- remainder = head.length > count ? head.slice(count) : remainder.slice(count);
62
- head = next;
63
- }
64
- return [...head.flat(), ...remainder.flat()];
65
- }
@@ -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
- }