@jpdutoit/squelchy 0.0.1
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/LICENSE +21 -0
- package/README.md +73 -0
- package/chunk-NDUSYSHF.js +87 -0
- package/chunk-NDUSYSHF.js.map +7 -0
- package/docs/MODULES.md +1244 -0
- package/index.d.ts +1946 -0
- package/index.js +4846 -0
- package/index.js.map +7 -0
- package/meta.json +4492 -0
- package/package.json +43 -0
- package/worklets-inline-MUVFLGPY.js +60 -0
- package/worklets-inline-MUVFLGPY.js.map +7 -0
package/index.js
ADDED
|
@@ -0,0 +1,4846 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adsr_envelope_default,
|
|
3
|
+
bitcrusher_default,
|
|
4
|
+
comb_filter_default,
|
|
5
|
+
env_follower_default,
|
|
6
|
+
fdn_reverb_default,
|
|
7
|
+
flanger_default,
|
|
8
|
+
formant_table_default,
|
|
9
|
+
freq_divider_default,
|
|
10
|
+
freq_shifter_default,
|
|
11
|
+
glide_default,
|
|
12
|
+
ladder_filter_default,
|
|
13
|
+
map_res_default,
|
|
14
|
+
map_voct_to_freq_default,
|
|
15
|
+
map_voct_to_period_default,
|
|
16
|
+
ping_pong_default,
|
|
17
|
+
pitch_shifter_default,
|
|
18
|
+
spring_reverb_default,
|
|
19
|
+
tape_delay_default,
|
|
20
|
+
tape_stop_default,
|
|
21
|
+
vca_default,
|
|
22
|
+
waveshaper_default
|
|
23
|
+
} from "./chunk-NDUSYSHF.js";
|
|
24
|
+
|
|
25
|
+
// src/lib-runtime/core.ts
|
|
26
|
+
var pendingSources = {};
|
|
27
|
+
var registrations = /* @__PURE__ */ new WeakMap();
|
|
28
|
+
function preload(ctx, opts) {
|
|
29
|
+
const existing = registrations.get(ctx);
|
|
30
|
+
if (existing) return existing;
|
|
31
|
+
const sources = opts?.allWorklets ? import("./worklets-inline-MUVFLGPY.js").then((m) => m.WORKLET_SOURCES) : Promise.resolve(pendingSources);
|
|
32
|
+
const promise = sources.then(
|
|
33
|
+
(map) => Promise.all(Object.entries(map).map(async ([name, src]) => {
|
|
34
|
+
const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
|
|
35
|
+
try {
|
|
36
|
+
await ctx.audioWorklet.addModule(url);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
throw new Error(`squelchy: failed to register worklet "${name}": ${err}`);
|
|
39
|
+
} finally {
|
|
40
|
+
URL.revokeObjectURL(url);
|
|
41
|
+
}
|
|
42
|
+
}))
|
|
43
|
+
).then(() => {
|
|
44
|
+
});
|
|
45
|
+
registrations.set(ctx, promise);
|
|
46
|
+
return promise;
|
|
47
|
+
}
|
|
48
|
+
var instanceCounter = 0;
|
|
49
|
+
function defineModule(desc, opts) {
|
|
50
|
+
if (opts?.worklets) Object.assign(pendingSources, opts.worklets);
|
|
51
|
+
const meta = { type: desc.type, ports: desc.ports, params: desc.params ?? {} };
|
|
52
|
+
return {
|
|
53
|
+
meta,
|
|
54
|
+
create(ctx, params50 = {}) {
|
|
55
|
+
const module = { ...desc.createModule(`lib-${desc.type}-${instanceCounter++}`, { x: 0, y: 0 }), ...params50 };
|
|
56
|
+
const nodes = desc.createNodes(ctx, module);
|
|
57
|
+
return withPortProperties(new Instance(desc, module, nodes), desc);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
var RESERVED_PROPS = /* @__PURE__ */ new Set(["input", "output", "set", "dispose", "params", "then"]);
|
|
62
|
+
function withPortProperties(inst, desc) {
|
|
63
|
+
const props = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const [slug, p] of Object.entries(desc.ports)) {
|
|
65
|
+
if (RESERVED_PROPS.has(slug) || props.has(slug)) continue;
|
|
66
|
+
props.set(slug, p.type === "output" ? () => inst.output(slug) : () => inst.input(slug));
|
|
67
|
+
}
|
|
68
|
+
return new Proxy(inst, {
|
|
69
|
+
get(target, key, receiver) {
|
|
70
|
+
if (typeof key === "string") {
|
|
71
|
+
const hit = props.get(key);
|
|
72
|
+
if (hit) return hit();
|
|
73
|
+
}
|
|
74
|
+
return Reflect.get(target, key, receiver);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
var Instance = class {
|
|
79
|
+
constructor(desc, module, nodes) {
|
|
80
|
+
this.desc = desc;
|
|
81
|
+
this.module = module;
|
|
82
|
+
this.nodes = nodes;
|
|
83
|
+
}
|
|
84
|
+
port(slug) {
|
|
85
|
+
const p = this.desc.ports[slug];
|
|
86
|
+
if (!p) throw new Error(`squelchy: unknown port "${slug}" on ${this.desc.type}`);
|
|
87
|
+
return p;
|
|
88
|
+
}
|
|
89
|
+
nodeFor(slug) {
|
|
90
|
+
const p = this.port(slug);
|
|
91
|
+
const node = p.node ? this.nodes[p.node] : void 0;
|
|
92
|
+
if (!node) throw new Error(`squelchy: port "${slug}" on ${this.desc.type} has no audio node`);
|
|
93
|
+
return node;
|
|
94
|
+
}
|
|
95
|
+
input(slug) {
|
|
96
|
+
const p = this.port(slug);
|
|
97
|
+
const node = this.nodeFor(slug);
|
|
98
|
+
if (!p.field) return node;
|
|
99
|
+
return this.resolveParam(p.node, p.field, `port "${slug}"`);
|
|
100
|
+
}
|
|
101
|
+
output(slug) {
|
|
102
|
+
return this.nodeFor(slug);
|
|
103
|
+
}
|
|
104
|
+
/** Native AudioParams behind params declaring `node` + `field` (1:1 mapped,
|
|
105
|
+
* no conversion) — full Web Audio automation: ramps, curves, CV. */
|
|
106
|
+
get params() {
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const [key, spec] of Object.entries(this.desc.params ?? {})) {
|
|
109
|
+
if (!spec.node || !spec.field) continue;
|
|
110
|
+
Object.defineProperty(out, key, { enumerable: true, get: () => this.resolveParam(spec.node, spec.field, `param "${key}"`) });
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
resolveParam(nodeKey, field, what) {
|
|
115
|
+
const node = this.nodes[nodeKey];
|
|
116
|
+
const param = node?.[field] ?? node?.parameters?.get(field);
|
|
117
|
+
if (!param || typeof param.setValueAtTime !== "function") throw new Error(`squelchy: ${what} on ${this.desc.type} has no AudioParam "${nodeKey}.${field}"`);
|
|
118
|
+
return param;
|
|
119
|
+
}
|
|
120
|
+
set(key, value) {
|
|
121
|
+
this.module[key] = value;
|
|
122
|
+
this.desc.updateParam(this.nodes, this.module, key, value);
|
|
123
|
+
}
|
|
124
|
+
dispose() {
|
|
125
|
+
this.desc.dispose?.(this.nodes);
|
|
126
|
+
for (const node of Object.values(this.nodes)) {
|
|
127
|
+
if (typeof node.stop === "function") {
|
|
128
|
+
try {
|
|
129
|
+
node.stop();
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
node.disconnect();
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/lib-runtime/time/transport.ts
|
|
142
|
+
var EPS = 1e-9;
|
|
143
|
+
function isRest(v) {
|
|
144
|
+
return v == null || v === false || v === 0 || v === "." || v === "-";
|
|
145
|
+
}
|
|
146
|
+
function normStep(s) {
|
|
147
|
+
if (isRest(s)) return null;
|
|
148
|
+
if (typeof s === "object" && s !== null && "value" in s) {
|
|
149
|
+
const o = s;
|
|
150
|
+
return isRest(o.value) ? null : o;
|
|
151
|
+
}
|
|
152
|
+
return { value: s };
|
|
153
|
+
}
|
|
154
|
+
function callVoice(voice2, hit) {
|
|
155
|
+
if (typeof voice2 === "function") voice2(hit.time, hit);
|
|
156
|
+
else voice2.trigger(hit.time, hit);
|
|
157
|
+
}
|
|
158
|
+
function createTransport(ctx, opts = {}) {
|
|
159
|
+
const beatsPerBar = opts.beatsPerBar ?? 4;
|
|
160
|
+
const lookahead = opts.lookahead ?? 0.1;
|
|
161
|
+
const offline = typeof ctx.length === "number" && typeof ctx.close !== "function";
|
|
162
|
+
let bpm = opts.bpm ?? 120;
|
|
163
|
+
let aBeat = 0;
|
|
164
|
+
let aTime = 0;
|
|
165
|
+
let running = false;
|
|
166
|
+
let horizon = 0;
|
|
167
|
+
let timer = null;
|
|
168
|
+
const jobs = /* @__PURE__ */ new Set();
|
|
169
|
+
const taps = /* @__PURE__ */ new Set();
|
|
170
|
+
const tapTimers = /* @__PURE__ */ new Set();
|
|
171
|
+
const spb = () => 60 / bpm;
|
|
172
|
+
const beatAt = (t2) => aBeat + (t2 - aTime) / spb();
|
|
173
|
+
const timeAt = (b) => aTime + (b - aBeat) * spb();
|
|
174
|
+
const current = () => running ? Math.max(0, beatAt(ctx.currentTime + 5e-3)) : 0;
|
|
175
|
+
function resolve(at) {
|
|
176
|
+
return resolveNum(at);
|
|
177
|
+
}
|
|
178
|
+
function resolveNum(at) {
|
|
179
|
+
const now2 = current();
|
|
180
|
+
if (at === "now") return now2;
|
|
181
|
+
if (at === "beat") return Math.ceil(now2 - EPS);
|
|
182
|
+
if (at === "bar") return Math.ceil(now2 / beatsPerBar - EPS) * beatsPerBar;
|
|
183
|
+
if ("bar" in at) return at.bar * beatsPerBar;
|
|
184
|
+
return at.beat;
|
|
185
|
+
}
|
|
186
|
+
const latency = () => ctx.outputLatency || ctx.baseLatency || 0;
|
|
187
|
+
function emitTap(hit, local) {
|
|
188
|
+
if (!local && taps.size === 0) return;
|
|
189
|
+
const fire = () => {
|
|
190
|
+
local?.(hit);
|
|
191
|
+
for (const cb of taps) cb(hit);
|
|
192
|
+
};
|
|
193
|
+
if (offline) {
|
|
194
|
+
fire();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const delay = Math.max(0, (hit.time - ctx.currentTime + latency()) * 1e3);
|
|
198
|
+
const id = setTimeout(() => {
|
|
199
|
+
tapTimers.delete(id);
|
|
200
|
+
fire();
|
|
201
|
+
}, delay);
|
|
202
|
+
tapTimers.add(id);
|
|
203
|
+
}
|
|
204
|
+
function horizonBeat() {
|
|
205
|
+
if (offline) return beatAt(ctx.length / ctx.sampleRate);
|
|
206
|
+
return beatAt(ctx.currentTime + lookahead);
|
|
207
|
+
}
|
|
208
|
+
function tick() {
|
|
209
|
+
if (!running) return;
|
|
210
|
+
const until = horizonBeat();
|
|
211
|
+
for (const job of jobs) if (!job.run(until)) jobs.delete(job);
|
|
212
|
+
horizon = Math.max(horizon, until);
|
|
213
|
+
}
|
|
214
|
+
const t = {
|
|
215
|
+
ctx,
|
|
216
|
+
beatsPerBar,
|
|
217
|
+
get bpm() {
|
|
218
|
+
return bpm;
|
|
219
|
+
},
|
|
220
|
+
set bpm(v) {
|
|
221
|
+
if (!(v > 0)) throw new RangeError("bpm must be > 0");
|
|
222
|
+
if (running) {
|
|
223
|
+
const b = Math.max(horizon, beatAt(ctx.currentTime));
|
|
224
|
+
aTime = timeAt(b);
|
|
225
|
+
aBeat = b;
|
|
226
|
+
}
|
|
227
|
+
bpm = v;
|
|
228
|
+
},
|
|
229
|
+
get running() {
|
|
230
|
+
return running;
|
|
231
|
+
},
|
|
232
|
+
start(when = ctx.currentTime + 0.05) {
|
|
233
|
+
if (running) return;
|
|
234
|
+
aBeat = 0;
|
|
235
|
+
aTime = when;
|
|
236
|
+
horizon = 0;
|
|
237
|
+
running = true;
|
|
238
|
+
tick();
|
|
239
|
+
if (!offline && opts.clock !== "manual") timer = setInterval(tick, 25);
|
|
240
|
+
},
|
|
241
|
+
stop() {
|
|
242
|
+
running = false;
|
|
243
|
+
if (timer) clearInterval(timer);
|
|
244
|
+
timer = null;
|
|
245
|
+
for (const id of tapTimers) clearTimeout(id);
|
|
246
|
+
tapTimers.clear();
|
|
247
|
+
jobs.clear();
|
|
248
|
+
},
|
|
249
|
+
beatAt,
|
|
250
|
+
timeAt,
|
|
251
|
+
resolve,
|
|
252
|
+
play(pattern, voice2, o = {}) {
|
|
253
|
+
const every = o.every ?? 0.25;
|
|
254
|
+
const gate = o.gate ?? 0.5;
|
|
255
|
+
const swing = o.swing ?? 0;
|
|
256
|
+
const maxCycles = o.loop === false ? 1 : o.loop === true || o.loop === void 0 ? Infinity : o.loop;
|
|
257
|
+
let pat = pattern;
|
|
258
|
+
let pending = null;
|
|
259
|
+
const startBeat = resolve(o.start ?? "bar");
|
|
260
|
+
let stopBeat = Infinity;
|
|
261
|
+
let k = 0;
|
|
262
|
+
let done = false;
|
|
263
|
+
const stepBeat = (i) => startBeat + i * every + (i % 2 === 1 ? swing * every * 0.5 : 0);
|
|
264
|
+
const job = {
|
|
265
|
+
nextBeat: () => stepBeat(k),
|
|
266
|
+
run(until) {
|
|
267
|
+
while (!done) {
|
|
268
|
+
if (pat.length === 0) {
|
|
269
|
+
done = true;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
const idx = k % pat.length;
|
|
273
|
+
if (idx === 0 && pending) {
|
|
274
|
+
pat = pending;
|
|
275
|
+
pending = null;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const cycle = Math.floor(k / pat.length);
|
|
279
|
+
const b = stepBeat(k);
|
|
280
|
+
if (cycle >= maxCycles || b >= stopBeat - EPS) {
|
|
281
|
+
done = true;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
if (b >= until) break;
|
|
285
|
+
const s = normStep(pat[idx]);
|
|
286
|
+
if (s) {
|
|
287
|
+
const time = timeAt(b);
|
|
288
|
+
const hit = {
|
|
289
|
+
time,
|
|
290
|
+
beat: b,
|
|
291
|
+
step: idx,
|
|
292
|
+
cycle,
|
|
293
|
+
value: s.value,
|
|
294
|
+
velocity: s.velocity ?? 1,
|
|
295
|
+
duration: (s.length ?? gate) * every * spb()
|
|
296
|
+
};
|
|
297
|
+
callVoice(voice2, hit);
|
|
298
|
+
emitTap(hit, o.onHit);
|
|
299
|
+
}
|
|
300
|
+
k++;
|
|
301
|
+
}
|
|
302
|
+
return !done;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
jobs.add(job);
|
|
306
|
+
if (running) job.run(horizonBeat());
|
|
307
|
+
return {
|
|
308
|
+
set(p) {
|
|
309
|
+
pending = p;
|
|
310
|
+
},
|
|
311
|
+
stop(at) {
|
|
312
|
+
if (at) stopBeat = resolve(at);
|
|
313
|
+
else {
|
|
314
|
+
done = true;
|
|
315
|
+
jobs.delete(job);
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
get done() {
|
|
319
|
+
return done;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
at(when, fn) {
|
|
324
|
+
const b = typeof when === "number" ? when : resolve(when);
|
|
325
|
+
let fired = false;
|
|
326
|
+
const job = {
|
|
327
|
+
nextBeat: () => b,
|
|
328
|
+
run(until) {
|
|
329
|
+
if (fired) return false;
|
|
330
|
+
if (b < until) {
|
|
331
|
+
fired = true;
|
|
332
|
+
fn(timeAt(b));
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
jobs.add(job);
|
|
339
|
+
if (running) tick();
|
|
340
|
+
return () => {
|
|
341
|
+
fired = true;
|
|
342
|
+
jobs.delete(job);
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
onHit(cb) {
|
|
346
|
+
taps.add(cb);
|
|
347
|
+
return () => {
|
|
348
|
+
taps.delete(cb);
|
|
349
|
+
};
|
|
350
|
+
},
|
|
351
|
+
tick,
|
|
352
|
+
dispose() {
|
|
353
|
+
t.stop();
|
|
354
|
+
taps.clear();
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
return t;
|
|
358
|
+
}
|
|
359
|
+
function steps(s) {
|
|
360
|
+
return [...s.replace(/[\s|]/g, "")].map((c) => c === "x" || c === "X");
|
|
361
|
+
}
|
|
362
|
+
var NOTE_RE = /^([A-Ga-g])([#b]?)(-?\d+)$/;
|
|
363
|
+
var PC = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 };
|
|
364
|
+
function note(name) {
|
|
365
|
+
if (typeof name === "number") return name;
|
|
366
|
+
const m = NOTE_RE.exec(name.trim());
|
|
367
|
+
if (!m) throw new Error(`note(): can't parse "${name}"`);
|
|
368
|
+
return (parseInt(m[3], 10) + 1) * 12 + PC[m[1].toLowerCase()] + (m[2] === "#" ? 1 : m[2] === "b" ? -1 : 0);
|
|
369
|
+
}
|
|
370
|
+
function notes(s) {
|
|
371
|
+
return s.trim().split(/\s+/).map((tok) => tok === "." || tok === "-" ? null : note(tok));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/lib/dsp/prng.ts
|
|
375
|
+
function mulberry32(seed) {
|
|
376
|
+
let a = seed >>> 0;
|
|
377
|
+
return () => {
|
|
378
|
+
a = a + 1831565813 | 0;
|
|
379
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
380
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
381
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function noiseSource(seed) {
|
|
385
|
+
const rand = mulberry32(seed);
|
|
386
|
+
return () => rand() * 2 - 1;
|
|
387
|
+
}
|
|
388
|
+
var NOISE_SEEDS = {
|
|
389
|
+
noiseWhite: 2654435769,
|
|
390
|
+
noisePink: 2246822507,
|
|
391
|
+
noiseBrown: 3266489909,
|
|
392
|
+
loFiCrackle: 668265263,
|
|
393
|
+
drumSnare: 374761393,
|
|
394
|
+
drumHat: 3550635116,
|
|
395
|
+
d808Snare: 4251993797,
|
|
396
|
+
d808ClosedHat: 3042594569,
|
|
397
|
+
d808OpenHat: 724231106
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// src/lib-runtime/time/voices.ts
|
|
401
|
+
function voice(output, trigger) {
|
|
402
|
+
return {
|
|
403
|
+
output,
|
|
404
|
+
trigger,
|
|
405
|
+
connect: (d) => {
|
|
406
|
+
output.connect(d);
|
|
407
|
+
return d;
|
|
408
|
+
},
|
|
409
|
+
disconnect: () => output.disconnect()
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function noiseBuffer(ctx, seconds, seed) {
|
|
413
|
+
const n = Math.floor(ctx.sampleRate * seconds);
|
|
414
|
+
const buf = ctx.createBuffer(1, n, ctx.sampleRate);
|
|
415
|
+
const d = buf.getChannelData(0);
|
|
416
|
+
const rand = noiseSource(seed);
|
|
417
|
+
for (let i = 0; i < n; i++) d[i] = rand();
|
|
418
|
+
return buf;
|
|
419
|
+
}
|
|
420
|
+
var vel = (h) => Math.max(0, Math.min(1, h?.velocity ?? 1));
|
|
421
|
+
var Kick = {
|
|
422
|
+
create(ctx) {
|
|
423
|
+
const out = ctx.createGain();
|
|
424
|
+
return voice(out, (when, h) => {
|
|
425
|
+
const osc = ctx.createOscillator();
|
|
426
|
+
const g = ctx.createGain();
|
|
427
|
+
osc.frequency.setValueAtTime(150, when);
|
|
428
|
+
osc.frequency.exponentialRampToValueAtTime(40, when + 0.1);
|
|
429
|
+
g.gain.setValueAtTime(0.8 * vel(h) + 1e-4, when);
|
|
430
|
+
g.gain.exponentialRampToValueAtTime(1e-4, when + 0.3);
|
|
431
|
+
osc.connect(g).connect(out);
|
|
432
|
+
osc.start(when);
|
|
433
|
+
osc.stop(when + 0.3);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
var Snare = {
|
|
438
|
+
create(ctx) {
|
|
439
|
+
const out = ctx.createGain();
|
|
440
|
+
const buf = noiseBuffer(ctx, 0.15, NOISE_SEEDS.drumSnare);
|
|
441
|
+
return voice(out, (when, h) => {
|
|
442
|
+
const v = vel(h);
|
|
443
|
+
const n = ctx.createBufferSource();
|
|
444
|
+
n.buffer = buf;
|
|
445
|
+
const ng = ctx.createGain();
|
|
446
|
+
ng.gain.setValueAtTime(0.4 * v + 1e-4, when);
|
|
447
|
+
ng.gain.exponentialRampToValueAtTime(1e-4, when + 0.15);
|
|
448
|
+
n.connect(ng).connect(out);
|
|
449
|
+
const osc = ctx.createOscillator();
|
|
450
|
+
osc.type = "triangle";
|
|
451
|
+
const og = ctx.createGain();
|
|
452
|
+
osc.frequency.setValueAtTime(200, when);
|
|
453
|
+
osc.frequency.exponentialRampToValueAtTime(80, when + 0.05);
|
|
454
|
+
og.gain.setValueAtTime(0.5 * v + 1e-4, when);
|
|
455
|
+
og.gain.exponentialRampToValueAtTime(1e-4, when + 0.08);
|
|
456
|
+
osc.connect(og).connect(out);
|
|
457
|
+
n.start(when);
|
|
458
|
+
osc.start(when);
|
|
459
|
+
osc.stop(when + 0.15);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var Hat = {
|
|
464
|
+
create(ctx) {
|
|
465
|
+
const out = ctx.createGain();
|
|
466
|
+
const buf = noiseBuffer(ctx, 0.05, NOISE_SEEDS.drumHat);
|
|
467
|
+
return voice(out, (when, h) => {
|
|
468
|
+
const n = ctx.createBufferSource();
|
|
469
|
+
n.buffer = buf;
|
|
470
|
+
const f = ctx.createBiquadFilter();
|
|
471
|
+
f.type = "highpass";
|
|
472
|
+
f.frequency.value = 8e3;
|
|
473
|
+
const g = ctx.createGain();
|
|
474
|
+
g.gain.setValueAtTime(0.2 * vel(h) + 1e-4, when);
|
|
475
|
+
g.gain.exponentialRampToValueAtTime(1e-4, when + 0.05);
|
|
476
|
+
n.connect(f).connect(g).connect(out);
|
|
477
|
+
n.start(when);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
var NoteCv = {
|
|
482
|
+
create(ctx) {
|
|
483
|
+
const gate = ctx.createConstantSource();
|
|
484
|
+
gate.offset.value = 0;
|
|
485
|
+
gate.start();
|
|
486
|
+
const pitch = ctx.createConstantSource();
|
|
487
|
+
pitch.offset.value = 60;
|
|
488
|
+
pitch.start();
|
|
489
|
+
return {
|
|
490
|
+
gate,
|
|
491
|
+
pitch,
|
|
492
|
+
trigger(time, h) {
|
|
493
|
+
if (typeof h?.value === "number") pitch.offset.setValueAtTime(h.value, time);
|
|
494
|
+
gate.offset.setValueAtTime(0, Math.max(0, time - 1 / ctx.sampleRate));
|
|
495
|
+
gate.offset.setValueAtTime(vel(h), time);
|
|
496
|
+
gate.offset.setValueAtTime(0, time + (h?.duration ?? 0.1));
|
|
497
|
+
},
|
|
498
|
+
dispose() {
|
|
499
|
+
gate.stop();
|
|
500
|
+
pitch.stop();
|
|
501
|
+
gate.disconnect();
|
|
502
|
+
pitch.disconnect();
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// src/lib-runtime/units.ts
|
|
509
|
+
var sec = (v) => v;
|
|
510
|
+
var ms = (v) => v;
|
|
511
|
+
var beats = (v) => v;
|
|
512
|
+
var hz = (v) => v;
|
|
513
|
+
var semitones = (v) => v;
|
|
514
|
+
var cents = (v) => v;
|
|
515
|
+
var db = (v) => v;
|
|
516
|
+
var noteToHz = (n) => 440 * 2 ** ((n - 69) / 12);
|
|
517
|
+
var hzToNote = (f) => 69 + 12 * Math.log2(f / 440);
|
|
518
|
+
var transpose = (n, by) => n + by;
|
|
519
|
+
var interval = (a, b) => b - a;
|
|
520
|
+
var centsToSemitones = (c) => c / 100;
|
|
521
|
+
var msToSeconds = (ms2) => ms2 / 1e3;
|
|
522
|
+
var dbToGain = (db2) => 10 ** (db2 / 20);
|
|
523
|
+
var gainToDb = (g) => 20 * Math.log10(g);
|
|
524
|
+
var now = (ctx) => ctx.currentTime;
|
|
525
|
+
var asPitch = (n) => n;
|
|
526
|
+
|
|
527
|
+
// src/lib/dsp/pulse.ts
|
|
528
|
+
var PULSE_S = 1e-3;
|
|
529
|
+
function pulse(target, when) {
|
|
530
|
+
const ctx = target.context;
|
|
531
|
+
const at = Math.max(when ?? 0, ctx.currentTime);
|
|
532
|
+
const src = ctx.createConstantSource();
|
|
533
|
+
src.offset.value = 1;
|
|
534
|
+
src.connect(target);
|
|
535
|
+
src.start(at);
|
|
536
|
+
src.stop(at + PULSE_S);
|
|
537
|
+
src.onended = () => src.disconnect();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/lib/samples/types.ts
|
|
541
|
+
var VOICE_KEYS = [
|
|
542
|
+
"bd",
|
|
543
|
+
"sd",
|
|
544
|
+
"rim",
|
|
545
|
+
"clap",
|
|
546
|
+
"ch",
|
|
547
|
+
"oh",
|
|
548
|
+
"lt",
|
|
549
|
+
"mt",
|
|
550
|
+
"ht",
|
|
551
|
+
"cym",
|
|
552
|
+
"perc"
|
|
553
|
+
];
|
|
554
|
+
var VOICE_FALLBACKS = {
|
|
555
|
+
bd: ["lt", "mt"],
|
|
556
|
+
sd: ["clap", "rim"],
|
|
557
|
+
rim: ["perc", "ch"],
|
|
558
|
+
clap: ["sd", "perc"],
|
|
559
|
+
ch: ["perc", "rim"],
|
|
560
|
+
oh: ["ch", "cym"],
|
|
561
|
+
lt: ["mt", "bd"],
|
|
562
|
+
mt: ["lt", "ht"],
|
|
563
|
+
ht: ["mt", "lt"],
|
|
564
|
+
cym: ["oh", "ch"],
|
|
565
|
+
perc: ["rim", "ch"]
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// src/lib-runtime/samples.ts
|
|
569
|
+
var DEFAULT_SAMPLE_BASE_URL = "https://squelchy.studio";
|
|
570
|
+
function defineSampleSets(sets) {
|
|
571
|
+
return sets;
|
|
572
|
+
}
|
|
573
|
+
async function loadSampleSet(ctx, set, opts = {}) {
|
|
574
|
+
const base = opts.baseUrl ?? DEFAULT_SAMPLE_BASE_URL;
|
|
575
|
+
const f = opts.fetch ?? fetch;
|
|
576
|
+
const buffers = {};
|
|
577
|
+
await Promise.all(
|
|
578
|
+
Object.entries(set.voices).map(async ([key, path]) => {
|
|
579
|
+
const res = await f(new URL(path, base).href);
|
|
580
|
+
if (!res.ok) throw new Error(`loadSampleSet: ${key} ${res.status}`);
|
|
581
|
+
buffers[key] = await ctx.decodeAudioData(await res.arrayBuffer());
|
|
582
|
+
})
|
|
583
|
+
);
|
|
584
|
+
return {
|
|
585
|
+
set,
|
|
586
|
+
buffers,
|
|
587
|
+
buffer(key) {
|
|
588
|
+
return buffers[key] ?? VOICE_FALLBACKS[key].map((k) => buffers[k]).find(Boolean);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
var SampleVoice = {
|
|
593
|
+
create(ctx, kit, key) {
|
|
594
|
+
const out = ctx.createGain();
|
|
595
|
+
const buf = kit.buffer(key);
|
|
596
|
+
const trigger = (when, h) => {
|
|
597
|
+
if (!buf) return;
|
|
598
|
+
const src = ctx.createBufferSource();
|
|
599
|
+
const g = ctx.createGain();
|
|
600
|
+
src.buffer = buf;
|
|
601
|
+
if (typeof h?.value === "number") src.playbackRate.value = 2 ** (h.value / 12);
|
|
602
|
+
g.gain.value = Math.max(0, Math.min(1, h?.velocity ?? 1));
|
|
603
|
+
src.connect(g).connect(out);
|
|
604
|
+
src.start(when);
|
|
605
|
+
if (h?.duration) src.stop(when + h.duration);
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
output: out,
|
|
609
|
+
trigger,
|
|
610
|
+
connect: (d) => {
|
|
611
|
+
out.connect(d);
|
|
612
|
+
return d;
|
|
613
|
+
},
|
|
614
|
+
disconnect: () => out.disconnect()
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// src/lib-runtime/generated/sample-sets.ts
|
|
620
|
+
var SampleSets = /* @__PURE__ */ defineSampleSets({
|
|
621
|
+
"roland-808": {
|
|
622
|
+
"name": "Roland TR-808",
|
|
623
|
+
"blurb": "The original machine, sampled from hardware. Deep sub kick, ticking hats.",
|
|
624
|
+
"family": "Machines",
|
|
625
|
+
"credit": "Michael Fischer / Technopolis TR-808 sample set, released to the public domain",
|
|
626
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
627
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
628
|
+
"voices": {
|
|
629
|
+
"bd": "/__l5e/assets-v1/c4b2fc55-dcf1-4248-a7fb-2cd3653a659b/roland-808-bd.flac",
|
|
630
|
+
"ch": "/__l5e/assets-v1/c1ec3b3d-571f-46a6-9d05-12b2fe5f6fc5/roland-808-ch.flac",
|
|
631
|
+
"clap": "/__l5e/assets-v1/8bd15e4b-96b7-46b6-b26a-35283c816f1e/roland-808-clap.flac",
|
|
632
|
+
"cym": "/__l5e/assets-v1/033f0719-913a-4a7a-998e-92a7319d806f/roland-808-cym.flac",
|
|
633
|
+
"ht": "/__l5e/assets-v1/ca555782-7544-446a-a461-9b56b4d30072/roland-808-ht.flac",
|
|
634
|
+
"lt": "/__l5e/assets-v1/8e100e1c-3844-42aa-bbe7-9eb52b9d704e/roland-808-lt.flac",
|
|
635
|
+
"mt": "/__l5e/assets-v1/93dd309f-9347-4ea1-909f-56196b08dc88/roland-808-mt.flac",
|
|
636
|
+
"oh": "/__l5e/assets-v1/4881941c-b558-4159-babd-fe224b0f1543/roland-808-oh.flac",
|
|
637
|
+
"perc": "/__l5e/assets-v1/d13076b5-50ca-4675-9d4e-70afd6e6203d/roland-808-perc.flac",
|
|
638
|
+
"rim": "/__l5e/assets-v1/ce82e0e9-402d-4a5e-bf01-59dfb922d1ba/roland-808-rim.flac",
|
|
639
|
+
"sd": "/__l5e/assets-v1/67a799c9-e019-442b-8025-99d69bc493b9/roland-808-sd.flac"
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
"tr-909": {
|
|
643
|
+
"name": "Roland TR-909",
|
|
644
|
+
"blurb": "Punchy transistor kick, snappy sampled snare, sizzling metal hats. House and techno.",
|
|
645
|
+
"family": "Machines",
|
|
646
|
+
"credit": "MckAudio TR8 sample pack, dedicated to the public domain (CC0)",
|
|
647
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
648
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
649
|
+
"voices": {
|
|
650
|
+
"bd": "/__l5e/assets-v1/fd3911bb-6783-4db6-bee2-bb8c5a589f26/tr-909-bd.flac",
|
|
651
|
+
"ch": "/__l5e/assets-v1/6f795c20-fb89-4cd5-b696-3a505d487baa/tr-909-ch.flac",
|
|
652
|
+
"clap": "/__l5e/assets-v1/7b1ccf8e-0dd6-4189-82d7-03a910e895f2/tr-909-clap.flac",
|
|
653
|
+
"cym": "/__l5e/assets-v1/f0d5ce25-3238-4e89-bb09-f5b7118fa0a7/tr-909-cym.flac",
|
|
654
|
+
"ht": "/__l5e/assets-v1/5f9b4c37-25a7-492c-aaed-f7fe24fbfe2c/tr-909-ht.flac",
|
|
655
|
+
"lt": "/__l5e/assets-v1/8477dddd-b9ed-4957-9a48-b5501bc77056/tr-909-lt.flac",
|
|
656
|
+
"mt": "/__l5e/assets-v1/92d461e5-d74a-4ba2-a899-4dea1ad06d13/tr-909-mt.flac",
|
|
657
|
+
"oh": "/__l5e/assets-v1/20c51259-d8d1-4ee3-a1d6-e850c3227b1a/tr-909-oh.flac",
|
|
658
|
+
"perc": "/__l5e/assets-v1/147d346e-9ae7-4413-a50a-de4f03ef7342/tr-909-perc.flac",
|
|
659
|
+
"rim": "/__l5e/assets-v1/0c9f157d-1329-4804-aa12-6819651d69fb/tr-909-rim.flac",
|
|
660
|
+
"sd": "/__l5e/assets-v1/bd8069c2-e07c-4d5c-b05a-fb5b9a31eadb/tr-909-sd.flac"
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
"tr-707": {
|
|
664
|
+
"name": "Roland TR-707",
|
|
665
|
+
"blurb": "Twelve-bit sampled kit with early-digital crunch. Big eighties snare and claps.",
|
|
666
|
+
"family": "Machines",
|
|
667
|
+
"credit": "MckAudio TR8 sample pack, dedicated to the public domain (CC0)",
|
|
668
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
669
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
670
|
+
"voices": {
|
|
671
|
+
"bd": "/__l5e/assets-v1/825b2c7c-5c5f-411c-8191-306549ee26f2/tr-707-bd.flac",
|
|
672
|
+
"ch": "/__l5e/assets-v1/6b35b393-7e9c-462d-8d2f-dc834417062b/tr-707-ch.flac",
|
|
673
|
+
"clap": "/__l5e/assets-v1/efbe819a-b13e-4bf2-a6bd-9bc15a09ff3f/tr-707-clap.flac",
|
|
674
|
+
"cym": "/__l5e/assets-v1/4a34902b-8592-46d8-b339-05a0648cac4f/tr-707-cym.flac",
|
|
675
|
+
"ht": "/__l5e/assets-v1/e58beee9-45e5-4b65-af99-a05fcb96f55b/tr-707-ht.flac",
|
|
676
|
+
"lt": "/__l5e/assets-v1/a0d6f499-4af6-4d34-96de-5ad7c2397229/tr-707-lt.flac",
|
|
677
|
+
"mt": "/__l5e/assets-v1/6c4bd12b-78ff-4f70-863b-cd482f684eb6/tr-707-mt.flac",
|
|
678
|
+
"oh": "/__l5e/assets-v1/c07159f3-d763-4698-a786-aeae60d38ac9/tr-707-oh.flac",
|
|
679
|
+
"perc": "/__l5e/assets-v1/2f8cffcb-8e86-4f65-99db-a705727ed3ab/tr-707-perc.flac",
|
|
680
|
+
"rim": "/__l5e/assets-v1/3d6445b1-38bd-4e9f-b048-db8c079b3c03/tr-707-rim.flac",
|
|
681
|
+
"sd": "/__l5e/assets-v1/aaf70dd8-c740-4b05-9369-feb80f3e26be/tr-707-sd.flac"
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
"tr-606": {
|
|
685
|
+
"name": "Roland TR-606",
|
|
686
|
+
"blurb": "Tiny Drumatix voices \u2014 thin kick, spitty snare, ticking hats. Perfect with a 303.",
|
|
687
|
+
"family": "Machines",
|
|
688
|
+
"credit": "MckAudio TR8 sample pack, dedicated to the public domain (CC0)",
|
|
689
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
690
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
691
|
+
"voices": {
|
|
692
|
+
"bd": "/__l5e/assets-v1/f46ec897-9319-4c25-b63b-74df979de508/tr-606-bd.flac",
|
|
693
|
+
"ch": "/__l5e/assets-v1/f4c57254-c711-43b0-82c6-52b011e205ba/tr-606-ch.flac",
|
|
694
|
+
"cym": "/__l5e/assets-v1/db524ec1-36d3-43db-bfc4-fd84a1ddd2ac/tr-606-cym.flac",
|
|
695
|
+
"ht": "/__l5e/assets-v1/74b486cf-c1f1-45c7-bd2a-4fd0c3794e1a/tr-606-ht.flac",
|
|
696
|
+
"lt": "/__l5e/assets-v1/6b1a564f-6388-40c1-8028-3ea9b6824256/tr-606-lt.flac",
|
|
697
|
+
"mt": "/__l5e/assets-v1/d871a86a-fcc8-45a6-b5a4-83977a689593/tr-606-mt.flac",
|
|
698
|
+
"oh": "/__l5e/assets-v1/a4c607d2-d62c-49b5-ac99-549fd6262468/tr-606-oh.flac",
|
|
699
|
+
"sd": "/__l5e/assets-v1/2062f1d4-e34f-406f-911b-2eb2ec523a89/tr-606-sd.flac"
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
"compu-78": {
|
|
703
|
+
"name": "Compu-78",
|
|
704
|
+
"blurb": "Tiny, dry preset-box percussion. Bossa nova organ rhythm energy.",
|
|
705
|
+
"family": "Machines",
|
|
706
|
+
"credit": "Synthesised for Squelchy",
|
|
707
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
708
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
709
|
+
"voices": {
|
|
710
|
+
"bd": "/__l5e/assets-v1/150a91e2-8b92-40f2-80e0-10b30f10976b/compu-78-bd.flac",
|
|
711
|
+
"ch": "/__l5e/assets-v1/da9dcfc4-5f29-43b9-979f-9679edeb09ed/compu-78-ch.flac",
|
|
712
|
+
"clap": "/__l5e/assets-v1/f3b5d485-44bc-4b34-b2bc-d37f436dffc2/compu-78-clap.flac",
|
|
713
|
+
"cym": "/__l5e/assets-v1/627db2bf-08d5-4054-8de9-b8ec0ca4436a/compu-78-cym.flac",
|
|
714
|
+
"ht": "/__l5e/assets-v1/1323f20c-3c98-45fb-abd1-e4b1e4be158f/compu-78-ht.flac",
|
|
715
|
+
"lt": "/__l5e/assets-v1/ba67ce5c-3f01-4e1b-9fd8-a96bb5428cf4/compu-78-lt.flac",
|
|
716
|
+
"mt": "/__l5e/assets-v1/f8cd42d6-2f98-4e9d-9428-ba091ec125cf/compu-78-mt.flac",
|
|
717
|
+
"oh": "/__l5e/assets-v1/b4a1d5b2-0022-4f1b-ac52-1618ba03ea67/compu-78-oh.flac",
|
|
718
|
+
"perc": "/__l5e/assets-v1/55e15a92-ba17-48cb-aec0-47e5198f073f/compu-78-perc.flac",
|
|
719
|
+
"rim": "/__l5e/assets-v1/698aaf24-8e84-4b3b-8f13-dc95f2b3f157/compu-78-rim.flac",
|
|
720
|
+
"sd": "/__l5e/assets-v1/41357245-d767-410e-8415-08dbb9e7503e/compu-78-sd.flac"
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
"dr5-studio": {
|
|
724
|
+
"name": "Studio Kit",
|
|
725
|
+
"blurb": "Acoustic drums in a small dry room. Skin, air and a little ambience.",
|
|
726
|
+
"family": "Acoustic",
|
|
727
|
+
"credit": "MckAudio DR5 sample pack, dedicated to the public domain (CC0)",
|
|
728
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
729
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
730
|
+
"voices": {
|
|
731
|
+
"bd": "/__l5e/assets-v1/9bc4c40f-432c-4fd1-98f8-dc3343b5dd31/dr5-studio-bd.flac",
|
|
732
|
+
"ch": "/__l5e/assets-v1/862411a4-6d87-45e7-a3a2-2c4062f8e79b/dr5-studio-ch.flac",
|
|
733
|
+
"clap": "/__l5e/assets-v1/2feebc4f-49d2-4ea0-9b5a-890059c24a9c/dr5-studio-clap.flac",
|
|
734
|
+
"cym": "/__l5e/assets-v1/5abff1b5-b403-403b-b681-9ac37a394442/dr5-studio-cym.flac",
|
|
735
|
+
"ht": "/__l5e/assets-v1/4e6883a5-e310-4035-a6ae-ee4033c7a0a1/dr5-studio-ht.flac",
|
|
736
|
+
"lt": "/__l5e/assets-v1/faf35ae9-f89f-4ceb-ad5e-c562dd5d261a/dr5-studio-lt.flac",
|
|
737
|
+
"mt": "/__l5e/assets-v1/4b923b44-9e79-4aa0-a9ef-4178a9f0279b/dr5-studio-mt.flac",
|
|
738
|
+
"oh": "/__l5e/assets-v1/5a5e6d0c-4c8f-4dc5-9dea-55f90083a55a/dr5-studio-oh.flac",
|
|
739
|
+
"perc": "/__l5e/assets-v1/8d286dd0-4201-49f6-9738-0a39e3a3d860/dr5-studio-perc.flac",
|
|
740
|
+
"rim": "/__l5e/assets-v1/1d139e86-288e-48bd-baba-f7d2502271a5/dr5-studio-rim.flac",
|
|
741
|
+
"sd": "/__l5e/assets-v1/eb7b02d6-7063-4350-9515-40010dd583cb/dr5-studio-sd.flac"
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
"tape-dust": {
|
|
745
|
+
"name": "Tape Dust",
|
|
746
|
+
"blurb": "Wobbly, hissy and rolled-off. Everything through a tired cassette.",
|
|
747
|
+
"family": "Character",
|
|
748
|
+
"credit": "Synthesised for Squelchy",
|
|
749
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
750
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
751
|
+
"voices": {
|
|
752
|
+
"bd": "/__l5e/assets-v1/7e90ada0-1da8-4930-9c05-48c395e8a863/tape-dust-bd.flac",
|
|
753
|
+
"ch": "/__l5e/assets-v1/538fd73f-f7ee-408e-ab69-f8865506f4f5/tape-dust-ch.flac",
|
|
754
|
+
"clap": "/__l5e/assets-v1/21b5b5ea-6dce-495d-b740-e7162941c972/tape-dust-clap.flac",
|
|
755
|
+
"cym": "/__l5e/assets-v1/ac1a7f2d-9a96-4f61-b2ec-743854f15bbe/tape-dust-cym.flac",
|
|
756
|
+
"ht": "/__l5e/assets-v1/93dd3f9c-8267-4f4e-b292-807cf0022c9e/tape-dust-ht.flac",
|
|
757
|
+
"lt": "/__l5e/assets-v1/7a51ff67-62ec-408b-b6c3-4a0cae248eaf/tape-dust-lt.flac",
|
|
758
|
+
"mt": "/__l5e/assets-v1/ba3340c5-6ead-419a-836a-a041448f06da/tape-dust-mt.flac",
|
|
759
|
+
"oh": "/__l5e/assets-v1/e924553d-ac9a-427e-a69a-2939032ee618/tape-dust-oh.flac",
|
|
760
|
+
"perc": "/__l5e/assets-v1/abfad1e6-998b-47a8-8c98-6f511c151143/tape-dust-perc.flac",
|
|
761
|
+
"rim": "/__l5e/assets-v1/c86262d8-9711-41d4-b866-19a1ffcaf443/tape-dust-rim.flac",
|
|
762
|
+
"sd": "/__l5e/assets-v1/2d1090cf-0284-4c33-9ba2-430c5c7b4730/tape-dust-sd.flac"
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
"hard-cell": {
|
|
766
|
+
"name": "Hard Cell",
|
|
767
|
+
"blurb": "Overdriven and bit-crushed. Industrial techno with no headroom left.",
|
|
768
|
+
"family": "Character",
|
|
769
|
+
"credit": "Synthesised for Squelchy",
|
|
770
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
771
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
772
|
+
"voices": {
|
|
773
|
+
"bd": "/__l5e/assets-v1/d8f1c5cf-8e07-4a1e-a519-d7f69781329b/hard-cell-bd.flac",
|
|
774
|
+
"ch": "/__l5e/assets-v1/c3275e9a-dd79-4ac8-b4b9-84ebe25b5529/hard-cell-ch.flac",
|
|
775
|
+
"clap": "/__l5e/assets-v1/160a5d2c-01e0-49da-a998-149a0cf08317/hard-cell-clap.flac",
|
|
776
|
+
"cym": "/__l5e/assets-v1/910f3623-0209-439f-9e34-0c0f842bc78f/hard-cell-cym.flac",
|
|
777
|
+
"ht": "/__l5e/assets-v1/36f5b37b-9d44-49b8-a57b-bf5d2a1aa523/hard-cell-ht.flac",
|
|
778
|
+
"lt": "/__l5e/assets-v1/6065e512-578b-477f-88d9-49b3ccccacea/hard-cell-lt.flac",
|
|
779
|
+
"mt": "/__l5e/assets-v1/4e33c147-b8da-4b03-abca-4fd254d5006d/hard-cell-mt.flac",
|
|
780
|
+
"oh": "/__l5e/assets-v1/3e5b3bce-f21b-4ecb-8f28-375ce871427f/hard-cell-oh.flac",
|
|
781
|
+
"perc": "/__l5e/assets-v1/ab27e781-3d41-4e04-8f44-f7a568543586/hard-cell-perc.flac",
|
|
782
|
+
"rim": "/__l5e/assets-v1/ea2989c5-2354-4574-ae38-4786deeee151/hard-cell-rim.flac",
|
|
783
|
+
"sd": "/__l5e/assets-v1/5bd3dca9-bdb7-4618-8e0b-9ed1c0b062c7/hard-cell-sd.flac"
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
"rx5-fm": {
|
|
787
|
+
"name": "Yamaha RX5",
|
|
788
|
+
"blurb": "FM percussion \u2014 glassy, bell-like, unmistakably late-eighties digital.",
|
|
789
|
+
"family": "Digital",
|
|
790
|
+
"credit": "MckAudio RX5 sample pack, dedicated to the public domain (CC0)",
|
|
791
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
792
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
793
|
+
"voices": {
|
|
794
|
+
"bd": "/__l5e/assets-v1/22b27f9f-d301-48e2-a0e4-e6b6e538ec31/rx5-fm-bd.flac",
|
|
795
|
+
"ch": "/__l5e/assets-v1/236fb4f5-7d2a-48c0-be18-bc3777a6be91/rx5-fm-ch.flac",
|
|
796
|
+
"clap": "/__l5e/assets-v1/58402fc0-996c-4577-9074-bb40b585c26e/rx5-fm-clap.flac",
|
|
797
|
+
"cym": "/__l5e/assets-v1/d3e6ba9b-a2b8-4467-8923-97594723720e/rx5-fm-cym.flac",
|
|
798
|
+
"ht": "/__l5e/assets-v1/a5edef5d-7861-4b3a-99bc-10ece111ca64/rx5-fm-ht.flac",
|
|
799
|
+
"lt": "/__l5e/assets-v1/7151d30d-fe90-4ac5-9c9f-e91a808a1efa/rx5-fm-lt.flac",
|
|
800
|
+
"mt": "/__l5e/assets-v1/46119925-9ecd-431a-94ee-2135c8ce4382/rx5-fm-mt.flac",
|
|
801
|
+
"oh": "/__l5e/assets-v1/9fe4938f-fda7-43f8-84c6-c53325fd674c/rx5-fm-oh.flac",
|
|
802
|
+
"perc": "/__l5e/assets-v1/1aa471d4-4737-45e6-9070-6301892dcdfa/rx5-fm-perc.flac",
|
|
803
|
+
"rim": "/__l5e/assets-v1/2b0409a0-1aa6-41a4-9593-553247a2e624/rx5-fm-rim.flac",
|
|
804
|
+
"sd": "/__l5e/assets-v1/3d9c426c-7783-4106-940f-1e6b6405bdd5/rx5-fm-sd.flac"
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
"micro-click": {
|
|
808
|
+
"name": "Micro Click",
|
|
809
|
+
"blurb": "Clicks, ticks and pops. Minimal and micro-house, nothing sustains.",
|
|
810
|
+
"family": "Digital",
|
|
811
|
+
"credit": "Synthesised for Squelchy",
|
|
812
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
813
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
814
|
+
"voices": {
|
|
815
|
+
"bd": "/__l5e/assets-v1/58e3ce80-d300-47c6-8d79-52b00376550f/micro-click-bd.flac",
|
|
816
|
+
"ch": "/__l5e/assets-v1/1c12e400-5c5f-4aae-b006-5a3fe3c6ef2c/micro-click-ch.flac",
|
|
817
|
+
"clap": "/__l5e/assets-v1/b1f402f6-a85d-421b-9684-4b78719726d5/micro-click-clap.flac",
|
|
818
|
+
"cym": "/__l5e/assets-v1/fe501055-43d7-427f-9ab4-5efd0423292e/micro-click-cym.flac",
|
|
819
|
+
"ht": "/__l5e/assets-v1/3aab93cb-cd84-4600-a3e5-458fa74a2a86/micro-click-ht.flac",
|
|
820
|
+
"lt": "/__l5e/assets-v1/b09a0d61-c9a2-4b31-b0f2-10d3c10ef83c/micro-click-lt.flac",
|
|
821
|
+
"mt": "/__l5e/assets-v1/988e7a56-e914-43ad-aff1-71ec3a04b8e4/micro-click-mt.flac",
|
|
822
|
+
"oh": "/__l5e/assets-v1/122a3f93-4542-471b-b390-996665248653/micro-click-oh.flac",
|
|
823
|
+
"perc": "/__l5e/assets-v1/cae413d7-a68c-411a-a4b4-b7c20a83946d/micro-click-perc.flac",
|
|
824
|
+
"rim": "/__l5e/assets-v1/41922292-1c6e-4bef-ac8e-fa1ce09eefca/micro-click-rim.flac",
|
|
825
|
+
"sd": "/__l5e/assets-v1/6218ccdd-cba1-466e-af8b-665d7a2d4640/micro-click-sd.flac"
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
"deep-analogue": {
|
|
829
|
+
"name": "Deep Analogue",
|
|
830
|
+
"blurb": "Long sub kick, soft snare, warm hats. Deep house at three in the morning.",
|
|
831
|
+
"family": "Acoustic",
|
|
832
|
+
"credit": "Synthesised for Squelchy",
|
|
833
|
+
"license": "CC0 1.0 / public domain \u2014 free to use, no attribution required",
|
|
834
|
+
"licenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/",
|
|
835
|
+
"voices": {
|
|
836
|
+
"bd": "/__l5e/assets-v1/93c55e12-44f1-4cf9-8b19-82833257a68f/deep-analogue-bd.flac",
|
|
837
|
+
"ch": "/__l5e/assets-v1/7c881111-83d8-4c1a-b4af-4f09d2a09f4b/deep-analogue-ch.flac",
|
|
838
|
+
"clap": "/__l5e/assets-v1/e8b67bb8-8f7f-4b87-9ee4-d9cb7bc43efe/deep-analogue-clap.flac",
|
|
839
|
+
"cym": "/__l5e/assets-v1/b329f9e1-3eb0-471c-b7c7-8ad7f639323f/deep-analogue-cym.flac",
|
|
840
|
+
"ht": "/__l5e/assets-v1/a381a6b5-fd09-4fa6-b88e-86a29b8f25d9/deep-analogue-ht.flac",
|
|
841
|
+
"lt": "/__l5e/assets-v1/c9ebc90a-20aa-4f76-9e79-f440258d4bac/deep-analogue-lt.flac",
|
|
842
|
+
"mt": "/__l5e/assets-v1/5e1b803c-748e-4f09-bed7-c7b4511dc8b0/deep-analogue-mt.flac",
|
|
843
|
+
"oh": "/__l5e/assets-v1/289d67f2-6d55-4a04-8607-302442465e81/deep-analogue-oh.flac",
|
|
844
|
+
"perc": "/__l5e/assets-v1/8ef1f592-a3a4-45a5-9ea4-80362953aee4/deep-analogue-perc.flac",
|
|
845
|
+
"rim": "/__l5e/assets-v1/102a1c23-6e5f-468e-95c8-e414ce849884/deep-analogue-rim.flac",
|
|
846
|
+
"sd": "/__l5e/assets-v1/9be98c57-13c4-4337-bb2f-53c0b2ff7150/deep-analogue-sd.flac"
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
// src/modules/ModuleDescriptor.ts
|
|
852
|
+
function paramDefaults(params50) {
|
|
853
|
+
const out = {};
|
|
854
|
+
for (const k in params50) out[k] = params50[k].default;
|
|
855
|
+
return out;
|
|
856
|
+
}
|
|
857
|
+
function defineDescriptor(desc) {
|
|
858
|
+
if (desc.params && !desc.controlDescriptions) {
|
|
859
|
+
const cd = {};
|
|
860
|
+
for (const p of Object.values(desc.params)) {
|
|
861
|
+
if (p.kind !== "enum" && p.label) cd[p.label] = { short: p.short, long: p.long };
|
|
862
|
+
}
|
|
863
|
+
return { ...desc, controlDescriptions: cd };
|
|
864
|
+
}
|
|
865
|
+
return desc;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/lib-runtime/generated/worklet-factories.ts
|
|
869
|
+
function createVoctFreqNode(ctx) {
|
|
870
|
+
return new AudioWorkletNode(ctx, "map-voct-to-freq", {
|
|
871
|
+
numberOfInputs: 1,
|
|
872
|
+
numberOfOutputs: 1,
|
|
873
|
+
outputChannelCount: [1]
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
function createVoctPeriodNode(ctx) {
|
|
877
|
+
return new AudioWorkletNode(ctx, "map-voct-to-period", {
|
|
878
|
+
numberOfInputs: 1,
|
|
879
|
+
numberOfOutputs: 1,
|
|
880
|
+
outputChannelCount: [1]
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
function createAdsrNode(ctx, init) {
|
|
884
|
+
const node = new AudioWorkletNode(ctx, "adsr-envelope", {
|
|
885
|
+
numberOfInputs: 1,
|
|
886
|
+
numberOfOutputs: 1,
|
|
887
|
+
outputChannelCount: [1]
|
|
888
|
+
});
|
|
889
|
+
if (init) {
|
|
890
|
+
for (const [k, v] of Object.entries(init)) {
|
|
891
|
+
if (v === void 0) continue;
|
|
892
|
+
const p = node.parameters.get(k);
|
|
893
|
+
if (p) p.value = v;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return node;
|
|
897
|
+
}
|
|
898
|
+
function createFormantNode(ctx) {
|
|
899
|
+
return new AudioWorkletNode(ctx, "formant-table", {
|
|
900
|
+
numberOfInputs: 1,
|
|
901
|
+
numberOfOutputs: 1,
|
|
902
|
+
outputChannelCount: [5]
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
function createBitcrusherNode(ctx) {
|
|
906
|
+
return new AudioWorkletNode(ctx, "bitcrusher", {
|
|
907
|
+
numberOfInputs: 1,
|
|
908
|
+
numberOfOutputs: 1,
|
|
909
|
+
outputChannelCount: [1]
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
function createSubOctaveNode(ctx) {
|
|
913
|
+
return new AudioWorkletNode(ctx, "freq-divider", {
|
|
914
|
+
numberOfInputs: 1,
|
|
915
|
+
numberOfOutputs: 1,
|
|
916
|
+
outputChannelCount: [1]
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
function createEnvFollowerNode(ctx) {
|
|
920
|
+
return new AudioWorkletNode(ctx, "env-follower", {
|
|
921
|
+
numberOfInputs: 1,
|
|
922
|
+
numberOfOutputs: 1,
|
|
923
|
+
outputChannelCount: [1]
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
function createWaveshaperNode(ctx) {
|
|
927
|
+
return new AudioWorkletNode(ctx, "waveshaper", {
|
|
928
|
+
numberOfInputs: 1,
|
|
929
|
+
numberOfOutputs: 1,
|
|
930
|
+
outputChannelCount: [1]
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
function createGlideNode(ctx) {
|
|
934
|
+
return new AudioWorkletNode(ctx, "glide", {
|
|
935
|
+
numberOfInputs: 2,
|
|
936
|
+
numberOfOutputs: 1,
|
|
937
|
+
outputChannelCount: [1]
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function createTapeStopNode(ctx, time = 0.5, mix = 1) {
|
|
941
|
+
return new AudioWorkletNode(ctx, "tape-stop", {
|
|
942
|
+
numberOfInputs: 2,
|
|
943
|
+
numberOfOutputs: 1,
|
|
944
|
+
outputChannelCount: [2],
|
|
945
|
+
channelCount: 2,
|
|
946
|
+
channelCountMode: "explicit",
|
|
947
|
+
parameterData: { time, mix }
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
function createSpringReverbNode(ctx, params50) {
|
|
951
|
+
return new AudioWorkletNode(ctx, "spring-reverb", {
|
|
952
|
+
numberOfInputs: 2,
|
|
953
|
+
numberOfOutputs: 1,
|
|
954
|
+
outputChannelCount: [2],
|
|
955
|
+
channelCount: 2,
|
|
956
|
+
channelCountMode: "explicit",
|
|
957
|
+
parameterData: { decay: params50.decay, tone: params50.tone, drip: params50.drip }
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
function createFdnReverbNode(ctx, opts) {
|
|
961
|
+
return new AudioWorkletNode(ctx, "fdn-reverb", {
|
|
962
|
+
numberOfInputs: 1,
|
|
963
|
+
numberOfOutputs: 1,
|
|
964
|
+
// Stereo tank: L/R in (mono upmixes to L=R → identical mid), L/R out.
|
|
965
|
+
outputChannelCount: [2],
|
|
966
|
+
channelCount: 2,
|
|
967
|
+
channelCountMode: "explicit",
|
|
968
|
+
parameterData: { rt60: opts.rt60, damping: opts.damping },
|
|
969
|
+
processorOptions: {
|
|
970
|
+
size: opts.size ?? 1,
|
|
971
|
+
feedbackCeiling: opts.feedbackCeiling ?? 0.97
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
function createFreqShifterNode(ctx, shift = 0) {
|
|
976
|
+
return new AudioWorkletNode(ctx, "freq-shifter", {
|
|
977
|
+
numberOfInputs: 1,
|
|
978
|
+
numberOfOutputs: 1,
|
|
979
|
+
outputChannelCount: [1],
|
|
980
|
+
parameterData: { shift }
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
function createOctaverDividerNode(ctx) {
|
|
984
|
+
return new AudioWorkletNode(ctx, "freq-divider", {
|
|
985
|
+
numberOfInputs: 1,
|
|
986
|
+
numberOfOutputs: 3,
|
|
987
|
+
outputChannelCount: [1, 2, 1]
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
function createPitchShifterNode(ctx, semitones2 = 12) {
|
|
991
|
+
return new AudioWorkletNode(ctx, "pitch-shifter", {
|
|
992
|
+
numberOfInputs: 1,
|
|
993
|
+
numberOfOutputs: 1,
|
|
994
|
+
outputChannelCount: [1],
|
|
995
|
+
channelCount: 1,
|
|
996
|
+
channelCountMode: "explicit",
|
|
997
|
+
parameterData: { semitones: semitones2 }
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
function createVcaNode(ctx, gain = 0.5, cvAmount = 1) {
|
|
1001
|
+
return new AudioWorkletNode(ctx, "vca", {
|
|
1002
|
+
numberOfInputs: 1,
|
|
1003
|
+
numberOfOutputs: 1,
|
|
1004
|
+
parameterData: { gain, cvAmount }
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
function createLadderFilterNode(ctx, init) {
|
|
1008
|
+
return new AudioWorkletNode(ctx, "ladder-filter", {
|
|
1009
|
+
numberOfInputs: 1,
|
|
1010
|
+
numberOfOutputs: 1,
|
|
1011
|
+
parameterData: {
|
|
1012
|
+
cutoff: 0,
|
|
1013
|
+
resonance: init?.resonance ?? 0.6,
|
|
1014
|
+
drive: init?.drive ?? 0.3
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
function createMapResNode(ctx, init) {
|
|
1019
|
+
const qMin = init?.qMin ?? 0.1;
|
|
1020
|
+
const qMax = init?.qMax ?? 20;
|
|
1021
|
+
return new AudioWorkletNode(ctx, "map-res", {
|
|
1022
|
+
numberOfInputs: 1,
|
|
1023
|
+
numberOfOutputs: 1,
|
|
1024
|
+
parameterData: {
|
|
1025
|
+
base: init?.base ?? 1,
|
|
1026
|
+
cvScale: init?.cvScale ?? qMax - qMin,
|
|
1027
|
+
qMin,
|
|
1028
|
+
qMax,
|
|
1029
|
+
dbOut: init?.dbOut === false ? 0 : 1
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
function createCombFilterNode(ctx, feedback = 0.7, maxPeriod = 0.1, channels = 1) {
|
|
1034
|
+
return new AudioWorkletNode(ctx, "comb-filter", {
|
|
1035
|
+
numberOfInputs: 1,
|
|
1036
|
+
numberOfOutputs: 1,
|
|
1037
|
+
outputChannelCount: [channels],
|
|
1038
|
+
channelCount: channels,
|
|
1039
|
+
channelCountMode: "explicit",
|
|
1040
|
+
channelInterpretation: "speakers",
|
|
1041
|
+
parameterData: { period: 0, feedback },
|
|
1042
|
+
processorOptions: { maxPeriod }
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
function createFlangerNode(ctx, opts) {
|
|
1046
|
+
return new AudioWorkletNode(ctx, "flanger", {
|
|
1047
|
+
numberOfInputs: 1,
|
|
1048
|
+
numberOfOutputs: 1,
|
|
1049
|
+
parameterData: { rate: opts.rate, depth: opts.depth, feedback: opts.feedback }
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
function createTapeDelayNode(ctx, opts) {
|
|
1053
|
+
return new AudioWorkletNode(ctx, "tape-delay", {
|
|
1054
|
+
numberOfInputs: 1,
|
|
1055
|
+
numberOfOutputs: 1,
|
|
1056
|
+
parameterData: { ...opts }
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
function createPingPongNode(ctx, opts) {
|
|
1060
|
+
return new AudioWorkletNode(ctx, "ping-pong", {
|
|
1061
|
+
numberOfInputs: 1,
|
|
1062
|
+
numberOfOutputs: 2,
|
|
1063
|
+
parameterData: { ...opts }
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/lib-runtime/generated/modules/303-filter.tsx
|
|
1068
|
+
var params = {
|
|
1069
|
+
note: { node: "_cutoffVoct", field: "transpose", unit: "note", default: 67, min: 15, max: 135 },
|
|
1070
|
+
// Split across two cascaded biquads' Q (0.7x / 0.4x) — not a single
|
|
1071
|
+
// AudioParam — so this stays set-only.
|
|
1072
|
+
resonance: { unit: "factor", default: 8, min: 0, max: 15, scale: "sqrt" },
|
|
1073
|
+
envMod: { unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
1074
|
+
accent: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
1075
|
+
decay: { node: "envSmoother", field: "decay", unit: "s", default: 0.2, min: 0.05, max: 1, scale: "log" }
|
|
1076
|
+
};
|
|
1077
|
+
var filter303Descriptor = /* @__PURE__ */ defineDescriptor({
|
|
1078
|
+
type: "303-filter",
|
|
1079
|
+
params,
|
|
1080
|
+
ports: {
|
|
1081
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1082
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
1083
|
+
"trigger": { type: "input", signal: "gate", node: "trigInput" },
|
|
1084
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_cutoffVoct" }
|
|
1085
|
+
},
|
|
1086
|
+
createModule(id, position) {
|
|
1087
|
+
return { id, type: "303-filter", position, ...paramDefaults(params) };
|
|
1088
|
+
},
|
|
1089
|
+
createNodes(ctx, module) {
|
|
1090
|
+
const input = ctx.createGain();
|
|
1091
|
+
const filter1 = ctx.createBiquadFilter();
|
|
1092
|
+
filter1.type = "lowpass";
|
|
1093
|
+
filter1.frequency.value = 0;
|
|
1094
|
+
filter1.Q.value = module.resonance * 0.7;
|
|
1095
|
+
const filter2 = ctx.createBiquadFilter();
|
|
1096
|
+
filter2.type = "lowpass";
|
|
1097
|
+
filter2.frequency.value = 0;
|
|
1098
|
+
filter2.Q.value = module.resonance * 0.4;
|
|
1099
|
+
const accentVCA = ctx.createGain();
|
|
1100
|
+
accentVCA.gain.value = 1;
|
|
1101
|
+
const output = ctx.createGain();
|
|
1102
|
+
const _cutoffVoct = createVoctFreqNode(ctx);
|
|
1103
|
+
_cutoffVoct.parameters.get("transpose").value = module.note;
|
|
1104
|
+
const _cutoffBase = ctx.createConstantSource();
|
|
1105
|
+
_cutoffBase.offset.value = 0;
|
|
1106
|
+
_cutoffBase.start();
|
|
1107
|
+
_cutoffBase.connect(_cutoffVoct);
|
|
1108
|
+
_cutoffVoct.connect(filter1.frequency);
|
|
1109
|
+
_cutoffVoct.connect(filter2.frequency);
|
|
1110
|
+
const trigInput = ctx.createGain();
|
|
1111
|
+
const envSmoother = createAdsrNode(ctx, {
|
|
1112
|
+
attack: 3e-3,
|
|
1113
|
+
decay: module.decay,
|
|
1114
|
+
sustain: 0,
|
|
1115
|
+
release: 0.01
|
|
1116
|
+
});
|
|
1117
|
+
const envDepth = ctx.createGain();
|
|
1118
|
+
envDepth.gain.value = module.envMod * (1 + module.accent) * 36;
|
|
1119
|
+
const accentEnvGain = ctx.createGain();
|
|
1120
|
+
accentEnvGain.gain.value = module.accent * 0.5;
|
|
1121
|
+
input.connect(filter1);
|
|
1122
|
+
filter1.connect(filter2);
|
|
1123
|
+
filter2.connect(accentVCA);
|
|
1124
|
+
accentVCA.connect(output);
|
|
1125
|
+
trigInput.connect(envSmoother);
|
|
1126
|
+
envSmoother.connect(envDepth);
|
|
1127
|
+
envDepth.connect(_cutoffVoct);
|
|
1128
|
+
envSmoother.connect(accentEnvGain);
|
|
1129
|
+
accentEnvGain.connect(accentVCA.gain);
|
|
1130
|
+
return { input, output, trigInput, filter1, filter2, accentVCA, accentEnvGain, envSmoother, envDepth, _cutoffVoct, _cutoffBase };
|
|
1131
|
+
},
|
|
1132
|
+
updateParam(nodes, module, param, value) {
|
|
1133
|
+
switch (param) {
|
|
1134
|
+
case "note":
|
|
1135
|
+
nodes._cutoffVoct.parameters.get("transpose").value = value;
|
|
1136
|
+
break;
|
|
1137
|
+
case "resonance": {
|
|
1138
|
+
const q = value;
|
|
1139
|
+
nodes.filter1.Q.value = q * 0.7;
|
|
1140
|
+
nodes.filter2.Q.value = q * 0.4;
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
case "envMod":
|
|
1144
|
+
nodes.envDepth.gain.value = value * (1 + module.accent) * 36;
|
|
1145
|
+
break;
|
|
1146
|
+
case "accent": {
|
|
1147
|
+
const acc = value;
|
|
1148
|
+
nodes.envDepth.gain.value = module.envMod * (1 + acc) * 36;
|
|
1149
|
+
nodes.accentEnvGain.gain.value = acc * 0.5;
|
|
1150
|
+
break;
|
|
1151
|
+
}
|
|
1152
|
+
case "decay": {
|
|
1153
|
+
const p = nodes.envSmoother.parameters.get("decay");
|
|
1154
|
+
if (p) p.value = value;
|
|
1155
|
+
break;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
dispose(nodes) {
|
|
1160
|
+
nodes._cutoffBase.stop();
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
// src/lib-runtime/generated/modules/303-filter.wrapper.ts
|
|
1165
|
+
var AcidFilter = /* @__PURE__ */ defineModule(filter303Descriptor, {
|
|
1166
|
+
worklets: {
|
|
1167
|
+
"adsr-envelope": adsr_envelope_default,
|
|
1168
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
// src/lib-runtime/generated/modules/auto-pan.tsx
|
|
1173
|
+
var shapes = ["sine", "triangle", "square"];
|
|
1174
|
+
var params2 = {
|
|
1175
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 2, min: 0.1, max: 10 },
|
|
1176
|
+
depth: { node: "lfoGain", field: "gain", unit: "factor", default: 0.8, min: 0, max: 1 },
|
|
1177
|
+
shape: { kind: "enum", options: shapes, default: "sine" }
|
|
1178
|
+
};
|
|
1179
|
+
var autoPanDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1180
|
+
type: "auto-pan",
|
|
1181
|
+
params: params2,
|
|
1182
|
+
ports: {
|
|
1183
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1184
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
1185
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" },
|
|
1186
|
+
"modDepth": { type: "modulation", signal: "modulation", node: "lfoGain", field: "gain" }
|
|
1187
|
+
},
|
|
1188
|
+
createModule(id, position) {
|
|
1189
|
+
return { id, type: "auto-pan", position, ...paramDefaults(params2) };
|
|
1190
|
+
},
|
|
1191
|
+
createNodes(ctx, module) {
|
|
1192
|
+
const input = ctx.createGain();
|
|
1193
|
+
const panner = ctx.createStereoPanner();
|
|
1194
|
+
const output = ctx.createGain();
|
|
1195
|
+
const lfo = ctx.createOscillator();
|
|
1196
|
+
const lfoGain = ctx.createGain();
|
|
1197
|
+
lfo.type = module.shape;
|
|
1198
|
+
lfo.frequency.value = module.rate;
|
|
1199
|
+
lfoGain.gain.value = module.depth;
|
|
1200
|
+
lfo.connect(lfoGain);
|
|
1201
|
+
lfoGain.connect(panner.pan);
|
|
1202
|
+
lfo.start();
|
|
1203
|
+
input.connect(panner);
|
|
1204
|
+
panner.connect(output);
|
|
1205
|
+
return { input, output, lfo, lfoGain };
|
|
1206
|
+
},
|
|
1207
|
+
updateParam(nodes, _m, param, value) {
|
|
1208
|
+
if (param === "rate") {
|
|
1209
|
+
nodes.lfo.frequency.value = value;
|
|
1210
|
+
} else if (param === "depth") {
|
|
1211
|
+
nodes.lfoGain.gain.value = value;
|
|
1212
|
+
} else if (param === "shape") {
|
|
1213
|
+
nodes.lfo.type = value;
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
dispose(nodes) {
|
|
1217
|
+
nodes.lfo.stop();
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
// src/lib-runtime/generated/modules/auto-pan.wrapper.ts
|
|
1222
|
+
var AutoPan = /* @__PURE__ */ defineModule(autoPanDescriptor);
|
|
1223
|
+
|
|
1224
|
+
// src/lib/dsp/mix.ts
|
|
1225
|
+
var CURVE_POINTS = 2049;
|
|
1226
|
+
function lawCurve(fn) {
|
|
1227
|
+
const c = new Float32Array(CURVE_POINTS);
|
|
1228
|
+
for (let i = 0; i < CURVE_POINTS; i++) {
|
|
1229
|
+
const x = -1 + 2 * i / (CURVE_POINTS - 1);
|
|
1230
|
+
c[i] = fn(Math.min(1, Math.max(0, x)));
|
|
1231
|
+
}
|
|
1232
|
+
return c;
|
|
1233
|
+
}
|
|
1234
|
+
function createMix(ctx, dry, wet, value, law, wetScale = 1) {
|
|
1235
|
+
const mix = ctx.createConstantSource();
|
|
1236
|
+
mix.offset.value = value;
|
|
1237
|
+
let toWet = mix;
|
|
1238
|
+
if (law === "linear") {
|
|
1239
|
+
dry.gain.value = 1;
|
|
1240
|
+
const invert = ctx.createGain();
|
|
1241
|
+
invert.gain.value = -1;
|
|
1242
|
+
mix.connect(invert).connect(dry.gain);
|
|
1243
|
+
} else {
|
|
1244
|
+
dry.gain.value = 0;
|
|
1245
|
+
const cos = ctx.createWaveShaper();
|
|
1246
|
+
cos.curve = lawCurve((m) => Math.cos(m * 0.5 * Math.PI));
|
|
1247
|
+
const sin = ctx.createWaveShaper();
|
|
1248
|
+
sin.curve = lawCurve((m) => Math.sin(m * 0.5 * Math.PI));
|
|
1249
|
+
mix.connect(cos).connect(dry.gain);
|
|
1250
|
+
toWet = mix.connect(sin);
|
|
1251
|
+
}
|
|
1252
|
+
wet.gain.value = 0;
|
|
1253
|
+
if (wetScale !== 1) {
|
|
1254
|
+
const scale = ctx.createGain();
|
|
1255
|
+
scale.gain.value = wetScale;
|
|
1256
|
+
toWet = toWet.connect(scale);
|
|
1257
|
+
}
|
|
1258
|
+
toWet.connect(wet.gain);
|
|
1259
|
+
mix.start();
|
|
1260
|
+
return mix;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/lib-runtime/generated/modules/bitcrusher.tsx
|
|
1264
|
+
var params3 = {
|
|
1265
|
+
bits: { node: "crusher", field: "bits", unit: "factor", default: 8, min: 1, max: 16 },
|
|
1266
|
+
rate: { node: "crusher", field: "rate", unit: "factor", default: 0.5, min: 0.01, max: 1, scale: "log" },
|
|
1267
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 1, min: 0, max: 1 }
|
|
1268
|
+
};
|
|
1269
|
+
var bitcrusherDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1270
|
+
type: "bitcrusher",
|
|
1271
|
+
params: params3,
|
|
1272
|
+
ports: {
|
|
1273
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1274
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
1275
|
+
},
|
|
1276
|
+
createModule(id, position) {
|
|
1277
|
+
return { id, type: "bitcrusher", position, ...paramDefaults(params3) };
|
|
1278
|
+
},
|
|
1279
|
+
createNodes(ctx, module) {
|
|
1280
|
+
const input = ctx.createGain();
|
|
1281
|
+
const output = ctx.createGain();
|
|
1282
|
+
const dryGain = ctx.createGain();
|
|
1283
|
+
const wetGain = ctx.createGain();
|
|
1284
|
+
const crusher = createBitcrusherNode(ctx);
|
|
1285
|
+
const cp = crusher.parameters;
|
|
1286
|
+
cp.get("bits").value = module.bits;
|
|
1287
|
+
cp.get("rate").value = module.rate;
|
|
1288
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
1289
|
+
input.connect(dryGain);
|
|
1290
|
+
input.connect(crusher);
|
|
1291
|
+
crusher.connect(wetGain);
|
|
1292
|
+
dryGain.connect(output);
|
|
1293
|
+
wetGain.connect(output);
|
|
1294
|
+
return { input, output, crusher, dryGain, wetGain, _mix };
|
|
1295
|
+
},
|
|
1296
|
+
updateParam(nodes, _m, param, value) {
|
|
1297
|
+
if (param === "bits") {
|
|
1298
|
+
nodes.crusher.parameters.get("bits").value = value;
|
|
1299
|
+
} else if (param === "rate") {
|
|
1300
|
+
nodes.crusher.parameters.get("rate").value = value;
|
|
1301
|
+
} else if (param === "mix") {
|
|
1302
|
+
nodes._mix.offset.value = value;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
// src/lib-runtime/generated/modules/bitcrusher.wrapper.ts
|
|
1308
|
+
var Bitcrusher = /* @__PURE__ */ defineModule(bitcrusherDescriptor, {
|
|
1309
|
+
worklets: {
|
|
1310
|
+
"bitcrusher": bitcrusher_default
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// src/lib-runtime/generated/modules/chorus.tsx
|
|
1315
|
+
var params4 = {
|
|
1316
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 1.5, min: 0.1, max: 5 },
|
|
1317
|
+
depth: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
1318
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
1319
|
+
};
|
|
1320
|
+
var chorusDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1321
|
+
type: "chorus",
|
|
1322
|
+
params: params4,
|
|
1323
|
+
ports: {
|
|
1324
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1325
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
1326
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" },
|
|
1327
|
+
"modMix": { type: "modulation", signal: "modulation", node: "_mix", field: "offset" }
|
|
1328
|
+
},
|
|
1329
|
+
createModule(id, position) {
|
|
1330
|
+
return { id, type: "chorus", position, ...paramDefaults(params4) };
|
|
1331
|
+
},
|
|
1332
|
+
createNodes(ctx, module) {
|
|
1333
|
+
const input = ctx.createGain();
|
|
1334
|
+
const output = ctx.createGain();
|
|
1335
|
+
const dryGain = ctx.createGain();
|
|
1336
|
+
const wetGain = ctx.createGain();
|
|
1337
|
+
const monoIn = ctx.createGain();
|
|
1338
|
+
monoIn.channelCount = 1;
|
|
1339
|
+
monoIn.channelCountMode = "explicit";
|
|
1340
|
+
monoIn.channelInterpretation = "speakers";
|
|
1341
|
+
const delayL = ctx.createDelay(0.1);
|
|
1342
|
+
const delayR = ctx.createDelay(0.1);
|
|
1343
|
+
const lfo = ctx.createOscillator();
|
|
1344
|
+
const lfoGain = ctx.createGain();
|
|
1345
|
+
const lfoInv = ctx.createGain();
|
|
1346
|
+
const merger = ctx.createChannelMerger(2);
|
|
1347
|
+
delayL.delayTime.value = 0.02;
|
|
1348
|
+
delayR.delayTime.value = 0.02;
|
|
1349
|
+
const _mix = ctx.createConstantSource();
|
|
1350
|
+
_mix.offset.value = module.mix;
|
|
1351
|
+
dryGain.gain.value = 1;
|
|
1352
|
+
const dryTap = ctx.createGain();
|
|
1353
|
+
dryTap.gain.value = -0.5;
|
|
1354
|
+
_mix.connect(dryTap).connect(dryGain.gain);
|
|
1355
|
+
wetGain.gain.value = 0;
|
|
1356
|
+
_mix.connect(wetGain.gain);
|
|
1357
|
+
_mix.start();
|
|
1358
|
+
lfo.type = "sine";
|
|
1359
|
+
lfo.frequency.value = module.rate;
|
|
1360
|
+
lfoGain.gain.value = module.depth * 0.01;
|
|
1361
|
+
lfoInv.gain.value = -1;
|
|
1362
|
+
lfo.connect(lfoGain);
|
|
1363
|
+
lfoGain.connect(delayL.delayTime);
|
|
1364
|
+
lfoGain.connect(lfoInv);
|
|
1365
|
+
lfoInv.connect(delayR.delayTime);
|
|
1366
|
+
lfo.start();
|
|
1367
|
+
input.connect(dryGain);
|
|
1368
|
+
input.connect(monoIn);
|
|
1369
|
+
monoIn.connect(delayL);
|
|
1370
|
+
monoIn.connect(delayR);
|
|
1371
|
+
delayL.connect(merger, 0, 0);
|
|
1372
|
+
delayR.connect(merger, 0, 1);
|
|
1373
|
+
merger.connect(wetGain);
|
|
1374
|
+
dryGain.connect(output);
|
|
1375
|
+
wetGain.connect(output);
|
|
1376
|
+
return { input, output, lfo, lfoGain, lfoInv, dryGain, wetGain, monoIn, delayL, delayR, merger, _mix };
|
|
1377
|
+
},
|
|
1378
|
+
updateParam(nodes, _m, param, value) {
|
|
1379
|
+
switch (param) {
|
|
1380
|
+
case "rate":
|
|
1381
|
+
nodes.lfo.frequency.value = value;
|
|
1382
|
+
break;
|
|
1383
|
+
case "depth":
|
|
1384
|
+
nodes.lfoGain.gain.value = value * 0.01;
|
|
1385
|
+
break;
|
|
1386
|
+
case "mix":
|
|
1387
|
+
nodes._mix.offset.value = value;
|
|
1388
|
+
break;
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1391
|
+
dispose(nodes) {
|
|
1392
|
+
nodes.lfo.stop();
|
|
1393
|
+
nodes._mix.stop();
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
// src/lib-runtime/generated/modules/chorus.wrapper.ts
|
|
1398
|
+
var Chorus = /* @__PURE__ */ defineModule(chorusDescriptor);
|
|
1399
|
+
|
|
1400
|
+
// src/types/synth.ts
|
|
1401
|
+
function hzToMidi(hz2) {
|
|
1402
|
+
return 12 * Math.log2(hz2 / 440) + 69;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/lib-runtime/generated/modules/comb-filter.tsx
|
|
1406
|
+
var params5 = {
|
|
1407
|
+
offset: { node: "_freqVoct", field: "transpose", default: 55, min: -48, max: 96, unit: "semitone" },
|
|
1408
|
+
feedback: { node: "comb", field: "feedback", unit: "factor", default: 0.7, min: 0, max: 0.95 },
|
|
1409
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
1410
|
+
tuning: { kind: "enum", options: ["chromatic", "free"], default: "chromatic" }
|
|
1411
|
+
};
|
|
1412
|
+
var patchCount = /* @__PURE__ */ new WeakMap();
|
|
1413
|
+
var combFilterDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1414
|
+
type: "comb-filter",
|
|
1415
|
+
params: params5,
|
|
1416
|
+
// Panel controls diverge from params (the ribbon edits two memories that
|
|
1417
|
+
// feed `offset`), so they are listed here, not derived from labels.
|
|
1418
|
+
ports: {
|
|
1419
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1420
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
1421
|
+
"voct": { type: "input", signal: "pitch", node: "_freqVoct" },
|
|
1422
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_freqVoct" }
|
|
1423
|
+
},
|
|
1424
|
+
createModule(id, position) {
|
|
1425
|
+
const { offset: _dsp, ...rest } = paramDefaults(params5);
|
|
1426
|
+
return { id, type: "comb-filter", position, ...rest, frequency: 200, transpose: 0 };
|
|
1427
|
+
},
|
|
1428
|
+
createNodes(ctx, module) {
|
|
1429
|
+
const input = ctx.createGain();
|
|
1430
|
+
const output = ctx.createGain();
|
|
1431
|
+
const dryGain = ctx.createGain();
|
|
1432
|
+
const wetGain = ctx.createGain();
|
|
1433
|
+
const comb = createCombFilterNode(ctx, module.feedback);
|
|
1434
|
+
const _freqVoct = createVoctPeriodNode(ctx);
|
|
1435
|
+
_freqVoct.parameters.get("transpose").value = module.offset ?? hzToMidi(module.frequency);
|
|
1436
|
+
const periodParam = comb.parameters.get("period");
|
|
1437
|
+
periodParam.value = 0;
|
|
1438
|
+
_freqVoct.connect(periodParam);
|
|
1439
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
1440
|
+
input.connect(dryGain);
|
|
1441
|
+
input.connect(comb);
|
|
1442
|
+
comb.connect(wetGain);
|
|
1443
|
+
dryGain.connect(output);
|
|
1444
|
+
wetGain.connect(output);
|
|
1445
|
+
return { input, output, comb, dryGain, wetGain, _freqVoct, _mix };
|
|
1446
|
+
},
|
|
1447
|
+
updateParam(nodes, _m, param, value) {
|
|
1448
|
+
const t = nodes._freqVoct.parameters.get("transpose");
|
|
1449
|
+
if (param === "offset") {
|
|
1450
|
+
t.value = value;
|
|
1451
|
+
} else if (param === "frequency") {
|
|
1452
|
+
if (!patchCount.get(nodes)) t.value = hzToMidi(value);
|
|
1453
|
+
} else if (param === "transpose") {
|
|
1454
|
+
if (patchCount.get(nodes)) t.value = value;
|
|
1455
|
+
} else if (param === "feedback") {
|
|
1456
|
+
nodes.comb.parameters.get("feedback").value = value;
|
|
1457
|
+
} else if (param === "mix") {
|
|
1458
|
+
nodes._mix.offset.value = value;
|
|
1459
|
+
}
|
|
1460
|
+
},
|
|
1461
|
+
// App-only: plain summing connect; the hooks only swap which panel memory
|
|
1462
|
+
// drives the offset.
|
|
1463
|
+
onConnect(nodes, module, portId, sourceNode) {
|
|
1464
|
+
if (portId !== "voct") return false;
|
|
1465
|
+
const v = nodes._freqVoct;
|
|
1466
|
+
sourceNode.connect(v);
|
|
1467
|
+
patchCount.set(nodes, (patchCount.get(nodes) ?? 0) + 1);
|
|
1468
|
+
v.parameters.get("transpose").value = module.transpose ?? 0;
|
|
1469
|
+
return true;
|
|
1470
|
+
},
|
|
1471
|
+
onDisconnect(nodes, module, portId, sourceNode) {
|
|
1472
|
+
if (portId !== "voct") return false;
|
|
1473
|
+
const v = nodes._freqVoct;
|
|
1474
|
+
try {
|
|
1475
|
+
sourceNode.disconnect(v);
|
|
1476
|
+
} catch (e) {
|
|
1477
|
+
}
|
|
1478
|
+
const n = Math.max(0, (patchCount.get(nodes) ?? 0) - 1);
|
|
1479
|
+
patchCount.set(nodes, n);
|
|
1480
|
+
if (!n) v.parameters.get("transpose").value = hzToMidi(module.frequency);
|
|
1481
|
+
return true;
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
// src/lib-runtime/generated/modules/comb-filter.wrapper.ts
|
|
1486
|
+
var CombFilter = /* @__PURE__ */ defineModule(combFilterDescriptor, {
|
|
1487
|
+
worklets: {
|
|
1488
|
+
"map-voct-to-period": map_voct_to_period_default,
|
|
1489
|
+
"comb-filter": comb_filter_default
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
// src/lib-runtime/generated/modules/comp.tsx
|
|
1494
|
+
var params6 = {
|
|
1495
|
+
threshold: { node: "comp", field: "threshold", unit: "dB", default: -24, min: -60, max: 0 },
|
|
1496
|
+
ratio: { node: "comp", field: "ratio", unit: "factor", default: 4, min: 1, max: 20, scale: "log" },
|
|
1497
|
+
attack: { node: "comp", field: "attack", unit: "s", default: 3e-3, min: 1e-3, max: 1, scale: "log" },
|
|
1498
|
+
release: { node: "comp", field: "release", unit: "s", default: 0.25, min: 0.01, max: 1, scale: "log" },
|
|
1499
|
+
knee: { node: "comp", field: "knee", unit: "dB", default: 10, min: 0, max: 40 },
|
|
1500
|
+
// makeup is stored in dB but drives a linear gain node (Math.pow(10, v/20)) —
|
|
1501
|
+
// not a 1:1 AudioParam mapping, so no node/field.
|
|
1502
|
+
makeup: { unit: "dB", default: 0, min: 0, max: 30 }
|
|
1503
|
+
};
|
|
1504
|
+
var compressorDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1505
|
+
type: "compressor",
|
|
1506
|
+
params: params6,
|
|
1507
|
+
ports: {
|
|
1508
|
+
"in": { type: "input", signal: "audio", node: "comp" },
|
|
1509
|
+
"out": { type: "output", signal: "audio", node: "makeup" }
|
|
1510
|
+
},
|
|
1511
|
+
createModule(id, position) {
|
|
1512
|
+
return { id, type: "compressor", position, ...paramDefaults(params6) };
|
|
1513
|
+
},
|
|
1514
|
+
createNodes(ctx, module) {
|
|
1515
|
+
const comp = ctx.createDynamicsCompressor();
|
|
1516
|
+
comp.threshold.value = module.threshold;
|
|
1517
|
+
comp.ratio.value = module.ratio;
|
|
1518
|
+
comp.attack.value = module.attack;
|
|
1519
|
+
comp.release.value = module.release;
|
|
1520
|
+
comp.knee.value = module.knee;
|
|
1521
|
+
const makeup = ctx.createGain();
|
|
1522
|
+
makeup.gain.value = Math.pow(10, (module.makeup || 0) / 20);
|
|
1523
|
+
comp.connect(makeup);
|
|
1524
|
+
return { comp, makeup };
|
|
1525
|
+
},
|
|
1526
|
+
updateParam(nodes, _m, param, value) {
|
|
1527
|
+
const v = value;
|
|
1528
|
+
switch (param) {
|
|
1529
|
+
case "threshold":
|
|
1530
|
+
nodes.comp.threshold.value = v;
|
|
1531
|
+
break;
|
|
1532
|
+
case "ratio":
|
|
1533
|
+
nodes.comp.ratio.value = v;
|
|
1534
|
+
break;
|
|
1535
|
+
case "attack":
|
|
1536
|
+
nodes.comp.attack.value = v;
|
|
1537
|
+
break;
|
|
1538
|
+
case "release":
|
|
1539
|
+
nodes.comp.release.value = v;
|
|
1540
|
+
break;
|
|
1541
|
+
case "knee":
|
|
1542
|
+
nodes.comp.knee.value = v;
|
|
1543
|
+
break;
|
|
1544
|
+
case "makeup":
|
|
1545
|
+
nodes.makeup.gain.value = Math.pow(10, v / 20);
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
// src/lib-runtime/generated/modules/comp.wrapper.ts
|
|
1552
|
+
var Compressor = /* @__PURE__ */ defineModule(compressorDescriptor);
|
|
1553
|
+
|
|
1554
|
+
// src/lib-runtime/generated/modules/crossfade.tsx
|
|
1555
|
+
var params7 = {
|
|
1556
|
+
blend: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
1557
|
+
curve: { kind: "enum", default: "equal-power", options: ["linear", "equal-power"] }
|
|
1558
|
+
};
|
|
1559
|
+
var CURVE_POINTS2 = 2049;
|
|
1560
|
+
function lawCurve2(fn) {
|
|
1561
|
+
const c = new Float32Array(CURVE_POINTS2);
|
|
1562
|
+
for (let i = 0; i < CURVE_POINTS2; i++) {
|
|
1563
|
+
const x = -1 + 2 * i / (CURVE_POINTS2 - 1);
|
|
1564
|
+
c[i] = fn(Math.min(1, Math.max(0, x)));
|
|
1565
|
+
}
|
|
1566
|
+
return c;
|
|
1567
|
+
}
|
|
1568
|
+
function curvesFor(curve) {
|
|
1569
|
+
if (curve === "equal-power") {
|
|
1570
|
+
return {
|
|
1571
|
+
a: lawCurve2((m) => Math.cos(m * Math.PI / 2)),
|
|
1572
|
+
b: lawCurve2((m) => Math.sin(m * Math.PI / 2))
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
return {
|
|
1576
|
+
a: lawCurve2((m) => 1 - m),
|
|
1577
|
+
b: lawCurve2((m) => m)
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
var crossfadeDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1581
|
+
type: "crossfade",
|
|
1582
|
+
params: params7,
|
|
1583
|
+
ports: {
|
|
1584
|
+
"a": { type: "input", signal: "audio", node: "inputA" },
|
|
1585
|
+
"b": { type: "input", signal: "audio", node: "inputB" },
|
|
1586
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
1587
|
+
},
|
|
1588
|
+
createModule(id, position) {
|
|
1589
|
+
return { id, type: "crossfade", position, ...paramDefaults(params7) };
|
|
1590
|
+
},
|
|
1591
|
+
createNodes(ctx, module) {
|
|
1592
|
+
const inputA = ctx.createGain();
|
|
1593
|
+
const inputB = ctx.createGain();
|
|
1594
|
+
const output = ctx.createGain();
|
|
1595
|
+
inputA.gain.value = 0;
|
|
1596
|
+
inputB.gain.value = 0;
|
|
1597
|
+
const _mix = ctx.createConstantSource();
|
|
1598
|
+
_mix.offset.value = module.blend;
|
|
1599
|
+
const { a, b } = curvesFor(module.curve);
|
|
1600
|
+
const curveA = ctx.createWaveShaper();
|
|
1601
|
+
curveA.curve = a;
|
|
1602
|
+
const curveB = ctx.createWaveShaper();
|
|
1603
|
+
curveB.curve = b;
|
|
1604
|
+
_mix.connect(curveA).connect(inputA.gain);
|
|
1605
|
+
_mix.connect(curveB).connect(inputB.gain);
|
|
1606
|
+
_mix.start();
|
|
1607
|
+
inputA.connect(output);
|
|
1608
|
+
inputB.connect(output);
|
|
1609
|
+
return { inputA, inputB, output, _mix, curveA, curveB };
|
|
1610
|
+
},
|
|
1611
|
+
updateParam(nodes, module, param, value) {
|
|
1612
|
+
if (param === "blend") {
|
|
1613
|
+
nodes._mix.offset.value = value;
|
|
1614
|
+
} else if (param === "curve") {
|
|
1615
|
+
const { a, b } = curvesFor(value);
|
|
1616
|
+
nodes.curveA.curve = a;
|
|
1617
|
+
nodes.curveB.curve = b;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
// src/lib-runtime/generated/modules/crossfade.wrapper.ts
|
|
1623
|
+
var Crossfade = /* @__PURE__ */ defineModule(crossfadeDescriptor);
|
|
1624
|
+
|
|
1625
|
+
// src/lib-runtime/generated/modules/delay.tsx
|
|
1626
|
+
var params8 = {
|
|
1627
|
+
time: { node: "delay", field: "period", default: 0.3, min: 0.01, max: 1, scale: "log", unit: "s" },
|
|
1628
|
+
feedback: { node: "delay", field: "feedback", unit: "factor", default: 0.4, min: 0, max: 0.95 },
|
|
1629
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
1630
|
+
};
|
|
1631
|
+
var delayDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1632
|
+
type: "delay",
|
|
1633
|
+
params: params8,
|
|
1634
|
+
ports: {
|
|
1635
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1636
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
1637
|
+
},
|
|
1638
|
+
createModule(id, position) {
|
|
1639
|
+
return { id, type: "delay", position, ...paramDefaults(params8) };
|
|
1640
|
+
},
|
|
1641
|
+
createNodes(ctx, module) {
|
|
1642
|
+
const delay = createCombFilterNode(ctx, module.feedback, 5, 2);
|
|
1643
|
+
delay.parameters.get("period").value = module.time;
|
|
1644
|
+
const dryGain = ctx.createGain();
|
|
1645
|
+
const wetGain = ctx.createGain();
|
|
1646
|
+
const input = ctx.createGain();
|
|
1647
|
+
const output = ctx.createGain();
|
|
1648
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
1649
|
+
input.connect(dryGain);
|
|
1650
|
+
input.connect(delay);
|
|
1651
|
+
delay.connect(wetGain);
|
|
1652
|
+
dryGain.connect(output);
|
|
1653
|
+
wetGain.connect(output);
|
|
1654
|
+
return { input, output, delay, dryGain, wetGain, _mix };
|
|
1655
|
+
},
|
|
1656
|
+
updateParam(nodes, _m, param, value) {
|
|
1657
|
+
switch (param) {
|
|
1658
|
+
case "time":
|
|
1659
|
+
nodes.delay.parameters.get("period").value = value;
|
|
1660
|
+
break;
|
|
1661
|
+
case "feedback":
|
|
1662
|
+
nodes.delay.parameters.get("feedback").value = value;
|
|
1663
|
+
break;
|
|
1664
|
+
case "mix":
|
|
1665
|
+
nodes._mix.offset.value = value;
|
|
1666
|
+
break;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
// src/lib-runtime/generated/modules/delay.wrapper.ts
|
|
1672
|
+
var Delay = /* @__PURE__ */ defineModule(delayDescriptor, {
|
|
1673
|
+
worklets: {
|
|
1674
|
+
"comb-filter": comb_filter_default
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
// src/lib-runtime/generated/modules/dimension.tsx
|
|
1679
|
+
var params9 = {
|
|
1680
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 0.5, min: 0.1, max: 5 },
|
|
1681
|
+
depth: { unit: "factor", default: 0.3, min: 0, max: 1 }
|
|
1682
|
+
};
|
|
1683
|
+
var dimensionDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1684
|
+
type: "dimension",
|
|
1685
|
+
params: params9,
|
|
1686
|
+
ports: {
|
|
1687
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1688
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
1689
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" }
|
|
1690
|
+
},
|
|
1691
|
+
createModule(id, position) {
|
|
1692
|
+
return { id, type: "dimension", position, ...paramDefaults(params9) };
|
|
1693
|
+
},
|
|
1694
|
+
createNodes(ctx, module) {
|
|
1695
|
+
const input = ctx.createGain();
|
|
1696
|
+
input.channelCount = 2;
|
|
1697
|
+
input.channelCountMode = "explicit";
|
|
1698
|
+
input.channelInterpretation = "speakers";
|
|
1699
|
+
const output = ctx.createGain();
|
|
1700
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
1701
|
+
const merger = ctx.createChannelMerger(2);
|
|
1702
|
+
const leftDelay = ctx.createDelay(0.05);
|
|
1703
|
+
const rightDelay = ctx.createDelay(0.05);
|
|
1704
|
+
const lfo = ctx.createOscillator();
|
|
1705
|
+
const lfoGainL = ctx.createGain();
|
|
1706
|
+
const lfoGainR = ctx.createGain();
|
|
1707
|
+
const llGain = ctx.createGain();
|
|
1708
|
+
const lrGain = ctx.createGain();
|
|
1709
|
+
const rlGain = ctx.createGain();
|
|
1710
|
+
const rrGain = ctx.createGain();
|
|
1711
|
+
const w = module.width;
|
|
1712
|
+
llGain.gain.value = (1 + w) / 2;
|
|
1713
|
+
rrGain.gain.value = (1 + w) / 2;
|
|
1714
|
+
rlGain.gain.value = (1 - w) / 2;
|
|
1715
|
+
lrGain.gain.value = (1 - w) / 2;
|
|
1716
|
+
leftDelay.delayTime.value = 0.01;
|
|
1717
|
+
rightDelay.delayTime.value = 0.012;
|
|
1718
|
+
lfo.type = "sine";
|
|
1719
|
+
lfo.frequency.value = module.rate;
|
|
1720
|
+
lfoGainL.gain.value = module.depth * 2e-3;
|
|
1721
|
+
lfoGainR.gain.value = -module.depth * 2e-3;
|
|
1722
|
+
lfo.connect(lfoGainL);
|
|
1723
|
+
lfo.connect(lfoGainR);
|
|
1724
|
+
lfoGainL.connect(leftDelay.delayTime);
|
|
1725
|
+
lfoGainR.connect(rightDelay.delayTime);
|
|
1726
|
+
lfo.start();
|
|
1727
|
+
input.connect(splitter);
|
|
1728
|
+
splitter.connect(leftDelay, 0);
|
|
1729
|
+
splitter.connect(rightDelay, 1);
|
|
1730
|
+
leftDelay.connect(llGain);
|
|
1731
|
+
leftDelay.connect(lrGain);
|
|
1732
|
+
rightDelay.connect(rrGain);
|
|
1733
|
+
rightDelay.connect(rlGain);
|
|
1734
|
+
llGain.connect(merger, 0, 0);
|
|
1735
|
+
rlGain.connect(merger, 0, 0);
|
|
1736
|
+
lrGain.connect(merger, 0, 1);
|
|
1737
|
+
rrGain.connect(merger, 0, 1);
|
|
1738
|
+
merger.connect(output);
|
|
1739
|
+
return { input, output, splitter, merger, leftDelay, rightDelay, lfo, lfoGainL, lfoGainR, llGain, rrGain, lrGain, rlGain };
|
|
1740
|
+
},
|
|
1741
|
+
updateParam(nodes, _m, param, value) {
|
|
1742
|
+
if (param === "width") {
|
|
1743
|
+
const w = value;
|
|
1744
|
+
nodes.llGain.gain.value = (1 + w) / 2;
|
|
1745
|
+
nodes.rrGain.gain.value = (1 + w) / 2;
|
|
1746
|
+
nodes.rlGain.gain.value = (1 - w) / 2;
|
|
1747
|
+
nodes.lrGain.gain.value = (1 - w) / 2;
|
|
1748
|
+
} else if (param === "rate") nodes.lfo.frequency.value = value;
|
|
1749
|
+
else if (param === "depth") {
|
|
1750
|
+
nodes.lfoGainL.gain.value = value * 2e-3;
|
|
1751
|
+
nodes.lfoGainR.gain.value = -value * 2e-3;
|
|
1752
|
+
}
|
|
1753
|
+
},
|
|
1754
|
+
dispose(nodes) {
|
|
1755
|
+
nodes.lfo.stop();
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
// src/lib-runtime/generated/modules/dimension.wrapper.ts
|
|
1760
|
+
var Dimension = /* @__PURE__ */ defineModule(dimensionDescriptor);
|
|
1761
|
+
|
|
1762
|
+
// src/lib-runtime/generated/modules/distortion.tsx
|
|
1763
|
+
var params10 = {
|
|
1764
|
+
distortionType: { kind: "enum", default: "soft", options: ["soft", "hard", "fuzz"] },
|
|
1765
|
+
drive: { unit: "factor", default: 5, min: 1, max: 20, scale: "log" },
|
|
1766
|
+
frequency: { node: "toneFilter", field: "frequency", unit: "Hz", default: 16e3, min: 500, max: 16e3, scale: "log" },
|
|
1767
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.8, min: 0, max: 1 }
|
|
1768
|
+
};
|
|
1769
|
+
var FUZZ_BIAS = 0.7;
|
|
1770
|
+
function transfer(type, k, x) {
|
|
1771
|
+
if (type === "soft") return Math.tanh(k * x);
|
|
1772
|
+
if (type === "hard") return Math.max(-1, Math.min(1, k * x));
|
|
1773
|
+
const f = (u) => Math.sign(u) * (1 - Math.exp(-Math.abs(u)));
|
|
1774
|
+
return (f(k * x + FUZZ_BIAS) - f(FUZZ_BIAS)) / (1 + f(FUZZ_BIAS));
|
|
1775
|
+
}
|
|
1776
|
+
var CURVE_LEN = 16384;
|
|
1777
|
+
function makeDistortionCurve(type, drive) {
|
|
1778
|
+
const k = Math.max(1, Math.min(20, drive));
|
|
1779
|
+
const curve = new Float32Array(CURVE_LEN);
|
|
1780
|
+
for (let i = 0; i < CURVE_LEN; i++) {
|
|
1781
|
+
const x = i * 2 / (CURVE_LEN - 1) - 1;
|
|
1782
|
+
curve[i] = transfer(type, k, x);
|
|
1783
|
+
}
|
|
1784
|
+
return curve;
|
|
1785
|
+
}
|
|
1786
|
+
function computeMakeup(type, drive) {
|
|
1787
|
+
const k = Math.max(1, Math.min(20, drive));
|
|
1788
|
+
const N = 512;
|
|
1789
|
+
let sum = 0;
|
|
1790
|
+
let sumSq = 0;
|
|
1791
|
+
for (let i = 0; i < N; i++) {
|
|
1792
|
+
const y = transfer(type, k, 0.5 * Math.sin(2 * Math.PI * i / N));
|
|
1793
|
+
sum += y;
|
|
1794
|
+
sumSq += y * y;
|
|
1795
|
+
}
|
|
1796
|
+
const mean = sum / N;
|
|
1797
|
+
const acRms = Math.sqrt(Math.max(0, sumSq / N - mean * mean));
|
|
1798
|
+
const rmsIn = 0.5 / Math.SQRT2;
|
|
1799
|
+
if (acRms < 1e-6) return 1;
|
|
1800
|
+
return Math.min(4, Math.max(0.25, rmsIn / acRms));
|
|
1801
|
+
}
|
|
1802
|
+
var distortionTypes = params10.distortionType.options;
|
|
1803
|
+
var distortionDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1804
|
+
type: "distortion",
|
|
1805
|
+
params: params10,
|
|
1806
|
+
ports: {
|
|
1807
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1808
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
1809
|
+
},
|
|
1810
|
+
createModule(id, position) {
|
|
1811
|
+
return {
|
|
1812
|
+
id,
|
|
1813
|
+
type: "distortion",
|
|
1814
|
+
position,
|
|
1815
|
+
...paramDefaults(params10)
|
|
1816
|
+
};
|
|
1817
|
+
},
|
|
1818
|
+
createNodes(ctx, module) {
|
|
1819
|
+
const input = ctx.createGain();
|
|
1820
|
+
const output = ctx.createGain();
|
|
1821
|
+
const dryGain = ctx.createGain();
|
|
1822
|
+
const wetGain = ctx.createGain();
|
|
1823
|
+
const waveshaper = ctx.createWaveShaper();
|
|
1824
|
+
const makeup = ctx.createGain();
|
|
1825
|
+
const dcBlock = ctx.createBiquadFilter();
|
|
1826
|
+
const toneFilter = ctx.createBiquadFilter();
|
|
1827
|
+
waveshaper.curve = makeDistortionCurve(module.distortionType, module.drive);
|
|
1828
|
+
waveshaper.oversample = "4x";
|
|
1829
|
+
makeup.gain.value = computeMakeup(module.distortionType, module.drive);
|
|
1830
|
+
dcBlock.type = "highpass";
|
|
1831
|
+
dcBlock.frequency.value = 10;
|
|
1832
|
+
dcBlock.Q.value = 0.707;
|
|
1833
|
+
toneFilter.type = "lowpass";
|
|
1834
|
+
toneFilter.frequency.value = module.frequency;
|
|
1835
|
+
toneFilter.Q.value = 0.707;
|
|
1836
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
1837
|
+
input.connect(dryGain);
|
|
1838
|
+
input.connect(waveshaper);
|
|
1839
|
+
waveshaper.connect(makeup);
|
|
1840
|
+
makeup.connect(dcBlock);
|
|
1841
|
+
dcBlock.connect(toneFilter);
|
|
1842
|
+
toneFilter.connect(wetGain);
|
|
1843
|
+
dryGain.connect(output);
|
|
1844
|
+
wetGain.connect(output);
|
|
1845
|
+
return { input, output, waveshaper, makeup, dcBlock, toneFilter, dryGain, wetGain, _mix };
|
|
1846
|
+
},
|
|
1847
|
+
updateParam(nodes, module, param, value) {
|
|
1848
|
+
if (param === "drive" || param === "distortionType") {
|
|
1849
|
+
nodes.waveshaper.curve = makeDistortionCurve(module.distortionType, module.drive);
|
|
1850
|
+
nodes.makeup.gain.value = computeMakeup(module.distortionType, module.drive);
|
|
1851
|
+
}
|
|
1852
|
+
if (param === "frequency") {
|
|
1853
|
+
nodes.toneFilter.frequency.value = value;
|
|
1854
|
+
}
|
|
1855
|
+
if (param === "mix") {
|
|
1856
|
+
nodes._mix.offset.value = value;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
// src/lib-runtime/generated/modules/distortion.wrapper.ts
|
|
1862
|
+
var Distortion = /* @__PURE__ */ defineModule(distortionDescriptor);
|
|
1863
|
+
|
|
1864
|
+
// src/lib-runtime/generated/modules/ducker.tsx
|
|
1865
|
+
var params11 = {
|
|
1866
|
+
// Feeds scInput.gain via 1/10^(threshold/20) — a conversion, not a 1:1
|
|
1867
|
+
// AudioParam write — so no node/field.
|
|
1868
|
+
threshold: { unit: "dB", default: -24, min: -60, max: 0 },
|
|
1869
|
+
// Feeds invertGain.gain via negation (-amount) — no node/field.
|
|
1870
|
+
amount: { unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
1871
|
+
attack: { node: "envFollower", field: "attack", unit: "s", default: 0.01, min: 1e-3, max: 0.1, scale: "log" },
|
|
1872
|
+
release: { node: "envFollower", field: "release", unit: "s", default: 0.2, min: 1e-3, max: 1, scale: "log" }
|
|
1873
|
+
};
|
|
1874
|
+
var duckerDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1875
|
+
type: "ducker",
|
|
1876
|
+
params: params11,
|
|
1877
|
+
ports: {
|
|
1878
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1879
|
+
"sc": { type: "input", signal: "audio", node: "scInput" },
|
|
1880
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
1881
|
+
},
|
|
1882
|
+
createModule(id, position) {
|
|
1883
|
+
return { id, type: "ducker", position, ...paramDefaults(params11) };
|
|
1884
|
+
},
|
|
1885
|
+
createNodes(ctx, module) {
|
|
1886
|
+
const input = ctx.createGain();
|
|
1887
|
+
const scInput = ctx.createGain();
|
|
1888
|
+
const output = ctx.createGain();
|
|
1889
|
+
const duckGain = ctx.createGain();
|
|
1890
|
+
scInput.gain.value = 1 / Math.max(1e-3, Math.pow(10, module.threshold / 20));
|
|
1891
|
+
const scClamp = ctx.createWaveShaper();
|
|
1892
|
+
scClamp.oversample = "none";
|
|
1893
|
+
const SAT = 1.2;
|
|
1894
|
+
const curve = new Float32Array(1024);
|
|
1895
|
+
for (let i = 0; i < curve.length; i++) {
|
|
1896
|
+
const x = i / (curve.length - 1) * 2 - 1;
|
|
1897
|
+
curve[i] = SAT * Math.tanh(x / SAT);
|
|
1898
|
+
}
|
|
1899
|
+
scClamp.curve = curve;
|
|
1900
|
+
const envFollower = createEnvFollowerNode(ctx);
|
|
1901
|
+
envFollower.parameters.get("attack").value = module.attack;
|
|
1902
|
+
envFollower.parameters.get("release").value = module.release;
|
|
1903
|
+
const envDrive = ctx.createGain();
|
|
1904
|
+
envDrive.gain.value = 1.2;
|
|
1905
|
+
const envClamp = ctx.createWaveShaper();
|
|
1906
|
+
envClamp.curve = new Float32Array([0, 0, 1]);
|
|
1907
|
+
const invertGain = ctx.createGain();
|
|
1908
|
+
invertGain.gain.value = -module.amount;
|
|
1909
|
+
const constantSource = ctx.createConstantSource();
|
|
1910
|
+
constantSource.offset.value = 1;
|
|
1911
|
+
constantSource.start();
|
|
1912
|
+
scInput.connect(scClamp);
|
|
1913
|
+
scClamp.connect(envFollower);
|
|
1914
|
+
envFollower.connect(envDrive);
|
|
1915
|
+
envDrive.connect(envClamp);
|
|
1916
|
+
envClamp.connect(invertGain);
|
|
1917
|
+
invertGain.connect(duckGain.gain);
|
|
1918
|
+
constantSource.connect(duckGain.gain);
|
|
1919
|
+
duckGain.gain.value = 0;
|
|
1920
|
+
input.connect(duckGain);
|
|
1921
|
+
duckGain.connect(output);
|
|
1922
|
+
return { input, scInput, scClamp, output, invertGain, envFollower, envDrive, envClamp, constantSource };
|
|
1923
|
+
},
|
|
1924
|
+
updateParam(nodes, _m, param, value) {
|
|
1925
|
+
if (param === "threshold") {
|
|
1926
|
+
nodes.scInput.gain.value = 1 / Math.max(1e-3, Math.pow(10, value / 20));
|
|
1927
|
+
} else if (param === "amount") {
|
|
1928
|
+
nodes.invertGain.gain.value = -value;
|
|
1929
|
+
} else if (param === "attack") {
|
|
1930
|
+
nodes.envFollower.parameters.get("attack").value = value;
|
|
1931
|
+
} else if (param === "release") {
|
|
1932
|
+
nodes.envFollower.parameters.get("release").value = value;
|
|
1933
|
+
}
|
|
1934
|
+
},
|
|
1935
|
+
dispose(nodes) {
|
|
1936
|
+
nodes.constantSource.stop();
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
|
|
1940
|
+
// src/lib-runtime/generated/modules/ducker.wrapper.ts
|
|
1941
|
+
var Ducker = /* @__PURE__ */ defineModule(duckerDescriptor, {
|
|
1942
|
+
worklets: {
|
|
1943
|
+
"env-follower": env_follower_default
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
// src/lib-runtime/generated/modules/env-follow.tsx
|
|
1948
|
+
var params12 = {
|
|
1949
|
+
attack: { node: "envFollower", field: "attack", unit: "s", default: 0.01, min: 1e-3, max: 0.5, scale: "log" },
|
|
1950
|
+
release: { node: "envFollower", field: "release", unit: "s", default: 0.1, min: 1e-3, max: 2, scale: "log" },
|
|
1951
|
+
// sensitivityGain.gain = sensitivity * 4 — a scaled conversion, not a 1:1
|
|
1952
|
+
// AudioParam write — so no node/field.
|
|
1953
|
+
sensitivity: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
1954
|
+
depth: { node: "depthGain", field: "gain", unit: "semitone", default: 48, min: 0, max: 96, scale: "sqrt" }
|
|
1955
|
+
};
|
|
1956
|
+
var envFollowDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
1957
|
+
type: "env-follow",
|
|
1958
|
+
params: params12,
|
|
1959
|
+
ports: {
|
|
1960
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
1961
|
+
"env": { type: "output", node: "depthGain", signal: "modulation" }
|
|
1962
|
+
},
|
|
1963
|
+
createModule(id, position) {
|
|
1964
|
+
return { id, type: "env-follow", position, ...paramDefaults(params12) };
|
|
1965
|
+
},
|
|
1966
|
+
createNodes(ctx, module) {
|
|
1967
|
+
const input = ctx.createGain();
|
|
1968
|
+
const depthGain = ctx.createGain();
|
|
1969
|
+
const sensitivityGain = ctx.createGain();
|
|
1970
|
+
sensitivityGain.gain.value = module.sensitivity * 4;
|
|
1971
|
+
depthGain.gain.value = module.depth;
|
|
1972
|
+
const envFollower = createEnvFollowerNode(ctx);
|
|
1973
|
+
envFollower.parameters.get("attack").value = module.attack;
|
|
1974
|
+
envFollower.parameters.get("release").value = module.release;
|
|
1975
|
+
input.connect(sensitivityGain);
|
|
1976
|
+
sensitivityGain.connect(envFollower);
|
|
1977
|
+
envFollower.connect(depthGain);
|
|
1978
|
+
return { input, depthGain, sensitivityGain, envFollower };
|
|
1979
|
+
},
|
|
1980
|
+
updateParam(nodes, _m, param, value) {
|
|
1981
|
+
if (param === "sensitivity") {
|
|
1982
|
+
nodes.sensitivityGain.gain.value = value * 4;
|
|
1983
|
+
} else if (param === "depth") {
|
|
1984
|
+
nodes.depthGain.gain.value = value;
|
|
1985
|
+
} else if (param === "attack") {
|
|
1986
|
+
nodes.envFollower.parameters.get("attack").value = value;
|
|
1987
|
+
} else if (param === "release") {
|
|
1988
|
+
nodes.envFollower.parameters.get("release").value = value;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
// src/lib-runtime/generated/modules/env-follow.wrapper.ts
|
|
1994
|
+
var EnvelopeFollower = /* @__PURE__ */ defineModule(envFollowDescriptor, {
|
|
1995
|
+
worklets: {
|
|
1996
|
+
"env-follower": env_follower_default
|
|
1997
|
+
}
|
|
1998
|
+
});
|
|
1999
|
+
|
|
2000
|
+
// src/lib-runtime/generated/modules/envelope.tsx
|
|
2001
|
+
var params13 = {
|
|
2002
|
+
attack: { node: "adsrNode", field: "attack", default: 0.01, min: 1e-3, max: 2, scale: "log", unit: "s" },
|
|
2003
|
+
decay: { node: "adsrNode", field: "decay", default: 0.1, min: 1e-3, max: 2, scale: "log", unit: "s" },
|
|
2004
|
+
sustain: { node: "adsrNode", field: "sustain", unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
2005
|
+
release: { node: "adsrNode", field: "release", default: 0.3, min: 1e-3, max: 3, scale: "log", unit: "s" },
|
|
2006
|
+
depth: { node: "envOut", field: "gain", default: 48, min: 0, max: 96, scale: "sqrt", unit: "semitone" }
|
|
2007
|
+
};
|
|
2008
|
+
var envelopeDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2009
|
+
type: "envelope",
|
|
2010
|
+
params: params13,
|
|
2011
|
+
ports: {
|
|
2012
|
+
"in": { type: "input", signal: "audio", node: "mainGain" },
|
|
2013
|
+
"out": { type: "output", signal: "audio", node: "mainGain" },
|
|
2014
|
+
"env": { type: "output", signal: "modulation", node: "envOut" },
|
|
2015
|
+
"trigger": { type: "input", signal: "gate", node: "adsrNode" }
|
|
2016
|
+
},
|
|
2017
|
+
createModule(id, position) {
|
|
2018
|
+
return { id, type: "envelope", position, ...paramDefaults(params13) };
|
|
2019
|
+
},
|
|
2020
|
+
createNodes(ctx, module) {
|
|
2021
|
+
const mainGain = ctx.createGain();
|
|
2022
|
+
mainGain.gain.value = 0;
|
|
2023
|
+
const adsrNode = createAdsrNode(ctx);
|
|
2024
|
+
adsrNode.parameters.get("attack").value = module.attack;
|
|
2025
|
+
adsrNode.parameters.get("decay").value = module.decay;
|
|
2026
|
+
adsrNode.parameters.get("sustain").value = module.sustain;
|
|
2027
|
+
adsrNode.parameters.get("release").value = module.release;
|
|
2028
|
+
adsrNode.connect(mainGain.gain);
|
|
2029
|
+
const depth = module.depth ?? 48;
|
|
2030
|
+
const envOut = ctx.createGain();
|
|
2031
|
+
envOut.gain.value = depth;
|
|
2032
|
+
adsrNode.connect(envOut);
|
|
2033
|
+
return { mainGain, adsrNode, envOut };
|
|
2034
|
+
},
|
|
2035
|
+
updateParam(nodes, _module, param, value) {
|
|
2036
|
+
const v = value;
|
|
2037
|
+
const p = nodes.adsrNode.parameters;
|
|
2038
|
+
switch (param) {
|
|
2039
|
+
case "attack":
|
|
2040
|
+
p.get("attack").value = v;
|
|
2041
|
+
break;
|
|
2042
|
+
case "decay":
|
|
2043
|
+
p.get("decay").value = v;
|
|
2044
|
+
break;
|
|
2045
|
+
case "sustain":
|
|
2046
|
+
p.get("sustain").value = v;
|
|
2047
|
+
break;
|
|
2048
|
+
case "release":
|
|
2049
|
+
p.get("release").value = v;
|
|
2050
|
+
break;
|
|
2051
|
+
case "depth":
|
|
2052
|
+
nodes.envOut.gain.value = v;
|
|
2053
|
+
break;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
});
|
|
2057
|
+
|
|
2058
|
+
// src/lib-runtime/generated/modules/envelope.wrapper.ts
|
|
2059
|
+
var Envelope = /* @__PURE__ */ defineModule(envelopeDescriptor, {
|
|
2060
|
+
worklets: {
|
|
2061
|
+
"adsr-envelope": adsr_envelope_default
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
// src/lib-runtime/generated/modules/eq3.tsx
|
|
2066
|
+
var params14 = {
|
|
2067
|
+
low: { node: "lowShelf", field: "gain", unit: "dB", default: 0, min: -12, max: 12 },
|
|
2068
|
+
mid: { node: "midPeak", field: "gain", unit: "dB", default: 0, min: -12, max: 12 },
|
|
2069
|
+
high: { node: "highShelf", field: "gain", unit: "dB", default: 0, min: -12, max: 12 },
|
|
2070
|
+
midFreq: { node: "midPeak", field: "frequency", unit: "Hz", default: 1e3, min: 200, max: 5e3, scale: "log" }
|
|
2071
|
+
};
|
|
2072
|
+
var eq3Descriptor = /* @__PURE__ */ defineDescriptor({
|
|
2073
|
+
type: "eq3",
|
|
2074
|
+
params: params14,
|
|
2075
|
+
ports: {
|
|
2076
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2077
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
2078
|
+
},
|
|
2079
|
+
createModule(id, position) {
|
|
2080
|
+
return { id, type: "eq3", position, ...paramDefaults(params14) };
|
|
2081
|
+
},
|
|
2082
|
+
createNodes(ctx, module) {
|
|
2083
|
+
const input = ctx.createGain();
|
|
2084
|
+
const output = ctx.createGain();
|
|
2085
|
+
const lowShelf = ctx.createBiquadFilter();
|
|
2086
|
+
const midPeak = ctx.createBiquadFilter();
|
|
2087
|
+
const highShelf = ctx.createBiquadFilter();
|
|
2088
|
+
lowShelf.type = "lowshelf";
|
|
2089
|
+
lowShelf.frequency.value = 320;
|
|
2090
|
+
lowShelf.gain.value = module.low;
|
|
2091
|
+
midPeak.type = "peaking";
|
|
2092
|
+
midPeak.frequency.value = module.midFreq;
|
|
2093
|
+
midPeak.Q.value = 1.5;
|
|
2094
|
+
midPeak.gain.value = module.mid;
|
|
2095
|
+
highShelf.type = "highshelf";
|
|
2096
|
+
highShelf.frequency.value = 3200;
|
|
2097
|
+
highShelf.gain.value = module.high;
|
|
2098
|
+
input.connect(lowShelf);
|
|
2099
|
+
lowShelf.connect(midPeak);
|
|
2100
|
+
midPeak.connect(highShelf);
|
|
2101
|
+
highShelf.connect(output);
|
|
2102
|
+
return { input, output, lowShelf, midPeak, highShelf };
|
|
2103
|
+
},
|
|
2104
|
+
updateParam(nodes, _m, param, value) {
|
|
2105
|
+
switch (param) {
|
|
2106
|
+
case "low":
|
|
2107
|
+
nodes.lowShelf.gain.value = value;
|
|
2108
|
+
break;
|
|
2109
|
+
case "mid":
|
|
2110
|
+
nodes.midPeak.gain.value = value;
|
|
2111
|
+
break;
|
|
2112
|
+
case "high":
|
|
2113
|
+
nodes.highShelf.gain.value = value;
|
|
2114
|
+
break;
|
|
2115
|
+
case "midFreq":
|
|
2116
|
+
nodes.midPeak.frequency.value = value;
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
|
|
2122
|
+
// src/lib-runtime/generated/modules/eq3.wrapper.ts
|
|
2123
|
+
var ThreeBandEq = /* @__PURE__ */ defineModule(eq3Descriptor);
|
|
2124
|
+
|
|
2125
|
+
// src/lib-runtime/generated/modules/exciter.tsx
|
|
2126
|
+
var params15 = {
|
|
2127
|
+
// drive rebuilds the saturation curve AND scales highshelf.gain — not a
|
|
2128
|
+
// 1:1 AudioParam write, so no node/field.
|
|
2129
|
+
drive: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
2130
|
+
// frequency writes BOTH highpass.frequency and highshelf.frequency — two
|
|
2131
|
+
// AudioParams, so no single node/field mapping.
|
|
2132
|
+
frequency: { unit: "Hz", default: 4e3, min: 2e3, max: 8e3, scale: "log" },
|
|
2133
|
+
// Additive blend, not a dry/wet crossfade: dry stays at unity while wetGain
|
|
2134
|
+
// scales the excited signal — a genuine 1:1 AudioParam write.
|
|
2135
|
+
mix: { node: "wetGain", field: "gain", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
2136
|
+
};
|
|
2137
|
+
function makeExciterCurve(amount) {
|
|
2138
|
+
const samples = 44100;
|
|
2139
|
+
const curve = new Float32Array(samples);
|
|
2140
|
+
const k = 1 + amount * 8;
|
|
2141
|
+
const norm = 0.5 / (Math.tanh(k * 0.5) * 1.15);
|
|
2142
|
+
for (let i = 0; i < samples; i++) {
|
|
2143
|
+
const x = i * 2 / samples - 1;
|
|
2144
|
+
curve[i] = Math.tanh(k * x) * (1 + Math.abs(x) * 0.3) * norm;
|
|
2145
|
+
}
|
|
2146
|
+
return curve;
|
|
2147
|
+
}
|
|
2148
|
+
var exciterDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2149
|
+
type: "exciter",
|
|
2150
|
+
params: params15,
|
|
2151
|
+
ports: {
|
|
2152
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2153
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
2154
|
+
},
|
|
2155
|
+
createModule(id, position) {
|
|
2156
|
+
return { id, type: "exciter", position, ...paramDefaults(params15) };
|
|
2157
|
+
},
|
|
2158
|
+
createNodes(ctx, module) {
|
|
2159
|
+
const input = ctx.createGain();
|
|
2160
|
+
const output = ctx.createGain();
|
|
2161
|
+
const dryGain = ctx.createGain();
|
|
2162
|
+
const wetGain = ctx.createGain();
|
|
2163
|
+
const highpass = ctx.createBiquadFilter();
|
|
2164
|
+
const saturation = ctx.createWaveShaper();
|
|
2165
|
+
const highshelf = ctx.createBiquadFilter();
|
|
2166
|
+
highpass.type = "highpass";
|
|
2167
|
+
highpass.frequency.value = module.frequency;
|
|
2168
|
+
highpass.Q.value = 0.7;
|
|
2169
|
+
saturation.curve = makeExciterCurve(module.drive);
|
|
2170
|
+
saturation.oversample = "2x";
|
|
2171
|
+
highshelf.type = "highshelf";
|
|
2172
|
+
highshelf.frequency.value = module.frequency;
|
|
2173
|
+
highshelf.gain.value = 6 + module.drive * 6;
|
|
2174
|
+
dryGain.gain.value = 1;
|
|
2175
|
+
wetGain.gain.value = module.mix;
|
|
2176
|
+
input.connect(dryGain);
|
|
2177
|
+
input.connect(highpass);
|
|
2178
|
+
highpass.connect(saturation);
|
|
2179
|
+
saturation.connect(highshelf);
|
|
2180
|
+
highshelf.connect(wetGain);
|
|
2181
|
+
dryGain.connect(output);
|
|
2182
|
+
wetGain.connect(output);
|
|
2183
|
+
return { input, output, highpass, saturation, highshelf, wetGain };
|
|
2184
|
+
},
|
|
2185
|
+
updateParam(nodes, _m, param, value) {
|
|
2186
|
+
if (param === "drive") {
|
|
2187
|
+
nodes.saturation.curve = makeExciterCurve(value);
|
|
2188
|
+
nodes.highshelf.gain.value = 6 + value * 6;
|
|
2189
|
+
} else if (param === "frequency") {
|
|
2190
|
+
nodes.highpass.frequency.value = value;
|
|
2191
|
+
nodes.highshelf.frequency.value = value;
|
|
2192
|
+
} else if (param === "mix") {
|
|
2193
|
+
nodes.wetGain.gain.value = value;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
// src/lib-runtime/generated/modules/exciter.wrapper.ts
|
|
2199
|
+
var Exciter = /* @__PURE__ */ defineModule(exciterDescriptor);
|
|
2200
|
+
|
|
2201
|
+
// src/lib-runtime/generated/modules/flanger.tsx
|
|
2202
|
+
var params16 = {
|
|
2203
|
+
rate: { node: "flanger", field: "rate", unit: "Hz", default: 0.5, min: 0.1, max: 5 },
|
|
2204
|
+
depth: { node: "flanger", field: "depth", unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
2205
|
+
feedback: { node: "flanger", field: "feedback", unit: "factor", default: 0.7, min: 0, max: 0.95 },
|
|
2206
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
2207
|
+
};
|
|
2208
|
+
var flangerDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2209
|
+
type: "flanger",
|
|
2210
|
+
params: params16,
|
|
2211
|
+
ports: {
|
|
2212
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2213
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2214
|
+
"modRate": { type: "modulation", signal: "modulation", node: "flanger", field: "rate" },
|
|
2215
|
+
"modDepth": { type: "modulation", signal: "modulation", node: "flanger", field: "depth" },
|
|
2216
|
+
"modFeedback": { type: "modulation", signal: "modulation", node: "flanger", field: "feedback" },
|
|
2217
|
+
"modMix": { type: "modulation", signal: "modulation", node: "_mix", field: "offset" }
|
|
2218
|
+
},
|
|
2219
|
+
createModule(id, position) {
|
|
2220
|
+
return { id, type: "flanger", position, ...paramDefaults(params16) };
|
|
2221
|
+
},
|
|
2222
|
+
createNodes(ctx, module) {
|
|
2223
|
+
const input = ctx.createGain();
|
|
2224
|
+
const output = ctx.createGain();
|
|
2225
|
+
const dryGain = ctx.createGain();
|
|
2226
|
+
const wetGain = ctx.createGain();
|
|
2227
|
+
const flanger = createFlangerNode(ctx, {
|
|
2228
|
+
rate: module.rate,
|
|
2229
|
+
depth: module.depth,
|
|
2230
|
+
feedback: module.feedback
|
|
2231
|
+
});
|
|
2232
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
2233
|
+
input.connect(dryGain);
|
|
2234
|
+
input.connect(flanger);
|
|
2235
|
+
flanger.connect(wetGain);
|
|
2236
|
+
dryGain.connect(output);
|
|
2237
|
+
wetGain.connect(output);
|
|
2238
|
+
return { input, output, flanger, dryGain, wetGain, _mix };
|
|
2239
|
+
},
|
|
2240
|
+
updateParam(nodes, _m, param, value) {
|
|
2241
|
+
switch (param) {
|
|
2242
|
+
case "rate":
|
|
2243
|
+
case "depth":
|
|
2244
|
+
case "feedback":
|
|
2245
|
+
nodes.flanger.parameters.get(param).value = value;
|
|
2246
|
+
break;
|
|
2247
|
+
case "mix":
|
|
2248
|
+
nodes._mix.offset.value = value;
|
|
2249
|
+
break;
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
dispose(nodes) {
|
|
2253
|
+
nodes._mix.stop();
|
|
2254
|
+
}
|
|
2255
|
+
});
|
|
2256
|
+
|
|
2257
|
+
// src/lib-runtime/generated/modules/flanger.wrapper.ts
|
|
2258
|
+
var Flanger = /* @__PURE__ */ defineModule(flangerDescriptor, {
|
|
2259
|
+
worklets: {
|
|
2260
|
+
"flanger": flanger_default
|
|
2261
|
+
}
|
|
2262
|
+
});
|
|
2263
|
+
|
|
2264
|
+
// src/lib-runtime/generated/modules/formant.tsx
|
|
2265
|
+
var params17 = {
|
|
2266
|
+
morph: { unit: "factor", default: 0, min: 0, max: 5 },
|
|
2267
|
+
resonance: { unit: "factor", default: 6, min: 1, max: 20, scale: "sqrt" },
|
|
2268
|
+
mix: { unit: "factor", default: 1, min: 0, max: 1 }
|
|
2269
|
+
};
|
|
2270
|
+
var formantDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2271
|
+
type: "formant",
|
|
2272
|
+
params: params17,
|
|
2273
|
+
ports: {
|
|
2274
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2275
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2276
|
+
"modVowel": { type: "modulation", signal: "modulation", node: "vowelCV" }
|
|
2277
|
+
},
|
|
2278
|
+
createModule(id, position) {
|
|
2279
|
+
return { id, type: "formant", position, ...paramDefaults(params17) };
|
|
2280
|
+
},
|
|
2281
|
+
createNodes(ctx, module) {
|
|
2282
|
+
const input = ctx.createGain();
|
|
2283
|
+
const output = ctx.createGain();
|
|
2284
|
+
const dryGain = ctx.createGain();
|
|
2285
|
+
const wetGain = ctx.createGain();
|
|
2286
|
+
const f1 = ctx.createBiquadFilter();
|
|
2287
|
+
const f2 = ctx.createBiquadFilter();
|
|
2288
|
+
const f3 = ctx.createBiquadFilter();
|
|
2289
|
+
const f4 = ctx.createBiquadFilter();
|
|
2290
|
+
const f5 = ctx.createBiquadFilter();
|
|
2291
|
+
const formantMix = ctx.createGain();
|
|
2292
|
+
const formantWorklet = createFormantNode(ctx);
|
|
2293
|
+
formantWorklet.parameters.get("baseMorph").value = module.morph;
|
|
2294
|
+
const vowelCV = ctx.createGain();
|
|
2295
|
+
vowelCV.gain.value = 1;
|
|
2296
|
+
vowelCV.connect(formantWorklet);
|
|
2297
|
+
const splitter = ctx.createChannelSplitter(5);
|
|
2298
|
+
formantWorklet.connect(splitter);
|
|
2299
|
+
splitter.connect(f1.frequency, 0);
|
|
2300
|
+
splitter.connect(f2.frequency, 1);
|
|
2301
|
+
splitter.connect(f3.frequency, 2);
|
|
2302
|
+
splitter.connect(f4.frequency, 3);
|
|
2303
|
+
splitter.connect(f5.frequency, 4);
|
|
2304
|
+
f1.frequency.value = 0;
|
|
2305
|
+
f2.frequency.value = 0;
|
|
2306
|
+
f3.frequency.value = 0;
|
|
2307
|
+
f4.frequency.value = 0;
|
|
2308
|
+
f5.frequency.value = 0;
|
|
2309
|
+
f1.type = "bandpass";
|
|
2310
|
+
f2.type = "bandpass";
|
|
2311
|
+
f3.type = "bandpass";
|
|
2312
|
+
f4.type = "bandpass";
|
|
2313
|
+
f5.type = "bandpass";
|
|
2314
|
+
f1.Q.value = module.resonance * 0.5;
|
|
2315
|
+
f2.Q.value = module.resonance * 0.4;
|
|
2316
|
+
f3.Q.value = module.resonance * 0.3;
|
|
2317
|
+
f4.Q.value = module.resonance * 0.25;
|
|
2318
|
+
f5.Q.value = module.resonance * 0.2;
|
|
2319
|
+
dryGain.gain.value = 1 - module.mix;
|
|
2320
|
+
wetGain.gain.value = module.mix;
|
|
2321
|
+
formantMix.gain.value = 2;
|
|
2322
|
+
const f4Gain = ctx.createGain();
|
|
2323
|
+
const f5Gain = ctx.createGain();
|
|
2324
|
+
f4Gain.gain.value = 0.5;
|
|
2325
|
+
f5Gain.gain.value = 0.35;
|
|
2326
|
+
input.connect(dryGain);
|
|
2327
|
+
input.connect(f1);
|
|
2328
|
+
input.connect(f2);
|
|
2329
|
+
input.connect(f3);
|
|
2330
|
+
input.connect(f4);
|
|
2331
|
+
input.connect(f5);
|
|
2332
|
+
f1.connect(formantMix);
|
|
2333
|
+
f2.connect(formantMix);
|
|
2334
|
+
f3.connect(formantMix);
|
|
2335
|
+
f4.connect(f4Gain);
|
|
2336
|
+
f5.connect(f5Gain);
|
|
2337
|
+
f4Gain.connect(formantMix);
|
|
2338
|
+
f5Gain.connect(formantMix);
|
|
2339
|
+
formantMix.connect(wetGain);
|
|
2340
|
+
dryGain.connect(output);
|
|
2341
|
+
wetGain.connect(output);
|
|
2342
|
+
return { input, output, vowelCV, f1, f2, f3, f4, f5, f4Gain, f5Gain, dryGain, wetGain, formantWorklet };
|
|
2343
|
+
},
|
|
2344
|
+
updateParam(nodes, _module, param, value) {
|
|
2345
|
+
if (param === "morph") {
|
|
2346
|
+
nodes.formantWorklet.parameters.get("baseMorph").value = value;
|
|
2347
|
+
} else if (param === "resonance") {
|
|
2348
|
+
const r = value;
|
|
2349
|
+
nodes.f1.Q.value = r * 0.5;
|
|
2350
|
+
nodes.f2.Q.value = r * 0.4;
|
|
2351
|
+
nodes.f3.Q.value = r * 0.3;
|
|
2352
|
+
nodes.f4.Q.value = r * 0.25;
|
|
2353
|
+
nodes.f5.Q.value = r * 0.2;
|
|
2354
|
+
} else if (param === "mix") {
|
|
2355
|
+
nodes.dryGain.gain.value = 1 - value;
|
|
2356
|
+
nodes.wetGain.gain.value = value;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
|
|
2361
|
+
// src/lib-runtime/generated/modules/formant.wrapper.ts
|
|
2362
|
+
var Formant = /* @__PURE__ */ defineModule(formantDescriptor, {
|
|
2363
|
+
worklets: {
|
|
2364
|
+
"formant-table": formant_table_default
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
// src/lib-runtime/generated/modules/freq-shifter.tsx
|
|
2369
|
+
var params18 = {
|
|
2370
|
+
shift: { node: "shifter", field: "shift", unit: "Hz", default: 0, min: -500, max: 500 },
|
|
2371
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 1, min: 0, max: 1 }
|
|
2372
|
+
};
|
|
2373
|
+
var freqShifterDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2374
|
+
type: "freq-shifter",
|
|
2375
|
+
params: params18,
|
|
2376
|
+
ports: {
|
|
2377
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2378
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2379
|
+
"modShift": { type: "modulation", signal: "modulation", node: "shifter", field: "shift" }
|
|
2380
|
+
},
|
|
2381
|
+
createModule(id, position) {
|
|
2382
|
+
return { id, type: "freq-shifter", position, ...paramDefaults(params18) };
|
|
2383
|
+
},
|
|
2384
|
+
createNodes(ctx, module) {
|
|
2385
|
+
const input = ctx.createGain();
|
|
2386
|
+
const output = ctx.createGain();
|
|
2387
|
+
const dryGain = ctx.createGain();
|
|
2388
|
+
const wetGain = ctx.createGain();
|
|
2389
|
+
const shifter = createFreqShifterNode(ctx, module.shift);
|
|
2390
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
2391
|
+
input.connect(dryGain);
|
|
2392
|
+
input.connect(shifter);
|
|
2393
|
+
shifter.connect(wetGain);
|
|
2394
|
+
dryGain.connect(output);
|
|
2395
|
+
wetGain.connect(output);
|
|
2396
|
+
return { input, output, shifter, dryGain, wetGain, _mix };
|
|
2397
|
+
},
|
|
2398
|
+
updateParam(nodes, _m, param, value) {
|
|
2399
|
+
if (param === "shift") {
|
|
2400
|
+
const p = nodes.shifter.parameters.get("shift");
|
|
2401
|
+
p.setTargetAtTime(value, nodes.shifter.context.currentTime, 0.01);
|
|
2402
|
+
} else if (param === "mix") {
|
|
2403
|
+
nodes._mix.offset.value = value;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
});
|
|
2407
|
+
|
|
2408
|
+
// src/lib-runtime/generated/modules/freq-shifter.wrapper.ts
|
|
2409
|
+
var FrequencyShifter = /* @__PURE__ */ defineModule(freqShifterDescriptor, {
|
|
2410
|
+
worklets: {
|
|
2411
|
+
"freq-shifter": freq_shifter_default
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
2414
|
+
|
|
2415
|
+
// src/lib-runtime/generated/modules/gate.tsx
|
|
2416
|
+
var params19 = {
|
|
2417
|
+
// threshold/range rebuild the gateShaper curve — not a 1:1 AudioParam
|
|
2418
|
+
// write — so no node/field.
|
|
2419
|
+
threshold: { unit: "dB", default: -40, min: -60, max: 0 },
|
|
2420
|
+
range: { unit: "factor", default: 0.8, min: 0, max: 1 },
|
|
2421
|
+
attack: { node: "envFollower", field: "attack", unit: "s", default: 5e-3, min: 1e-3, max: 0.05, scale: "log" },
|
|
2422
|
+
release: { node: "envFollower", field: "release", unit: "s", default: 0.1, min: 0.01, max: 0.5, scale: "log" }
|
|
2423
|
+
};
|
|
2424
|
+
function makeGateCurve(threshold, range) {
|
|
2425
|
+
const threshLinear = Math.pow(10, threshold / 20);
|
|
2426
|
+
const kneeLow = Math.pow(10, (threshold - 2) / 20);
|
|
2427
|
+
const closed = 1 - range;
|
|
2428
|
+
const gsLen = 32768;
|
|
2429
|
+
const gsCurve = new Float32Array(gsLen);
|
|
2430
|
+
for (let i = 0; i < gsLen; i++) {
|
|
2431
|
+
const x = i / (gsLen - 1) * 2 - 1;
|
|
2432
|
+
const env = Math.max(0, x);
|
|
2433
|
+
let g;
|
|
2434
|
+
if (env >= threshLinear) g = 1;
|
|
2435
|
+
else if (env <= kneeLow) g = closed;
|
|
2436
|
+
else g = closed + (1 - closed) * ((env - kneeLow) / (threshLinear - kneeLow));
|
|
2437
|
+
gsCurve[i] = g;
|
|
2438
|
+
}
|
|
2439
|
+
return gsCurve;
|
|
2440
|
+
}
|
|
2441
|
+
var gateDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2442
|
+
type: "gate",
|
|
2443
|
+
params: params19,
|
|
2444
|
+
ports: {
|
|
2445
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2446
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
2447
|
+
},
|
|
2448
|
+
createModule(id, position) {
|
|
2449
|
+
return { id, type: "gate", position, ...paramDefaults(params19) };
|
|
2450
|
+
},
|
|
2451
|
+
createNodes(ctx, module) {
|
|
2452
|
+
const input = ctx.createGain();
|
|
2453
|
+
const output = ctx.createGain();
|
|
2454
|
+
const gateGain = ctx.createGain();
|
|
2455
|
+
const envFollower = createEnvFollowerNode(ctx);
|
|
2456
|
+
envFollower.parameters.get("attack").value = module.attack;
|
|
2457
|
+
envFollower.parameters.get("release").value = module.release;
|
|
2458
|
+
const gateShaper = ctx.createWaveShaper();
|
|
2459
|
+
gateShaper.curve = makeGateCurve(module.threshold, module.range);
|
|
2460
|
+
gateGain.gain.value = 0;
|
|
2461
|
+
input.connect(envFollower);
|
|
2462
|
+
envFollower.connect(gateShaper);
|
|
2463
|
+
gateShaper.connect(gateGain.gain);
|
|
2464
|
+
input.connect(gateGain);
|
|
2465
|
+
gateGain.connect(output);
|
|
2466
|
+
return { input, output, envFollower, gateShaper };
|
|
2467
|
+
},
|
|
2468
|
+
updateParam(nodes, module, param, value) {
|
|
2469
|
+
if (param === "threshold" || param === "range") {
|
|
2470
|
+
const thresh = param === "threshold" ? value : module.threshold;
|
|
2471
|
+
const range = param === "range" ? value : module.range;
|
|
2472
|
+
nodes.gateShaper.curve = makeGateCurve(thresh, range);
|
|
2473
|
+
} else if (param === "attack") {
|
|
2474
|
+
nodes.envFollower.parameters.get("attack").value = value;
|
|
2475
|
+
} else if (param === "release") {
|
|
2476
|
+
nodes.envFollower.parameters.get("release").value = value;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
|
|
2481
|
+
// src/lib-runtime/generated/modules/gate.wrapper.ts
|
|
2482
|
+
var Gate = /* @__PURE__ */ defineModule(gateDescriptor, {
|
|
2483
|
+
worklets: {
|
|
2484
|
+
"env-follower": env_follower_default
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
|
|
2488
|
+
// src/lib-runtime/generated/modules/glide.tsx
|
|
2489
|
+
var params20 = {
|
|
2490
|
+
time: { node: "_glide", field: "time", unit: "s", default: 0.1, min: 0.01, max: 10, scale: "log" }
|
|
2491
|
+
};
|
|
2492
|
+
var glideDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2493
|
+
type: "glide",
|
|
2494
|
+
params: params20,
|
|
2495
|
+
ports: {
|
|
2496
|
+
"in": { type: "input", signal: "pitch", node: "_glide" },
|
|
2497
|
+
"gate": { type: "input", signal: "gate", node: "_gateIn" },
|
|
2498
|
+
"out": { type: "output", signal: "pitch", node: "output" }
|
|
2499
|
+
},
|
|
2500
|
+
createModule(id, position) {
|
|
2501
|
+
return { id, type: "glide", position, ...paramDefaults(params20) };
|
|
2502
|
+
},
|
|
2503
|
+
createNodes(ctx, module) {
|
|
2504
|
+
const _glide = createGlideNode(ctx);
|
|
2505
|
+
_glide.parameters.get("time").value = module.time;
|
|
2506
|
+
const output = ctx.createGain();
|
|
2507
|
+
output.gain.value = 1;
|
|
2508
|
+
_glide.connect(output);
|
|
2509
|
+
const _gateIn = ctx.createGain();
|
|
2510
|
+
_gateIn.gain.value = 1;
|
|
2511
|
+
_gateIn.connect(_glide, 0, 1);
|
|
2512
|
+
return { _glide, _gateIn, output };
|
|
2513
|
+
},
|
|
2514
|
+
updateParam(nodes, _m, param, value) {
|
|
2515
|
+
if (param === "time") {
|
|
2516
|
+
nodes._glide.parameters.get("time").value = value;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
|
|
2521
|
+
// src/lib-runtime/generated/modules/glide.wrapper.ts
|
|
2522
|
+
var Glide = /* @__PURE__ */ defineModule(glideDescriptor, {
|
|
2523
|
+
worklets: {
|
|
2524
|
+
"glide": glide_default
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
|
|
2528
|
+
// src/lib-runtime/generated/modules/haas.tsx
|
|
2529
|
+
var params21 = {
|
|
2530
|
+
delay: { unit: "ms", default: 15, min: 1, max: 35 },
|
|
2531
|
+
side: { kind: "enum", default: "right", options: ["left", "right"] },
|
|
2532
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 1, min: 0, max: 1 }
|
|
2533
|
+
};
|
|
2534
|
+
var haasDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2535
|
+
type: "haas",
|
|
2536
|
+
params: params21,
|
|
2537
|
+
ports: {
|
|
2538
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2539
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
2540
|
+
},
|
|
2541
|
+
createModule(id, position) {
|
|
2542
|
+
return { id, type: "haas", position, ...paramDefaults(params21) };
|
|
2543
|
+
},
|
|
2544
|
+
createNodes(ctx, module) {
|
|
2545
|
+
const input = ctx.createGain();
|
|
2546
|
+
const output = ctx.createGain();
|
|
2547
|
+
const delayNode = ctx.createDelay(0.05);
|
|
2548
|
+
const directGain = ctx.createGain();
|
|
2549
|
+
const delayedGain = ctx.createGain();
|
|
2550
|
+
const directBleed = ctx.createGain();
|
|
2551
|
+
const merger = ctx.createChannelMerger(2);
|
|
2552
|
+
const _mix = createMix(ctx, directBleed, delayedGain, module.mix, "linear");
|
|
2553
|
+
delayNode.delayTime.value = module.delay / 1e3;
|
|
2554
|
+
directGain.gain.value = 1;
|
|
2555
|
+
input.connect(directGain);
|
|
2556
|
+
input.connect(directBleed);
|
|
2557
|
+
input.connect(delayNode);
|
|
2558
|
+
delayNode.connect(delayedGain);
|
|
2559
|
+
if (module.side === "right") {
|
|
2560
|
+
directGain.connect(merger, 0, 0);
|
|
2561
|
+
delayedGain.connect(merger, 0, 1);
|
|
2562
|
+
directBleed.connect(merger, 0, 1);
|
|
2563
|
+
} else {
|
|
2564
|
+
delayedGain.connect(merger, 0, 0);
|
|
2565
|
+
directBleed.connect(merger, 0, 0);
|
|
2566
|
+
directGain.connect(merger, 0, 1);
|
|
2567
|
+
}
|
|
2568
|
+
merger.connect(output);
|
|
2569
|
+
return { input, output, delayNode, directGain, delayedGain, directBleed, merger, _mix };
|
|
2570
|
+
},
|
|
2571
|
+
updateParam(nodes, _m, param, value) {
|
|
2572
|
+
if (param === "delay") {
|
|
2573
|
+
nodes.delayNode.delayTime.value = value / 1e3;
|
|
2574
|
+
} else if (param === "mix") {
|
|
2575
|
+
nodes._mix.offset.value = value;
|
|
2576
|
+
} else if (param === "side") {
|
|
2577
|
+
try {
|
|
2578
|
+
nodes.directGain.disconnect(nodes.merger);
|
|
2579
|
+
} catch (_) {
|
|
2580
|
+
}
|
|
2581
|
+
try {
|
|
2582
|
+
nodes.delayedGain.disconnect(nodes.merger);
|
|
2583
|
+
} catch (_) {
|
|
2584
|
+
}
|
|
2585
|
+
try {
|
|
2586
|
+
nodes.directBleed.disconnect(nodes.merger);
|
|
2587
|
+
} catch (_) {
|
|
2588
|
+
}
|
|
2589
|
+
if (value === "right") {
|
|
2590
|
+
nodes.directGain.connect(nodes.merger, 0, 0);
|
|
2591
|
+
nodes.delayedGain.connect(nodes.merger, 0, 1);
|
|
2592
|
+
nodes.directBleed.connect(nodes.merger, 0, 1);
|
|
2593
|
+
} else {
|
|
2594
|
+
nodes.delayedGain.connect(nodes.merger, 0, 0);
|
|
2595
|
+
nodes.directBleed.connect(nodes.merger, 0, 0);
|
|
2596
|
+
nodes.directGain.connect(nodes.merger, 0, 1);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
|
|
2602
|
+
// src/lib-runtime/generated/modules/haas.wrapper.ts
|
|
2603
|
+
var HaasWidener = /* @__PURE__ */ defineModule(haasDescriptor);
|
|
2604
|
+
|
|
2605
|
+
// src/lib-runtime/generated/modules/harmonic-trem.tsx
|
|
2606
|
+
var params22 = {
|
|
2607
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 4, min: 1, max: 10 },
|
|
2608
|
+
depth: { unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
2609
|
+
crossover: { node: "lowpass", field: "frequency", unit: "Hz", default: 800, min: 200, max: 2e3 }
|
|
2610
|
+
};
|
|
2611
|
+
var harmonicTremDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2612
|
+
type: "harmonic-trem",
|
|
2613
|
+
params: params22,
|
|
2614
|
+
ports: {
|
|
2615
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2616
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2617
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" },
|
|
2618
|
+
"modCrossover": { type: "modulation", signal: "modulation", node: "lowpass", field: "frequency" }
|
|
2619
|
+
},
|
|
2620
|
+
createModule(id, position) {
|
|
2621
|
+
return { id, type: "harmonic-trem", position, ...paramDefaults(params22) };
|
|
2622
|
+
},
|
|
2623
|
+
createNodes(ctx, module) {
|
|
2624
|
+
const input = ctx.createGain();
|
|
2625
|
+
const output = ctx.createGain();
|
|
2626
|
+
const lowpass = ctx.createBiquadFilter();
|
|
2627
|
+
const lpInvert = ctx.createGain();
|
|
2628
|
+
const highBand = ctx.createGain();
|
|
2629
|
+
const lowGain = ctx.createGain();
|
|
2630
|
+
const highGain = ctx.createGain();
|
|
2631
|
+
const lfo = ctx.createOscillator();
|
|
2632
|
+
const lfoGainLow = ctx.createGain();
|
|
2633
|
+
const lfoGainHigh = ctx.createGain();
|
|
2634
|
+
const lfoInvert = ctx.createGain();
|
|
2635
|
+
lowpass.type = "lowpass";
|
|
2636
|
+
lowpass.frequency.value = module.crossover;
|
|
2637
|
+
lowpass.Q.value = -6.0206;
|
|
2638
|
+
lpInvert.gain.value = -1;
|
|
2639
|
+
highBand.gain.value = 1;
|
|
2640
|
+
lfo.type = "sine";
|
|
2641
|
+
lfo.frequency.value = module.rate;
|
|
2642
|
+
lfoGainLow.gain.value = module.depth * 0.5;
|
|
2643
|
+
lfoGainHigh.gain.value = module.depth * 0.5;
|
|
2644
|
+
lfoInvert.gain.value = -1;
|
|
2645
|
+
lowGain.gain.value = 1;
|
|
2646
|
+
highGain.gain.value = 1;
|
|
2647
|
+
input.connect(lowpass);
|
|
2648
|
+
input.connect(highBand);
|
|
2649
|
+
lowpass.connect(lpInvert);
|
|
2650
|
+
lpInvert.connect(highBand);
|
|
2651
|
+
lfo.connect(lfoGainLow);
|
|
2652
|
+
lfo.connect(lfoInvert);
|
|
2653
|
+
lfoInvert.connect(lfoGainHigh);
|
|
2654
|
+
lfoGainLow.connect(lowGain.gain);
|
|
2655
|
+
lfoGainHigh.connect(highGain.gain);
|
|
2656
|
+
lowpass.connect(lowGain);
|
|
2657
|
+
highBand.connect(highGain);
|
|
2658
|
+
lowGain.connect(output);
|
|
2659
|
+
highGain.connect(output);
|
|
2660
|
+
lfo.start();
|
|
2661
|
+
return { input, output, lfo, lfoGainLow, lfoGainHigh, lowpass, lpInvert, highBand };
|
|
2662
|
+
},
|
|
2663
|
+
updateParam(nodes, _m, param, value) {
|
|
2664
|
+
if (param === "rate") nodes.lfo.frequency.value = value;
|
|
2665
|
+
else if (param === "depth") {
|
|
2666
|
+
nodes.lfoGainLow.gain.value = value * 0.5;
|
|
2667
|
+
nodes.lfoGainHigh.gain.value = value * 0.5;
|
|
2668
|
+
} else if (param === "crossover") {
|
|
2669
|
+
nodes.lowpass.frequency.value = value;
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
dispose(nodes) {
|
|
2673
|
+
nodes.lfo.stop();
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
// src/lib-runtime/generated/modules/harmonic-trem.wrapper.ts
|
|
2678
|
+
var HarmonicTremolo = /* @__PURE__ */ defineModule(harmonicTremDescriptor);
|
|
2679
|
+
|
|
2680
|
+
// src/lib-runtime/generated/modules/ladder-filter.tsx
|
|
2681
|
+
var RES_TOP = 1.1;
|
|
2682
|
+
var params23 = {
|
|
2683
|
+
note: { node: "_cutoffVoct", field: "transpose", unit: "note", default: 83, min: 15, max: 135 },
|
|
2684
|
+
// Stored 0–4 (UI shows % of travel) but the worklet's resonance param is
|
|
2685
|
+
// resonance/4·RES_TOP — a scale, not a 1:1 mapping — so this stays set-only.
|
|
2686
|
+
resonance: { unit: "factor", default: 2.5, min: 0, max: 4, scale: "sqrt" },
|
|
2687
|
+
drive: { node: "ladder", field: "drive", unit: "factor", default: 0.3, min: 0, max: 1 }
|
|
2688
|
+
};
|
|
2689
|
+
var ladderFilterDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2690
|
+
type: "ladder-filter",
|
|
2691
|
+
params: params23,
|
|
2692
|
+
ports: {
|
|
2693
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2694
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2695
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_cutoffVoct" },
|
|
2696
|
+
"modRes": { type: "modulation", signal: "modulation", node: "ladder", field: "resonance" }
|
|
2697
|
+
},
|
|
2698
|
+
createModule(id, position) {
|
|
2699
|
+
return { id, type: "ladder-filter", position, ...paramDefaults(params23) };
|
|
2700
|
+
},
|
|
2701
|
+
createNodes(ctx, module) {
|
|
2702
|
+
const input = ctx.createGain();
|
|
2703
|
+
const output = ctx.createGain();
|
|
2704
|
+
const ladder = createLadderFilterNode(ctx, {
|
|
2705
|
+
resonance: module.resonance / 4 * RES_TOP,
|
|
2706
|
+
drive: module.drive
|
|
2707
|
+
});
|
|
2708
|
+
const _cutoffVoct = createVoctFreqNode(ctx);
|
|
2709
|
+
_cutoffVoct.parameters.get("transpose").value = module.note;
|
|
2710
|
+
const _cutoffBase = ctx.createConstantSource();
|
|
2711
|
+
_cutoffBase.offset.value = 0;
|
|
2712
|
+
_cutoffBase.start();
|
|
2713
|
+
_cutoffBase.connect(_cutoffVoct);
|
|
2714
|
+
_cutoffVoct.connect(ladder.parameters.get("cutoff"));
|
|
2715
|
+
input.connect(ladder);
|
|
2716
|
+
ladder.connect(output);
|
|
2717
|
+
return { input, output, ladder, _cutoffVoct, _cutoffBase };
|
|
2718
|
+
},
|
|
2719
|
+
updateParam(nodes, _module, param, value) {
|
|
2720
|
+
switch (param) {
|
|
2721
|
+
case "note":
|
|
2722
|
+
nodes._cutoffVoct.parameters.get("transpose").value = value;
|
|
2723
|
+
break;
|
|
2724
|
+
case "resonance":
|
|
2725
|
+
nodes.ladder.parameters.get("resonance").value = value / 4 * RES_TOP;
|
|
2726
|
+
break;
|
|
2727
|
+
case "drive":
|
|
2728
|
+
nodes.ladder.parameters.get("drive").value = value;
|
|
2729
|
+
break;
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
dispose(nodes) {
|
|
2733
|
+
try {
|
|
2734
|
+
nodes._cutoffBase.stop();
|
|
2735
|
+
} catch (_) {
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
});
|
|
2739
|
+
|
|
2740
|
+
// src/lib-runtime/generated/modules/ladder-filter.wrapper.ts
|
|
2741
|
+
var LadderFilter = /* @__PURE__ */ defineModule(ladderFilterDescriptor, {
|
|
2742
|
+
worklets: {
|
|
2743
|
+
"ladder-filter": ladder_filter_default,
|
|
2744
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
2745
|
+
}
|
|
2746
|
+
});
|
|
2747
|
+
|
|
2748
|
+
// src/lib-runtime/generated/modules/lfo.tsx
|
|
2749
|
+
var params24 = {
|
|
2750
|
+
skew: { node: "_ws", field: "skew", unit: "factor", default: 0, min: -1, max: 1 },
|
|
2751
|
+
shape: { node: "_ws", field: "shape", unit: "factor", default: 1, min: -1, max: 1 },
|
|
2752
|
+
frequency: { node: "_freqSrc", field: "offset", default: 2, min: -10, max: 20, unit: "Hz" },
|
|
2753
|
+
amount: { node: "gain", field: "gain", unit: "semitone", default: 12, min: 0, max: 500, scale: "sqrt" }
|
|
2754
|
+
};
|
|
2755
|
+
var lfoDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2756
|
+
type: "lfo",
|
|
2757
|
+
params: params24,
|
|
2758
|
+
ports: {
|
|
2759
|
+
"out": { type: "output", node: "gain", signal: "modulation" },
|
|
2760
|
+
"modRate": { type: "modulation", signal: "modulation", node: "_freqSrc", field: "offset" },
|
|
2761
|
+
"modAmount": { type: "modulation", signal: "modulation", node: "gain", field: "gain" },
|
|
2762
|
+
"modSkew": { type: "modulation", signal: "modulation", node: "_ws", field: "skew" },
|
|
2763
|
+
"modShape": { type: "modulation", signal: "modulation", node: "_ws", field: "shape" }
|
|
2764
|
+
},
|
|
2765
|
+
createModule(id, position) {
|
|
2766
|
+
return { id, type: "lfo", position, ...paramDefaults(params24) };
|
|
2767
|
+
},
|
|
2768
|
+
createNodes(ctx, module) {
|
|
2769
|
+
const _ws = createWaveshaperNode(ctx);
|
|
2770
|
+
const gain = ctx.createGain();
|
|
2771
|
+
const _freqSrc = ctx.createConstantSource();
|
|
2772
|
+
_freqSrc.offset.value = module.frequency;
|
|
2773
|
+
_freqSrc.start();
|
|
2774
|
+
_freqSrc.connect(_ws);
|
|
2775
|
+
_ws.parameters.get("skew").value = module.skew;
|
|
2776
|
+
_ws.parameters.get("shape").value = module.shape;
|
|
2777
|
+
gain.gain.value = module.amount;
|
|
2778
|
+
_ws.connect(gain);
|
|
2779
|
+
return { gain, _ws, _freqSrc };
|
|
2780
|
+
},
|
|
2781
|
+
updateParam(nodes, _module, param, value) {
|
|
2782
|
+
switch (param) {
|
|
2783
|
+
case "skew":
|
|
2784
|
+
nodes._ws.parameters.get("skew").value = value;
|
|
2785
|
+
break;
|
|
2786
|
+
case "shape":
|
|
2787
|
+
nodes._ws.parameters.get("shape").value = value;
|
|
2788
|
+
break;
|
|
2789
|
+
case "frequency":
|
|
2790
|
+
nodes._freqSrc.offset.value = value;
|
|
2791
|
+
break;
|
|
2792
|
+
case "amount":
|
|
2793
|
+
nodes.gain.gain.value = value;
|
|
2794
|
+
break;
|
|
2795
|
+
}
|
|
2796
|
+
},
|
|
2797
|
+
dispose(nodes) {
|
|
2798
|
+
nodes._freqSrc.stop();
|
|
2799
|
+
}
|
|
2800
|
+
});
|
|
2801
|
+
|
|
2802
|
+
// src/lib-runtime/generated/modules/lfo.wrapper.ts
|
|
2803
|
+
var Lfo = /* @__PURE__ */ defineModule(lfoDescriptor, {
|
|
2804
|
+
worklets: {
|
|
2805
|
+
"waveshaper": waveshaper_default
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
|
|
2809
|
+
// src/lib-runtime/generated/modules/lo-fi.tsx
|
|
2810
|
+
function makeLoFiSaturationCurve(amount) {
|
|
2811
|
+
const samples = 44100;
|
|
2812
|
+
const curve = new Float32Array(samples);
|
|
2813
|
+
const k = 1 + amount * 5;
|
|
2814
|
+
for (let i = 0; i < samples; i++) {
|
|
2815
|
+
const x = i * 2 / samples - 1;
|
|
2816
|
+
curve[i] = x >= 0 ? Math.tanh(k * x * 0.8) : Math.tanh(k * x * 1.2);
|
|
2817
|
+
}
|
|
2818
|
+
return curve;
|
|
2819
|
+
}
|
|
2820
|
+
var params25 = {
|
|
2821
|
+
drive: { unit: "factor", default: 0.3, min: 0, max: 1 },
|
|
2822
|
+
cutoff: { node: "lowpass", field: "frequency", unit: "Hz", default: 4e3, min: 500, max: 8e3 },
|
|
2823
|
+
noise: { unit: "factor", default: 0.2, min: 0, max: 1 },
|
|
2824
|
+
wobble: { unit: "factor", default: 0.1, min: 0, max: 1 }
|
|
2825
|
+
};
|
|
2826
|
+
var loFiDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2827
|
+
type: "lo-fi",
|
|
2828
|
+
params: params25,
|
|
2829
|
+
ports: {
|
|
2830
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2831
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
2832
|
+
"modCutoff": { type: "modulation", signal: "modulation", node: "lowpass", field: "frequency" }
|
|
2833
|
+
},
|
|
2834
|
+
createModule(id, position) {
|
|
2835
|
+
return { id, type: "lo-fi", position, ...paramDefaults(params25) };
|
|
2836
|
+
},
|
|
2837
|
+
createNodes(ctx, module) {
|
|
2838
|
+
const input = ctx.createGain();
|
|
2839
|
+
const output = ctx.createGain();
|
|
2840
|
+
const saturation = ctx.createWaveShaper();
|
|
2841
|
+
const lowpass = ctx.createBiquadFilter();
|
|
2842
|
+
const noiseGain = ctx.createGain();
|
|
2843
|
+
const wobbleLfo = ctx.createOscillator();
|
|
2844
|
+
const wobbleGain = ctx.createGain();
|
|
2845
|
+
saturation.curve = makeLoFiSaturationCurve(module.drive);
|
|
2846
|
+
saturation.oversample = "2x";
|
|
2847
|
+
lowpass.type = "lowpass";
|
|
2848
|
+
lowpass.frequency.value = module.cutoff;
|
|
2849
|
+
lowpass.Q.value = 0.7;
|
|
2850
|
+
const bufferSize = 2 * ctx.sampleRate;
|
|
2851
|
+
const noiseBuffer2 = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
|
2852
|
+
const data = noiseBuffer2.getChannelData(0);
|
|
2853
|
+
const rand = noiseSource(NOISE_SEEDS.loFiCrackle);
|
|
2854
|
+
let b0 = 0, b1 = 0, b2 = 0;
|
|
2855
|
+
for (let i = 0; i < bufferSize; i++) {
|
|
2856
|
+
const white = rand();
|
|
2857
|
+
b0 = 0.99765 * b0 + white * 0.099046;
|
|
2858
|
+
b1 = 0.963 * b1 + white * 0.2965164;
|
|
2859
|
+
b2 = 0.57 * b2 + white * 1.0526913;
|
|
2860
|
+
data[i] = (b0 + b1 + b2) * 0.1;
|
|
2861
|
+
}
|
|
2862
|
+
const noiseSrc = ctx.createBufferSource();
|
|
2863
|
+
noiseSrc.buffer = noiseBuffer2;
|
|
2864
|
+
noiseSrc.loop = true;
|
|
2865
|
+
noiseGain.gain.value = module.noise * 0.05;
|
|
2866
|
+
noiseSrc.connect(noiseGain);
|
|
2867
|
+
noiseSrc.start();
|
|
2868
|
+
const wobbleDelay = ctx.createDelay(0.05);
|
|
2869
|
+
wobbleDelay.delayTime.value = 0.01;
|
|
2870
|
+
wobbleLfo.type = "sine";
|
|
2871
|
+
wobbleLfo.frequency.value = 0.73;
|
|
2872
|
+
wobbleGain.gain.value = module.wobble * 2e-3;
|
|
2873
|
+
wobbleLfo.connect(wobbleGain);
|
|
2874
|
+
wobbleGain.connect(wobbleDelay.delayTime);
|
|
2875
|
+
wobbleLfo.start();
|
|
2876
|
+
input.connect(saturation);
|
|
2877
|
+
saturation.connect(lowpass);
|
|
2878
|
+
lowpass.connect(wobbleDelay);
|
|
2879
|
+
wobbleDelay.connect(output);
|
|
2880
|
+
noiseGain.connect(output);
|
|
2881
|
+
return { input, output, saturation, lowpass, wobbleDelay, noiseGain, wobbleGain, wobbleLfo, noiseSrc };
|
|
2882
|
+
},
|
|
2883
|
+
updateParam(nodes, _m, param, value) {
|
|
2884
|
+
if (param === "drive") {
|
|
2885
|
+
nodes.saturation.curve = makeLoFiSaturationCurve(value);
|
|
2886
|
+
} else if (param === "cutoff") {
|
|
2887
|
+
nodes.lowpass.frequency.value = value;
|
|
2888
|
+
} else if (param === "noise") {
|
|
2889
|
+
nodes.noiseGain.gain.value = value * 0.05;
|
|
2890
|
+
} else if (param === "wobble") {
|
|
2891
|
+
nodes.wobbleGain.gain.value = value * 2e-3;
|
|
2892
|
+
}
|
|
2893
|
+
},
|
|
2894
|
+
dispose(nodes) {
|
|
2895
|
+
nodes.wobbleLfo.stop();
|
|
2896
|
+
nodes.noiseSrc.stop();
|
|
2897
|
+
}
|
|
2898
|
+
});
|
|
2899
|
+
|
|
2900
|
+
// src/lib-runtime/generated/modules/lo-fi.wrapper.ts
|
|
2901
|
+
var LoFi = /* @__PURE__ */ defineModule(loFiDescriptor);
|
|
2902
|
+
|
|
2903
|
+
// src/lib-runtime/generated/modules/mid-side.tsx
|
|
2904
|
+
var params26 = {
|
|
2905
|
+
midGain: { node: "midGainNode", field: "gain", unit: "factor", default: 1, min: 0, max: 2 },
|
|
2906
|
+
sideGain: { node: "sideGainNode", field: "gain", unit: "factor", default: 1, min: 0, max: 2 }
|
|
2907
|
+
};
|
|
2908
|
+
var midSideDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
2909
|
+
type: "mid-side",
|
|
2910
|
+
params: params26,
|
|
2911
|
+
ports: {
|
|
2912
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
2913
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
2914
|
+
},
|
|
2915
|
+
createModule(id, position) {
|
|
2916
|
+
return { id, type: "mid-side", position, ...paramDefaults(params26) };
|
|
2917
|
+
},
|
|
2918
|
+
createNodes(ctx, module) {
|
|
2919
|
+
const input = ctx.createGain();
|
|
2920
|
+
input.channelCount = 2;
|
|
2921
|
+
input.channelCountMode = "explicit";
|
|
2922
|
+
input.channelInterpretation = "speakers";
|
|
2923
|
+
const output = ctx.createGain();
|
|
2924
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
2925
|
+
const merger = ctx.createChannelMerger(2);
|
|
2926
|
+
const midSum = ctx.createGain();
|
|
2927
|
+
midSum.gain.value = 0.5;
|
|
2928
|
+
const sideSum = ctx.createGain();
|
|
2929
|
+
sideSum.gain.value = 0.5;
|
|
2930
|
+
const rInvEnc = ctx.createGain();
|
|
2931
|
+
rInvEnc.gain.value = -1;
|
|
2932
|
+
input.connect(splitter);
|
|
2933
|
+
splitter.connect(midSum, 0);
|
|
2934
|
+
splitter.connect(midSum, 1);
|
|
2935
|
+
splitter.connect(sideSum, 0);
|
|
2936
|
+
splitter.connect(rInvEnc, 1);
|
|
2937
|
+
rInvEnc.connect(sideSum);
|
|
2938
|
+
const midGainNode = ctx.createGain();
|
|
2939
|
+
midGainNode.gain.value = module.midGain;
|
|
2940
|
+
const sideGainNode = ctx.createGain();
|
|
2941
|
+
sideGainNode.gain.value = module.sideGain;
|
|
2942
|
+
const widthNode = ctx.createGain();
|
|
2943
|
+
widthNode.gain.value = module.width;
|
|
2944
|
+
midSum.connect(midGainNode);
|
|
2945
|
+
sideSum.connect(sideGainNode);
|
|
2946
|
+
sideGainNode.connect(widthNode);
|
|
2947
|
+
const leftOut = ctx.createGain();
|
|
2948
|
+
const rightOut = ctx.createGain();
|
|
2949
|
+
const sideInvDec = ctx.createGain();
|
|
2950
|
+
sideInvDec.gain.value = -1;
|
|
2951
|
+
midGainNode.connect(leftOut);
|
|
2952
|
+
midGainNode.connect(rightOut);
|
|
2953
|
+
widthNode.connect(leftOut);
|
|
2954
|
+
widthNode.connect(sideInvDec);
|
|
2955
|
+
sideInvDec.connect(rightOut);
|
|
2956
|
+
leftOut.connect(merger, 0, 0);
|
|
2957
|
+
rightOut.connect(merger, 0, 1);
|
|
2958
|
+
merger.connect(output);
|
|
2959
|
+
return { input, output, midGainNode, sideGainNode, widthNode };
|
|
2960
|
+
},
|
|
2961
|
+
updateParam(nodes, _module, param, value) {
|
|
2962
|
+
if (param === "midGain") nodes.midGainNode.gain.value = value;
|
|
2963
|
+
else if (param === "sideGain") nodes.sideGainNode.gain.value = value;
|
|
2964
|
+
else if (param === "width") nodes.widthNode.gain.value = value;
|
|
2965
|
+
}
|
|
2966
|
+
});
|
|
2967
|
+
|
|
2968
|
+
// src/lib-runtime/generated/modules/mid-side.wrapper.ts
|
|
2969
|
+
var MidSide = /* @__PURE__ */ defineModule(midSideDescriptor);
|
|
2970
|
+
|
|
2971
|
+
// src/lib-runtime/generated/modules/noise.tsx
|
|
2972
|
+
var params27 = {
|
|
2973
|
+
noiseType: { kind: "enum", default: "white", options: ["white", "pink", "brown"] },
|
|
2974
|
+
gain: { node: "output", field: "gain", unit: "factor", default: 0.3, min: 0, max: 1 }
|
|
2975
|
+
};
|
|
2976
|
+
function generateNoiseBuffer(ctx, type) {
|
|
2977
|
+
const bufferSize = 2 * ctx.sampleRate;
|
|
2978
|
+
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
|
2979
|
+
const data = buffer.getChannelData(0);
|
|
2980
|
+
if (type === "white") {
|
|
2981
|
+
const rand = noiseSource(NOISE_SEEDS.noiseWhite);
|
|
2982
|
+
for (let i = 0; i < bufferSize; i++) data[i] = rand();
|
|
2983
|
+
} else if (type === "pink") {
|
|
2984
|
+
const rand = noiseSource(NOISE_SEEDS.noisePink);
|
|
2985
|
+
let b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0, b6 = 0;
|
|
2986
|
+
for (let i = 0; i < bufferSize; i++) {
|
|
2987
|
+
const w = rand();
|
|
2988
|
+
b0 = 0.99886 * b0 + w * 0.0555179;
|
|
2989
|
+
b1 = 0.99332 * b1 + w * 0.0750759;
|
|
2990
|
+
b2 = 0.969 * b2 + w * 0.153852;
|
|
2991
|
+
b3 = 0.8665 * b3 + w * 0.3104856;
|
|
2992
|
+
b4 = 0.55 * b4 + w * 0.5329522;
|
|
2993
|
+
b5 = -0.7616 * b5 - w * 0.016898;
|
|
2994
|
+
data[i] = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + w * 0.5362) * 0.11;
|
|
2995
|
+
b6 = w * 0.115926;
|
|
2996
|
+
}
|
|
2997
|
+
} else {
|
|
2998
|
+
const rand = noiseSource(NOISE_SEEDS.noiseBrown);
|
|
2999
|
+
let lastOut = 0;
|
|
3000
|
+
for (let i = 0; i < bufferSize; i++) {
|
|
3001
|
+
const w = rand();
|
|
3002
|
+
data[i] = (lastOut + 0.02 * w) / 1.02;
|
|
3003
|
+
lastOut = data[i];
|
|
3004
|
+
data[i] *= 3.5;
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return buffer;
|
|
3008
|
+
}
|
|
3009
|
+
var noiseDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3010
|
+
type: "noise",
|
|
3011
|
+
params: params27,
|
|
3012
|
+
ports: {
|
|
3013
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
3014
|
+
"modLevel": { type: "modulation", signal: "modulation", node: "output", field: "gain" }
|
|
3015
|
+
},
|
|
3016
|
+
createModule(id, position) {
|
|
3017
|
+
return { id, type: "noise", position, ...paramDefaults(params27) };
|
|
3018
|
+
},
|
|
3019
|
+
createNodes(ctx, module) {
|
|
3020
|
+
const whiteSrc = ctx.createBufferSource();
|
|
3021
|
+
whiteSrc.buffer = generateNoiseBuffer(ctx, "white");
|
|
3022
|
+
whiteSrc.loop = true;
|
|
3023
|
+
const pinkSrc = ctx.createBufferSource();
|
|
3024
|
+
pinkSrc.buffer = generateNoiseBuffer(ctx, "pink");
|
|
3025
|
+
pinkSrc.loop = true;
|
|
3026
|
+
const brownSrc = ctx.createBufferSource();
|
|
3027
|
+
brownSrc.buffer = generateNoiseBuffer(ctx, "brown");
|
|
3028
|
+
brownSrc.loop = true;
|
|
3029
|
+
const whiteGain = ctx.createGain();
|
|
3030
|
+
const pinkGain = ctx.createGain();
|
|
3031
|
+
const brownGain = ctx.createGain();
|
|
3032
|
+
whiteGain.gain.value = module.noiseType === "white" ? 1 : 0;
|
|
3033
|
+
pinkGain.gain.value = module.noiseType === "pink" ? 1 : 0;
|
|
3034
|
+
brownGain.gain.value = module.noiseType === "brown" ? 1 : 0;
|
|
3035
|
+
const output = ctx.createGain();
|
|
3036
|
+
output.gain.value = module.gain;
|
|
3037
|
+
whiteSrc.connect(whiteGain);
|
|
3038
|
+
pinkSrc.connect(pinkGain);
|
|
3039
|
+
brownSrc.connect(brownGain);
|
|
3040
|
+
whiteGain.connect(output);
|
|
3041
|
+
pinkGain.connect(output);
|
|
3042
|
+
brownGain.connect(output);
|
|
3043
|
+
whiteSrc.start(0);
|
|
3044
|
+
pinkSrc.start(0);
|
|
3045
|
+
brownSrc.start(0);
|
|
3046
|
+
return { output, whiteGain, pinkGain, brownGain, _whiteSrc: whiteSrc, _pinkSrc: pinkSrc, _brownSrc: brownSrc };
|
|
3047
|
+
},
|
|
3048
|
+
updateParam(nodes, _module, param, value) {
|
|
3049
|
+
const t = nodes.output.context.currentTime;
|
|
3050
|
+
switch (param) {
|
|
3051
|
+
case "noiseType":
|
|
3052
|
+
nodes.whiteGain.gain.setTargetAtTime(value === "white" ? 1 : 0, t, 5e-3);
|
|
3053
|
+
nodes.pinkGain.gain.setTargetAtTime(value === "pink" ? 1 : 0, t, 5e-3);
|
|
3054
|
+
nodes.brownGain.gain.setTargetAtTime(value === "brown" ? 1 : 0, t, 5e-3);
|
|
3055
|
+
break;
|
|
3056
|
+
case "gain":
|
|
3057
|
+
nodes.output.gain.setTargetAtTime(value, t, 5e-3);
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
},
|
|
3061
|
+
dispose(nodes) {
|
|
3062
|
+
try {
|
|
3063
|
+
nodes._whiteSrc.stop();
|
|
3064
|
+
} catch (_) {
|
|
3065
|
+
}
|
|
3066
|
+
try {
|
|
3067
|
+
nodes._pinkSrc.stop();
|
|
3068
|
+
} catch (_) {
|
|
3069
|
+
}
|
|
3070
|
+
try {
|
|
3071
|
+
nodes._brownSrc.stop();
|
|
3072
|
+
} catch (_) {
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
|
|
3077
|
+
// src/lib-runtime/generated/modules/noise.wrapper.ts
|
|
3078
|
+
var Noise = /* @__PURE__ */ defineModule(noiseDescriptor);
|
|
3079
|
+
|
|
3080
|
+
// src/lib-runtime/generated/modules/octaver.tsx
|
|
3081
|
+
var params28 = {
|
|
3082
|
+
octDown2: { node: "oct2Gain", field: "gain", unit: "factor", default: 0.3, min: 0, max: 1 },
|
|
3083
|
+
octDown1: { node: "oct1Gain", field: "gain", unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3084
|
+
dry: { node: "dryGain", field: "gain", unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
3085
|
+
octUp: { node: "octUpGain", field: "gain", unit: "factor", default: 0, min: 0, max: 1 }
|
|
3086
|
+
};
|
|
3087
|
+
function makeFullWaveRectifierCurve() {
|
|
3088
|
+
const samples = 8192;
|
|
3089
|
+
const curve = new Float32Array(samples);
|
|
3090
|
+
for (let i = 0; i < samples; i++) {
|
|
3091
|
+
const x = i * 2 / samples - 1;
|
|
3092
|
+
curve[i] = Math.abs(x) * 2 - 1;
|
|
3093
|
+
}
|
|
3094
|
+
return curve;
|
|
3095
|
+
}
|
|
3096
|
+
var octaverDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3097
|
+
type: "octaver",
|
|
3098
|
+
params: params28,
|
|
3099
|
+
ports: {
|
|
3100
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3101
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3102
|
+
},
|
|
3103
|
+
createModule(id, position) {
|
|
3104
|
+
return { id, type: "octaver", position, ...paramDefaults(params28) };
|
|
3105
|
+
},
|
|
3106
|
+
createNodes(ctx, module) {
|
|
3107
|
+
const input = ctx.createGain();
|
|
3108
|
+
const output = ctx.createGain();
|
|
3109
|
+
const dryGain = ctx.createGain();
|
|
3110
|
+
const oct1Gain = ctx.createGain();
|
|
3111
|
+
const oct2Gain = ctx.createGain();
|
|
3112
|
+
const octUpGain = ctx.createGain();
|
|
3113
|
+
const divider = createOctaverDividerNode(ctx);
|
|
3114
|
+
divider.parameters.get("oct1Level").value = 1;
|
|
3115
|
+
divider.parameters.get("oct2Level").value = 1;
|
|
3116
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
3117
|
+
const lowpass1 = ctx.createBiquadFilter();
|
|
3118
|
+
lowpass1.type = "lowpass";
|
|
3119
|
+
lowpass1.frequency.value = 1200;
|
|
3120
|
+
lowpass1.Q.value = 0.7;
|
|
3121
|
+
const lowpass2 = ctx.createBiquadFilter();
|
|
3122
|
+
lowpass2.type = "lowpass";
|
|
3123
|
+
lowpass2.frequency.value = 600;
|
|
3124
|
+
lowpass2.Q.value = 0.7;
|
|
3125
|
+
const waveshaperUp = ctx.createWaveShaper();
|
|
3126
|
+
waveshaperUp.curve = makeFullWaveRectifierCurve();
|
|
3127
|
+
const bpUp = ctx.createBiquadFilter();
|
|
3128
|
+
bpUp.type = "bandpass";
|
|
3129
|
+
bpUp.frequency.value = 20;
|
|
3130
|
+
bpUp.Q.value = 2.5;
|
|
3131
|
+
const freqTrack = ctx.createGain();
|
|
3132
|
+
freqTrack.gain.value = 2;
|
|
3133
|
+
dryGain.gain.value = module.dry;
|
|
3134
|
+
oct1Gain.gain.value = module.octDown1;
|
|
3135
|
+
oct2Gain.gain.value = module.octDown2;
|
|
3136
|
+
octUpGain.gain.value = module.octUp;
|
|
3137
|
+
input.connect(dryGain);
|
|
3138
|
+
input.connect(divider);
|
|
3139
|
+
divider.connect(splitter, 1, 0);
|
|
3140
|
+
splitter.connect(lowpass1, 0);
|
|
3141
|
+
splitter.connect(lowpass2, 1);
|
|
3142
|
+
lowpass1.connect(oct1Gain);
|
|
3143
|
+
lowpass2.connect(oct2Gain);
|
|
3144
|
+
divider.connect(freqTrack, 2, 0);
|
|
3145
|
+
freqTrack.connect(bpUp.frequency);
|
|
3146
|
+
input.connect(waveshaperUp);
|
|
3147
|
+
waveshaperUp.connect(bpUp);
|
|
3148
|
+
bpUp.connect(octUpGain);
|
|
3149
|
+
dryGain.connect(output);
|
|
3150
|
+
oct1Gain.connect(output);
|
|
3151
|
+
oct2Gain.connect(output);
|
|
3152
|
+
octUpGain.connect(output);
|
|
3153
|
+
return { input, output, dryGain, oct1Gain, oct2Gain, octUpGain, divider, bpUp, freqTrack };
|
|
3154
|
+
},
|
|
3155
|
+
updateParam(nodes, _m, param, value) {
|
|
3156
|
+
if (param === "dry") nodes.dryGain.gain.value = value;
|
|
3157
|
+
else if (param === "octDown1") nodes.oct1Gain.gain.value = value;
|
|
3158
|
+
else if (param === "octDown2") nodes.oct2Gain.gain.value = value;
|
|
3159
|
+
else if (param === "octUp") nodes.octUpGain.gain.value = value;
|
|
3160
|
+
}
|
|
3161
|
+
});
|
|
3162
|
+
|
|
3163
|
+
// src/lib-runtime/generated/modules/octaver.wrapper.ts
|
|
3164
|
+
var Octaver = /* @__PURE__ */ defineModule(octaverDescriptor, {
|
|
3165
|
+
worklets: {
|
|
3166
|
+
"freq-divider": freq_divider_default
|
|
3167
|
+
}
|
|
3168
|
+
});
|
|
3169
|
+
|
|
3170
|
+
// src/lib-runtime/generated/modules/phaser.tsx
|
|
3171
|
+
var params29 = {
|
|
3172
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 0.5, min: 0.1, max: 10 },
|
|
3173
|
+
depth: { unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
3174
|
+
feedback: { node: "feedback", field: "gain", unit: "factor", default: 0.6, min: 0, max: 0.9 },
|
|
3175
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
3176
|
+
};
|
|
3177
|
+
var phaserDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3178
|
+
type: "phaser",
|
|
3179
|
+
params: params29,
|
|
3180
|
+
ports: {
|
|
3181
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3182
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
3183
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" },
|
|
3184
|
+
"modFeedback": { type: "modulation", signal: "modulation", node: "feedback", field: "gain" },
|
|
3185
|
+
"modMix": { type: "modulation", signal: "modulation", node: "_mix", field: "offset" }
|
|
3186
|
+
},
|
|
3187
|
+
createModule(id, position) {
|
|
3188
|
+
return { id, type: "phaser", position, ...paramDefaults(params29) };
|
|
3189
|
+
},
|
|
3190
|
+
createNodes(ctx, module) {
|
|
3191
|
+
const input = ctx.createGain();
|
|
3192
|
+
const output = ctx.createGain();
|
|
3193
|
+
const dryGain = ctx.createGain();
|
|
3194
|
+
const wetGain = ctx.createGain();
|
|
3195
|
+
const lfo = ctx.createOscillator();
|
|
3196
|
+
const feedback = ctx.createGain();
|
|
3197
|
+
const stageBases = [200, 400, 600, 800];
|
|
3198
|
+
const allpasses = stageBases.map((base) => {
|
|
3199
|
+
const ap = ctx.createBiquadFilter();
|
|
3200
|
+
ap.type = "allpass";
|
|
3201
|
+
ap.frequency.value = base;
|
|
3202
|
+
ap.Q.value = 0.5;
|
|
3203
|
+
return ap;
|
|
3204
|
+
});
|
|
3205
|
+
const stageGains = stageBases.map((base) => {
|
|
3206
|
+
const g = ctx.createGain();
|
|
3207
|
+
g.gain.value = module.depth * base * 0.8;
|
|
3208
|
+
return g;
|
|
3209
|
+
});
|
|
3210
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
3211
|
+
feedback.gain.value = module.feedback;
|
|
3212
|
+
lfo.type = "sine";
|
|
3213
|
+
lfo.frequency.value = module.rate;
|
|
3214
|
+
allpasses.forEach((ap, i) => {
|
|
3215
|
+
lfo.connect(stageGains[i]);
|
|
3216
|
+
stageGains[i].connect(ap.frequency);
|
|
3217
|
+
});
|
|
3218
|
+
lfo.start();
|
|
3219
|
+
input.connect(dryGain);
|
|
3220
|
+
input.connect(allpasses[0]);
|
|
3221
|
+
allpasses[0].connect(allpasses[1]);
|
|
3222
|
+
allpasses[1].connect(allpasses[2]);
|
|
3223
|
+
allpasses[2].connect(allpasses[3]);
|
|
3224
|
+
allpasses[3].connect(feedback);
|
|
3225
|
+
feedback.connect(allpasses[0]);
|
|
3226
|
+
allpasses[3].connect(wetGain);
|
|
3227
|
+
dryGain.connect(output);
|
|
3228
|
+
wetGain.connect(output);
|
|
3229
|
+
return {
|
|
3230
|
+
input,
|
|
3231
|
+
output,
|
|
3232
|
+
lfo,
|
|
3233
|
+
feedback,
|
|
3234
|
+
dryGain,
|
|
3235
|
+
wetGain,
|
|
3236
|
+
_mix,
|
|
3237
|
+
stageGain0: stageGains[0],
|
|
3238
|
+
stageGain1: stageGains[1],
|
|
3239
|
+
stageGain2: stageGains[2],
|
|
3240
|
+
stageGain3: stageGains[3]
|
|
3241
|
+
};
|
|
3242
|
+
},
|
|
3243
|
+
updateParam(nodes, _m, param, value) {
|
|
3244
|
+
switch (param) {
|
|
3245
|
+
case "rate":
|
|
3246
|
+
nodes.lfo.frequency.value = value;
|
|
3247
|
+
break;
|
|
3248
|
+
case "depth": {
|
|
3249
|
+
const d = value;
|
|
3250
|
+
[200, 400, 600, 800].forEach((base, i) => {
|
|
3251
|
+
const g = nodes[`stageGain${i}`];
|
|
3252
|
+
g.gain.value = d * base * 0.8;
|
|
3253
|
+
});
|
|
3254
|
+
break;
|
|
3255
|
+
}
|
|
3256
|
+
case "feedback":
|
|
3257
|
+
nodes.feedback.gain.value = value;
|
|
3258
|
+
break;
|
|
3259
|
+
case "mix":
|
|
3260
|
+
nodes._mix.offset.value = value;
|
|
3261
|
+
break;
|
|
3262
|
+
}
|
|
3263
|
+
},
|
|
3264
|
+
dispose(nodes) {
|
|
3265
|
+
nodes.lfo.stop();
|
|
3266
|
+
nodes._mix.stop();
|
|
3267
|
+
}
|
|
3268
|
+
});
|
|
3269
|
+
|
|
3270
|
+
// src/lib-runtime/generated/modules/phaser.wrapper.ts
|
|
3271
|
+
var Phaser = /* @__PURE__ */ defineModule(phaserDescriptor);
|
|
3272
|
+
|
|
3273
|
+
// src/lib-runtime/generated/modules/ping-pong.tsx
|
|
3274
|
+
var params30 = {
|
|
3275
|
+
time: { node: "worklet", field: "time", unit: "s", default: 0.3, min: 0.1, max: 1, scale: "log" },
|
|
3276
|
+
feedback: { node: "worklet", field: "feedback", unit: "factor", default: 0.4, min: 0, max: 0.9 },
|
|
3277
|
+
spread: { unit: "factor", default: 1, min: 0, max: 1 },
|
|
3278
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
3279
|
+
};
|
|
3280
|
+
var pingPongDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3281
|
+
type: "ping-pong",
|
|
3282
|
+
params: params30,
|
|
3283
|
+
ports: {
|
|
3284
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3285
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3286
|
+
},
|
|
3287
|
+
createModule(id, position) {
|
|
3288
|
+
return { id, type: "ping-pong", position, ...paramDefaults(params30) };
|
|
3289
|
+
},
|
|
3290
|
+
// The delay lines and cross-feedback live in the 'ping-pong' AudioWorklet
|
|
3291
|
+
// (src/worklets/ping-pong.ts) as of 2026-08-08. A native two-DelayNode
|
|
3292
|
+
// cycle carries ONE render quantum of hidden latency at an UNSTABLE
|
|
3293
|
+
// position: identical code measured +3.01 dB (coherent grid) on one run
|
|
3294
|
+
// and −0.19 dB (right leg off by a quantum) on the next, so no static
|
|
3295
|
+
// delayTime compensation can hold the tap grid. The worklet's grid is
|
|
3296
|
+
// exact by construction. Pans, spread, and dry/wet mix stay native.
|
|
3297
|
+
createNodes(ctx, module) {
|
|
3298
|
+
const input = ctx.createGain();
|
|
3299
|
+
const output = ctx.createGain();
|
|
3300
|
+
const dryGain = ctx.createGain();
|
|
3301
|
+
const wetGain = ctx.createGain();
|
|
3302
|
+
const panL = ctx.createStereoPanner();
|
|
3303
|
+
const panR = ctx.createStereoPanner();
|
|
3304
|
+
const worklet = createPingPongNode(ctx, {
|
|
3305
|
+
time: module.time,
|
|
3306
|
+
feedback: module.feedback
|
|
3307
|
+
});
|
|
3308
|
+
panL.pan.value = -module.spread;
|
|
3309
|
+
panR.pan.value = module.spread;
|
|
3310
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
3311
|
+
input.connect(dryGain);
|
|
3312
|
+
input.connect(worklet);
|
|
3313
|
+
worklet.connect(panL, 0);
|
|
3314
|
+
worklet.connect(panR, 1);
|
|
3315
|
+
panL.connect(wetGain);
|
|
3316
|
+
panR.connect(wetGain);
|
|
3317
|
+
dryGain.connect(output);
|
|
3318
|
+
wetGain.connect(output);
|
|
3319
|
+
return { input, output, worklet, dryGain, wetGain, panL, panR, _mix };
|
|
3320
|
+
},
|
|
3321
|
+
updateParam(nodes, _m, param, value) {
|
|
3322
|
+
if (param === "time" || param === "feedback") {
|
|
3323
|
+
const p = nodes.worklet.parameters.get(param);
|
|
3324
|
+
if (p) p.value = value;
|
|
3325
|
+
} else if (param === "spread") {
|
|
3326
|
+
nodes.panL.pan.value = -value;
|
|
3327
|
+
nodes.panR.pan.value = value;
|
|
3328
|
+
} else if (param === "mix") {
|
|
3329
|
+
nodes._mix.offset.value = value;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
|
|
3334
|
+
// src/lib-runtime/generated/modules/ping-pong.wrapper.ts
|
|
3335
|
+
var PingPong = /* @__PURE__ */ defineModule(pingPongDescriptor, {
|
|
3336
|
+
worklets: {
|
|
3337
|
+
"ping-pong": ping_pong_default
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
// src/lib/dsp/modal.ts
|
|
3342
|
+
var MODAL_Q_MAX = 1e3;
|
|
3343
|
+
var MODAL_Q_MIN = 2;
|
|
3344
|
+
var T60_PER_Q_HZ = 3 * Math.LN10 / Math.PI;
|
|
3345
|
+
function dampingToQ(damping) {
|
|
3346
|
+
const d = Math.min(1, Math.max(0, Number.isFinite(damping) ? damping : 0.5));
|
|
3347
|
+
return MODAL_Q_MAX * Math.pow(MODAL_Q_MIN / MODAL_Q_MAX, d);
|
|
3348
|
+
}
|
|
3349
|
+
function modalMakeup(q) {
|
|
3350
|
+
return Math.sqrt(Math.max(1, q));
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
// src/lib-runtime/generated/modules/resonator.tsx
|
|
3354
|
+
var params31 = {
|
|
3355
|
+
// Root pitch = V/OCT input (absolute notes, silence = 0) + MOD (semitones) + offset.
|
|
3356
|
+
offset: { node: "_root", field: "offset", default: 57, min: -48, max: 108, unit: "semitone" },
|
|
3357
|
+
structure: { unit: "factor", default: 0, min: 0, max: 1 },
|
|
3358
|
+
damping: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3359
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
3360
|
+
};
|
|
3361
|
+
function partialOffset(partial, structure) {
|
|
3362
|
+
return 12 * (1 + structure) * Math.log2(partial);
|
|
3363
|
+
}
|
|
3364
|
+
var patchCount2 = /* @__PURE__ */ new WeakMap();
|
|
3365
|
+
var resonatorDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3366
|
+
type: "resonator",
|
|
3367
|
+
params: params31,
|
|
3368
|
+
ports: {
|
|
3369
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3370
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
3371
|
+
"voct": { type: "input", signal: "pitch", node: "_pitch" },
|
|
3372
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_pitch" }
|
|
3373
|
+
},
|
|
3374
|
+
createModule(id, position) {
|
|
3375
|
+
const { offset: _dsp, ...rest } = paramDefaults(params31);
|
|
3376
|
+
return { id, type: "resonator", position, ...rest, note: 57, transpose: 0 };
|
|
3377
|
+
},
|
|
3378
|
+
createNodes(ctx, module) {
|
|
3379
|
+
const input = ctx.createGain();
|
|
3380
|
+
const output = ctx.createGain();
|
|
3381
|
+
const dryGain = ctx.createGain();
|
|
3382
|
+
const wetGain = ctx.createGain();
|
|
3383
|
+
const _root = ctx.createConstantSource();
|
|
3384
|
+
_root.offset.value = module.offset ?? module.note;
|
|
3385
|
+
_root.start();
|
|
3386
|
+
const _pitch = ctx.createGain();
|
|
3387
|
+
_root.connect(_pitch);
|
|
3388
|
+
const filters = [];
|
|
3389
|
+
const partialGains = [];
|
|
3390
|
+
const voct = [];
|
|
3391
|
+
const q = dampingToQ(module.damping);
|
|
3392
|
+
const makeup = modalMakeup(q);
|
|
3393
|
+
for (let i = 1; i <= 5; i++) {
|
|
3394
|
+
const v = createVoctFreqNode(ctx);
|
|
3395
|
+
v.parameters.get("transpose").value = partialOffset(i, module.structure);
|
|
3396
|
+
_pitch.connect(v);
|
|
3397
|
+
const filter = ctx.createBiquadFilter();
|
|
3398
|
+
filter.type = "bandpass";
|
|
3399
|
+
filter.frequency.value = 0;
|
|
3400
|
+
v.connect(filter.frequency);
|
|
3401
|
+
filter.Q.value = q;
|
|
3402
|
+
filters.push(filter);
|
|
3403
|
+
voct.push(v);
|
|
3404
|
+
input.connect(filter);
|
|
3405
|
+
const partialGain = ctx.createGain();
|
|
3406
|
+
partialGain.gain.value = makeup / Math.sqrt(i);
|
|
3407
|
+
filter.connect(partialGain);
|
|
3408
|
+
partialGain.connect(wetGain);
|
|
3409
|
+
partialGains.push(partialGain);
|
|
3410
|
+
}
|
|
3411
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear", 0.6);
|
|
3412
|
+
input.connect(dryGain);
|
|
3413
|
+
dryGain.connect(output);
|
|
3414
|
+
wetGain.connect(output);
|
|
3415
|
+
return {
|
|
3416
|
+
input,
|
|
3417
|
+
output,
|
|
3418
|
+
dryGain,
|
|
3419
|
+
wetGain,
|
|
3420
|
+
_mix,
|
|
3421
|
+
_root,
|
|
3422
|
+
_pitch,
|
|
3423
|
+
v1: voct[0],
|
|
3424
|
+
v2: voct[1],
|
|
3425
|
+
v3: voct[2],
|
|
3426
|
+
v4: voct[3],
|
|
3427
|
+
v5: voct[4],
|
|
3428
|
+
f1: filters[0],
|
|
3429
|
+
f2: filters[1],
|
|
3430
|
+
f3: filters[2],
|
|
3431
|
+
f4: filters[3],
|
|
3432
|
+
f5: filters[4],
|
|
3433
|
+
g1: partialGains[0],
|
|
3434
|
+
g2: partialGains[1],
|
|
3435
|
+
g3: partialGains[2],
|
|
3436
|
+
g4: partialGains[3],
|
|
3437
|
+
g5: partialGains[4]
|
|
3438
|
+
};
|
|
3439
|
+
},
|
|
3440
|
+
updateParam(nodes, _module, param, value) {
|
|
3441
|
+
const filters = [nodes.f1, nodes.f2, nodes.f3, nodes.f4, nodes.f5];
|
|
3442
|
+
if (param === "offset") {
|
|
3443
|
+
nodes._root.offset.value = value;
|
|
3444
|
+
} else if (param === "note") {
|
|
3445
|
+
if (!patchCount2.get(nodes)) nodes._root.offset.value = value;
|
|
3446
|
+
} else if (param === "transpose") {
|
|
3447
|
+
if (patchCount2.get(nodes)) nodes._root.offset.value = value;
|
|
3448
|
+
} else if (param === "structure") {
|
|
3449
|
+
const voct = [nodes.v1, nodes.v2, nodes.v3, nodes.v4, nodes.v5];
|
|
3450
|
+
for (let i = 1; i < 5; i++) {
|
|
3451
|
+
voct[i].parameters.get("transpose").value = partialOffset(i + 1, value);
|
|
3452
|
+
}
|
|
3453
|
+
} else if (param === "damping") {
|
|
3454
|
+
const q = dampingToQ(value);
|
|
3455
|
+
const makeup = modalMakeup(q);
|
|
3456
|
+
const gains = [nodes.g1, nodes.g2, nodes.g3, nodes.g4, nodes.g5];
|
|
3457
|
+
for (let i = 0; i < 5; i++) {
|
|
3458
|
+
filters[i].Q.value = q;
|
|
3459
|
+
gains[i].gain.value = makeup / Math.sqrt(i + 1);
|
|
3460
|
+
}
|
|
3461
|
+
} else if (param === "mix") {
|
|
3462
|
+
nodes._mix.offset.value = value;
|
|
3463
|
+
}
|
|
3464
|
+
},
|
|
3465
|
+
// App-only: plain summing connect; the hooks only swap which panel memory
|
|
3466
|
+
// drives the offset.
|
|
3467
|
+
onConnect(nodes, module, portId, sourceNode) {
|
|
3468
|
+
if (portId !== "voct") return false;
|
|
3469
|
+
sourceNode.connect(nodes._pitch);
|
|
3470
|
+
patchCount2.set(nodes, (patchCount2.get(nodes) ?? 0) + 1);
|
|
3471
|
+
nodes._root.offset.value = module.transpose ?? 0;
|
|
3472
|
+
return true;
|
|
3473
|
+
},
|
|
3474
|
+
onDisconnect(nodes, module, portId, sourceNode) {
|
|
3475
|
+
if (portId !== "voct") return false;
|
|
3476
|
+
try {
|
|
3477
|
+
sourceNode.disconnect(nodes._pitch);
|
|
3478
|
+
} catch (_) {
|
|
3479
|
+
}
|
|
3480
|
+
const n = Math.max(0, (patchCount2.get(nodes) ?? 0) - 1);
|
|
3481
|
+
patchCount2.set(nodes, n);
|
|
3482
|
+
if (!n) nodes._root.offset.value = module.note;
|
|
3483
|
+
return true;
|
|
3484
|
+
},
|
|
3485
|
+
dispose(nodes) {
|
|
3486
|
+
nodes._root.stop();
|
|
3487
|
+
}
|
|
3488
|
+
});
|
|
3489
|
+
|
|
3490
|
+
// src/lib-runtime/generated/modules/resonator.wrapper.ts
|
|
3491
|
+
var Resonator = /* @__PURE__ */ defineModule(resonatorDescriptor, {
|
|
3492
|
+
worklets: {
|
|
3493
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
3494
|
+
}
|
|
3495
|
+
});
|
|
3496
|
+
|
|
3497
|
+
// src/lib/dsp/fdnTank.ts
|
|
3498
|
+
function createFdnTank(ctx, opts) {
|
|
3499
|
+
const input = ctx.createGain();
|
|
3500
|
+
const output = ctx.createGain();
|
|
3501
|
+
const node = createFdnReverbNode(ctx, opts);
|
|
3502
|
+
input.connect(node);
|
|
3503
|
+
node.connect(output);
|
|
3504
|
+
return { input, output, node };
|
|
3505
|
+
}
|
|
3506
|
+
function setTankDecay(tank, rt60, now2, t = 0.02) {
|
|
3507
|
+
tank.node.parameters.get("rt60").setTargetAtTime(rt60, now2, t);
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// src/lib-runtime/generated/modules/reverb.tsx
|
|
3511
|
+
var params32 = {
|
|
3512
|
+
decay: { node: "tankNode", field: "rt60", default: 2, min: 0.1, max: 8, scale: "log", unit: "s" },
|
|
3513
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.3, min: 0, max: 1 }
|
|
3514
|
+
};
|
|
3515
|
+
var reverbDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3516
|
+
type: "reverb",
|
|
3517
|
+
params: params32,
|
|
3518
|
+
ports: {
|
|
3519
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3520
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3521
|
+
},
|
|
3522
|
+
createModule(id, position) {
|
|
3523
|
+
return {
|
|
3524
|
+
id,
|
|
3525
|
+
type: "reverb",
|
|
3526
|
+
position,
|
|
3527
|
+
...paramDefaults(params32)
|
|
3528
|
+
};
|
|
3529
|
+
},
|
|
3530
|
+
createNodes(ctx, module) {
|
|
3531
|
+
const input = ctx.createGain();
|
|
3532
|
+
const output = ctx.createGain();
|
|
3533
|
+
const dryGain = ctx.createGain();
|
|
3534
|
+
const wetGain = ctx.createGain();
|
|
3535
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "equal-power");
|
|
3536
|
+
const preDelay = ctx.createDelay(0.5);
|
|
3537
|
+
preDelay.delayTime.value = 0.02;
|
|
3538
|
+
const tank = createFdnTank(ctx, { rt60: module.decay, damping: 0.55 });
|
|
3539
|
+
input.connect(dryGain);
|
|
3540
|
+
input.connect(preDelay);
|
|
3541
|
+
preDelay.connect(tank.input);
|
|
3542
|
+
tank.output.connect(wetGain);
|
|
3543
|
+
dryGain.connect(output);
|
|
3544
|
+
wetGain.connect(output);
|
|
3545
|
+
return {
|
|
3546
|
+
input,
|
|
3547
|
+
output,
|
|
3548
|
+
dryGain,
|
|
3549
|
+
wetGain,
|
|
3550
|
+
_mix,
|
|
3551
|
+
preDelay,
|
|
3552
|
+
tankInput: tank.input,
|
|
3553
|
+
tankOutput: tank.output,
|
|
3554
|
+
tankNode: tank.node
|
|
3555
|
+
};
|
|
3556
|
+
},
|
|
3557
|
+
updateParam(nodes, _m, param, value) {
|
|
3558
|
+
const now2 = nodes.input.context.currentTime;
|
|
3559
|
+
if (param === "mix") {
|
|
3560
|
+
nodes._mix.offset.setTargetAtTime(value, now2, 0.02);
|
|
3561
|
+
} else if (param === "decay") {
|
|
3562
|
+
setTankDecay({ node: nodes.tankNode }, value, now2);
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
|
|
3567
|
+
// src/lib-runtime/generated/modules/reverb.wrapper.ts
|
|
3568
|
+
var Reverb = /* @__PURE__ */ defineModule(reverbDescriptor, {
|
|
3569
|
+
worklets: {
|
|
3570
|
+
"fdn-reverb": fdn_reverb_default
|
|
3571
|
+
}
|
|
3572
|
+
});
|
|
3573
|
+
|
|
3574
|
+
// src/lib-runtime/generated/modules/ring-mod.tsx
|
|
3575
|
+
var params33 = {
|
|
3576
|
+
// Carrier pitch = V/OCT input (absolute notes, silence = 0) + MOD (semitones) + offset.
|
|
3577
|
+
offset: { node: "_carrierVoct", field: "transpose", unit: "semitone", default: 69, min: -48, max: 108 },
|
|
3578
|
+
// Dry/wet is split across two gain nodes, not one AudioParam — set-only.
|
|
3579
|
+
mix: { unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
3580
|
+
};
|
|
3581
|
+
var patchCount3 = /* @__PURE__ */ new WeakMap();
|
|
3582
|
+
var ringModDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3583
|
+
type: "ring-mod",
|
|
3584
|
+
params: params33,
|
|
3585
|
+
ports: {
|
|
3586
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3587
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
3588
|
+
"voct": { type: "input", signal: "pitch", node: "_carrierVoct" },
|
|
3589
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_carrierVoct" }
|
|
3590
|
+
},
|
|
3591
|
+
createModule(id, position) {
|
|
3592
|
+
const { offset: _dsp, ...rest } = paramDefaults(params33);
|
|
3593
|
+
return { id, type: "ring-mod", position, ...rest, note: 69, transpose: 0 };
|
|
3594
|
+
},
|
|
3595
|
+
createNodes(ctx, module) {
|
|
3596
|
+
const input = ctx.createGain();
|
|
3597
|
+
const output = ctx.createGain();
|
|
3598
|
+
const dryGain = ctx.createGain();
|
|
3599
|
+
const wetGain = ctx.createGain();
|
|
3600
|
+
const carrier = ctx.createOscillator();
|
|
3601
|
+
const ringMod = ctx.createGain();
|
|
3602
|
+
dryGain.gain.value = 1 - module.mix;
|
|
3603
|
+
wetGain.gain.value = module.mix;
|
|
3604
|
+
carrier.type = "sine";
|
|
3605
|
+
ringMod.gain.value = 0;
|
|
3606
|
+
carrier.connect(ringMod.gain);
|
|
3607
|
+
carrier.start();
|
|
3608
|
+
carrier.frequency.value = 0;
|
|
3609
|
+
const _carrierVoct = createVoctFreqNode(ctx);
|
|
3610
|
+
_carrierVoct.parameters.get("transpose").value = module.offset ?? module.note;
|
|
3611
|
+
const _carrierBase = ctx.createConstantSource();
|
|
3612
|
+
_carrierBase.offset.value = 0;
|
|
3613
|
+
_carrierBase.start();
|
|
3614
|
+
_carrierBase.connect(_carrierVoct);
|
|
3615
|
+
_carrierVoct.connect(carrier.frequency);
|
|
3616
|
+
input.connect(dryGain);
|
|
3617
|
+
input.connect(ringMod);
|
|
3618
|
+
ringMod.connect(wetGain);
|
|
3619
|
+
dryGain.connect(output);
|
|
3620
|
+
wetGain.connect(output);
|
|
3621
|
+
return { input, output, carrier, ringMod, dryGain, wetGain, _carrierVoct, _carrierBase };
|
|
3622
|
+
},
|
|
3623
|
+
updateParam(nodes, _m, param, value) {
|
|
3624
|
+
const t = nodes._carrierVoct.parameters.get("transpose");
|
|
3625
|
+
if (param === "offset") t.value = value;
|
|
3626
|
+
else if (param === "note") {
|
|
3627
|
+
if (!patchCount3.get(nodes)) t.value = value;
|
|
3628
|
+
} else if (param === "transpose") {
|
|
3629
|
+
if (patchCount3.get(nodes)) t.value = value;
|
|
3630
|
+
} else if (param === "mix") {
|
|
3631
|
+
nodes.dryGain.gain.value = 1 - value;
|
|
3632
|
+
nodes.wetGain.gain.value = value;
|
|
3633
|
+
}
|
|
3634
|
+
},
|
|
3635
|
+
// App-only: plain summing connect; the hooks only swap which panel memory
|
|
3636
|
+
// drives the offset.
|
|
3637
|
+
onConnect(nodes, module, portId, sourceNode) {
|
|
3638
|
+
if (portId !== "voct") return false;
|
|
3639
|
+
sourceNode.connect(nodes._carrierVoct);
|
|
3640
|
+
patchCount3.set(nodes, (patchCount3.get(nodes) ?? 0) + 1);
|
|
3641
|
+
nodes._carrierVoct.parameters.get("transpose").value = module.transpose ?? 0;
|
|
3642
|
+
return true;
|
|
3643
|
+
},
|
|
3644
|
+
onDisconnect(nodes, module, portId, sourceNode) {
|
|
3645
|
+
if (portId !== "voct") return false;
|
|
3646
|
+
try {
|
|
3647
|
+
sourceNode.disconnect(nodes._carrierVoct);
|
|
3648
|
+
} catch (_) {
|
|
3649
|
+
}
|
|
3650
|
+
const n = Math.max(0, (patchCount3.get(nodes) ?? 0) - 1);
|
|
3651
|
+
patchCount3.set(nodes, n);
|
|
3652
|
+
if (!n) nodes._carrierVoct.parameters.get("transpose").value = module.note;
|
|
3653
|
+
return true;
|
|
3654
|
+
},
|
|
3655
|
+
dispose(nodes) {
|
|
3656
|
+
try {
|
|
3657
|
+
nodes.carrier.stop();
|
|
3658
|
+
} catch (_) {
|
|
3659
|
+
}
|
|
3660
|
+
try {
|
|
3661
|
+
nodes._carrierBase.stop();
|
|
3662
|
+
} catch (_) {
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
});
|
|
3666
|
+
|
|
3667
|
+
// src/lib-runtime/generated/modules/ring-mod.wrapper.ts
|
|
3668
|
+
var RingModulator = /* @__PURE__ */ defineModule(ringModDescriptor, {
|
|
3669
|
+
worklets: {
|
|
3670
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
3671
|
+
}
|
|
3672
|
+
});
|
|
3673
|
+
|
|
3674
|
+
// src/lib-runtime/generated/modules/rotary.tsx
|
|
3675
|
+
var CROSSOVER_HZ = 800;
|
|
3676
|
+
var BUTTERWORTH_Q_DB = -3.0103;
|
|
3677
|
+
var DRUM_RATIO = 0.85;
|
|
3678
|
+
var HORN = { baseDelay: 3e-3, dopplerSec: 44e-5, ampBase: 0.7, ampDepth: 0.3, phaseDeg: 0, tauUp: 0.35, tauDown: 0.55 };
|
|
3679
|
+
var DRUM = { baseDelay: 3e-3, dopplerSec: 2e-4, ampBase: 0.8, ampDepth: 0.2, phaseDeg: 90, tauUp: 1.5, tauDown: 1.7 };
|
|
3680
|
+
var params34 = {
|
|
3681
|
+
speed: { kind: "enum", options: ["slow", "fast"], default: "slow" },
|
|
3682
|
+
slowRate: { unit: "Hz", default: 0.8, min: 0.5, max: 2 },
|
|
3683
|
+
fastRate: { unit: "Hz", default: 6, min: 4, max: 8 },
|
|
3684
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.7, min: 0, max: 1 }
|
|
3685
|
+
};
|
|
3686
|
+
var rotaryDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3687
|
+
type: "rotary",
|
|
3688
|
+
params: params34,
|
|
3689
|
+
ports: {
|
|
3690
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3691
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
3692
|
+
"modMix": { type: "modulation", signal: "modulation", node: "_mix", field: "offset" }
|
|
3693
|
+
},
|
|
3694
|
+
createModule(id, position) {
|
|
3695
|
+
return { id, type: "rotary", position, ...paramDefaults(params34) };
|
|
3696
|
+
},
|
|
3697
|
+
createNodes(ctx, module) {
|
|
3698
|
+
const input = ctx.createGain();
|
|
3699
|
+
const output = ctx.createGain();
|
|
3700
|
+
const dryGain = ctx.createGain();
|
|
3701
|
+
const wetGain = ctx.createGain();
|
|
3702
|
+
const monoIn = ctx.createGain();
|
|
3703
|
+
monoIn.channelCount = 1;
|
|
3704
|
+
monoIn.channelCountMode = "explicit";
|
|
3705
|
+
monoIn.channelInterpretation = "speakers";
|
|
3706
|
+
const merger = ctx.createChannelMerger(2);
|
|
3707
|
+
const MIC_DEG = 60;
|
|
3708
|
+
const biquad = (type) => {
|
|
3709
|
+
const f = ctx.createBiquadFilter();
|
|
3710
|
+
f.type = type;
|
|
3711
|
+
f.frequency.value = CROSSOVER_HZ;
|
|
3712
|
+
f.Q.value = BUTTERWORTH_Q_DB;
|
|
3713
|
+
return f;
|
|
3714
|
+
};
|
|
3715
|
+
const lp1 = biquad("lowpass"), lp2 = biquad("lowpass");
|
|
3716
|
+
const hp1 = biquad("highpass"), hp2 = biquad("highpass");
|
|
3717
|
+
monoIn.connect(lp1);
|
|
3718
|
+
lp1.connect(lp2);
|
|
3719
|
+
monoIn.connect(hp1);
|
|
3720
|
+
hp1.connect(hp2);
|
|
3721
|
+
const hornRate = module.speed === "slow" ? module.slowRate : module.fastRate;
|
|
3722
|
+
const sources = [];
|
|
3723
|
+
const rotor = (band, spec, rate) => {
|
|
3724
|
+
const speed = ctx.createConstantSource();
|
|
3725
|
+
speed.offset.value = rate;
|
|
3726
|
+
sources.push(speed);
|
|
3727
|
+
[MIC_DEG, -MIC_DEG].forEach((micDeg, ch) => {
|
|
3728
|
+
const phi = (micDeg + spec.phaseDeg) * Math.PI / 180;
|
|
3729
|
+
const wave = ctx.createPeriodicWave(
|
|
3730
|
+
new Float32Array([0, Math.cos(phi)]),
|
|
3731
|
+
new Float32Array([0, Math.sin(phi)]),
|
|
3732
|
+
{ disableNormalization: true }
|
|
3733
|
+
);
|
|
3734
|
+
const osc = ctx.createOscillator();
|
|
3735
|
+
osc.setPeriodicWave(wave);
|
|
3736
|
+
osc.frequency.value = 0;
|
|
3737
|
+
speed.connect(osc.frequency);
|
|
3738
|
+
sources.push(osc);
|
|
3739
|
+
const delay = ctx.createDelay(0.02);
|
|
3740
|
+
delay.delayTime.value = spec.baseDelay;
|
|
3741
|
+
const dop = ctx.createGain();
|
|
3742
|
+
dop.gain.value = -spec.dopplerSec;
|
|
3743
|
+
osc.connect(dop);
|
|
3744
|
+
dop.connect(delay.delayTime);
|
|
3745
|
+
const amp = ctx.createGain();
|
|
3746
|
+
amp.gain.value = spec.ampBase;
|
|
3747
|
+
const am = ctx.createGain();
|
|
3748
|
+
am.gain.value = spec.ampDepth;
|
|
3749
|
+
osc.connect(am);
|
|
3750
|
+
am.connect(amp.gain);
|
|
3751
|
+
band.connect(delay);
|
|
3752
|
+
delay.connect(amp);
|
|
3753
|
+
amp.connect(merger, 0, ch);
|
|
3754
|
+
});
|
|
3755
|
+
return speed;
|
|
3756
|
+
};
|
|
3757
|
+
const hornSpeed = rotor(hp2, HORN, hornRate);
|
|
3758
|
+
const drumSpeed = rotor(lp2, DRUM, hornRate * DRUM_RATIO);
|
|
3759
|
+
sources.forEach((n) => n.start());
|
|
3760
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
3761
|
+
input.connect(dryGain);
|
|
3762
|
+
input.connect(monoIn);
|
|
3763
|
+
merger.connect(wetGain);
|
|
3764
|
+
dryGain.connect(output);
|
|
3765
|
+
wetGain.connect(output);
|
|
3766
|
+
const srcNodes = Object.fromEntries(sources.map((n, i) => [`src${i}`, n]));
|
|
3767
|
+
return { input, output, dryGain, wetGain, hornSpeed, drumSpeed, _mix, ...srcNodes };
|
|
3768
|
+
},
|
|
3769
|
+
updateParam(nodes, module, param, value) {
|
|
3770
|
+
const now2 = nodes.input.context.currentTime;
|
|
3771
|
+
const setRate = (horn, inertia) => {
|
|
3772
|
+
const hs = nodes.hornSpeed;
|
|
3773
|
+
const ds = nodes.drumSpeed;
|
|
3774
|
+
const up = horn > hs.offset.value;
|
|
3775
|
+
const tH = inertia ? up ? HORN.tauUp : HORN.tauDown : 0.05;
|
|
3776
|
+
const tD = inertia ? up ? DRUM.tauUp : DRUM.tauDown : 0.05;
|
|
3777
|
+
hs.offset.cancelScheduledValues(now2);
|
|
3778
|
+
ds.offset.cancelScheduledValues(now2);
|
|
3779
|
+
hs.offset.setTargetAtTime(horn, now2, tH);
|
|
3780
|
+
ds.offset.setTargetAtTime(horn * DRUM_RATIO, now2, tD);
|
|
3781
|
+
};
|
|
3782
|
+
if (param === "speed") {
|
|
3783
|
+
setRate(value === "slow" ? module.slowRate : module.fastRate, true);
|
|
3784
|
+
} else if (param === "slowRate") {
|
|
3785
|
+
if (module.speed === "slow") setRate(value, false);
|
|
3786
|
+
} else if (param === "fastRate") {
|
|
3787
|
+
if (module.speed === "fast") setRate(value, false);
|
|
3788
|
+
} else if (param === "mix") {
|
|
3789
|
+
nodes._mix.offset.value = value;
|
|
3790
|
+
}
|
|
3791
|
+
},
|
|
3792
|
+
dispose(nodes) {
|
|
3793
|
+
for (const [k, n] of Object.entries(nodes)) {
|
|
3794
|
+
if (k.startsWith("src")) n.stop();
|
|
3795
|
+
}
|
|
3796
|
+
nodes._mix.stop();
|
|
3797
|
+
}
|
|
3798
|
+
});
|
|
3799
|
+
|
|
3800
|
+
// src/lib-runtime/generated/modules/rotary.wrapper.ts
|
|
3801
|
+
var Rotary = /* @__PURE__ */ defineModule(rotaryDescriptor);
|
|
3802
|
+
|
|
3803
|
+
// src/lib-runtime/generated/modules/saturator.tsx
|
|
3804
|
+
var params35 = {
|
|
3805
|
+
// drive feeds both driveGain.gain (1 + drive*4) and the waveshaper curve —
|
|
3806
|
+
// a conversion, not a 1:1 AudioParam write, so no node/field.
|
|
3807
|
+
drive: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3808
|
+
// warmthFilter.gain = warmth * 6 — a scaled conversion, so no node/field.
|
|
3809
|
+
warmth: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3810
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 1, min: 0, max: 1 }
|
|
3811
|
+
};
|
|
3812
|
+
function makeSaturationCurve(amount) {
|
|
3813
|
+
const samples = 44100;
|
|
3814
|
+
const curve = new Float32Array(samples);
|
|
3815
|
+
const k = 1 + amount * 10;
|
|
3816
|
+
for (let i = 0; i < samples; i++) {
|
|
3817
|
+
const x = i * 2 / samples - 1;
|
|
3818
|
+
curve[i] = Math.tanh(k * x) / Math.tanh(k);
|
|
3819
|
+
}
|
|
3820
|
+
return curve;
|
|
3821
|
+
}
|
|
3822
|
+
var saturatorDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3823
|
+
type: "saturator",
|
|
3824
|
+
params: params35,
|
|
3825
|
+
ports: {
|
|
3826
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3827
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3828
|
+
},
|
|
3829
|
+
createModule(id, position) {
|
|
3830
|
+
return { id, type: "saturator", position, ...paramDefaults(params35) };
|
|
3831
|
+
},
|
|
3832
|
+
createNodes(ctx, module) {
|
|
3833
|
+
const input = ctx.createGain();
|
|
3834
|
+
const output = ctx.createGain();
|
|
3835
|
+
const dryGain = ctx.createGain();
|
|
3836
|
+
const wetGain = ctx.createGain();
|
|
3837
|
+
const driveGain = ctx.createGain();
|
|
3838
|
+
const waveshaper = ctx.createWaveShaper();
|
|
3839
|
+
const warmthFilter = ctx.createBiquadFilter();
|
|
3840
|
+
driveGain.gain.value = 1 + module.drive * 4;
|
|
3841
|
+
waveshaper.curve = makeSaturationCurve(module.drive);
|
|
3842
|
+
waveshaper.oversample = "2x";
|
|
3843
|
+
warmthFilter.type = "lowshelf";
|
|
3844
|
+
warmthFilter.frequency.value = 300;
|
|
3845
|
+
warmthFilter.gain.value = module.warmth * 6;
|
|
3846
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
3847
|
+
input.connect(dryGain);
|
|
3848
|
+
input.connect(driveGain);
|
|
3849
|
+
driveGain.connect(waveshaper);
|
|
3850
|
+
waveshaper.connect(warmthFilter);
|
|
3851
|
+
warmthFilter.connect(wetGain);
|
|
3852
|
+
dryGain.connect(output);
|
|
3853
|
+
wetGain.connect(output);
|
|
3854
|
+
return { input, output, driveGain, waveshaper, warmthFilter, dryGain, wetGain, _mix };
|
|
3855
|
+
},
|
|
3856
|
+
updateParam(nodes, _m, param, value) {
|
|
3857
|
+
if (param === "drive") {
|
|
3858
|
+
nodes.driveGain.gain.value = 1 + value * 4;
|
|
3859
|
+
nodes.waveshaper.curve = makeSaturationCurve(value);
|
|
3860
|
+
} else if (param === "warmth") {
|
|
3861
|
+
nodes.warmthFilter.gain.value = value * 6;
|
|
3862
|
+
} else if (param === "mix") {
|
|
3863
|
+
nodes._mix.offset.value = value;
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
});
|
|
3867
|
+
|
|
3868
|
+
// src/lib-runtime/generated/modules/saturator.wrapper.ts
|
|
3869
|
+
var Saturator = /* @__PURE__ */ defineModule(saturatorDescriptor);
|
|
3870
|
+
|
|
3871
|
+
// src/lib-runtime/generated/modules/shimmer.tsx
|
|
3872
|
+
var params36 = {
|
|
3873
|
+
decay: { unit: "s", default: 3, min: 0.5, max: 10, scale: "log" },
|
|
3874
|
+
pitch: { unit: "semitone", default: 12, min: -12, max: 24 },
|
|
3875
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3876
|
+
shimmer: { node: "regenGain", field: "gain", unit: "factor", default: 0.5, min: 0, max: 0.95 }
|
|
3877
|
+
};
|
|
3878
|
+
var shimmerDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3879
|
+
type: "shimmer",
|
|
3880
|
+
params: params36,
|
|
3881
|
+
ports: {
|
|
3882
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3883
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3884
|
+
},
|
|
3885
|
+
createModule(id, position) {
|
|
3886
|
+
return { id, type: "shimmer", position, ...paramDefaults(params36) };
|
|
3887
|
+
},
|
|
3888
|
+
createNodes(ctx, module) {
|
|
3889
|
+
const input = ctx.createGain();
|
|
3890
|
+
const output = ctx.createGain();
|
|
3891
|
+
const dryGain = ctx.createGain();
|
|
3892
|
+
const wetGain = ctx.createGain();
|
|
3893
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "equal-power");
|
|
3894
|
+
const tank = createFdnTank(ctx, { rt60: module.decay, damping: 0.5, feedbackCeiling: 0.93 });
|
|
3895
|
+
const splitter = ctx.createChannelSplitter(2);
|
|
3896
|
+
const merger = ctx.createChannelMerger(2);
|
|
3897
|
+
const mkRegenTap = () => {
|
|
3898
|
+
const hp = ctx.createBiquadFilter();
|
|
3899
|
+
hp.type = "highpass";
|
|
3900
|
+
hp.frequency.value = 300;
|
|
3901
|
+
const shifter = createPitchShifterNode(ctx, module.pitch);
|
|
3902
|
+
return { hp, shifter };
|
|
3903
|
+
};
|
|
3904
|
+
const tapL = mkRegenTap();
|
|
3905
|
+
const tapR = mkRegenTap();
|
|
3906
|
+
const regenGain = ctx.createGain();
|
|
3907
|
+
regenGain.gain.value = module.shimmer;
|
|
3908
|
+
const regenDelay = ctx.createDelay(0.1);
|
|
3909
|
+
regenDelay.delayTime.value = 0.02;
|
|
3910
|
+
tank.output.connect(splitter);
|
|
3911
|
+
splitter.connect(tapL.hp, 0);
|
|
3912
|
+
splitter.connect(tapR.hp, 1);
|
|
3913
|
+
tapL.hp.connect(tapL.shifter);
|
|
3914
|
+
tapR.hp.connect(tapR.shifter);
|
|
3915
|
+
tapL.shifter.connect(merger, 0, 0);
|
|
3916
|
+
tapR.shifter.connect(merger, 0, 1);
|
|
3917
|
+
merger.connect(regenGain);
|
|
3918
|
+
regenGain.connect(regenDelay);
|
|
3919
|
+
regenDelay.connect(tank.input);
|
|
3920
|
+
input.connect(dryGain);
|
|
3921
|
+
input.connect(tank.input);
|
|
3922
|
+
tank.output.connect(wetGain);
|
|
3923
|
+
dryGain.connect(output);
|
|
3924
|
+
wetGain.connect(output);
|
|
3925
|
+
return {
|
|
3926
|
+
input,
|
|
3927
|
+
output,
|
|
3928
|
+
dryGain,
|
|
3929
|
+
wetGain,
|
|
3930
|
+
_mix,
|
|
3931
|
+
splitter,
|
|
3932
|
+
merger,
|
|
3933
|
+
highpassL: tapL.hp,
|
|
3934
|
+
highpassR: tapR.hp,
|
|
3935
|
+
pitchShifterL: tapL.shifter,
|
|
3936
|
+
pitchShifterR: tapR.shifter,
|
|
3937
|
+
regenGain,
|
|
3938
|
+
regenDelay,
|
|
3939
|
+
tankInput: tank.input,
|
|
3940
|
+
tankOutput: tank.output,
|
|
3941
|
+
tankNode: tank.node
|
|
3942
|
+
};
|
|
3943
|
+
},
|
|
3944
|
+
updateParam(nodes, _m, param, value) {
|
|
3945
|
+
const now2 = nodes.input.context.currentTime;
|
|
3946
|
+
if (param === "mix") {
|
|
3947
|
+
nodes._mix.offset.value = value;
|
|
3948
|
+
} else if (param === "shimmer") {
|
|
3949
|
+
nodes.regenGain.gain.setTargetAtTime(value, now2, 0.02);
|
|
3950
|
+
} else if (param === "pitch") {
|
|
3951
|
+
for (const key of ["pitchShifterL", "pitchShifterR"]) {
|
|
3952
|
+
nodes[key].parameters.get("semitones").setTargetAtTime(value, now2, 0.02);
|
|
3953
|
+
}
|
|
3954
|
+
} else if (param === "decay") {
|
|
3955
|
+
setTankDecay({ node: nodes.tankNode }, value, now2);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
});
|
|
3959
|
+
|
|
3960
|
+
// src/lib-runtime/generated/modules/shimmer.wrapper.ts
|
|
3961
|
+
var Shimmer = /* @__PURE__ */ defineModule(shimmerDescriptor, {
|
|
3962
|
+
worklets: {
|
|
3963
|
+
"pitch-shifter": pitch_shifter_default,
|
|
3964
|
+
"fdn-reverb": fdn_reverb_default
|
|
3965
|
+
}
|
|
3966
|
+
});
|
|
3967
|
+
|
|
3968
|
+
// src/lib-runtime/generated/modules/soft-limit.tsx
|
|
3969
|
+
var params37 = {
|
|
3970
|
+
drive: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3971
|
+
knee: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
3972
|
+
ceiling: { unit: "dB", default: -0.5, min: -12, max: 0 }
|
|
3973
|
+
};
|
|
3974
|
+
function makeSoftLimitCurve(knee, drive) {
|
|
3975
|
+
const samples = 44100;
|
|
3976
|
+
const curve = new Float32Array(samples);
|
|
3977
|
+
const k = 0.1 + knee * 0.9;
|
|
3978
|
+
const driveMul = 1 + drive * 3;
|
|
3979
|
+
for (let i = 0; i < samples; i++) {
|
|
3980
|
+
const x = (i * 2 / samples - 1) * driveMul;
|
|
3981
|
+
if (Math.abs(x) < k) {
|
|
3982
|
+
curve[i] = x;
|
|
3983
|
+
} else {
|
|
3984
|
+
const sign = x > 0 ? 1 : -1;
|
|
3985
|
+
curve[i] = sign * (k + (1 - k) * Math.tanh((Math.abs(x) - k) / (1 - k)));
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
return curve;
|
|
3989
|
+
}
|
|
3990
|
+
var softLimitDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
3991
|
+
type: "soft-limit",
|
|
3992
|
+
params: params37,
|
|
3993
|
+
ports: {
|
|
3994
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
3995
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
3996
|
+
},
|
|
3997
|
+
createModule(id, position) {
|
|
3998
|
+
return { id, type: "soft-limit", position, ...paramDefaults(params37) };
|
|
3999
|
+
},
|
|
4000
|
+
createNodes(ctx, module) {
|
|
4001
|
+
const input = ctx.createGain();
|
|
4002
|
+
const output = ctx.createGain();
|
|
4003
|
+
const waveshaper = ctx.createWaveShaper();
|
|
4004
|
+
const ceiling = ctx.createGain();
|
|
4005
|
+
waveshaper.curve = makeSoftLimitCurve(module.knee, module.drive);
|
|
4006
|
+
waveshaper.oversample = "2x";
|
|
4007
|
+
ceiling.gain.value = Math.pow(10, module.ceiling / 20);
|
|
4008
|
+
input.connect(waveshaper);
|
|
4009
|
+
waveshaper.connect(ceiling);
|
|
4010
|
+
ceiling.connect(output);
|
|
4011
|
+
return { input, output, waveshaper, ceiling };
|
|
4012
|
+
},
|
|
4013
|
+
updateParam(nodes, m, param, value) {
|
|
4014
|
+
if (param === "drive") nodes.waveshaper.curve = makeSoftLimitCurve(m.knee, value);
|
|
4015
|
+
else if (param === "knee") nodes.waveshaper.curve = makeSoftLimitCurve(value, m.drive);
|
|
4016
|
+
else if (param === "ceiling") nodes.ceiling.gain.value = Math.pow(10, value / 20);
|
|
4017
|
+
}
|
|
4018
|
+
});
|
|
4019
|
+
|
|
4020
|
+
// src/lib-runtime/generated/modules/soft-limit.wrapper.ts
|
|
4021
|
+
var SoftLimiter = /* @__PURE__ */ defineModule(softLimitDescriptor);
|
|
4022
|
+
|
|
4023
|
+
// src/lib-runtime/generated/modules/spring-reverb.tsx
|
|
4024
|
+
var params38 = {
|
|
4025
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.35, min: 0, max: 1 },
|
|
4026
|
+
decay: { node: "_worklet", field: "decay", default: 1.5, min: 0.3, max: 4, scale: "log", unit: "s" },
|
|
4027
|
+
tone: { node: "_worklet", field: "tone", unit: "factor", default: 0.6, min: 0, max: 1 },
|
|
4028
|
+
drip: { node: "_worklet", field: "drip", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
4029
|
+
};
|
|
4030
|
+
var springReverbDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4031
|
+
type: "spring-reverb",
|
|
4032
|
+
params: params38,
|
|
4033
|
+
ports: {
|
|
4034
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4035
|
+
"knock": { type: "input", signal: "gate", node: "_knockIn" },
|
|
4036
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4037
|
+
},
|
|
4038
|
+
createModule(id, position) {
|
|
4039
|
+
return { id, type: "spring-reverb", position, ...paramDefaults(params38) };
|
|
4040
|
+
},
|
|
4041
|
+
createNodes(ctx, module) {
|
|
4042
|
+
const input = ctx.createGain();
|
|
4043
|
+
const output = ctx.createGain();
|
|
4044
|
+
const dryGain = ctx.createGain();
|
|
4045
|
+
const wetGain = ctx.createGain();
|
|
4046
|
+
const _worklet = createSpringReverbNode(ctx, {
|
|
4047
|
+
decay: module.decay,
|
|
4048
|
+
tone: module.tone,
|
|
4049
|
+
drip: module.drip
|
|
4050
|
+
});
|
|
4051
|
+
const _knockIn = ctx.createGain();
|
|
4052
|
+
_knockIn.gain.value = 1;
|
|
4053
|
+
_knockIn.connect(_worklet, 0, 1);
|
|
4054
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "equal-power");
|
|
4055
|
+
input.connect(dryGain);
|
|
4056
|
+
input.connect(_worklet, 0, 0);
|
|
4057
|
+
_worklet.connect(wetGain);
|
|
4058
|
+
dryGain.connect(output);
|
|
4059
|
+
wetGain.connect(output);
|
|
4060
|
+
return { input, output, dryGain, wetGain, _mix, _worklet, _knockIn };
|
|
4061
|
+
},
|
|
4062
|
+
updateParam(nodes, _module, param, value, when) {
|
|
4063
|
+
const worklet = nodes._worklet;
|
|
4064
|
+
if (param === "mix") {
|
|
4065
|
+
nodes._mix.offset.value = value;
|
|
4066
|
+
} else if (param === "decay" || param === "tone" || param === "drip") {
|
|
4067
|
+
worklet.parameters.get(param).value = value;
|
|
4068
|
+
} else if (param === "knock") {
|
|
4069
|
+
pulse(nodes._knockIn, when);
|
|
4070
|
+
}
|
|
4071
|
+
},
|
|
4072
|
+
dispose(_nodes) {
|
|
4073
|
+
}
|
|
4074
|
+
});
|
|
4075
|
+
|
|
4076
|
+
// src/lib-runtime/generated/modules/spring-reverb.wrapper.ts
|
|
4077
|
+
var SpringReverb = /* @__PURE__ */ defineModule(springReverbDescriptor, {
|
|
4078
|
+
worklets: {
|
|
4079
|
+
"spring-reverb": spring_reverb_default
|
|
4080
|
+
}
|
|
4081
|
+
});
|
|
4082
|
+
|
|
4083
|
+
// src/lib-runtime/generated/modules/stereo-spread.tsx
|
|
4084
|
+
var params39 = {
|
|
4085
|
+
spread: { unit: "factor", default: 0.7, min: 0, max: 1 },
|
|
4086
|
+
center: { unit: "Hz", default: 800, min: 200, max: 2e3, scale: "log" }
|
|
4087
|
+
};
|
|
4088
|
+
var stereoSpreadDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4089
|
+
type: "stereo-spread",
|
|
4090
|
+
params: params39,
|
|
4091
|
+
ports: {
|
|
4092
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4093
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4094
|
+
},
|
|
4095
|
+
createModule(id, position) {
|
|
4096
|
+
return { id, type: "stereo-spread", position, ...paramDefaults(params39) };
|
|
4097
|
+
},
|
|
4098
|
+
createNodes(ctx, module) {
|
|
4099
|
+
const input = ctx.createGain();
|
|
4100
|
+
const output = ctx.createGain();
|
|
4101
|
+
const lowpass = ctx.createBiquadFilter();
|
|
4102
|
+
const highpass = ctx.createBiquadFilter();
|
|
4103
|
+
lowpass.type = "lowpass";
|
|
4104
|
+
lowpass.frequency.value = module.center;
|
|
4105
|
+
highpass.type = "highpass";
|
|
4106
|
+
highpass.frequency.value = module.center;
|
|
4107
|
+
const panLeft = ctx.createStereoPanner();
|
|
4108
|
+
const panRight = ctx.createStereoPanner();
|
|
4109
|
+
const haasDelay = ctx.createDelay(0.02);
|
|
4110
|
+
panLeft.pan.value = -module.spread;
|
|
4111
|
+
panRight.pan.value = module.spread;
|
|
4112
|
+
haasDelay.delayTime.value = module.spread * 5e-3;
|
|
4113
|
+
const wetGain = ctx.createGain();
|
|
4114
|
+
wetGain.gain.value = module.width;
|
|
4115
|
+
input.connect(lowpass);
|
|
4116
|
+
lowpass.connect(output);
|
|
4117
|
+
input.connect(highpass);
|
|
4118
|
+
highpass.connect(panLeft);
|
|
4119
|
+
highpass.connect(haasDelay);
|
|
4120
|
+
haasDelay.connect(panRight);
|
|
4121
|
+
panLeft.connect(wetGain);
|
|
4122
|
+
panRight.connect(wetGain);
|
|
4123
|
+
wetGain.connect(output);
|
|
4124
|
+
return { input, output, lowpass, highpass, panLeft, panRight, haasDelay, wetGain };
|
|
4125
|
+
},
|
|
4126
|
+
updateParam(nodes, _m, param, value) {
|
|
4127
|
+
if (param === "spread") {
|
|
4128
|
+
nodes.panLeft.pan.value = -value;
|
|
4129
|
+
nodes.panRight.pan.value = value;
|
|
4130
|
+
nodes.haasDelay.delayTime.value = value * 5e-3;
|
|
4131
|
+
} else if (param === "width") {
|
|
4132
|
+
nodes.wetGain.gain.value = value;
|
|
4133
|
+
} else if (param === "center") {
|
|
4134
|
+
nodes.lowpass.frequency.value = value;
|
|
4135
|
+
nodes.highpass.frequency.value = value;
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
|
|
4140
|
+
// src/lib-runtime/generated/modules/stereo-spread.wrapper.ts
|
|
4141
|
+
var StereoSpread = /* @__PURE__ */ defineModule(stereoSpreadDescriptor);
|
|
4142
|
+
|
|
4143
|
+
// src/lib-runtime/generated/modules/sub-harm.tsx
|
|
4144
|
+
var params40 = {
|
|
4145
|
+
direct: { node: "dryGain", field: "gain", unit: "factor", default: 1, min: 0, max: 1 },
|
|
4146
|
+
octave1: { node: "subOctave", field: "oct1Level", unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
4147
|
+
octave2: { node: "subOctave", field: "oct2Level", unit: "factor", default: 0.3, min: 0, max: 1 }
|
|
4148
|
+
};
|
|
4149
|
+
var subHarmDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4150
|
+
type: "sub-harm",
|
|
4151
|
+
params: params40,
|
|
4152
|
+
ports: {
|
|
4153
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4154
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4155
|
+
},
|
|
4156
|
+
createModule(id, position) {
|
|
4157
|
+
return { id, type: "sub-harm", position, ...paramDefaults(params40) };
|
|
4158
|
+
},
|
|
4159
|
+
createNodes(ctx, module) {
|
|
4160
|
+
const input = ctx.createGain();
|
|
4161
|
+
const output = ctx.createGain();
|
|
4162
|
+
const dryGain = ctx.createGain();
|
|
4163
|
+
const wetGain = ctx.createGain();
|
|
4164
|
+
const subOctave = createSubOctaveNode(ctx);
|
|
4165
|
+
subOctave.parameters.get("oct1Level").value = module.octave1;
|
|
4166
|
+
subOctave.parameters.get("oct2Level").value = module.octave2;
|
|
4167
|
+
const subFilter = ctx.createBiquadFilter();
|
|
4168
|
+
subFilter.type = "lowpass";
|
|
4169
|
+
subFilter.frequency.value = 400;
|
|
4170
|
+
subFilter.Q.value = 0.7;
|
|
4171
|
+
dryGain.gain.value = module.direct;
|
|
4172
|
+
wetGain.gain.value = 1;
|
|
4173
|
+
input.connect(dryGain);
|
|
4174
|
+
input.connect(subOctave);
|
|
4175
|
+
subOctave.connect(subFilter);
|
|
4176
|
+
subFilter.connect(wetGain);
|
|
4177
|
+
dryGain.connect(output);
|
|
4178
|
+
wetGain.connect(output);
|
|
4179
|
+
return { input, output, subOctave, dryGain, wetGain };
|
|
4180
|
+
},
|
|
4181
|
+
updateParam(nodes, _m, param, value) {
|
|
4182
|
+
if (param === "octave1") {
|
|
4183
|
+
nodes.subOctave.parameters.get("oct1Level").value = value;
|
|
4184
|
+
} else if (param === "octave2") {
|
|
4185
|
+
nodes.subOctave.parameters.get("oct2Level").value = value;
|
|
4186
|
+
} else if (param === "direct") {
|
|
4187
|
+
nodes.dryGain.gain.value = value;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
});
|
|
4191
|
+
|
|
4192
|
+
// src/lib-runtime/generated/modules/sub-harm.wrapper.ts
|
|
4193
|
+
var Subharmonic = /* @__PURE__ */ defineModule(subHarmDescriptor, {
|
|
4194
|
+
worklets: {
|
|
4195
|
+
"freq-divider": freq_divider_default
|
|
4196
|
+
}
|
|
4197
|
+
});
|
|
4198
|
+
|
|
4199
|
+
// src/lib-runtime/generated/modules/tape-delay.tsx
|
|
4200
|
+
var params41 = {
|
|
4201
|
+
time: { node: "worklet", field: "time", unit: "s", default: 0.375, min: 0.05, max: 1.5, scale: "log" },
|
|
4202
|
+
feedback: { node: "worklet", field: "feedback", unit: "factor", default: 0.5, min: 0, max: 0.95 },
|
|
4203
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 0.4, min: 0, max: 1 },
|
|
4204
|
+
flutter: { node: "worklet", field: "flutter", unit: "factor", default: 0.3, min: 0, max: 1 },
|
|
4205
|
+
saturation: { node: "worklet", field: "saturation", unit: "factor", default: 0.4, min: 0, max: 1 }
|
|
4206
|
+
};
|
|
4207
|
+
var tapeDelayDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4208
|
+
type: "tape-delay",
|
|
4209
|
+
params: params41,
|
|
4210
|
+
ports: {
|
|
4211
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4212
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4213
|
+
},
|
|
4214
|
+
createModule(id, position) {
|
|
4215
|
+
return { id, type: "tape-delay", position, ...paramDefaults(params41) };
|
|
4216
|
+
},
|
|
4217
|
+
createNodes(ctx, module) {
|
|
4218
|
+
const input = ctx.createGain();
|
|
4219
|
+
const output = ctx.createGain();
|
|
4220
|
+
const dryGain = ctx.createGain();
|
|
4221
|
+
const wetGain = ctx.createGain();
|
|
4222
|
+
const worklet = createTapeDelayNode(ctx, {
|
|
4223
|
+
time: module.time,
|
|
4224
|
+
feedback: module.feedback,
|
|
4225
|
+
saturation: module.saturation,
|
|
4226
|
+
flutter: module.flutter
|
|
4227
|
+
});
|
|
4228
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
4229
|
+
input.connect(dryGain);
|
|
4230
|
+
input.connect(worklet);
|
|
4231
|
+
worklet.connect(wetGain);
|
|
4232
|
+
dryGain.connect(output);
|
|
4233
|
+
wetGain.connect(output);
|
|
4234
|
+
return { input, output, worklet, dryGain, wetGain, _mix };
|
|
4235
|
+
},
|
|
4236
|
+
updateParam(nodes, _m, param, value) {
|
|
4237
|
+
const worklet = nodes.worklet;
|
|
4238
|
+
if (param === "time" || param === "feedback" || param === "saturation" || param === "flutter") {
|
|
4239
|
+
const p = worklet.parameters.get(param);
|
|
4240
|
+
if (p) p.value = value;
|
|
4241
|
+
} else if (param === "mix") {
|
|
4242
|
+
nodes._mix.offset.value = value;
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
});
|
|
4246
|
+
|
|
4247
|
+
// src/lib-runtime/generated/modules/tape-delay.wrapper.ts
|
|
4248
|
+
var TapeDelay = /* @__PURE__ */ defineModule(tapeDelayDescriptor, {
|
|
4249
|
+
worklets: {
|
|
4250
|
+
"tape-delay": tape_delay_default
|
|
4251
|
+
}
|
|
4252
|
+
});
|
|
4253
|
+
|
|
4254
|
+
// src/lib-runtime/generated/modules/tape-stop.tsx
|
|
4255
|
+
var params42 = {
|
|
4256
|
+
time: { node: "_worklet", field: "time", default: 0.5, min: 0.01, max: 4, scale: "log", unit: "s" },
|
|
4257
|
+
mix: { node: "_worklet", field: "mix", unit: "factor", default: 1, min: 0, max: 1 }
|
|
4258
|
+
};
|
|
4259
|
+
var tapeStopDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4260
|
+
type: "tape-stop",
|
|
4261
|
+
params: params42,
|
|
4262
|
+
ports: {
|
|
4263
|
+
"in": { type: "input", signal: "audio", node: "_worklet" },
|
|
4264
|
+
"trigger": { type: "input", signal: "gate", node: "_trigIn" },
|
|
4265
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4266
|
+
},
|
|
4267
|
+
createModule(id, position) {
|
|
4268
|
+
return { id, type: "tape-stop", position, ...paramDefaults(params42) };
|
|
4269
|
+
},
|
|
4270
|
+
createNodes(ctx, module) {
|
|
4271
|
+
const _worklet = createTapeStopNode(ctx, module.time, module.mix);
|
|
4272
|
+
const _trigIn = ctx.createGain();
|
|
4273
|
+
_trigIn.gain.value = 1;
|
|
4274
|
+
_trigIn.connect(_worklet, 0, 1);
|
|
4275
|
+
const output = ctx.createGain();
|
|
4276
|
+
_worklet.connect(output);
|
|
4277
|
+
return { _worklet, _trigIn, output };
|
|
4278
|
+
},
|
|
4279
|
+
updateParam(nodes, _module, param, value, when) {
|
|
4280
|
+
if (param === "time") {
|
|
4281
|
+
nodes._worklet.parameters.get("time").value = value;
|
|
4282
|
+
} else if (param === "mix") {
|
|
4283
|
+
nodes._worklet.parameters.get("mix").value = value;
|
|
4284
|
+
} else if (param === "trigger") {
|
|
4285
|
+
pulse(nodes._trigIn, when);
|
|
4286
|
+
}
|
|
4287
|
+
},
|
|
4288
|
+
dispose(_nodes) {
|
|
4289
|
+
}
|
|
4290
|
+
});
|
|
4291
|
+
|
|
4292
|
+
// src/lib-runtime/generated/modules/tape-stop.wrapper.ts
|
|
4293
|
+
var TapeStop = /* @__PURE__ */ defineModule(tapeStopDescriptor, {
|
|
4294
|
+
worklets: {
|
|
4295
|
+
"tape-stop": tape_stop_default
|
|
4296
|
+
}
|
|
4297
|
+
});
|
|
4298
|
+
|
|
4299
|
+
// src/lib-runtime/generated/modules/tilt.tsx
|
|
4300
|
+
var params43 = {
|
|
4301
|
+
tilt: { unit: "dB", default: 0, min: -12, max: 12 },
|
|
4302
|
+
frequency: { unit: "Hz", default: 1e3, min: 200, max: 5e3 }
|
|
4303
|
+
};
|
|
4304
|
+
var tiltEqDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4305
|
+
type: "tilt-eq",
|
|
4306
|
+
params: params43,
|
|
4307
|
+
ports: {
|
|
4308
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4309
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4310
|
+
},
|
|
4311
|
+
createModule(id, position) {
|
|
4312
|
+
return { id, type: "tilt-eq", position, ...paramDefaults(params43) };
|
|
4313
|
+
},
|
|
4314
|
+
createNodes(ctx, module) {
|
|
4315
|
+
const input = ctx.createGain();
|
|
4316
|
+
const output = ctx.createGain();
|
|
4317
|
+
const lowShelf = ctx.createBiquadFilter();
|
|
4318
|
+
const highShelf = ctx.createBiquadFilter();
|
|
4319
|
+
lowShelf.type = "lowshelf";
|
|
4320
|
+
lowShelf.frequency.value = module.frequency;
|
|
4321
|
+
lowShelf.gain.value = -module.tilt;
|
|
4322
|
+
highShelf.type = "highshelf";
|
|
4323
|
+
highShelf.frequency.value = module.frequency;
|
|
4324
|
+
highShelf.gain.value = module.tilt;
|
|
4325
|
+
input.connect(lowShelf);
|
|
4326
|
+
lowShelf.connect(highShelf);
|
|
4327
|
+
highShelf.connect(output);
|
|
4328
|
+
return { input, output, lowShelf, highShelf };
|
|
4329
|
+
},
|
|
4330
|
+
updateParam(nodes, _m, param, value) {
|
|
4331
|
+
if (param === "tilt") {
|
|
4332
|
+
nodes.lowShelf.gain.value = -value;
|
|
4333
|
+
nodes.highShelf.gain.value = value;
|
|
4334
|
+
} else if (param === "frequency") {
|
|
4335
|
+
nodes.lowShelf.frequency.value = value;
|
|
4336
|
+
nodes.highShelf.frequency.value = value;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
});
|
|
4340
|
+
|
|
4341
|
+
// src/lib-runtime/generated/modules/tilt.wrapper.ts
|
|
4342
|
+
var Tilt = /* @__PURE__ */ defineModule(tiltEqDescriptor);
|
|
4343
|
+
|
|
4344
|
+
// src/lib-runtime/generated/modules/tremolo.tsx
|
|
4345
|
+
var shapes2 = ["sine", "triangle", "square"];
|
|
4346
|
+
var params44 = {
|
|
4347
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 5, min: 0.5, max: 20 },
|
|
4348
|
+
depth: { unit: "factor", default: 0.5, min: 0, max: 1 },
|
|
4349
|
+
shape: { kind: "enum", options: shapes2, default: "sine" }
|
|
4350
|
+
};
|
|
4351
|
+
var tremoloDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4352
|
+
type: "tremolo",
|
|
4353
|
+
params: params44,
|
|
4354
|
+
ports: {
|
|
4355
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4356
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
4357
|
+
"modRate": { type: "modulation", signal: "modulation", node: "lfo", field: "frequency" }
|
|
4358
|
+
},
|
|
4359
|
+
createModule(id, position) {
|
|
4360
|
+
return { id, type: "tremolo", position, ...paramDefaults(params44) };
|
|
4361
|
+
},
|
|
4362
|
+
createNodes(ctx, module) {
|
|
4363
|
+
const input = ctx.createGain();
|
|
4364
|
+
const output = ctx.createGain();
|
|
4365
|
+
const tremGain = ctx.createGain();
|
|
4366
|
+
const lfo = ctx.createOscillator();
|
|
4367
|
+
const lfoGain = ctx.createGain();
|
|
4368
|
+
const dcOffset = ctx.createConstantSource();
|
|
4369
|
+
tremGain.gain.value = 0;
|
|
4370
|
+
lfo.type = module.shape;
|
|
4371
|
+
lfo.frequency.value = module.rate;
|
|
4372
|
+
lfoGain.gain.value = module.depth * 0.5;
|
|
4373
|
+
dcOffset.offset.value = 1 - module.depth * 0.5;
|
|
4374
|
+
lfo.connect(lfoGain);
|
|
4375
|
+
lfoGain.connect(tremGain.gain);
|
|
4376
|
+
dcOffset.connect(tremGain.gain);
|
|
4377
|
+
lfo.start();
|
|
4378
|
+
dcOffset.start();
|
|
4379
|
+
input.connect(tremGain);
|
|
4380
|
+
tremGain.connect(output);
|
|
4381
|
+
return { input, output, lfo, lfoGain, dcOffset };
|
|
4382
|
+
},
|
|
4383
|
+
updateParam(nodes, _m, param, value) {
|
|
4384
|
+
switch (param) {
|
|
4385
|
+
case "rate":
|
|
4386
|
+
nodes.lfo.frequency.value = value;
|
|
4387
|
+
break;
|
|
4388
|
+
case "depth":
|
|
4389
|
+
nodes.lfoGain.gain.value = value * 0.5;
|
|
4390
|
+
nodes.dcOffset.offset.value = 1 - value * 0.5;
|
|
4391
|
+
break;
|
|
4392
|
+
case "shape":
|
|
4393
|
+
nodes.lfo.type = value;
|
|
4394
|
+
break;
|
|
4395
|
+
}
|
|
4396
|
+
},
|
|
4397
|
+
dispose(nodes) {
|
|
4398
|
+
nodes.lfo.stop();
|
|
4399
|
+
nodes.dcOffset.stop();
|
|
4400
|
+
}
|
|
4401
|
+
});
|
|
4402
|
+
|
|
4403
|
+
// src/lib-runtime/generated/modules/tremolo.wrapper.ts
|
|
4404
|
+
var Tremolo = /* @__PURE__ */ defineModule(tremoloDescriptor);
|
|
4405
|
+
|
|
4406
|
+
// src/lib-runtime/generated/modules/vca.tsx
|
|
4407
|
+
var params45 = {
|
|
4408
|
+
gain: { node: "gain", field: "gain", unit: "factor", default: 0.5, min: 0, max: 2 },
|
|
4409
|
+
cvAmount: { node: "gain", field: "cvAmount", unit: "factor", default: 1, min: -1, max: 1 }
|
|
4410
|
+
};
|
|
4411
|
+
var gainDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4412
|
+
type: "gain",
|
|
4413
|
+
params: params45,
|
|
4414
|
+
ports: {
|
|
4415
|
+
"in": { type: "input", signal: "audio", node: "gain" },
|
|
4416
|
+
"out": { type: "output", signal: "audio", node: "gain" },
|
|
4417
|
+
"modGain": { type: "modulation", signal: "modulation", node: "gain", field: "cv" }
|
|
4418
|
+
},
|
|
4419
|
+
createModule(id, position) {
|
|
4420
|
+
return { id, type: "gain", position, ...paramDefaults(params45) };
|
|
4421
|
+
},
|
|
4422
|
+
createNodes(ctx, module) {
|
|
4423
|
+
const gain = createVcaNode(ctx, module.gain, module.cvAmount ?? 1);
|
|
4424
|
+
return { gain };
|
|
4425
|
+
},
|
|
4426
|
+
updateParam(nodes, _module, param, value) {
|
|
4427
|
+
if (param === "gain" || param === "cvAmount") {
|
|
4428
|
+
const p = nodes.gain.parameters.get(param);
|
|
4429
|
+
if (p) p.value = value;
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
});
|
|
4433
|
+
|
|
4434
|
+
// src/lib-runtime/generated/modules/vca.wrapper.ts
|
|
4435
|
+
var Vca = /* @__PURE__ */ defineModule(gainDescriptor, {
|
|
4436
|
+
worklets: {
|
|
4437
|
+
"vca": vca_default
|
|
4438
|
+
}
|
|
4439
|
+
});
|
|
4440
|
+
|
|
4441
|
+
// src/lib/dsp/gainComp.ts
|
|
4442
|
+
function bandpassMakeup(q, cap = 4) {
|
|
4443
|
+
if (!Number.isFinite(q) || q <= 1) return 1;
|
|
4444
|
+
return Math.min(Math.sqrt(q), cap);
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
// src/lib-runtime/generated/modules/vcf.tsx
|
|
4448
|
+
var makeupFor = (type, q) => type === "bandpass" ? bandpassMakeup(q) : 1;
|
|
4449
|
+
var qIsDb = (type) => type === "lowpass" || type === "highpass";
|
|
4450
|
+
var params46 = {
|
|
4451
|
+
filterType: { kind: "enum", options: ["lowpass", "highpass", "bandpass", "notch"], default: "lowpass" },
|
|
4452
|
+
note: { node: "_cutoffVoct", field: "transpose", unit: "note", default: 83, min: 15, max: 135 },
|
|
4453
|
+
// Resonance is split across the map-res worklet AND a compensation gain,
|
|
4454
|
+
// so it cannot honestly declare a single AudioParam — see lib/dsp/mix.ts
|
|
4455
|
+
// convention: rebuild or leave set-only. It stays set-only here.
|
|
4456
|
+
resonance: { unit: "factor", default: 1, min: 0.1, max: 20, scale: "sqrt" }
|
|
4457
|
+
};
|
|
4458
|
+
var filterDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4459
|
+
type: "filter",
|
|
4460
|
+
params: params46,
|
|
4461
|
+
ports: {
|
|
4462
|
+
"in": { type: "input", signal: "audio", node: "filter" },
|
|
4463
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
4464
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_cutoffVoct" },
|
|
4465
|
+
"modQ": { type: "modulation", signal: "modulation", node: "_resMap" }
|
|
4466
|
+
},
|
|
4467
|
+
createModule(id, position) {
|
|
4468
|
+
return { id, type: "filter", position, resUnit: "q", ...paramDefaults(params46) };
|
|
4469
|
+
},
|
|
4470
|
+
createNodes(ctx, module) {
|
|
4471
|
+
const filter = ctx.createBiquadFilter();
|
|
4472
|
+
filter.type = module.filterType;
|
|
4473
|
+
filter.frequency.value = 0;
|
|
4474
|
+
filter.Q.value = 0;
|
|
4475
|
+
const output = ctx.createGain();
|
|
4476
|
+
output.gain.value = makeupFor(module.filterType, module.resonance);
|
|
4477
|
+
filter.connect(output);
|
|
4478
|
+
const _cutoffVoct = createVoctFreqNode(ctx);
|
|
4479
|
+
_cutoffVoct.parameters.get("transpose").value = module.note;
|
|
4480
|
+
const _cutoffBase = ctx.createConstantSource();
|
|
4481
|
+
_cutoffBase.offset.value = 0;
|
|
4482
|
+
_cutoffBase.start();
|
|
4483
|
+
_cutoffBase.connect(_cutoffVoct);
|
|
4484
|
+
_cutoffVoct.connect(filter.frequency);
|
|
4485
|
+
const _resMap = createMapResNode(ctx, {
|
|
4486
|
+
base: module.resonance,
|
|
4487
|
+
dbOut: qIsDb(module.filterType)
|
|
4488
|
+
});
|
|
4489
|
+
_resMap.connect(filter.Q);
|
|
4490
|
+
return { filter, output, _cutoffVoct, _cutoffBase, _resMap };
|
|
4491
|
+
},
|
|
4492
|
+
dispose(nodes) {
|
|
4493
|
+
try {
|
|
4494
|
+
nodes._cutoffBase.stop();
|
|
4495
|
+
} catch (_) {
|
|
4496
|
+
}
|
|
4497
|
+
},
|
|
4498
|
+
updateParam(nodes, module, param, value) {
|
|
4499
|
+
switch (param) {
|
|
4500
|
+
case "filterType":
|
|
4501
|
+
nodes.filter.type = value;
|
|
4502
|
+
nodes._resMap.parameters.get("dbOut").value = qIsDb(value) ? 1 : 0;
|
|
4503
|
+
nodes.output.gain.value = makeupFor(value, module.resonance);
|
|
4504
|
+
break;
|
|
4505
|
+
case "note":
|
|
4506
|
+
nodes._cutoffVoct.parameters.get("transpose").value = value;
|
|
4507
|
+
break;
|
|
4508
|
+
case "resonance":
|
|
4509
|
+
nodes._resMap.parameters.get("base").value = value;
|
|
4510
|
+
nodes.output.gain.value = makeupFor(module.filterType, value);
|
|
4511
|
+
break;
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
});
|
|
4515
|
+
|
|
4516
|
+
// src/lib-runtime/generated/modules/vcf.wrapper.ts
|
|
4517
|
+
var Vcf = /* @__PURE__ */ defineModule(filterDescriptor, {
|
|
4518
|
+
worklets: {
|
|
4519
|
+
"map-res": map_res_default,
|
|
4520
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
4521
|
+
}
|
|
4522
|
+
});
|
|
4523
|
+
|
|
4524
|
+
// src/lib-runtime/generated/modules/vco.tsx
|
|
4525
|
+
var params47 = {
|
|
4526
|
+
skew: { node: "_ws", field: "skew", unit: "factor", default: 1, min: -1, max: 1 },
|
|
4527
|
+
shape: { node: "_ws", field: "shape", unit: "factor", default: 0, min: -1, max: 1 },
|
|
4528
|
+
offset: { node: "_voct", field: "transpose", default: 69, min: -48, max: 108, unit: "semitone" },
|
|
4529
|
+
detune: { node: "_detune", field: "offset", default: 0, min: -1, max: 1, unit: "semitone" },
|
|
4530
|
+
gain: { node: "gain", field: "gain", unit: "factor", default: 0.5, min: 0, max: 1 }
|
|
4531
|
+
};
|
|
4532
|
+
var patchCount4 = /* @__PURE__ */ new WeakMap();
|
|
4533
|
+
var oscillatorDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4534
|
+
type: "oscillator",
|
|
4535
|
+
ports: {
|
|
4536
|
+
"voct": { type: "input", node: "_voct", signal: "pitch" },
|
|
4537
|
+
"out": { type: "output", signal: "audio", node: "gain" },
|
|
4538
|
+
"modSkew": { type: "modulation", signal: "modulation", node: "_ws", field: "skew" },
|
|
4539
|
+
"modShape": { type: "modulation", signal: "modulation", node: "_ws", field: "shape" },
|
|
4540
|
+
"modDetune": { type: "modulation", signal: "modulation", node: "_detune", field: "offset" }
|
|
4541
|
+
},
|
|
4542
|
+
params: params47,
|
|
4543
|
+
// ─── Factory ─────────────────────────────────────────────────────
|
|
4544
|
+
createModule(id, position) {
|
|
4545
|
+
const { offset: _dsp, ...rest } = paramDefaults(params47);
|
|
4546
|
+
return { id, type: "oscillator", position, ...rest, note: 69, transpose: 0 };
|
|
4547
|
+
},
|
|
4548
|
+
// ─── Audio Engine ────────────────────────────────────────────────
|
|
4549
|
+
createNodes(ctx, module) {
|
|
4550
|
+
const _ws = createWaveshaperNode(ctx);
|
|
4551
|
+
const gain = ctx.createGain();
|
|
4552
|
+
const _detune = ctx.createConstantSource();
|
|
4553
|
+
_detune.offset.value = module.detune;
|
|
4554
|
+
_detune.start();
|
|
4555
|
+
const _voct = createVoctFreqNode(ctx);
|
|
4556
|
+
_detune.connect(_voct);
|
|
4557
|
+
_voct.connect(_ws);
|
|
4558
|
+
_voct.parameters.get("transpose").value = module.offset ?? module.note;
|
|
4559
|
+
_ws.parameters.get("skew").value = module.skew;
|
|
4560
|
+
_ws.parameters.get("shape").value = module.shape;
|
|
4561
|
+
_ws.parameters.get("normalize").value = 1;
|
|
4562
|
+
gain.gain.value = module.gain;
|
|
4563
|
+
_ws.connect(gain);
|
|
4564
|
+
return { gain, _ws, _voct, _detune };
|
|
4565
|
+
},
|
|
4566
|
+
updateParam(nodes, _module, param, value) {
|
|
4567
|
+
switch (param) {
|
|
4568
|
+
case "skew":
|
|
4569
|
+
nodes._ws.parameters.get("skew").value = value;
|
|
4570
|
+
break;
|
|
4571
|
+
case "shape":
|
|
4572
|
+
nodes._ws.parameters.get("shape").value = value;
|
|
4573
|
+
break;
|
|
4574
|
+
case "offset":
|
|
4575
|
+
nodes._voct.parameters.get("transpose").value = value;
|
|
4576
|
+
break;
|
|
4577
|
+
// App panel memories: only the one matching the patch state is live.
|
|
4578
|
+
case "note":
|
|
4579
|
+
if (!patchCount4.get(nodes)) nodes._voct.parameters.get("transpose").value = value;
|
|
4580
|
+
break;
|
|
4581
|
+
case "transpose":
|
|
4582
|
+
if (patchCount4.get(nodes)) nodes._voct.parameters.get("transpose").value = value;
|
|
4583
|
+
break;
|
|
4584
|
+
case "detune":
|
|
4585
|
+
nodes._detune.offset.value = value;
|
|
4586
|
+
break;
|
|
4587
|
+
case "gain":
|
|
4588
|
+
nodes.gain.gain.value = value;
|
|
4589
|
+
break;
|
|
4590
|
+
}
|
|
4591
|
+
},
|
|
4592
|
+
// App-only: the connection is a plain summing connect; these hooks only
|
|
4593
|
+
// swap which panel memory drives the offset.
|
|
4594
|
+
onConnect(nodes, module, portId, sourceNode) {
|
|
4595
|
+
if (portId !== "voct") return false;
|
|
4596
|
+
sourceNode.connect(nodes._voct);
|
|
4597
|
+
patchCount4.set(nodes, (patchCount4.get(nodes) ?? 0) + 1);
|
|
4598
|
+
nodes._voct.parameters.get("transpose").value = module.transpose ?? 0;
|
|
4599
|
+
return true;
|
|
4600
|
+
},
|
|
4601
|
+
onDisconnect(nodes, module, portId, sourceNode) {
|
|
4602
|
+
if (portId !== "voct") return false;
|
|
4603
|
+
try {
|
|
4604
|
+
sourceNode.disconnect(nodes._voct);
|
|
4605
|
+
} catch (e) {
|
|
4606
|
+
}
|
|
4607
|
+
const n = Math.max(0, (patchCount4.get(nodes) ?? 0) - 1);
|
|
4608
|
+
patchCount4.set(nodes, n);
|
|
4609
|
+
if (!n) nodes._voct.parameters.get("transpose").value = module.note;
|
|
4610
|
+
return true;
|
|
4611
|
+
},
|
|
4612
|
+
dispose(nodes) {
|
|
4613
|
+
nodes._detune.stop();
|
|
4614
|
+
}
|
|
4615
|
+
});
|
|
4616
|
+
|
|
4617
|
+
// src/lib-runtime/generated/modules/vco.wrapper.ts
|
|
4618
|
+
var Vco = /* @__PURE__ */ defineModule(oscillatorDescriptor, {
|
|
4619
|
+
worklets: {
|
|
4620
|
+
"map-voct-to-freq": map_voct_to_freq_default,
|
|
4621
|
+
"waveshaper": waveshaper_default
|
|
4622
|
+
}
|
|
4623
|
+
});
|
|
4624
|
+
|
|
4625
|
+
// src/lib-runtime/generated/modules/wah.tsx
|
|
4626
|
+
var params48 = {
|
|
4627
|
+
note: { node: "_cutoffVoct", field: "transpose", unit: "note", default: 79, min: 55, max: 95 },
|
|
4628
|
+
// Resonance drives both the filter Q and a separate makeup gain — a split, so it stays set-only.
|
|
4629
|
+
resonance: { unit: "factor", default: 8, min: 1, max: 20, scale: "log" },
|
|
4630
|
+
rate: { node: "lfo", field: "frequency", unit: "Hz", default: 1, min: 0.1, max: 10, scale: "log" },
|
|
4631
|
+
// Depth is stored 0-1 but scaled by 36 semitones onto lfoGain.gain — a conversion, so set-only.
|
|
4632
|
+
depth: { unit: "factor", default: 0.6, min: 0, max: 1 }
|
|
4633
|
+
};
|
|
4634
|
+
var wahDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4635
|
+
type: "wah",
|
|
4636
|
+
params: params48,
|
|
4637
|
+
ports: {
|
|
4638
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4639
|
+
"out": { type: "output", signal: "audio", node: "output" },
|
|
4640
|
+
"modFreq": { type: "modulation", signal: "modulation", node: "_cutoffVoct" }
|
|
4641
|
+
},
|
|
4642
|
+
createModule(id, position) {
|
|
4643
|
+
return { id, type: "wah", position, ...paramDefaults(params48) };
|
|
4644
|
+
},
|
|
4645
|
+
createNodes(ctx, module) {
|
|
4646
|
+
const input = ctx.createGain();
|
|
4647
|
+
const output = ctx.createGain();
|
|
4648
|
+
const filter = ctx.createBiquadFilter();
|
|
4649
|
+
const lfo = ctx.createOscillator();
|
|
4650
|
+
const lfoGain = ctx.createGain();
|
|
4651
|
+
filter.type = "bandpass";
|
|
4652
|
+
filter.frequency.value = 0;
|
|
4653
|
+
filter.Q.value = module.resonance;
|
|
4654
|
+
const makeup = ctx.createGain();
|
|
4655
|
+
makeup.gain.value = bandpassMakeup(module.resonance);
|
|
4656
|
+
const _cutoffVoct = createVoctFreqNode(ctx);
|
|
4657
|
+
_cutoffVoct.parameters.get("transpose").value = module.note;
|
|
4658
|
+
const _cutoffBase = ctx.createConstantSource();
|
|
4659
|
+
_cutoffBase.offset.value = 0;
|
|
4660
|
+
_cutoffBase.start();
|
|
4661
|
+
_cutoffBase.connect(_cutoffVoct);
|
|
4662
|
+
_cutoffVoct.connect(filter.frequency);
|
|
4663
|
+
lfo.type = "sine";
|
|
4664
|
+
lfo.frequency.value = module.rate;
|
|
4665
|
+
lfoGain.gain.value = module.depth * 36;
|
|
4666
|
+
lfo.connect(lfoGain);
|
|
4667
|
+
lfoGain.connect(_cutoffVoct);
|
|
4668
|
+
lfo.start();
|
|
4669
|
+
input.connect(filter);
|
|
4670
|
+
filter.connect(makeup);
|
|
4671
|
+
makeup.connect(output);
|
|
4672
|
+
return { input, output, filter, makeup, lfo, lfoGain, _cutoffVoct, _cutoffBase };
|
|
4673
|
+
},
|
|
4674
|
+
updateParam(nodes, _module, param, value) {
|
|
4675
|
+
if (param === "note") {
|
|
4676
|
+
nodes._cutoffVoct.parameters.get("transpose").value = value;
|
|
4677
|
+
} else if (param === "resonance") {
|
|
4678
|
+
nodes.filter.Q.value = value;
|
|
4679
|
+
nodes.makeup.gain.value = bandpassMakeup(value);
|
|
4680
|
+
} else if (param === "rate") {
|
|
4681
|
+
nodes.lfo.frequency.value = value;
|
|
4682
|
+
} else if (param === "depth") {
|
|
4683
|
+
nodes.lfoGain.gain.value = value * 36;
|
|
4684
|
+
}
|
|
4685
|
+
},
|
|
4686
|
+
dispose(nodes) {
|
|
4687
|
+
nodes.lfo.stop();
|
|
4688
|
+
nodes._cutoffBase.stop();
|
|
4689
|
+
}
|
|
4690
|
+
});
|
|
4691
|
+
|
|
4692
|
+
// src/lib-runtime/generated/modules/wah.wrapper.ts
|
|
4693
|
+
var Wah = /* @__PURE__ */ defineModule(wahDescriptor, {
|
|
4694
|
+
worklets: {
|
|
4695
|
+
"map-voct-to-freq": map_voct_to_freq_default
|
|
4696
|
+
}
|
|
4697
|
+
});
|
|
4698
|
+
|
|
4699
|
+
// src/lib-runtime/generated/modules/wavefolder.tsx
|
|
4700
|
+
var params49 = {
|
|
4701
|
+
folds: { unit: "factor", default: 2, min: 1, max: 10 },
|
|
4702
|
+
symmetry: { unit: "factor", default: 0, min: -1, max: 1 },
|
|
4703
|
+
mix: { node: "_mix", field: "offset", unit: "factor", default: 1, min: 0, max: 1 }
|
|
4704
|
+
};
|
|
4705
|
+
function makeWavefoldCurve(folds, symmetry) {
|
|
4706
|
+
const samples = 65536;
|
|
4707
|
+
const curve = new Float32Array(samples);
|
|
4708
|
+
const bias = symmetry * 0.2;
|
|
4709
|
+
const w = Math.PI / 2 * folds;
|
|
4710
|
+
const dc = Math.sin(w * bias);
|
|
4711
|
+
let peak = 0;
|
|
4712
|
+
for (let i = 0; i < samples; i++) {
|
|
4713
|
+
const x = i * 2 / samples - 1;
|
|
4714
|
+
const y = Math.sin(w * (x + bias)) - dc;
|
|
4715
|
+
curve[i] = y;
|
|
4716
|
+
const a = Math.abs(y);
|
|
4717
|
+
if (a > peak) peak = a;
|
|
4718
|
+
}
|
|
4719
|
+
if (peak > 1) {
|
|
4720
|
+
const inv = 1 / peak;
|
|
4721
|
+
for (let i = 0; i < samples; i++) curve[i] *= inv;
|
|
4722
|
+
}
|
|
4723
|
+
return curve;
|
|
4724
|
+
}
|
|
4725
|
+
var wavefolderDescriptor = /* @__PURE__ */ defineDescriptor({
|
|
4726
|
+
type: "wavefolder",
|
|
4727
|
+
params: params49,
|
|
4728
|
+
ports: {
|
|
4729
|
+
"in": { type: "input", signal: "audio", node: "input" },
|
|
4730
|
+
"out": { type: "output", signal: "audio", node: "output" }
|
|
4731
|
+
},
|
|
4732
|
+
createModule(id, position) {
|
|
4733
|
+
return { id, type: "wavefolder", position, ...paramDefaults(params49) };
|
|
4734
|
+
},
|
|
4735
|
+
createNodes(ctx, module) {
|
|
4736
|
+
const input = ctx.createGain();
|
|
4737
|
+
const output = ctx.createGain();
|
|
4738
|
+
const dryGain = ctx.createGain();
|
|
4739
|
+
const wetGain = ctx.createGain();
|
|
4740
|
+
const waveshaper = ctx.createWaveShaper();
|
|
4741
|
+
waveshaper.curve = makeWavefoldCurve(module.folds, module.symmetry);
|
|
4742
|
+
waveshaper.oversample = "4x";
|
|
4743
|
+
const _mix = createMix(ctx, dryGain, wetGain, module.mix, "linear");
|
|
4744
|
+
input.connect(dryGain);
|
|
4745
|
+
input.connect(waveshaper);
|
|
4746
|
+
waveshaper.connect(wetGain);
|
|
4747
|
+
dryGain.connect(output);
|
|
4748
|
+
wetGain.connect(output);
|
|
4749
|
+
return { input, output, waveshaper, dryGain, wetGain, _mix };
|
|
4750
|
+
},
|
|
4751
|
+
updateParam(nodes, module, param, value) {
|
|
4752
|
+
if (param === "folds" || param === "symmetry") {
|
|
4753
|
+
nodes.waveshaper.curve = makeWavefoldCurve(module.folds, module.symmetry);
|
|
4754
|
+
} else if (param === "mix") {
|
|
4755
|
+
nodes._mix.offset.value = value;
|
|
4756
|
+
}
|
|
4757
|
+
}
|
|
4758
|
+
});
|
|
4759
|
+
|
|
4760
|
+
// src/lib-runtime/generated/modules/wavefolder.wrapper.ts
|
|
4761
|
+
var Wavefolder = /* @__PURE__ */ defineModule(wavefolderDescriptor);
|
|
4762
|
+
export {
|
|
4763
|
+
AcidFilter,
|
|
4764
|
+
AutoPan,
|
|
4765
|
+
Bitcrusher,
|
|
4766
|
+
Chorus,
|
|
4767
|
+
CombFilter,
|
|
4768
|
+
Compressor,
|
|
4769
|
+
Crossfade,
|
|
4770
|
+
DEFAULT_SAMPLE_BASE_URL,
|
|
4771
|
+
Delay,
|
|
4772
|
+
Dimension,
|
|
4773
|
+
Distortion,
|
|
4774
|
+
Ducker,
|
|
4775
|
+
Envelope,
|
|
4776
|
+
EnvelopeFollower,
|
|
4777
|
+
Exciter,
|
|
4778
|
+
Flanger,
|
|
4779
|
+
Formant,
|
|
4780
|
+
FrequencyShifter,
|
|
4781
|
+
Gate,
|
|
4782
|
+
Glide,
|
|
4783
|
+
HaasWidener,
|
|
4784
|
+
HarmonicTremolo,
|
|
4785
|
+
Hat,
|
|
4786
|
+
Kick,
|
|
4787
|
+
LadderFilter,
|
|
4788
|
+
Lfo,
|
|
4789
|
+
LoFi,
|
|
4790
|
+
MidSide,
|
|
4791
|
+
Noise,
|
|
4792
|
+
NoteCv,
|
|
4793
|
+
Octaver,
|
|
4794
|
+
Phaser,
|
|
4795
|
+
PingPong,
|
|
4796
|
+
Resonator,
|
|
4797
|
+
Reverb,
|
|
4798
|
+
RingModulator,
|
|
4799
|
+
Rotary,
|
|
4800
|
+
SampleSets,
|
|
4801
|
+
SampleVoice,
|
|
4802
|
+
Saturator,
|
|
4803
|
+
Shimmer,
|
|
4804
|
+
Snare,
|
|
4805
|
+
SoftLimiter,
|
|
4806
|
+
SpringReverb,
|
|
4807
|
+
StereoSpread,
|
|
4808
|
+
Subharmonic,
|
|
4809
|
+
TapeDelay,
|
|
4810
|
+
TapeStop,
|
|
4811
|
+
ThreeBandEq,
|
|
4812
|
+
Tilt,
|
|
4813
|
+
Tremolo,
|
|
4814
|
+
VOICE_KEYS,
|
|
4815
|
+
Vca,
|
|
4816
|
+
Vcf,
|
|
4817
|
+
Vco,
|
|
4818
|
+
Wah,
|
|
4819
|
+
Wavefolder,
|
|
4820
|
+
asPitch,
|
|
4821
|
+
beats,
|
|
4822
|
+
cents,
|
|
4823
|
+
centsToSemitones,
|
|
4824
|
+
createTransport,
|
|
4825
|
+
db,
|
|
4826
|
+
dbToGain,
|
|
4827
|
+
gainToDb,
|
|
4828
|
+
hz,
|
|
4829
|
+
hzToNote,
|
|
4830
|
+
interval,
|
|
4831
|
+
isRest,
|
|
4832
|
+
loadSampleSet,
|
|
4833
|
+
ms,
|
|
4834
|
+
msToSeconds,
|
|
4835
|
+
note,
|
|
4836
|
+
noteToHz,
|
|
4837
|
+
notes,
|
|
4838
|
+
now,
|
|
4839
|
+
preload,
|
|
4840
|
+
pulse,
|
|
4841
|
+
sec,
|
|
4842
|
+
semitones,
|
|
4843
|
+
steps,
|
|
4844
|
+
transpose
|
|
4845
|
+
};
|
|
4846
|
+
//# sourceMappingURL=index.js.map
|