@coldsurf/synth-kit 0.1.0

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.
@@ -0,0 +1,494 @@
1
+ // src/drumkit/drumkit.ts
2
+ function attackTo(param, peak, when, attack, curve) {
3
+ param.setValueAtTime(1e-4, when);
4
+ if (curve === "exp") param.exponentialRampToValueAtTime(peak, when + attack);
5
+ else param.linearRampToValueAtTime(peak, when + attack);
6
+ }
7
+ function createDrumKit(ctx, bus, preset) {
8
+ const noiseBuf = ctx.createBuffer(1, Math.floor(ctx.sampleRate * 1), ctx.sampleRate);
9
+ {
10
+ const d = noiseBuf.getChannelData(0);
11
+ for (let i = 0; i < d.length; i++) d[i] = Math.random() * 2 - 1;
12
+ }
13
+ let stopped = false;
14
+ const pending = /* @__PURE__ */ new Set();
15
+ const trackDispose = (fn, lifetimeMs) => {
16
+ pending.add(fn);
17
+ window.setTimeout(() => {
18
+ if (pending.has(fn)) {
19
+ fn();
20
+ pending.delete(fn);
21
+ }
22
+ }, lifetimeMs);
23
+ };
24
+ function playBody(b, when, velocity) {
25
+ const osc = ctx.createOscillator();
26
+ osc.type = "sine";
27
+ osc.frequency.setValueAtTime(b.startHz, when);
28
+ osc.frequency.exponentialRampToValueAtTime(b.endHz, when + b.pitchTau);
29
+ const g = ctx.createGain();
30
+ attackTo(g.gain, velocity * (b.peakMul ?? 1), when, b.attack, b.attackCurve);
31
+ g.gain.exponentialRampToValueAtTime(1e-4, when + b.decay);
32
+ osc.connect(g).connect(bus);
33
+ osc.start(when);
34
+ osc.stop(when + b.stop);
35
+ trackDispose(
36
+ () => {
37
+ try {
38
+ osc.stop();
39
+ } catch {
40
+ }
41
+ osc.disconnect();
42
+ g.disconnect();
43
+ },
44
+ (b.stop + 0.1) * 1e3
45
+ );
46
+ }
47
+ function kick(when, velocity) {
48
+ if (stopped) return;
49
+ const k = preset.kick;
50
+ playBody(k.body, when, velocity);
51
+ if (k.sub) playBody(k.sub, when, velocity);
52
+ if (k.click) {
53
+ const c = k.click;
54
+ const src = ctx.createBufferSource();
55
+ src.buffer = noiseBuf;
56
+ const hp = ctx.createBiquadFilter();
57
+ hp.type = "highpass";
58
+ hp.frequency.value = c.hpHz;
59
+ const g = ctx.createGain();
60
+ attackTo(g.gain, velocity * c.level, when, c.attack, c.attackCurve);
61
+ g.gain.exponentialRampToValueAtTime(1e-4, when + c.decay);
62
+ src.connect(hp).connect(g).connect(bus);
63
+ src.start(when);
64
+ src.stop(when + c.stop);
65
+ trackDispose(
66
+ () => {
67
+ try {
68
+ src.stop();
69
+ } catch {
70
+ }
71
+ src.disconnect();
72
+ hp.disconnect();
73
+ g.disconnect();
74
+ },
75
+ (c.stop + 0.1) * 1e3
76
+ );
77
+ }
78
+ }
79
+ function snare(when, velocity) {
80
+ if (stopped || !preset.snare) return;
81
+ const s = preset.snare;
82
+ if (s.tone) {
83
+ const t = s.tone;
84
+ const osc = ctx.createOscillator();
85
+ osc.type = t.type;
86
+ osc.frequency.setValueAtTime(t.startHz, when);
87
+ if (t.endHz !== void 0) {
88
+ osc.frequency.exponentialRampToValueAtTime(t.endHz, when + (t.pitchTau ?? 0.08));
89
+ }
90
+ const g = ctx.createGain();
91
+ attackTo(g.gain, velocity * t.level, when, 3e-3, "lin");
92
+ g.gain.exponentialRampToValueAtTime(1e-4, when + t.decay);
93
+ osc.connect(g).connect(bus);
94
+ osc.start(when);
95
+ osc.stop(when + t.stop);
96
+ trackDispose(
97
+ () => {
98
+ try {
99
+ osc.stop();
100
+ } catch {
101
+ }
102
+ osc.disconnect();
103
+ g.disconnect();
104
+ },
105
+ (t.stop + 0.1) * 1e3
106
+ );
107
+ }
108
+ if (s.noise) {
109
+ const n = s.noise;
110
+ const src = ctx.createBufferSource();
111
+ src.buffer = noiseBuf;
112
+ src.playbackRate.value = n.prMin + Math.random() * (n.prMax - n.prMin);
113
+ const bp = ctx.createBiquadFilter();
114
+ bp.type = "bandpass";
115
+ bp.frequency.value = n.bpHz;
116
+ bp.Q.value = n.q;
117
+ const g = ctx.createGain();
118
+ attackTo(g.gain, velocity * n.level, when, 2e-3, "lin");
119
+ g.gain.exponentialRampToValueAtTime(1e-4, when + n.decay);
120
+ src.connect(bp).connect(g).connect(bus);
121
+ src.start(when);
122
+ src.stop(when + n.stop);
123
+ trackDispose(
124
+ () => {
125
+ try {
126
+ src.stop();
127
+ } catch {
128
+ }
129
+ src.disconnect();
130
+ bp.disconnect();
131
+ g.disconnect();
132
+ },
133
+ (n.stop + 0.1) * 1e3
134
+ );
135
+ }
136
+ if (s.gatedTails) {
137
+ for (const tl of s.gatedTails) {
138
+ const src = ctx.createBufferSource();
139
+ src.buffer = noiseBuf;
140
+ src.playbackRate.value = 0.7 + Math.random() * 0.2;
141
+ const bp = ctx.createBiquadFilter();
142
+ bp.type = "bandpass";
143
+ bp.frequency.value = tl.centerHz;
144
+ bp.Q.value = tl.q;
145
+ const g = ctx.createGain();
146
+ const peak = velocity * tl.level;
147
+ g.gain.setValueAtTime(1e-4, when);
148
+ g.gain.linearRampToValueAtTime(peak, when + 6e-3);
149
+ g.gain.setValueAtTime(peak * 0.85, when + tl.plateau);
150
+ g.gain.linearRampToValueAtTime(1e-4, when + tl.plateau + tl.cut);
151
+ src.connect(bp).connect(g).connect(bus);
152
+ src.start(when);
153
+ const stop = tl.plateau + tl.cut + 0.05;
154
+ src.stop(when + stop);
155
+ trackDispose(
156
+ () => {
157
+ try {
158
+ src.stop();
159
+ } catch {
160
+ }
161
+ src.disconnect();
162
+ bp.disconnect();
163
+ g.disconnect();
164
+ },
165
+ (stop + 0.1) * 1e3
166
+ );
167
+ }
168
+ }
169
+ }
170
+ function hat(when, velocity, open) {
171
+ if (stopped || !preset.hat) return;
172
+ const h = preset.hat;
173
+ const src = ctx.createBufferSource();
174
+ src.buffer = noiseBuf;
175
+ src.playbackRate.value = h.prCenter + (Math.random() - 0.5) * h.prSpread;
176
+ const hp = ctx.createBiquadFilter();
177
+ hp.type = "highpass";
178
+ hp.frequency.value = h.hpHz;
179
+ if (h.hpQ !== void 0) hp.Q.value = h.hpQ;
180
+ let tail = hp;
181
+ let bp = null;
182
+ if (h.bpHz !== void 0) {
183
+ bp = ctx.createBiquadFilter();
184
+ bp.type = "bandpass";
185
+ bp.frequency.value = h.bpHz;
186
+ if (h.bpQ !== void 0) bp.Q.value = h.bpQ;
187
+ hp.connect(bp);
188
+ tail = bp;
189
+ }
190
+ const decay = open ? h.openDecay : h.closedDecay;
191
+ const peak = velocity * (open ? h.openLevel : h.closedLevel);
192
+ const g = ctx.createGain();
193
+ attackTo(g.gain, peak, when, h.attack, "lin");
194
+ g.gain.exponentialRampToValueAtTime(1e-4, when + decay);
195
+ src.connect(hp);
196
+ tail.connect(g).connect(bus);
197
+ src.start(when);
198
+ const stop = decay + h.stopPad;
199
+ src.stop(when + stop);
200
+ trackDispose(
201
+ () => {
202
+ try {
203
+ src.stop();
204
+ } catch {
205
+ }
206
+ src.disconnect();
207
+ hp.disconnect();
208
+ bp?.disconnect();
209
+ g.disconnect();
210
+ },
211
+ (stop + 0.1) * 1e3
212
+ );
213
+ }
214
+ return {
215
+ kick,
216
+ snare,
217
+ hat,
218
+ dispose() {
219
+ stopped = true;
220
+ for (const fn of pending) {
221
+ try {
222
+ fn();
223
+ } catch {
224
+ }
225
+ }
226
+ pending.clear();
227
+ }
228
+ };
229
+ }
230
+
231
+ // src/drumkit/kits.ts
232
+ var DRY_KIT = {
233
+ kick: {
234
+ body: {
235
+ startHz: 135,
236
+ endHz: 48,
237
+ pitchTau: 0.06,
238
+ attack: 4e-3,
239
+ attackCurve: "lin",
240
+ decay: 0.32,
241
+ stop: 0.4
242
+ }
243
+ },
244
+ snare: {
245
+ tone: { type: "triangle", startHz: 220, level: 0.45, decay: 0.14, stop: 0.18 },
246
+ noise: { bpHz: 1600, q: 0.8, prMin: 0.6, prMax: 1, level: 0.5, decay: 0.18, stop: 0.22 }
247
+ },
248
+ hat: {
249
+ hpHz: 7200,
250
+ hpQ: 0.6,
251
+ prCenter: 1,
252
+ prSpread: 0.1,
253
+ attack: 2e-3,
254
+ closedDecay: 0.04,
255
+ openDecay: 0.22,
256
+ closedLevel: 1,
257
+ openLevel: 1,
258
+ stopPad: 0.05
259
+ }
260
+ };
261
+ var WET_KIT = {
262
+ kick: {
263
+ body: {
264
+ startHz: 110,
265
+ endHz: 42,
266
+ pitchTau: 0.09,
267
+ attack: 6e-3,
268
+ attackCurve: "lin",
269
+ decay: 0.5,
270
+ stop: 0.55
271
+ }
272
+ },
273
+ snare: {
274
+ tone: { type: "triangle", startHz: 200, level: 0.5, decay: 0.12, stop: 0.18 },
275
+ noise: { bpHz: 1800, q: 0.9, prMin: 1, prMax: 1, level: 0.6, decay: 0.14, stop: 0.18 },
276
+ gatedTails: [
277
+ { centerHz: 1400, q: 1.2, level: 0.55, plateau: 0.18, cut: 0.02 },
278
+ { centerHz: 450, q: 1, level: 0.4, plateau: 0.18, cut: 0.02 }
279
+ ]
280
+ }
281
+ };
282
+ var IRON_KIT = {
283
+ kick: {
284
+ body: {
285
+ startHz: 95,
286
+ endHz: 38,
287
+ pitchTau: 0.05,
288
+ attack: 3e-3,
289
+ attackCurve: "lin",
290
+ decay: 0.28,
291
+ stop: 0.35
292
+ },
293
+ click: { hpHz: 2200, level: 0.35, attack: 1e-3, attackCurve: "lin", decay: 0.012, stop: 0.03 }
294
+ },
295
+ // grit = ๊ฑฐ์นœ highpass noise. open ๋ชจ๋“œ ์—†์ด closed ๋งŒ ํ˜ธ์ถœ โ€” openDecay = closedDecay.
296
+ hat: {
297
+ hpHz: 6500,
298
+ hpQ: 0.5,
299
+ prCenter: 1,
300
+ prSpread: 0.3,
301
+ attack: 1e-3,
302
+ closedDecay: 0.035,
303
+ openDecay: 0.035,
304
+ closedLevel: 1,
305
+ openLevel: 1,
306
+ stopPad: 0.045
307
+ }
308
+ };
309
+ var BLACKGAZE_SNARE = {
310
+ noise: { bpHz: 1800, q: 1.3, prMin: 1, prMax: 1, level: 0.6, decay: 0.2, stop: 0.25 },
311
+ tone: {
312
+ type: "triangle",
313
+ startHz: 260,
314
+ endHz: 160,
315
+ pitchTau: 0.08,
316
+ level: 0.32,
317
+ decay: 0.13,
318
+ stop: 0.18
319
+ }
320
+ };
321
+ var BATHEDAY_KIT = {
322
+ kick: {
323
+ body: {
324
+ startHz: 150,
325
+ endHz: 45,
326
+ pitchTau: 0.07,
327
+ attack: 4e-3,
328
+ attackCurve: "lin",
329
+ decay: 0.34,
330
+ stop: 0.4
331
+ },
332
+ click: { hpHz: 2200, level: 0.28, attack: 1e-3, attackCurve: "lin", decay: 0.01, stop: 0.03 }
333
+ },
334
+ snare: BLACKGAZE_SNARE
335
+ };
336
+ var SUBLIME_KIT = BATHEDAY_KIT;
337
+ var REVEAL_KIT = {
338
+ kick: {
339
+ body: {
340
+ startHz: 140,
341
+ endHz: 42,
342
+ pitchTau: 0.07,
343
+ attack: 4e-3,
344
+ attackCurve: "lin",
345
+ decay: 0.38,
346
+ stop: 0.45
347
+ },
348
+ click: { hpHz: 1800, level: 0.25, attack: 1e-3, attackCurve: "lin", decay: 0.01, stop: 0.03 }
349
+ },
350
+ snare: {
351
+ noise: { bpHz: 1700, q: 1.4, prMin: 1, prMax: 1, level: 0.55, decay: 0.18, stop: 0.25 },
352
+ tone: {
353
+ type: "triangle",
354
+ startHz: 240,
355
+ endHz: 150,
356
+ pitchTau: 0.08,
357
+ level: 0.3,
358
+ decay: 0.12,
359
+ stop: 0.18
360
+ }
361
+ },
362
+ hat: {
363
+ hpHz: 7200,
364
+ prCenter: 1,
365
+ prSpread: 0.3,
366
+ attack: 1e-3,
367
+ closedDecay: 0.025,
368
+ openDecay: 0.12,
369
+ closedLevel: 1,
370
+ openLevel: 1,
371
+ stopPad: 0.02
372
+ }
373
+ };
374
+ var DUB_RELAY_KIT = {
375
+ kick: {
376
+ body: {
377
+ startHz: 130,
378
+ endHz: 48,
379
+ pitchTau: 0.08,
380
+ attack: 4e-3,
381
+ attackCurve: "exp",
382
+ peakMul: 1.3,
383
+ decay: 0.6,
384
+ stop: 0.65
385
+ },
386
+ sub: {
387
+ startHz: 60,
388
+ endHz: 38,
389
+ pitchTau: 0.12,
390
+ attack: 8e-3,
391
+ attackCurve: "exp",
392
+ peakMul: 0.95,
393
+ decay: 0.45,
394
+ stop: 0.5
395
+ },
396
+ click: { hpHz: 2500, level: 0.4, attack: 1e-3, attackCurve: "exp", decay: 0.02, stop: 0.04 }
397
+ },
398
+ hat: {
399
+ hpHz: 7800,
400
+ bpHz: 9e3,
401
+ bpQ: 0.9,
402
+ prCenter: 1,
403
+ prSpread: 0.05,
404
+ attack: 2e-3,
405
+ closedDecay: 0.035,
406
+ openDecay: 0.13,
407
+ closedLevel: 0.22,
408
+ openLevel: 0.32,
409
+ stopPad: 0.02
410
+ }
411
+ };
412
+ var DUB_HOLY_KIT = {
413
+ kick: {
414
+ body: {
415
+ startHz: 140,
416
+ endHz: 44,
417
+ pitchTau: 0.1,
418
+ attack: 5e-3,
419
+ attackCurve: "exp",
420
+ peakMul: 1.4,
421
+ decay: 0.85,
422
+ stop: 0.9
423
+ },
424
+ sub: {
425
+ startHz: 56,
426
+ endHz: 36,
427
+ pitchTau: 0.15,
428
+ attack: 0.01,
429
+ attackCurve: "exp",
430
+ peakMul: 1.05,
431
+ decay: 0.7,
432
+ stop: 0.75
433
+ },
434
+ click: {
435
+ hpHz: 2200,
436
+ level: 0.32,
437
+ attack: 15e-4,
438
+ attackCurve: "exp",
439
+ decay: 0.025,
440
+ stop: 0.05
441
+ }
442
+ }
443
+ };
444
+ var DUB_POWER_KIT = {
445
+ kick: {
446
+ body: {
447
+ startHz: 140,
448
+ endHz: 45,
449
+ pitchTau: 0.1,
450
+ attack: 5e-3,
451
+ attackCurve: "exp",
452
+ peakMul: 1.35,
453
+ decay: 0.75,
454
+ stop: 0.8
455
+ },
456
+ sub: {
457
+ startHz: 64.6,
458
+ endHz: 38,
459
+ pitchTau: 0.15,
460
+ attack: 0.01,
461
+ attackCurve: "exp",
462
+ peakMul: 1.05,
463
+ decay: 0.6,
464
+ stop: 0.65
465
+ },
466
+ click: { hpHz: 2200, level: 0.32, attack: 1e-3, attackCurve: "exp", decay: 0.025, stop: 0.04 }
467
+ },
468
+ hat: {
469
+ hpHz: 6500,
470
+ bpHz: 7200,
471
+ bpQ: 0.85,
472
+ prCenter: 1,
473
+ prSpread: 0.06,
474
+ attack: 2e-3,
475
+ closedDecay: 0.045,
476
+ openDecay: 0.22,
477
+ closedLevel: 0.24,
478
+ openLevel: 0.34,
479
+ stopPad: 0.02
480
+ }
481
+ };
482
+ export {
483
+ BATHEDAY_KIT,
484
+ DRY_KIT,
485
+ DUB_HOLY_KIT,
486
+ DUB_POWER_KIT,
487
+ DUB_RELAY_KIT,
488
+ IRON_KIT,
489
+ REVEAL_KIT,
490
+ SUBLIME_KIT,
491
+ WET_KIT,
492
+ createDrumKit
493
+ };
494
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/drumkit/drumkit.ts","../../src/drumkit/kits.ts"],"sourcesContent":["/**\n * Drumkit โ€” *ํƒ€์•…* ํ•ฉ์„ฑ ์—”์ง„. catalog ํŠธ๋ž™๋“ค์ด ๊ธ€์ž ๊ทธ๋Œ€๋กœ ๋ณต์ œํ•˜๋˜ kickยทsnareยทhat ๊ณจ๊ฒฉ์„\n * ํ•œ ์•…๊ธฐ๋กœ. sunwall ๊ณผ ๊ฐ™์€ VST ๋ชจ๋ธ: `createDrumKit` ์€ *์ƒ‰-๋ถˆ๋ฌธ ์—”์ง„*, `*_KIT` ์€ *ํŒจ์น˜*.\n *\n * kick : body osc(ํ”ผ์น˜ envelope) โ†’ gain env โ†’ bus (+ ์˜ต์…˜ sub body, click transient)\n * snare : tone osc + bandpass noise (+ ์˜ต์…˜ gated reverb tail ร— N)\n * hat : highpass(+์˜ต์…˜ bandpass) noise โ†’ gain env (closed/open)\n *\n * ์—”์ง„์€ *voice ํ•ฉ์„ฑ* ๋งŒ ํ•œ๋‹ค โ€” `(when, velocity)` ์ ˆ๋Œ€์‹œ๊ฐ„์œผ๋กœ `bus` ์— ํ•œ ํƒ€๊ฒฉ์„ ๊ทธ๋ฆฐ๋‹ค.\n * ์Šค์ผ€์ค„๋ง(์Šคํ… ๋ฃจํ”„)ยทmaster fadeยท์‚ฌ์ด๋“œ์ฒด์ธ duckยทdub send ๋Š” *ํŠธ๋ž™* ์˜ ์ฑ…์ž„์ด๋ฉฐ ์—”์ง„ ๋ฐ–์ด๋‹ค.\n * ํ•œ ๊ณก ์ „์šฉ voice(metalHitยทtomยทshimmerยทsaluteยท\n * crashยทclapยทrim)๋Š” rule-of-three ๋ฏธ๋‹ฌ์ด๋ผ ํŠธ๋ž™ ์ธ๋ผ์ธ์œผ๋กœ ๋‚จ๋Š”๋‹ค.\n *\n * ๋ณด์กด ๊ธฐ์ค€: ๋“ค๋ฆฌ๋Š” envelope(attackยทpeakยทdecay)ยทํ•„ํ„ฐยทํ”ผ์น˜๋Š” ์›๋ณธ ์ƒ์ˆ˜ ๊ทธ๋Œ€๋กœ. ๋“ค๋ฆฌ์ง€ ์•Š๋Š”\n * ๊ฒƒ๋งŒ ์ •๊ทœํ™” โ€” โ‘  white noise ๋ฒ„ํผ ๋‚ด์šฉ(ํ†ต๊ณ„์ ์œผ๋กœ ๋™์ผํ•œ ๋‚œ์ˆ˜), โ‘ก ์ผ๋ถ€ ๋…ธ๋“œ stop() ์‹œ์ ์€\n * preset ์˜ `stop`/`stopPad` ๋กœ ๊ทธ๋Œ€๋กœ ๋ฐ•๋Š”๋‹ค.\n */\n\n/** envelope ramp ์ข…๋ฅ˜. dub kick ์€ exp attack(peak>velocity ํ—ˆ์šฉ), ๊ทธ ์™ธ์—” lin. */\ntype Ramp = 'lin' | 'exp'\n\n/** ํ”ผ์น˜ + gain envelope ํ•œ layer (kick bodyยทsub ๊ณต์šฉ). */\nexport interface KickBody {\n startHz: number\n endHz: number\n /** freq exponentialRamp ๋„๋‹ฌ ์‹œ๊ฐ„. */\n pitchTau: number\n /** gain attack ์‹œ๊ฐ„. */\n attack: number\n attackCurve: Ramp\n /** peak gain = velocity * peakMul. ๊ธฐ๋ณธ 1. dub body1 ์€ 1.3 ๋“ฑ. */\n peakMul?: number\n /** gain exponentialRampโ†’0 ๋„๋‹ฌ ์‹œ๊ฐ„(=๊ฐ์‡  ๋). */\n decay: number\n /** osc.stop(when + stop). */\n stop: number\n}\n\n/** kick transient click โ€” ์งง์€ highpass noise burst. */\nexport interface KickClick {\n hpHz: number\n /** peak = velocity * level. */\n level: number\n attack: number\n attackCurve: Ramp\n decay: number\n stop: number\n}\n\nexport interface KickPreset {\n body: KickBody\n /** dub ์ „์šฉ โ€” ๊นŠ์€ sub tail layer. */\n sub?: KickBody\n click?: KickClick\n}\n\nexport interface SnareTone {\n type: OscillatorType\n startHz: number\n /** ์žˆ์œผ๋ฉด startHz โ†’ endHz ํ”ผ์น˜ envelope. */\n endHz?: number\n pitchTau?: number\n /** peak = velocity * level. */\n level: number\n decay: number\n stop: number\n}\n\nexport interface SnareNoise {\n bpHz: number\n q: number\n /** playbackRate โˆˆ [prMin, prMax] ๋ฌด์ž‘์œ„. ๊ณ ์ •์ด๋ฉด ๋‘ ๊ฐ’ ๋™์ผ. */\n prMin: number\n prMax: number\n level: number\n decay: number\n stop: number\n}\n\n/** Gated reverb tail (wet-room ์˜ *In the Air Tonight* ๊ฒฐ). ์ž์—ฐ ๊ฐ์‡  ๋Œ€์‹  plateau โ†’ cut. */\nexport interface GatedTail {\n centerHz: number\n q: number\n /** peak = velocity * level. */\n level: number\n plateau: number\n cut: number\n}\n\nexport interface SnarePreset {\n tone?: SnareTone\n noise?: SnareNoise\n gatedTails?: GatedTail[]\n}\n\nexport interface HatPreset {\n hpHz: number\n hpQ?: number\n /** ์žˆ์œผ๋ฉด highpass ๋’ค์— bandpass ํ•œ ๋‹จ(dub hat). */\n bpHz?: number\n bpQ?: number\n /** playbackRate = prCenter + (random - 0.5) * prSpread. */\n prCenter: number\n prSpread: number\n attack: number\n closedDecay: number\n openDecay: number\n /** peak = velocity * (open ? openLevel : closedLevel). dry/iron/reveal ์€ 1. */\n closedLevel: number\n openLevel: number\n /** stop = when + tail + stopPad. */\n stopPad: number\n}\n\nexport interface DrumKitPreset {\n kick: KickPreset\n snare?: SnarePreset\n hat?: HatPreset\n}\n\nexport interface DrumKitHandle {\n /** when ์— kick ํ•œ ํƒ€. */\n kick(when: number, velocity: number): void\n /** when ์— snare ํ•œ ํƒ€. preset.snare ์—†์œผ๋ฉด no-op. */\n snare(when: number, velocity: number): void\n /** when ์— hat ํ•œ ํƒ€(open/closed). preset.hat ์—†์œผ๋ฉด no-op. */\n hat(when: number, velocity: number, open: boolean): void\n /** ๊ณต์œ  ๋…ธ๋“œ ์ •๋ฆฌ. ephemeral voice ๋…ธ๋“œ๋Š” self-stop + ๋‚ด๋ถ€ ํƒ€์ž„์•„์›ƒ disconnect. */\n dispose(): void\n}\n\n/** attack ramp ํ•œ ์ค„ โ€” 0.0001 ์—์„œ peak ๊นŒ์ง€ lin/exp. */\nfunction attackTo(param: AudioParam, peak: number, when: number, attack: number, curve: Ramp) {\n param.setValueAtTime(0.0001, when)\n if (curve === 'exp') param.exponentialRampToValueAtTime(peak, when + attack)\n else param.linearRampToValueAtTime(peak, when + attack)\n}\n\n/**\n * ๋“œ๋Ÿผํ‚ท์„ ๋งŒ๋“ค์–ด `bus` ๋กœ voice ๋ฅผ ํ•ฉ์„ฑํ•˜๋Š” ํ•ธ๋“ค์„ ๋Œ๋ ค์ค€๋‹ค.\n *\n * @param bus ๊ฐ ํƒ€๊ฒฉ์ด ์—ฐ๊ฒฐ๋  ๋ชฉ์ ์ง€(ํŠธ๋ž™์˜ drumBusยทtoneยทmaster ๋“ฑ).\n */\nexport function createDrumKit(\n ctx: AudioContext,\n bus: AudioNode,\n preset: DrumKitPreset,\n): DrumKitHandle {\n // ๊ณต์œ  white noise ๋ฒ„ํผ โ€” snareยทhat ์˜ ์žฌ๋ฃŒ. 1s ๋ฉด ๋ชจ๋“  kit voice tail ์„ ๋ฎ๋Š”๋‹ค.\n // ๋‚ด์šฉ์€ ๋‚œ์ˆ˜๋ผ ํŠธ๋ž™๋ณ„ ์›๋ณธ๊ณผ *ํ†ต๊ณ„์ ์œผ๋กœ ๋™์ผ*(perceptually identical).\n const noiseBuf = ctx.createBuffer(1, Math.floor(ctx.sampleRate * 1.0), ctx.sampleRate)\n {\n const d = noiseBuf.getChannelData(0)\n for (let i = 0; i < d.length; i++) d[i] = Math.random() * 2 - 1\n }\n\n // ephemeral voice ๋…ธ๋“œ์˜ ์ง€์—ฐ disconnect โ€” ๊ธฐ์กด trackDispose ๋™์ž‘์„ ์—”์ง„ ๋‚ด๋ถ€๋กœ ํก์ˆ˜.\n let stopped = false\n const pending = new Set<() => void>()\n const trackDispose = (fn: () => void, lifetimeMs: number) => {\n pending.add(fn)\n window.setTimeout(() => {\n if (pending.has(fn)) {\n fn()\n pending.delete(fn)\n }\n }, lifetimeMs)\n }\n\n function playBody(b: KickBody, when: number, velocity: number) {\n const osc = ctx.createOscillator()\n osc.type = 'sine'\n osc.frequency.setValueAtTime(b.startHz, when)\n osc.frequency.exponentialRampToValueAtTime(b.endHz, when + b.pitchTau)\n const g = ctx.createGain()\n attackTo(g.gain, velocity * (b.peakMul ?? 1), when, b.attack, b.attackCurve)\n g.gain.exponentialRampToValueAtTime(0.0001, when + b.decay)\n osc.connect(g).connect(bus)\n osc.start(when)\n osc.stop(when + b.stop)\n trackDispose(\n () => {\n try {\n osc.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n osc.disconnect()\n g.disconnect()\n },\n (b.stop + 0.1) * 1000,\n )\n }\n\n function kick(when: number, velocity: number) {\n if (stopped) return\n const k = preset.kick\n playBody(k.body, when, velocity)\n if (k.sub) playBody(k.sub, when, velocity)\n if (k.click) {\n const c = k.click\n const src = ctx.createBufferSource()\n src.buffer = noiseBuf\n const hp = ctx.createBiquadFilter()\n hp.type = 'highpass'\n hp.frequency.value = c.hpHz\n const g = ctx.createGain()\n attackTo(g.gain, velocity * c.level, when, c.attack, c.attackCurve)\n g.gain.exponentialRampToValueAtTime(0.0001, when + c.decay)\n src.connect(hp).connect(g).connect(bus)\n src.start(when)\n src.stop(when + c.stop)\n trackDispose(\n () => {\n try {\n src.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n src.disconnect()\n hp.disconnect()\n g.disconnect()\n },\n (c.stop + 0.1) * 1000,\n )\n }\n }\n\n function snare(when: number, velocity: number) {\n if (stopped || !preset.snare) return\n const s = preset.snare\n\n if (s.tone) {\n const t = s.tone\n const osc = ctx.createOscillator()\n osc.type = t.type\n osc.frequency.setValueAtTime(t.startHz, when)\n if (t.endHz !== undefined) {\n osc.frequency.exponentialRampToValueAtTime(t.endHz, when + (t.pitchTau ?? 0.08))\n }\n const g = ctx.createGain()\n attackTo(g.gain, velocity * t.level, when, 0.003, 'lin')\n g.gain.exponentialRampToValueAtTime(0.0001, when + t.decay)\n osc.connect(g).connect(bus)\n osc.start(when)\n osc.stop(when + t.stop)\n trackDispose(\n () => {\n try {\n osc.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n osc.disconnect()\n g.disconnect()\n },\n (t.stop + 0.1) * 1000,\n )\n }\n\n if (s.noise) {\n const n = s.noise\n const src = ctx.createBufferSource()\n src.buffer = noiseBuf\n src.playbackRate.value = n.prMin + Math.random() * (n.prMax - n.prMin)\n const bp = ctx.createBiquadFilter()\n bp.type = 'bandpass'\n bp.frequency.value = n.bpHz\n bp.Q.value = n.q\n const g = ctx.createGain()\n attackTo(g.gain, velocity * n.level, when, 0.002, 'lin')\n g.gain.exponentialRampToValueAtTime(0.0001, when + n.decay)\n src.connect(bp).connect(g).connect(bus)\n src.start(when)\n src.stop(when + n.stop)\n trackDispose(\n () => {\n try {\n src.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n src.disconnect()\n bp.disconnect()\n g.disconnect()\n },\n (n.stop + 0.1) * 1000,\n )\n }\n\n if (s.gatedTails) {\n for (const tl of s.gatedTails) {\n const src = ctx.createBufferSource()\n src.buffer = noiseBuf\n src.playbackRate.value = 0.7 + Math.random() * 0.2\n const bp = ctx.createBiquadFilter()\n bp.type = 'bandpass'\n bp.frequency.value = tl.centerHz\n bp.Q.value = tl.q\n const g = ctx.createGain()\n const peak = velocity * tl.level\n // attack โ†’ plateau โ†’ ๊ฐ‘์ž๊ธฐ cut (์ž์—ฐ ๊ฐ์‡ ๊ฐ€ ์•„๋‹Œ *gated*).\n g.gain.setValueAtTime(0.0001, when)\n g.gain.linearRampToValueAtTime(peak, when + 0.006)\n g.gain.setValueAtTime(peak * 0.85, when + tl.plateau)\n g.gain.linearRampToValueAtTime(0.0001, when + tl.plateau + tl.cut)\n src.connect(bp).connect(g).connect(bus)\n src.start(when)\n const stop = tl.plateau + tl.cut + 0.05\n src.stop(when + stop)\n trackDispose(\n () => {\n try {\n src.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n src.disconnect()\n bp.disconnect()\n g.disconnect()\n },\n (stop + 0.1) * 1000,\n )\n }\n }\n }\n\n function hat(when: number, velocity: number, open: boolean) {\n if (stopped || !preset.hat) return\n const h = preset.hat\n const src = ctx.createBufferSource()\n src.buffer = noiseBuf\n src.playbackRate.value = h.prCenter + (Math.random() - 0.5) * h.prSpread\n const hp = ctx.createBiquadFilter()\n hp.type = 'highpass'\n hp.frequency.value = h.hpHz\n if (h.hpQ !== undefined) hp.Q.value = h.hpQ\n\n let tail: AudioNode = hp\n let bp: BiquadFilterNode | null = null\n if (h.bpHz !== undefined) {\n bp = ctx.createBiquadFilter()\n bp.type = 'bandpass'\n bp.frequency.value = h.bpHz\n if (h.bpQ !== undefined) bp.Q.value = h.bpQ\n hp.connect(bp)\n tail = bp\n }\n\n const decay = open ? h.openDecay : h.closedDecay\n const peak = velocity * (open ? h.openLevel : h.closedLevel)\n const g = ctx.createGain()\n attackTo(g.gain, peak, when, h.attack, 'lin')\n g.gain.exponentialRampToValueAtTime(0.0001, when + decay)\n src.connect(hp)\n tail.connect(g).connect(bus)\n src.start(when)\n const stop = decay + h.stopPad\n src.stop(when + stop)\n trackDispose(\n () => {\n try {\n src.stop()\n } catch {\n // ์ด๋ฏธ ์ •์ง€ โ€” ๋ฌดํ•ด.\n }\n src.disconnect()\n hp.disconnect()\n bp?.disconnect()\n g.disconnect()\n },\n (stop + 0.1) * 1000,\n )\n }\n\n return {\n kick,\n snare,\n hat,\n dispose() {\n stopped = true\n for (const fn of pending) {\n try {\n fn()\n } catch {\n // ์ด๋ฏธ ์ •๋ฆฌ๋จ โ€” ๋ฌดํ•ด.\n }\n }\n pending.clear()\n },\n }\n}\n","/**\n * Drumkit presets โ€” *kit ํŒจ์น˜*. ํŠธ๋ž™๋‹น ํ•˜๋‚˜. ๊ฐ ํŠธ๋ž™ ์ธ๋ผ์ธ ๋“œ๋Ÿผ์˜ *๋“ค๋ฆฌ๋Š” ์ƒ์ˆ˜* ๋ฅผ ๊ทธ๋Œ€๋กœ\n * ์˜ฎ๊ธด ๊ฒƒ์ด๋ผ, ๊ฑฐ์˜ ๊ฐ™์•„ ๋ณด์—ฌ๋„(์˜ˆ: batheday 150โ†’45 vs gratis 140โ†’42) ํ•ฉ์น˜์ง€ ์•Š๋Š”๋‹ค โ€”\n * *์†Œ๋ฆฌ ๋ณด์กด* ์ด ๊ธฐ์ค€. dedup ์ด๋“์€ ์—”์ง„(`createDrumKit`)์ด์ง€\n * preset ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋‹ค.\n */\n\nimport type { DrumKitPreset, SnarePreset } from './drumkit'\n\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n// Dry / Wet / Iron โ€” *๋จธ์‹  ์–ดํœ˜* 3 ๋ณ€์ฃผ.\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** dry-room โ€” LinnDrum/DR-55 ์‹ dry punch. tail ์—†๋Š” ๋งˆ๋ฅธ ๋จธ์‹ . */\nexport const DRY_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 135,\n endHz: 48,\n pitchTau: 0.06,\n attack: 0.004,\n attackCurve: 'lin',\n decay: 0.32,\n stop: 0.4,\n },\n },\n snare: {\n tone: { type: 'triangle', startHz: 220, level: 0.45, decay: 0.14, stop: 0.18 },\n noise: { bpHz: 1600, q: 0.8, prMin: 0.6, prMax: 1.0, level: 0.5, decay: 0.18, stop: 0.22 },\n },\n hat: {\n hpHz: 7200,\n hpQ: 0.6,\n prCenter: 1.0,\n prSpread: 0.1,\n attack: 0.002,\n closedDecay: 0.04,\n openDecay: 0.22,\n closedLevel: 1,\n openLevel: 1,\n stopPad: 0.05,\n },\n}\n\n/** wet-room โ€” ๊นŠ๊ณ  ๊ธด sine kick + *gated reverb* snare(In the Air Tonight). */\nexport const WET_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 110,\n endHz: 42,\n pitchTau: 0.09,\n attack: 0.006,\n attackCurve: 'lin',\n decay: 0.5,\n stop: 0.55,\n },\n },\n snare: {\n tone: { type: 'triangle', startHz: 200, level: 0.5, decay: 0.12, stop: 0.18 },\n noise: { bpHz: 1800, q: 0.9, prMin: 1.0, prMax: 1.0, level: 0.6, decay: 0.14, stop: 0.18 },\n gatedTails: [\n { centerHz: 1400, q: 1.2, level: 0.55, plateau: 0.18, cut: 0.02 },\n { centerHz: 450, q: 1.0, level: 0.4, plateau: 0.18, cut: 0.02 },\n ],\n },\n}\n\n/** iron-pulse โ€” ์‚ฐ์—… sub ํŽŒํ”„ kick(+click) + ๊ฑฐ์นœ highpass grit(hat). */\nexport const IRON_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 95,\n endHz: 38,\n pitchTau: 0.05,\n attack: 0.003,\n attackCurve: 'lin',\n decay: 0.28,\n stop: 0.35,\n },\n click: { hpHz: 2200, level: 0.35, attack: 0.001, attackCurve: 'lin', decay: 0.012, stop: 0.03 },\n },\n // grit = ๊ฑฐ์นœ highpass noise. open ๋ชจ๋“œ ์—†์ด closed ๋งŒ ํ˜ธ์ถœ โ€” openDecay = closedDecay.\n hat: {\n hpHz: 6500,\n hpQ: 0.5,\n prCenter: 1.0,\n prSpread: 0.3,\n attack: 0.001,\n closedDecay: 0.035,\n openDecay: 0.035,\n closedLevel: 1,\n openLevel: 1,\n stopPad: 0.045,\n },\n}\n\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n// ๋ธ”๋ž™๊ฒŒ์ด์ฆˆ(bathedayยทsublime-hate) โ€” kick(+click) + snare. crash ๋Š” ์ธ๋ผ์ธ.\n// batheday === sublime-hate(์™„์ „ ๋™์ผ). gratis ๋Š” ๋ณธ ์ถ”์ถœ ์ œ์™ธ(์•„๋ž˜ NOTE).\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** batheday/gratis/sublime ๊ณตํ†ต snare โ€” bp1800 noise + ํ”ผ์น˜ envelope tone. */\nconst BLACKGAZE_SNARE: SnarePreset = {\n noise: { bpHz: 1800, q: 1.3, prMin: 1.0, prMax: 1.0, level: 0.6, decay: 0.2, stop: 0.25 },\n tone: {\n type: 'triangle',\n startHz: 260,\n endHz: 160,\n pitchTau: 0.08,\n level: 0.32,\n decay: 0.13,\n stop: 0.18,\n },\n}\n\n/** batheday โ€” ๋ธ”๋ž™๊ฒŒ์ด์ฆˆ ๊ณ„์—ด ๊ฒฐ์˜ kick/snare. */\nexport const BATHEDAY_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 150,\n endHz: 45,\n pitchTau: 0.07,\n attack: 0.004,\n attackCurve: 'lin',\n decay: 0.34,\n stop: 0.4,\n },\n click: { hpHz: 2200, level: 0.28, attack: 0.001, attackCurve: 'lin', decay: 0.01, stop: 0.03 },\n },\n snare: BLACKGAZE_SNARE,\n}\n\n/** sublime-hate โ€” kick/snare ๊ฐ€ batheday ์™€ ์™„์ „ ๋™์ผ. */\nexport const SUBLIME_KIT: DrumKitPreset = BATHEDAY_KIT\n\n// NOTE: gratis ๋Š” ๋ณธ ์ถ”์ถœ์—์„œ ์ œ์™ธ โ€” gratis.ts ์˜ ์ธ๋ผ์ธ ๋“œ๋Ÿผ ์œ ์ง€. ์Šน๊ฒฉ ์‹œ GRATIS_KIT ์ถ”๊ฐ€\n// (kick ๋งŒ ๋ฏธ์„ธ์ฐจ: body 140โ†’42, click level 0.26. snare ๋Š” BLACKGAZE_SNARE ๊ณตํ†ต).\n\n/** reveal-the-truth โ€” IDM ๊ณ„์—ด์˜ *snappy* kick(+click) + snare + hat. */\nexport const REVEAL_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 140,\n endHz: 42,\n pitchTau: 0.07,\n attack: 0.004,\n attackCurve: 'lin',\n decay: 0.38,\n stop: 0.45,\n },\n click: { hpHz: 1800, level: 0.25, attack: 0.001, attackCurve: 'lin', decay: 0.01, stop: 0.03 },\n },\n snare: {\n noise: { bpHz: 1700, q: 1.4, prMin: 1.0, prMax: 1.0, level: 0.55, decay: 0.18, stop: 0.25 },\n tone: {\n type: 'triangle',\n startHz: 240,\n endHz: 150,\n pitchTau: 0.08,\n level: 0.3,\n decay: 0.12,\n stop: 0.18,\n },\n },\n hat: {\n hpHz: 7200,\n prCenter: 1.0,\n prSpread: 0.3,\n attack: 0.001,\n closedDecay: 0.025,\n openDecay: 0.12,\n closedLevel: 1,\n openLevel: 1,\n stopPad: 0.02,\n },\n}\n\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n// Dub(relayยทholyยทpower) โ€” sub tail kick(exp attack, peak>velocity) + click.\n// duck rampยทclapยทrimยทshimmer ๋Š” ํŠธ๋ž™ ์ธ๋ผ์ธ(์—”์ง„ ๋ฐ–). dub-relay/holy ๋Š” hat ๋ฏธ์‚ฌ์šฉ.\n// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** dub-relay โ€” 144 BPM, 2-body sub kick. */\nexport const DUB_RELAY_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 130,\n endHz: 48,\n pitchTau: 0.08,\n attack: 0.004,\n attackCurve: 'exp',\n peakMul: 1.3,\n decay: 0.6,\n stop: 0.65,\n },\n sub: {\n startHz: 60,\n endHz: 38,\n pitchTau: 0.12,\n attack: 0.008,\n attackCurve: 'exp',\n peakMul: 0.95,\n decay: 0.45,\n stop: 0.5,\n },\n click: { hpHz: 2500, level: 0.4, attack: 0.001, attackCurve: 'exp', decay: 0.02, stop: 0.04 },\n },\n hat: {\n hpHz: 7800,\n bpHz: 9000,\n bpQ: 0.9,\n prCenter: 1.0,\n prSpread: 0.05,\n attack: 0.002,\n closedDecay: 0.035,\n openDecay: 0.13,\n closedLevel: 0.22,\n openLevel: 0.32,\n stopPad: 0.02,\n },\n}\n\n/** dub-holy โ€” 70 BPM half-time, *๋” ๊นŠ๊ณ  ๊ธด* sub kick. hat ๋ฏธ์‚ฌ์šฉ. */\nexport const DUB_HOLY_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 140,\n endHz: 44,\n pitchTau: 0.1,\n attack: 0.005,\n attackCurve: 'exp',\n peakMul: 1.4,\n decay: 0.85,\n stop: 0.9,\n },\n sub: {\n startHz: 56,\n endHz: 36,\n pitchTau: 0.15,\n attack: 0.01,\n attackCurve: 'exp',\n peakMul: 1.05,\n decay: 0.7,\n stop: 0.75,\n },\n click: {\n hpHz: 2200,\n level: 0.32,\n attack: 0.0015,\n attackCurve: 'exp',\n decay: 0.025,\n stop: 0.05,\n },\n },\n}\n\n/** dub-power โ€” 78 BPM, *๊ถŒ์œ„์˜ ๋ฐ•* kick + hat. */\nexport const DUB_POWER_KIT: DrumKitPreset = {\n kick: {\n body: {\n startHz: 140,\n endHz: 45,\n pitchTau: 0.1,\n attack: 0.005,\n attackCurve: 'exp',\n peakMul: 1.35,\n decay: 0.75,\n stop: 0.8,\n },\n sub: {\n startHz: 64.6,\n endHz: 38,\n pitchTau: 0.15,\n attack: 0.01,\n attackCurve: 'exp',\n peakMul: 1.05,\n decay: 0.6,\n stop: 0.65,\n },\n click: { hpHz: 2200, level: 0.32, attack: 0.001, attackCurve: 'exp', decay: 0.025, stop: 0.04 },\n },\n hat: {\n hpHz: 6500,\n bpHz: 7200,\n bpQ: 0.85,\n prCenter: 1.0,\n prSpread: 0.06,\n attack: 0.002,\n closedDecay: 0.045,\n openDecay: 0.22,\n closedLevel: 0.24,\n openLevel: 0.34,\n stopPad: 0.02,\n },\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Dub chamber โ€” dub-relayยทdub-holyยทdub-power ๊ฐ€ ๊ธ€์ž ๊ทธ๋Œ€๋กœ ๋ณต์ œํ•˜๋˜ *๋งˆ์Šคํ„ฐ/๋ฏน์Šค ์ฑ”๋ฒ„* ๋ฅผ ํ•œ
3
+ * ๋ชจ๋“ˆ๋กœ. drumkitยทsunwall ๊ณผ ๊ฐ™์€ VST ๋ชจ๋ธ: `createDubChamber` ์€ *์ƒ‰-๋ถˆ๋ฌธ ์ฑ”๋ฒ„ ์—”์ง„*,
4
+ * `*_CHAMBER` ์€ *ํŒจ์น˜*. ์ฑ”๋ฒ„๋Š” *์„ธ ๋‹จ์˜ ์ง๋ ฌ ๋ฏน์Šค* ๋งŒ ์†Œ์œ ํ•œ๋‹ค โ€”
5
+ *
6
+ * โ‘  ์‚ฌ์ด๋“œ์ฒด์ธ ducking : kick ์ด ๋–จ์–ด์ง€๋ฉด chordยทpadยทsub(=`ductable`)๊ฐ€ ์ž ๊น ๋ˆŒ๋ฆฐ๋‹ค.
7
+ * drum/perc(=`drumBus`)๋Š” ์šฐํšŒํ•ด *์ง๊ฒฉ* ์œผ๋กœ ์นœ๋‹ค.
8
+ * โ‘ก dub delay return : cross-feedback stereo delay โ†’ HP+LP โ†’ return โ†’ ductable(๋ฅ tail ๋„ ํ˜ธํก).
9
+ * send ๋Ÿ‰์˜ *per-section automation* ์€ ํŠธ๋ž™์— ๋‚จ๋Š”๋‹ค(`dubSend` ๋…ธ๋“œ๋งŒ ๋…ธ์ถœ).
10
+ * โ‘ข tape saturation : soft-clip(tanh) waveshaper โ†’ ๋งˆ์Šคํ„ฐ lowpass โ†’ masterOut โ†’ destination.
11
+ *
12
+ * ์ฑ”๋ฒ„ ๋ฐ–(ํŠธ๋ž™)์— ๋‚จ๋Š” ๊ฒƒ: voice ํ•ฉ์„ฑ(kickยทchordยทpadยทโ€ฆ), dubSend ์˜ section ๊ณก์„ , 16th ์‹œํ€€์Šค.
13
+ * 16th lookahead ๋ฃจํ”„๋Š” `../scheduler` ์˜ `runStepClock` ์œผ๋กœ ๋”ฐ๋กœ ์ถ”์ถœ๋๋‹ค.
14
+ * ๋ณด์กด ๊ธฐ์ค€: ๋…ธ๋“œ ํ† ํด๋กœ์ง€ยท์ƒ์ˆ˜ ๊ทธ๋Œ€๋กœ โ€” ์ˆœ์ˆ˜ ์ถ”์ถœ.
15
+ */
16
+ /** ๋งˆ์Šคํ„ฐ ๋‹จ โ€” tape lowpass + soft-clip saturation. */
17
+ interface MasterStage {
18
+ /** tape lowpass ์ปท(Hz). relay/power 12k, holy 14k. */
19
+ lowpassHz: number;
20
+ /** tanh saturation drive. relay 2.4, holy 2.0, power 2.2. */
21
+ satDrive: number;
22
+ }
23
+ /** ๋งˆ์Šคํ„ฐ fade in/out envelope. */
24
+ interface FadeStage {
25
+ /** 0 โ†’ 1 ๊นŒ์ง€ linear ramp ์‹œ๊ฐ„(์ดˆ). */
26
+ inSec: number;
27
+ /** ๋๋‚˜๊ธฐ `outLeadSec` ์ดˆ ์ „๋ถ€ํ„ฐ exp ramp โ†’ 0. */
28
+ outLeadSec: number;
29
+ }
30
+ /** kick ์‚ฌ์ด๋“œ์ฒด์ธ duck โ€” ductable ์„ ๋ˆŒ๋ €๋‹ค ๋ณต๊ท€. */
31
+ interface DuckStage {
32
+ /** ๋ˆŒ๋ฆฌ๋Š” ์ตœ์ € gain. relay 0.7(์™„๋งŒ), holy/power 0.55(๊นŠ๊ฒŒ). */
33
+ min: number;
34
+ /** min ๋„๋‹ฌ ์‹œ๊ฐ„(์ดˆ). */
35
+ attackSec: number;
36
+ /** 1.0 ๋ณต๊ท€ ์™„๋ฃŒ ์‹œ๊ฐ„(์ดˆ). half-time ์ผ์ˆ˜๋ก ๊ธธ๋‹ค. */
37
+ recoverSec: number;
38
+ }
39
+ /** dub delay return โ€” cross-feedback stereo delay + HP/LP tail. */
40
+ interface DelayStage {
41
+ /** DelayNode maxDelayTime. */
42
+ maxSec: number;
43
+ /** L delay(์ ์Œ 8๋ถ„), R delay(4๋ถ„). */
44
+ timeL: number;
45
+ timeR: number;
46
+ /** cross feedback gain. */
47
+ feedback: number;
48
+ /** tail HP(sub ๋ถ„๋ฆฌ) / LP(dark dub). */
49
+ hpHz: number;
50
+ lpHz: number;
51
+ /** return gain โ€” ๋‘๊ป˜. */
52
+ returnGain: number;
53
+ }
54
+ interface DubChamberPreset {
55
+ master: MasterStage;
56
+ fade: FadeStage;
57
+ duck: DuckStage;
58
+ delay: DelayStage;
59
+ }
60
+ interface DubChamber {
61
+ /** chordยทpadยทsubยทreese ๊ฐ€ ๋ชจ์ด๋Š” *ducked* bus. */
62
+ readonly ductable: GainNode;
63
+ /** drumยทperc ๊ฐ€ ๋ชจ์ด๋Š” *duck ์šฐํšŒ* bus. */
64
+ readonly drumBus: GainNode;
65
+ /** dub delay send. ํŠธ๋ž™์ด section ๋ณ„๋กœ gain automation. */
66
+ readonly dubSend: GainNode;
67
+ /** kick ๋งˆ๋‹ค ํ˜ธ์ถœ โ€” ductable ์„ ๋ˆŒ๋ €๋‹ค ๋ณต๊ท€(์‚ฌ์ด๋“œ์ฒด์ธ). */
68
+ duck(when: number): void;
69
+ /** ๋งˆ์Šคํ„ฐ fade in/out ์„ ํ•œ ๋ฒˆ์— ์Šค์ผ€์ค„. start=๊ทธ๋ฆฌ๋“œ ์›์ , totalSec=๊ณก ๊ธธ์ด. */
70
+ open(start: number, totalSec: number): void;
71
+ /** teardown โ€” ๋น ๋ฅธ fade out ํ›„ 250 ms ๋’ค ์ฑ”๋ฒ„ ๋…ธ๋“œ ์ „๋ถ€ disconnect. */
72
+ dispose(): void;
73
+ }
74
+ /**
75
+ * dub ๋งˆ์Šคํ„ฐ/๋ฏน์Šค ์ฑ”๋ฒ„๋ฅผ ๋งŒ๋“ค์–ด ํ•ธ๋“ค์„ ๋Œ๋ ค์ค€๋‹ค. ํŠธ๋ž™ voice ๋Š” `ductable`ยท`drumBus`ยท`dubSend`
76
+ * ์— ์—ฐ๊ฒฐํ•˜๊ณ , kick ๋งˆ๋‹ค `duck(when)`, ์‹œ์ž‘์— `open(start, total)`, teardown ์— `dispose()`.
77
+ *
78
+ * @param destination ํŠธ๋ž™ score ๊ฐ€ ๋ฐ›์€ ์ตœ์ข… ๋ชฉ์ ์ง€(AudioContext.destination ๋“ฑ).
79
+ */
80
+ declare function createDubChamber(ctx: AudioContext, destination: AudioNode, preset: DubChamberPreset): DubChamber;
81
+
82
+ /**
83
+ * Dub chamber presets โ€” *์ฑ”๋ฒ„ ํŒจ์น˜*. ํŠธ๋ž™๋‹น ํ•˜๋‚˜. ๊ฐ dub ํŠธ๋ž™ ์ธ๋ผ์ธ ๋งˆ์Šคํ„ฐ/๋ฏน์Šค ์ฑ”๋ฒ„์˜ *๋“ค๋ฆฌ๋Š”
84
+ * ์ƒ์ˆ˜* ๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธด ๊ฒƒ์ด๋ผ, ๊ฑฐ์˜ ๊ฐ™์•„ ๋ณด์—ฌ๋„ ํ•ฉ์น˜์ง€ ์•Š๋Š”๋‹ค โ€” *์†Œ๋ฆฌ ๋ณด์กด* ์ด ๊ธฐ์ค€.
85
+ * dedup ์ด๋“์€ ์—”์ง„(`createDubChamber`)์ด์ง€ preset ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋‹ค.
86
+ *
87
+ * delay time ์€ *BPM ์˜ ์ ์Œ 8๋ถ„/4๋ถ„* ์ด๋ผ ํŠธ๋ž™๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค(144 / 70 / 78 BPM).
88
+ */
89
+
90
+ /** dub-relay โ€” 144 BPM. ์–•์€ duck(0.7, ๋ฒ ์ด์Šค *์ง€์† ์••๋ ฅ*) + ์งง์€ 313 ms echo. */
91
+ declare const DUB_RELAY_CHAMBER: DubChamberPreset;
92
+ /** dub-holy โ€” 70 BPM half-time. *๊นŠ๊ณ  ๊ธด* duck(0.55 / 0.45 s) + 643 ms ๋‘๊บผ์šด echo. */
93
+ declare const DUB_HOLY_CHAMBER: DubChamberPreset;
94
+ /** dub-power โ€” 78 BPM. ๊นŠ๊ณ  ๊ธด duck(0.55 / 0.32 s) + 577 ms *๊ถŒ์œ„์˜ ์ž”ํ–ฅ*. */
95
+ declare const DUB_POWER_CHAMBER: DubChamberPreset;
96
+ /**
97
+ * crush(22) โ€” 66 BPM sludge. *์„ธ ์—”์ง„ ๋™์‹œ ์ตœ๋Œ€์น˜* ์˜ ๋งˆ์Šคํ„ฐ(๋ฒฝ ํฌ๋ ˆ์…ด๋„ + ์‡  ๋ฐ• + ๋ฅ ์ž”ํ–ฅ).
98
+ * ๊ฐ€์žฅ ๊ฑฐ์นœ saturation(2.6) + ๊ฐ€์žฅ ์–ด๋‘์šด LP(8000) โ€” ๊ฒ€์€ ๋ฒฝ์˜ ๊ฑฐ์นœ ๊ณ ์—ญ์„ ํ•œ ๊ฒน ๋” ๊นŽ๋Š”๋‹ค.
99
+ * duck ์€ ์–•๊ฒŒ(0.6) โ€” *์‡ * kick ์ด ๋ฒฝ์„ ๋šซ๋˜ ๋ฒฝ์„ ๋ฌด๋„ˆ๋œจ๋ฆฌ์ง„ ์•Š๊ฒŒ. 682 ms *์ง“๋ˆ„๋ฅด๋Š”* dub tail.
100
+ */
101
+ declare const CRUSH_CHAMBER: DubChamberPreset;
102
+
103
+ export { CRUSH_CHAMBER, DUB_HOLY_CHAMBER, DUB_POWER_CHAMBER, DUB_RELAY_CHAMBER, type DelayStage, type DubChamber, type DubChamberPreset, type DuckStage, type FadeStage, type MasterStage, createDubChamber };