@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,119 +0,0 @@
1
- import type { BackingDensity, SectionType } from './internal.js';
2
- import { MoodCategory, sectionIndex } from './internal.js';
3
- import type { DrumRng } from './rng.js';
4
-
5
- /** Ghost-note position within a beat: the "e" (1st 16th) or "a" (3rd 16th). */
6
- export type GhostPosition = 'e' | 'a';
7
-
8
- /** Ghost density level ordinal (0 = none, 3 = heavy). */
9
- type GhostDensityLevel = 0 | 1 | 2 | 3;
10
-
11
- // Ghost density level per [section index][mood category].
12
- const GHOST_DENSITY_TABLE: GhostDensityLevel[][] = [
13
- /* intro */ [0, 1, 1],
14
- /* a */ [0, 1, 2],
15
- /* b */ [1, 2, 2],
16
- /* chorus */ [1, 2, 3],
17
- /* bridge */ [1, 1, 2],
18
- /* interlude */ [0, 1, 1],
19
- /* outro */ [0, 1, 1],
20
- ];
21
-
22
- function levelToProbability(level: GhostDensityLevel): number {
23
- switch (level) {
24
- case 0:
25
- return 0;
26
- case 1:
27
- return 0.15;
28
- case 2:
29
- return 0.3;
30
- case 3:
31
- return 0.45;
32
- }
33
- }
34
-
35
- function clampLevel(level: number): GhostDensityLevel {
36
- return Math.max(0, Math.min(3, level)) as GhostDensityLevel;
37
- }
38
-
39
- function adjustForBpm(level: GhostDensityLevel, bpm: number): GhostDensityLevel {
40
- if (bpm >= 160 && level !== 0) {
41
- return clampLevel(level - 1);
42
- }
43
- if (bpm <= 90 && level !== 3) {
44
- return clampLevel(level + 1);
45
- }
46
- return level;
47
- }
48
-
49
- /** Ghost-note trigger probability for a section, mood, density, and tempo. */
50
- export function getGhostDensity(
51
- mood: MoodCategory,
52
- section: SectionType,
53
- backingDensity: BackingDensity,
54
- bpm: number,
55
- ): number {
56
- const row = GHOST_DENSITY_TABLE[sectionIndex(section)] ?? GHOST_DENSITY_TABLE[1];
57
- const level = adjustForBpm((row?.[mood] ?? 0) as GhostDensityLevel, bpm);
58
-
59
- if (backingDensity === 'thin' && level !== 0) {
60
- return levelToProbability(clampLevel(level - 1));
61
- }
62
- if (backingDensity === 'thick' && level !== 3) {
63
- return levelToProbability(clampLevel(level + 1));
64
- }
65
- return levelToProbability(level);
66
- }
67
-
68
- /** Ghost-note velocity multiplier for a section and metric position. */
69
- export function getGhostVelocity(
70
- section: SectionType,
71
- beatPosition: number,
72
- isAfterSnare: boolean,
73
- ): number {
74
- let base = 0.4;
75
- const even = beatPosition % 2 === 0 ? 0.05 : 0;
76
- switch (section) {
77
- case 'a':
78
- base = 0.35 + even;
79
- break;
80
- case 'chorus':
81
- base = 0.5 + even;
82
- break;
83
- case 'bridge':
84
- base = 0.25 + even;
85
- break;
86
- case 'b':
87
- base = 0.4 + even;
88
- break;
89
- case 'intro':
90
- case 'outro':
91
- base = 0.38;
92
- break;
93
- }
94
- if (isAfterSnare) {
95
- base += 0.1;
96
- }
97
- return Math.max(0.25, Math.min(0.65, base));
98
- }
99
-
100
- /** Probability of a ghost at a specific 16th position (higher near the snare). */
101
- export function getGhostProbabilityAtPosition(beat: number, sixteenthInBeat: number): number {
102
- const nearSnare =
103
- (beat === 0 && sixteenthInBeat === 3) ||
104
- (beat === 1 && sixteenthInBeat === 1) ||
105
- (beat === 2 && sixteenthInBeat === 3) ||
106
- (beat === 3 && sixteenthInBeat === 1);
107
- return nearSnare ? 0.6 : 0.25;
108
- }
109
-
110
- /** Choose which ghost positions a groove favours. */
111
- export function selectGhostPositions(mood: MoodCategory, rng: DrumRng): GhostPosition[] {
112
- if (mood === MoodCategory.Energetic) {
113
- return ['e', 'a'];
114
- }
115
- if (mood === MoodCategory.Calm) {
116
- return rng.prob(0.5) ? ['e'] : [];
117
- }
118
- return ['e'];
119
- }
@@ -1,285 +0,0 @@
1
- import type { DrumRole, DrumStyle, SectionType } from './internal.js';
2
- import { GM } from './internal.js';
3
- import type { DrumRng } from './rng.js';
4
-
5
- /** Hi-hat subdivision level. */
6
- export type HiHatLevel = 'quarter' | 'eighth' | 'sixteenth';
7
-
8
- /** Hi-hat articulation type. */
9
- export type HiHatType = 'closed' | 'pedal' | 'open' | 'halfOpen' | 'ride';
10
-
11
- /** BPM at or above which 16th-note hi-hats become 8ths for playability. */
12
- export const HH_16TH_BPM_THRESHOLD = 150;
13
-
14
- const FHH_VEL_MIN = 45;
15
- const FHH_VEL_MAX = 60;
16
- /** Velocity boost applied to dynamic open hi-hat accents. */
17
- export const OHH_VEL_BOOST = 7;
18
-
19
- /** True unless the role suppresses timekeeping cymbals entirely. */
20
- export function shouldPlayHiHat(role: DrumRole): boolean {
21
- return role !== 'fxOnly';
22
- }
23
-
24
- /** Preferred timekeeping instrument for a role. */
25
- export function roleHiHatInstrument(role: DrumRole, useRide: boolean): number {
26
- if (role === 'ambient') {
27
- return GM.RIDE;
28
- }
29
- return useRide ? GM.RIDE : GM.CHH;
30
- }
31
-
32
- function sparser(level: HiHatLevel): HiHatLevel {
33
- if (level === 'sixteenth') {
34
- return 'eighth';
35
- }
36
- if (level === 'eighth') {
37
- return 'quarter';
38
- }
39
- return 'quarter';
40
- }
41
-
42
- function denser(level: HiHatLevel): HiHatLevel {
43
- if (level === 'quarter') {
44
- return 'eighth';
45
- }
46
- if (level === 'eighth') {
47
- return 'sixteenth';
48
- }
49
- return 'sixteenth';
50
- }
51
-
52
- /** Choose the hi-hat subdivision for a section/style/density/BPM. */
53
- export function getHiHatLevel(
54
- section: SectionType,
55
- style: DrumStyle,
56
- backingDensity: 'thin' | 'normal' | 'thick',
57
- bpm: number,
58
- rng: DrumRng,
59
- ): HiHatLevel {
60
- const allow16th = bpm < HH_16TH_BPM_THRESHOLD;
61
- let base: HiHatLevel = 'eighth';
62
-
63
- if (style === 'sparse') {
64
- base = section === 'chorus' ? 'eighth' : 'quarter';
65
- } else if (style === 'fourOnFloor') {
66
- if (allow16th && section === 'chorus' && rng.prob(0.25)) {
67
- return 'sixteenth';
68
- }
69
- return 'eighth';
70
- } else if (style === 'synth') {
71
- if (!allow16th) {
72
- return 'eighth';
73
- }
74
- if (section === 'a' && rng.prob(0.2)) {
75
- return 'eighth';
76
- }
77
- return 'sixteenth';
78
- } else if (style === 'trap') {
79
- return allow16th ? 'sixteenth' : 'eighth';
80
- } else if (style === 'latin') {
81
- if (allow16th && section === 'chorus' && rng.prob(0.3)) {
82
- return 'sixteenth';
83
- }
84
- return 'eighth';
85
- } else {
86
- switch (section) {
87
- case 'intro':
88
- base = 'quarter';
89
- break;
90
- case 'outro':
91
- base = 'eighth';
92
- break;
93
- case 'a':
94
- base = rng.prob(0.3) ? 'quarter' : 'eighth';
95
- break;
96
- case 'b':
97
- base = allow16th && rng.prob(0.25) ? 'sixteenth' : 'eighth';
98
- break;
99
- case 'chorus':
100
- base = allow16th && rng.prob(0.35) ? 'sixteenth' : 'eighth';
101
- break;
102
- case 'bridge':
103
- base = 'eighth';
104
- break;
105
- }
106
- }
107
-
108
- if (backingDensity === 'thin') {
109
- base = sparser(base);
110
- } else if (backingDensity === 'thick') {
111
- base = denser(base);
112
- }
113
-
114
- if (!allow16th && base === 'sixteenth') {
115
- base = 'eighth';
116
- }
117
- return base;
118
- }
119
-
120
- /** Metric velocity multiplier for a 16th position within a beat. */
121
- export function hiHatVelocityMultiplier(sixteenth: number, rng: DrumRng): number {
122
- let baseValue: number;
123
- switch (sixteenth) {
124
- case 0:
125
- baseValue = 0.95;
126
- break;
127
- case 2:
128
- baseValue = 0.75;
129
- break;
130
- case 1:
131
- baseValue = 0.55;
132
- break;
133
- default:
134
- baseValue = 0.5;
135
- break;
136
- }
137
- return baseValue * rng.float(0.95, 1.05);
138
- }
139
-
140
- /** Bars between dynamic open hi-hat accents (0 disables them). */
141
- export function openHiHatBarInterval(section: SectionType, style: DrumStyle): number {
142
- if (style === 'sparse') {
143
- return section === 'chorus' ? 4 : 0;
144
- }
145
- switch (section) {
146
- case 'intro':
147
- return style === 'fourOnFloor' ? 4 : 0;
148
- case 'a':
149
- return style === 'fourOnFloor' || style === 'upbeat' ? 2 : 4;
150
- case 'b':
151
- return 2;
152
- case 'chorus':
153
- return style === 'rock' || style === 'fourOnFloor' ? 1 : 2;
154
- case 'bridge':
155
- return 0;
156
- case 'outro':
157
- return 4;
158
- }
159
- }
160
-
161
- /** Beat that receives the dynamic open hi-hat within a bar. */
162
- export function openHiHatBeat(section: SectionType, rng: DrumRng): number {
163
- if (section === 'chorus') {
164
- const choice = rng.range(0, 3);
165
- if (choice < 2) {
166
- return 3;
167
- }
168
- if (choice < 3) {
169
- return 1;
170
- }
171
- return 2;
172
- }
173
- return 3;
174
- }
175
-
176
- /** Whether the section uses an independent foot hi-hat pulse. */
177
- export function shouldUseFootHiHat(section: SectionType, role: DrumRole): boolean {
178
- if (role === 'fxOnly') {
179
- return false;
180
- }
181
- switch (section) {
182
- case 'intro':
183
- case 'bridge':
184
- case 'outro':
185
- return true;
186
- default:
187
- return role === 'ambient' || role === 'minimal';
188
- }
189
- }
190
-
191
- /** Primary hi-hat articulation for a section. */
192
- export function sectionHiHatType(section: SectionType, role: DrumRole): HiHatType {
193
- if (role === 'ambient') {
194
- return 'ride';
195
- }
196
- if (role === 'minimal') {
197
- return 'pedal';
198
- }
199
- switch (section) {
200
- case 'intro':
201
- case 'a':
202
- return 'pedal';
203
- case 'b':
204
- return 'closed';
205
- case 'chorus':
206
- return 'open';
207
- case 'bridge':
208
- return 'ride';
209
- case 'outro':
210
- return 'halfOpen';
211
- }
212
- }
213
-
214
- /** GM note for a hi-hat articulation. */
215
- export function hiHatNote(type: HiHatType): number {
216
- switch (type) {
217
- case 'pedal':
218
- return GM.FHH;
219
- case 'open':
220
- return GM.OHH;
221
- case 'ride':
222
- return GM.RIDE;
223
- default:
224
- return GM.CHH;
225
- }
226
- }
227
-
228
- /** Velocity multiplier for a hi-hat articulation. */
229
- export function hiHatTypeVelocityMultiplier(type: HiHatType): number {
230
- switch (type) {
231
- case 'halfOpen':
232
- return 0.75;
233
- case 'pedal':
234
- return 0.65;
235
- case 'open':
236
- return 1.0;
237
- case 'ride':
238
- return 0.9;
239
- default:
240
- return 0.85;
241
- }
242
- }
243
-
244
- /** Whether to accent with an open hi-hat at a given beat. */
245
- export function shouldAddOpenHHAccent(
246
- section: SectionType,
247
- beat: number,
248
- bar: number,
249
- rng: DrumRng,
250
- ): boolean {
251
- if (section !== 'chorus' && section !== 'b') {
252
- return false;
253
- }
254
- if (section === 'chorus') {
255
- if (beat === 1 || beat === 3) {
256
- return rng.prob(0.6);
257
- }
258
- return false;
259
- }
260
- if (beat === 3 && bar % 2 === 1) {
261
- return rng.prob(0.4);
262
- }
263
- return false;
264
- }
265
-
266
- /** Foot hi-hat velocity with slight humanization. */
267
- export function footHiHatVelocity(rng: DrumRng): number {
268
- return rng.range(FHH_VEL_MIN, FHH_VEL_MAX);
269
- }
270
-
271
- /** Whether the section uses a ride cymbal instead of hi-hats. */
272
- export function shouldUseRideForSection(section: SectionType, style: DrumStyle): boolean {
273
- if (style === 'rock' && section === 'chorus') {
274
- return true;
275
- }
276
- if (style === 'sparse') {
277
- return false;
278
- }
279
- return section === 'bridge';
280
- }
281
-
282
- /** Whether a bridge beat is carried by a cross-stick instead of the hi-hat. */
283
- export function shouldUseBridgeCrossStick(section: SectionType, beat: number): boolean {
284
- return section === 'bridge' && (beat === 1 || beat === 3);
285
- }
@@ -1,31 +0,0 @@
1
- import { clampVel, GM } from './internal.js';
2
-
3
- /** A single percussion onset. */
4
- export type DrumHit = {
5
- pitch: number;
6
- startBeat: number;
7
- durationBeat: number;
8
- velocity: number;
9
- };
10
-
11
- /** Accumulator for drum onsets with velocity clamping and simple lookups. */
12
- export class HitList {
13
- readonly hits: DrumHit[] = [];
14
-
15
- /** Append a hit, clamping velocity and dropping non-positive positions. */
16
- add(pitch: number, startBeat: number, durationBeat: number, velocity: number): void {
17
- this.hits.push({
18
- pitch,
19
- startBeat: Math.max(0, startBeat),
20
- durationBeat,
21
- velocity: clampVel(velocity),
22
- });
23
- }
24
-
25
- /** True when a crash already sits within a 16th of `startBeat`. */
26
- hasCrashNear(startBeat: number): boolean {
27
- return this.hits.some(
28
- (h) => h.pitch === GM.CRASH && h.startBeat >= startBeat && h.startBeat < startBeat + 0.25,
29
- );
30
- }
31
- }