@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.
- package/README.md +6 -5
- package/dist/analyze/index.cjs +41 -1676
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +194 -122
- package/dist/analyze/index.d.ts +194 -122
- package/dist/analyze/index.js +19 -8
- package/dist/chunk-36N73KZ3.cjs +246 -0
- package/dist/chunk-36N73KZ3.cjs.map +1 -0
- package/dist/chunk-4HLLZPXT.cjs +3077 -0
- package/dist/chunk-4HLLZPXT.cjs.map +1 -0
- package/dist/chunk-7MGZ7D62.cjs +498 -0
- package/dist/chunk-7MGZ7D62.cjs.map +1 -0
- package/dist/{chunk-R3S2LUBH.js → chunk-7NKDPF74.js} +660 -305
- package/dist/chunk-7NKDPF74.js.map +1 -0
- package/dist/chunk-CAN76NCU.cjs +679 -0
- package/dist/chunk-CAN76NCU.cjs.map +1 -0
- package/dist/{chunk-7Y7WF64D.js → chunk-D2FAJBYL.js} +104 -35
- package/dist/chunk-D2FAJBYL.js.map +1 -0
- package/dist/{chunk-COKTX2G5.js → chunk-EHVEBWM5.js} +1052 -510
- package/dist/chunk-EHVEBWM5.js.map +1 -0
- package/dist/chunk-FCANXBAT.cjs +30 -0
- package/dist/chunk-FCANXBAT.cjs.map +1 -0
- package/dist/chunk-FNDWXRRI.cjs +479 -0
- package/dist/chunk-FNDWXRRI.cjs.map +1 -0
- package/dist/{chunk-GQKN5KDT.js → chunk-GQSGS2N4.js} +43 -29
- package/dist/chunk-GQSGS2N4.js.map +1 -0
- package/dist/chunk-HKGYCQ34.js +53 -0
- package/dist/chunk-HKGYCQ34.js.map +1 -0
- package/dist/chunk-I24MV65U.js +106 -0
- package/dist/chunk-I24MV65U.js.map +1 -0
- package/dist/{chunk-LN7ZZMD2.js → chunk-KMHOICIY.js} +322 -173
- package/dist/chunk-KMHOICIY.js.map +1 -0
- package/dist/chunk-LW53AHQU.cjs +1117 -0
- package/dist/chunk-LW53AHQU.cjs.map +1 -0
- package/dist/chunk-N6IHQUOX.js +1 -0
- package/dist/chunk-N6IHQUOX.js.map +1 -0
- package/dist/chunk-NKIBQ6PT.js +30 -0
- package/dist/chunk-NKIBQ6PT.js.map +1 -0
- package/dist/chunk-NP4NA2FB.cjs +106 -0
- package/dist/chunk-NP4NA2FB.cjs.map +1 -0
- package/dist/chunk-NVE34YGZ.cjs +1449 -0
- package/dist/chunk-NVE34YGZ.cjs.map +1 -0
- package/dist/{chunk-A4532ZFR.js → chunk-NYYWMV2C.js} +115 -47
- package/dist/chunk-NYYWMV2C.js.map +1 -0
- package/dist/{chunk-5HTNMQW4.js → chunk-Q2XUP227.js} +310 -118
- package/dist/chunk-Q2XUP227.js.map +1 -0
- package/dist/chunk-Q6OP4U43.cjs +327 -0
- package/dist/chunk-Q6OP4U43.cjs.map +1 -0
- package/dist/chunk-RC6GBMCD.cjs +845 -0
- package/dist/chunk-RC6GBMCD.cjs.map +1 -0
- package/dist/{chunk-XTC3X6MS.js → chunk-RK35RIN7.js} +453 -152
- package/dist/chunk-RK35RIN7.js.map +1 -0
- package/dist/chunk-RUMU3UA6.cjs +53 -0
- package/dist/chunk-RUMU3UA6.cjs.map +1 -0
- package/dist/chunk-T64C7ZD7.cjs +1 -0
- package/dist/chunk-T64C7ZD7.cjs.map +1 -0
- package/dist/chunk-XEBG3G53.js +498 -0
- package/dist/chunk-XEBG3G53.js.map +1 -0
- package/dist/chunk-YLEMGEFH.js +246 -0
- package/dist/chunk-YLEMGEFH.js.map +1 -0
- package/dist/chunk-ZEJR4TKL.cjs +173 -0
- package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
- package/dist/core/index.cjs +130 -431
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +331 -17
- package/dist/core/index.d.ts +331 -17
- package/dist/core/index.js +71 -13
- package/dist/{function-2bJW1Zp_.d.ts → function-BtiZb9i9.d.ts} +28 -7
- package/dist/{function-r2sh-jOO.d.cts → function-DNugAOrt.d.cts} +28 -7
- package/dist/generate/index.cjs +30 -3751
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +373 -59
- package/dist/generate/index.d.ts +373 -59
- package/dist/generate/index.js +23 -9
- package/dist/{index-DZ1M21do.d.cts → index-6VR8-kit.d.ts} +20 -4
- package/dist/index-70p168VM.d.ts +192 -0
- package/dist/{index-D_ZTUNKp.d.cts → index-Be74mcon.d.cts} +18 -17
- package/dist/index-BtXT6UQ0.d.cts +192 -0
- package/dist/{index-BMPxwjwV.d.ts → index-C5UBbQ4G.d.ts} +18 -17
- package/dist/{index-BOBXBNwP.d.cts → index-C83mZI1P.d.ts} +93 -8
- package/dist/{index-BdeQ1r_p.d.cts → index-D6b6na69.d.cts} +14 -11
- package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
- package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
- package/dist/index-OG65ocOr.d.cts +272 -0
- package/dist/index-OG65ocOr.d.ts +272 -0
- package/dist/{index-BreoJRw0.d.ts → index-tJhkmQgE.d.cts} +20 -4
- package/dist/{index-CP3wAfEH.d.ts → index-tSQh-xwY.d.ts} +14 -11
- package/dist/{index-CM5W4rcm.d.ts → index-y_ak9_Hi.d.cts} +93 -8
- package/dist/index.cjs +419 -6848
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -13
- package/dist/index.d.ts +14 -13
- package/dist/index.js +132 -27
- package/dist/model/index.cjs +8 -2423
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +433 -74
- package/dist/model/index.d.ts +433 -74
- package/dist/model/index.js +6 -7
- package/dist/styled-DIRkk9Uz.d.ts +506 -0
- package/dist/styled-DJbkJQfj.d.cts +506 -0
- package/dist/theory/index.cjs +172 -1676
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +151 -233
- package/dist/theory/index.d.ts +151 -233
- package/dist/theory/index.js +31 -7
- package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
- package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
- package/package.json +73 -24
- package/dist/chunk-5HTNMQW4.js.map +0 -1
- package/dist/chunk-7Y7WF64D.js.map +0 -1
- package/dist/chunk-A4532ZFR.js.map +0 -1
- package/dist/chunk-BHUT2S5G.js +0 -22
- package/dist/chunk-BHUT2S5G.js.map +0 -1
- package/dist/chunk-CCBLVAPK.js +0 -42
- package/dist/chunk-CCBLVAPK.js.map +0 -1
- package/dist/chunk-COKTX2G5.js.map +0 -1
- package/dist/chunk-DYWFNC4A.js +0 -152
- package/dist/chunk-DYWFNC4A.js.map +0 -1
- package/dist/chunk-GQKN5KDT.js.map +0 -1
- package/dist/chunk-LN7ZZMD2.js.map +0 -1
- package/dist/chunk-MSWFZF7J.js +0 -118
- package/dist/chunk-MSWFZF7J.js.map +0 -1
- package/dist/chunk-R3S2LUBH.js.map +0 -1
- package/dist/chunk-SDM3VJJ5.js +0 -41
- package/dist/chunk-SDM3VJJ5.js.map +0 -1
- package/dist/chunk-XTC3X6MS.js.map +0 -1
- package/dist/chunk-ZXW5YIWK.js +0 -55
- package/dist/chunk-ZXW5YIWK.js.map +0 -1
- package/dist/index-CGMZNCx6.d.cts +0 -140
- package/dist/index-CGMZNCx6.d.ts +0 -140
- package/dist/styled-BjXtOFNk.d.cts +0 -282
- package/dist/styled-D7BAx3S3.d.ts +0 -282
- package/src/analyze/arrange/index.ts +0 -21
- package/src/analyze/arrange/internal.ts +0 -125
- package/src/analyze/arrange/tension.ts +0 -187
- package/src/analyze/arrange/tracks.ts +0 -279
- package/src/analyze/detect/index.ts +0 -235
- package/src/analyze/functional/borrowed.ts +0 -84
- package/src/analyze/functional/cadence.ts +0 -66
- package/src/analyze/functional/function.ts +0 -184
- package/src/analyze/functional/index.ts +0 -23
- package/src/analyze/functional/internal.ts +0 -29
- package/src/analyze/functional/roman.ts +0 -393
- package/src/analyze/index.ts +0 -49
- package/src/analyze/timeline/index.ts +0 -415
- package/src/analyze/voice/index.ts +0 -330
- package/src/core/index.ts +0 -47
- package/src/core/interval/index.ts +0 -81
- package/src/core/meter/index.ts +0 -282
- package/src/core/pitch/index.ts +0 -311
- package/src/core/random/index.ts +0 -53
- package/src/core/tuning/index.ts +0 -156
- package/src/core/types.ts +0 -29
- package/src/generate/bass/index.ts +0 -350
- package/src/generate/countermelody/index.ts +0 -379
- package/src/generate/drums/beat.ts +0 -266
- package/src/generate/drums/euclid.ts +0 -58
- package/src/generate/drums/fills.ts +0 -356
- package/src/generate/drums/ghost.ts +0 -119
- package/src/generate/drums/hihat.ts +0 -285
- package/src/generate/drums/hit.ts +0 -31
- package/src/generate/drums/index.ts +0 -329
- package/src/generate/drums/internal.ts +0 -257
- package/src/generate/drums/kick.ts +0 -160
- package/src/generate/drums/percussion.ts +0 -121
- package/src/generate/drums/rng.ts +0 -7
- package/src/generate/drums/swing.ts +0 -71
- package/src/generate/groove/index.ts +0 -326
- package/src/generate/harmonize/index.ts +0 -440
- package/src/generate/index.ts +0 -59
- package/src/generate/motif/index.ts +0 -398
- package/src/generate/progression/index.ts +0 -370
- package/src/generate/reharmony/index.ts +0 -330
- package/src/generate/rhythm/index.ts +0 -165
- package/src/index.ts +0 -247
- package/src/model/chord.ts +0 -423
- package/src/model/index.ts +0 -15
- package/src/model/interval.ts +0 -82
- package/src/model/key.ts +0 -266
- package/src/model/note.ts +0 -176
- package/src/model/progression.ts +0 -161
- package/src/model/shared.ts +0 -12
- package/src/theory/chord/index.ts +0 -373
- package/src/theory/chordscale/index.ts +0 -374
- package/src/theory/counterpoint/index.ts +0 -230
- package/src/theory/harmony/index.ts +0 -92
- package/src/theory/index.ts +0 -107
- package/src/theory/safety/index.ts +0 -419
- package/src/theory/scale/degrees.ts +0 -123
- package/src/theory/scale/index.ts +0 -28
- package/src/theory/scale/key.ts +0 -56
- package/src/theory/scale/masks.ts +0 -153
- package/src/theory/spelling/index.ts +0 -182
- package/src/theory/symbol/index.ts +0 -268
- package/src/theory/voicing/index.ts +0 -5
- package/src/theory/voicing/internal.ts +0 -168
- package/src/theory/voicing/leading.ts +0 -112
- package/src/theory/voicing/satb.ts +0 -195
- package/src/theory/voicing/styled.ts +0 -176
- 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
|
-
}
|