gitframes 1.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENCE +202 -0
  2. package/README.md +77 -0
  3. package/dist/audio/index.d.mts +2 -0
  4. package/dist/audio/index.mjs +6 -0
  5. package/dist/audio-C0YslYEG.d.mts +32 -0
  6. package/dist/audio-C0YslYEG.d.mts.map +1 -0
  7. package/dist/audio-C4kiPbrV.mjs +1866 -0
  8. package/dist/audio-C4kiPbrV.mjs.map +1 -0
  9. package/dist/audio-CiXUMGyk.mjs +578 -0
  10. package/dist/audio-CiXUMGyk.mjs.map +1 -0
  11. package/dist/chunk-GMkBE123.mjs +47 -0
  12. package/dist/client-Dyypr8tt.mjs +66 -0
  13. package/dist/client-Dyypr8tt.mjs.map +1 -0
  14. package/dist/dist-C37594c6.mjs +27195 -0
  15. package/dist/dist-C37594c6.mjs.map +1 -0
  16. package/dist/dist-CMF9eNLB.mjs +4062 -0
  17. package/dist/dist-CMF9eNLB.mjs.map +1 -0
  18. package/dist/dist-cjs--KIl7Gqy.mjs +6 -0
  19. package/dist/dist-cjs-B-_4TVjP.mjs +66 -0
  20. package/dist/dist-cjs-B-_4TVjP.mjs.map +1 -0
  21. package/dist/dist-cjs-B3TIyZ3K.mjs +437 -0
  22. package/dist/dist-cjs-B3TIyZ3K.mjs.map +1 -0
  23. package/dist/dist-cjs-Bsz2eGzl.mjs +9069 -0
  24. package/dist/dist-cjs-Bsz2eGzl.mjs.map +1 -0
  25. package/dist/dist-cjs-BuJ2FXob.mjs +181 -0
  26. package/dist/dist-cjs-BuJ2FXob.mjs.map +1 -0
  27. package/dist/dist-cjs-C-FMsqmo.mjs +1091 -0
  28. package/dist/dist-cjs-C-FMsqmo.mjs.map +1 -0
  29. package/dist/dist-cjs-C4b9TCXd.mjs +48 -0
  30. package/dist/dist-cjs-C4b9TCXd.mjs.map +1 -0
  31. package/dist/dist-cjs-C817PNLc.mjs +51 -0
  32. package/dist/dist-cjs-C817PNLc.mjs.map +1 -0
  33. package/dist/dist-cjs-CBnnS4k8.mjs +43 -0
  34. package/dist/dist-cjs-CBnnS4k8.mjs.map +1 -0
  35. package/dist/dist-cjs-CWbeD0Mu.mjs +64 -0
  36. package/dist/dist-cjs-CWbeD0Mu.mjs.map +1 -0
  37. package/dist/dist-cjs-ChXEJ1ro.mjs +85 -0
  38. package/dist/dist-cjs-ChXEJ1ro.mjs.map +1 -0
  39. package/dist/dist-cjs-CslF8Dju.mjs +153 -0
  40. package/dist/dist-cjs-CslF8Dju.mjs.map +1 -0
  41. package/dist/dist-cjs-D52jYyEm.mjs +98 -0
  42. package/dist/dist-cjs-D52jYyEm.mjs.map +1 -0
  43. package/dist/dist-cjs-DhWYgNfa.mjs +209 -0
  44. package/dist/dist-cjs-DhWYgNfa.mjs.map +1 -0
  45. package/dist/dist-cjs-DssYqayv.mjs +5015 -0
  46. package/dist/dist-cjs-DssYqayv.mjs.map +1 -0
  47. package/dist/dist-cjs-Du6DnSjo.mjs +137 -0
  48. package/dist/dist-cjs-Du6DnSjo.mjs.map +1 -0
  49. package/dist/dist-cjs-b-oBQaxe.mjs +323 -0
  50. package/dist/dist-cjs-b-oBQaxe.mjs.map +1 -0
  51. package/dist/effects/index.d.mts +2 -0
  52. package/dist/effects/index.mjs +4 -0
  53. package/dist/effects-4t4qk-BR.mjs +21130 -0
  54. package/dist/effects-4t4qk-BR.mjs.map +1 -0
  55. package/dist/event-streams-B0YUBtyM.mjs +204 -0
  56. package/dist/event-streams-B0YUBtyM.mjs.map +1 -0
  57. package/dist/fonts/index.d.mts +2 -0
  58. package/dist/fonts/index.mjs +3 -0
  59. package/dist/fonts-Dn18r_ST.mjs +188 -0
  60. package/dist/fonts-Dn18r_ST.mjs.map +1 -0
  61. package/dist/index-B1557AUi.d.mts +928 -0
  62. package/dist/index-B1557AUi.d.mts.map +1 -0
  63. package/dist/index-DSaMXGoA.d.mts +65 -0
  64. package/dist/index-DSaMXGoA.d.mts.map +1 -0
  65. package/dist/index-FICUHo4P.d.mts +69 -0
  66. package/dist/index-FICUHo4P.d.mts.map +1 -0
  67. package/dist/index-YZsTjqut.d.mts +147 -0
  68. package/dist/index-YZsTjqut.d.mts.map +1 -0
  69. package/dist/index.d.mts +736 -0
  70. package/dist/index.d.mts.map +1 -0
  71. package/dist/index.mjs +7135 -0
  72. package/dist/index.mjs.map +1 -0
  73. package/dist/package-5RsA21g-.mjs +6 -0
  74. package/dist/package-5RsA21g-.mjs.map +1 -0
  75. package/dist/react/index.d.mts +3 -0
  76. package/dist/react/index.mjs +4 -0
  77. package/dist/renderer/index.d.mts +2 -0
  78. package/dist/renderer/index.mjs +6 -0
  79. package/dist/renderer-DVi31fSv.mjs +23274 -0
  80. package/dist/renderer-DVi31fSv.mjs.map +1 -0
  81. package/dist/renderers-CLJQRPVU-CMZnHNK0.mjs +49 -0
  82. package/dist/renderers-CLJQRPVU-CMZnHNK0.mjs.map +1 -0
  83. package/dist/signals/index.d.mts +9 -0
  84. package/dist/signals/index.mjs +8 -0
  85. package/dist/signals-BB_kdndF.mjs +227 -0
  86. package/dist/signals-BB_kdndF.mjs.map +1 -0
  87. package/dist/signin-CVUAIuED.mjs +865 -0
  88. package/dist/signin-CVUAIuED.mjs.map +1 -0
  89. package/dist/sso-oidc-D1GnNQYW.mjs +1051 -0
  90. package/dist/sso-oidc-D1GnNQYW.mjs.map +1 -0
  91. package/dist/sts-Bz-e3kNW.mjs +6 -0
  92. package/dist/sts-I4KhuCBs.mjs +1395 -0
  93. package/dist/sts-I4KhuCBs.mjs.map +1 -0
  94. package/package.json +174 -0
@@ -0,0 +1,1866 @@
1
+ import { F as defineRenderer } from "./dist-CMF9eNLB.mjs";
2
+ import { T as WebGPUAudioProcessor } from "./dist-C37594c6.mjs";
3
+
4
+ //#region ../../nodes/node-audio-compressor/dist/renderers-BxN6ejmd.mjs
5
+ const PARAM_ORDER$5 = [
6
+ "threshold",
7
+ "ratio",
8
+ "attack",
9
+ "release",
10
+ "knee",
11
+ "makeupGain"
12
+ ];
13
+ const COMPRESSOR_SHADER_TEMPLATE = () => `
14
+ struct Uniforms {
15
+ sampleRate : f32,
16
+ threshold : f32,
17
+ ratio : f32,
18
+ attack : f32,
19
+ release : f32,
20
+ knee : f32,
21
+ makeupGain : f32,
22
+ hasThresholdSig : f32,
23
+ hasRatioSig : f32,
24
+ hasAttackSig : f32,
25
+ hasReleaseSig : f32,
26
+ hasKneeSig : f32,
27
+ hasMakeupGainSig : f32,
28
+ baseTime : f32,
29
+ frame : f32,
30
+ numSamples : f32,
31
+ numChannels : f32,
32
+ pad1 : f32,
33
+ pad2 : f32,
34
+ pad3 : f32,
35
+ };
36
+
37
+ @group(0) @binding(0) var<uniform> u : Uniforms;
38
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
39
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
40
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>; // state[0] = envelope
41
+ @group(0) @binding(4) var<storage, read> thresholdSignal : array<f32>;
42
+ @group(0) @binding(5) var<storage, read> ratioSignal : array<f32>;
43
+ @group(0) @binding(6) var<storage, read> attackSignal : array<f32>;
44
+ @group(0) @binding(7) var<storage, read> releaseSignal : array<f32>;
45
+ @group(0) @binding(8) var<storage, read> kneeSignal : array<f32>;
46
+ @group(0) @binding(9) var<storage, read> makeupGainSignal : array<f32>;
47
+
48
+ fn log10(x: f32) -> f32 {
49
+ return log(x) * 0.4342944819032518f;
50
+ }
51
+
52
+ fn tanh(x: f32) -> f32 {
53
+ let exp2x = exp(2.0f * x);
54
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
55
+ }
56
+
57
+ fn is_nan_or_inf(v: f32) -> bool {
58
+ return (v != v) || (abs(v) > 3.402823466e+38f);
59
+ }
60
+
61
+ @compute @workgroup_size(1)
62
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
63
+ if (gid.x != 0u) { return; }
64
+
65
+ let numSamples = u32(u.numSamples);
66
+ let numChannels = u32(u.numChannels);
67
+
68
+ var envelope = state[0];
69
+ if (is_nan_or_inf(envelope)) {
70
+ envelope = 0.0f;
71
+ }
72
+
73
+ for (var index = 0u; index < numSamples; index = index + 1u) {
74
+ // 1. Dynamic Parameter Modulation & Anti-Explosion Clamping
75
+ var effThreshold = u.threshold;
76
+ if (u.hasThresholdSig > 0.5f) {
77
+ effThreshold = thresholdSignal[index];
78
+ }
79
+ effThreshold = clamp(effThreshold, -120.0f, 0.0f);
80
+
81
+ var effRatio = u.ratio;
82
+ if (u.hasRatioSig > 0.5f) {
83
+ effRatio = ratioSignal[index];
84
+ }
85
+ effRatio = clamp(effRatio, 1.0f, 100.0f);
86
+
87
+ var effAttack = u.attack;
88
+ if (u.hasAttackSig > 0.5f) {
89
+ effAttack = attackSignal[index];
90
+ }
91
+ effAttack = clamp(effAttack, 0.0001f, 10.0f);
92
+
93
+ var effRelease = u.release;
94
+ if (u.hasReleaseSig > 0.5f) {
95
+ effRelease = releaseSignal[index];
96
+ }
97
+ effRelease = clamp(effRelease, 0.001f, 10.0f);
98
+
99
+ var effKnee = u.knee;
100
+ if (u.hasKneeSig > 0.5f) {
101
+ effKnee = kneeSignal[index];
102
+ }
103
+ effKnee = clamp(effKnee, 0.0f, 40.0f);
104
+
105
+ var effMakeupGain = u.makeupGain;
106
+ if (u.hasMakeupGainSig > 0.5f) {
107
+ effMakeupGain = makeupGainSignal[index];
108
+ }
109
+ effMakeupGain = clamp(effMakeupGain, -60.0f, 36.0f);
110
+
111
+ // 2. Safe Attack / Release Coefficients
112
+ let attackCoeff = clamp(1.0f - exp(-1.0f / (u.sampleRate * effAttack)), 0.00001f, 1.0f);
113
+ let releaseCoeff = clamp(1.0f - exp(-1.0f / (u.sampleRate * effRelease)), 0.00001f, 1.0f);
114
+
115
+ // 3. Peak Detection with NaN / Inf sanitization
116
+ var peak = 0.0f;
117
+ for (var c = 0u; c < numChannels; c = c + 1u) {
118
+ var val = abs(inputChannels[c * numSamples + index]);
119
+ if (is_nan_or_inf(val)) {
120
+ val = 0.0f;
121
+ }
122
+ if (val > peak) {
123
+ peak = val;
124
+ }
125
+ }
126
+
127
+ // 4. Convert Peak to dBFS
128
+ let dB = 20.0f * log10(max(peak, 1e-9f));
129
+
130
+ // 5. Compute target reduction (soft-knee & ratio)
131
+ var targetReductiondB = 0.0f;
132
+ if (effKnee > 0.0f) {
133
+ let x = dB - effThreshold;
134
+ if (x <= -effKnee / 2.0f) {
135
+ targetReductiondB = 0.0f;
136
+ } else if (x >= effKnee / 2.0f) {
137
+ targetReductiondB = (dB - effThreshold) * (1.0f - 1.0f / effRatio);
138
+ } else {
139
+ let kneeTerm = x + effKnee / 2.0f;
140
+ targetReductiondB = ((kneeTerm * kneeTerm) / (2.0f * effKnee)) * (1.0f - 1.0f / effRatio);
141
+ }
142
+ } else {
143
+ if (dB > effThreshold) {
144
+ targetReductiondB = (dB - effThreshold) * (1.0f - 1.0f / effRatio);
145
+ }
146
+ }
147
+
148
+ targetReductiondB = clamp(targetReductiondB, 0.0f, 120.0f);
149
+
150
+ // 6. Envelope Smoothing
151
+ let targetGaindB = -targetReductiondB;
152
+ if (targetGaindB < envelope) {
153
+ envelope += (targetGaindB - envelope) * attackCoeff;
154
+ } else {
155
+ envelope += (targetGaindB - envelope) * releaseCoeff;
156
+ }
157
+
158
+ if (is_nan_or_inf(envelope)) {
159
+ envelope = 0.0f;
160
+ }
161
+ envelope = clamp(envelope, -120.0f, 0.0f);
162
+
163
+ // 7. Apply linear gain and soft clipping
164
+ let totalGaindB = clamp(envelope + effMakeupGain, -120.0f, 36.0f);
165
+ let gainLinear = clamp(pow(10.0f, totalGaindB / 20.0f), 0.0f, 63.1f);
166
+
167
+ for (var c = 0u; c < numChannels; c = c + 1u) {
168
+ let sampleIndex = c * numSamples + index;
169
+ var inVal = inputChannels[sampleIndex];
170
+ if (is_nan_or_inf(inVal)) {
171
+ inVal = 0.0f;
172
+ }
173
+
174
+ var outVal = inVal * gainLinear;
175
+ if (is_nan_or_inf(outVal)) {
176
+ outVal = 0.0f;
177
+ }
178
+
179
+ // Soft clipping at ±1.0 with smooth tanh transition starting at ±0.9
180
+ if (outVal > 1.0f) {
181
+ outVal = 1.0f;
182
+ } else if (outVal < -1.0f) {
183
+ outVal = -1.0f;
184
+ } else if (outVal > 0.9f) {
185
+ outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f);
186
+ } else if (outVal < -0.9f) {
187
+ outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f);
188
+ }
189
+
190
+ outputChannels[sampleIndex] = outVal;
191
+ }
192
+ }
193
+
194
+ if (is_nan_or_inf(envelope)) {
195
+ state[0] = 0.0f;
196
+ } else {
197
+ state[0] = clamp(envelope, -120.0f, 0.0f);
198
+ }
199
+ }
200
+ `;
201
+ const compressorAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
202
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Dynamics Compressor.");
203
+ const op = virtualMedia.operation || {};
204
+ const inputs = op.inputs || {};
205
+ const threshold = typeof op.threshold === "number" ? op.threshold : -24;
206
+ const ratio = typeof op.ratio === "number" ? op.ratio : 4;
207
+ const attack = typeof op.attack === "number" ? op.attack : .003;
208
+ const release = typeof op.release === "number" ? op.release : .25;
209
+ const knee = typeof op.knee === "number" ? op.knee : 6;
210
+ const makeupGain = typeof op.makeupGain === "number" ? op.makeupGain : 0;
211
+ const isHandleConnected = (handleIdKey) => {
212
+ const handleId = op[handleIdKey];
213
+ if (typeof handleId !== "string" || !handleId) return false;
214
+ const input = inputs[handleId];
215
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
216
+ };
217
+ const hasThresholdSig = isHandleConnected("thresholdHandleId") ? 1 : 0;
218
+ const hasRatioSig = isHandleConnected("ratioHandleId") ? 1 : 0;
219
+ const hasAttackSig = isHandleConnected("attackHandleId") ? 1 : 0;
220
+ const hasReleaseSig = isHandleConnected("releaseHandleId") ? 1 : 0;
221
+ const hasKneeSig = isHandleConnected("kneeHandleId") ? 1 : 0;
222
+ const hasMakeupGainSig = isHandleConnected("makeupGainHandleId") ? 1 : 0;
223
+ const numChannels = channels.length;
224
+ if (numChannels === 0) return;
225
+ const numSamples = channels[0].length;
226
+ if (numSamples === 0) return;
227
+ const nodeId = op.id || "audio-compressor";
228
+ const frame = ctx.frame ?? 0;
229
+ const fps = ctx.fps ?? 24;
230
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, COMPRESSOR_SHADER_TEMPLATE, (t) => [
231
+ sampleRate,
232
+ threshold,
233
+ ratio,
234
+ attack,
235
+ release,
236
+ knee,
237
+ makeupGain,
238
+ hasThresholdSig,
239
+ hasRatioSig,
240
+ hasAttackSig,
241
+ hasReleaseSig,
242
+ hasKneeSig,
243
+ hasMakeupGainSig,
244
+ t,
245
+ frame,
246
+ numSamples,
247
+ numChannels,
248
+ 0,
249
+ 0,
250
+ 0
251
+ ], 20, 1, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, void 0, void 0, PARAM_ORDER$5);
252
+ };
253
+ var renderers_default$6 = defineRenderer({ audioProcessor: compressorAudioProcessor });
254
+
255
+ //#endregion
256
+ //#region ../../nodes/node-audio-delay/dist/renderers-DxDAZF2y.mjs
257
+ const PARAM_ORDER$4 = [
258
+ "delayTime",
259
+ "feedback",
260
+ "wet",
261
+ "dry"
262
+ ];
263
+ const DELAY_SHADER_TEMPLATE = () => `
264
+ struct Uniforms {
265
+ sampleRate : f32,
266
+ delaySec : f32,
267
+ feedback : f32,
268
+ wet : f32,
269
+ dry : f32,
270
+ pingPong : f32,
271
+ bufferLength : f32,
272
+ hasDelayTimeSig : f32,
273
+ hasFeedbackSig : f32,
274
+ hasWetSig : f32,
275
+ hasDrySig : f32,
276
+ baseTime : f32,
277
+ frame : f32,
278
+ numSamples : f32,
279
+ numChannels : f32,
280
+ pad1 : f32,
281
+ };
282
+
283
+ @group(0) @binding(0) var<uniform> u : Uniforms;
284
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
285
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
286
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>;
287
+ @group(0) @binding(4) var<storage, read> delayTimeSignal : array<f32>;
288
+ @group(0) @binding(5) var<storage, read> feedbackSignal : array<f32>;
289
+ @group(0) @binding(6) var<storage, read> wetSignal : array<f32>;
290
+ @group(0) @binding(7) var<storage, read> drySignal : array<f32>;
291
+
292
+ fn tanh(x: f32) -> f32 {
293
+ let exp2x = exp(2.0f * x);
294
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
295
+ }
296
+
297
+ fn is_nan_or_inf(v: f32) -> bool {
298
+ return (v != v) || (abs(v) > 3.402823466e+38f);
299
+ }
300
+
301
+ @compute @workgroup_size(1)
302
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
303
+ if (gid.x != 0u) { return; }
304
+
305
+ let numSamples = u32(u.numSamples);
306
+ let numChannels = u32(u.numChannels);
307
+ let bufferLength = u32(u.bufferLength);
308
+
309
+ var writeIndex = u32(state[0]);
310
+ if (is_nan_or_inf(state[0])) {
311
+ writeIndex = 0u;
312
+ }
313
+
314
+ for (var index = 0u; index < numSamples; index = index + 1u) {
315
+ // 1. Dynamic Parameter Modulation & Anti-Explosion Clamping
316
+ var effDelaySec = u.delaySec;
317
+ if (u.hasDelayTimeSig > 0.5f) {
318
+ effDelaySec = delayTimeSignal[index];
319
+ }
320
+ effDelaySec = clamp(effDelaySec, 0.001f, 5.0f);
321
+
322
+ var effFeedback = u.feedback;
323
+ if (u.hasFeedbackSig > 0.5f) {
324
+ effFeedback = feedbackSignal[index];
325
+ }
326
+ effFeedback = clamp(effFeedback, 0.0f, 0.95f);
327
+
328
+ var effWet = u.wet;
329
+ if (u.hasWetSig > 0.5f) {
330
+ effWet = wetSignal[index];
331
+ }
332
+ effWet = clamp(effWet, 0.0f, 2.0f);
333
+
334
+ var effDry = u.dry;
335
+ if (u.hasDrySig > 0.5f) {
336
+ effDry = drySignal[index];
337
+ }
338
+ effDry = clamp(effDry, 0.0f, 2.0f);
339
+
340
+ // 2. Safe Dynamic Read Index
341
+ let readOffset = u32(clamp(effDelaySec * u.sampleRate, 1.0f, f32(bufferLength - 1u)));
342
+ let readIndex = (writeIndex + bufferLength - readOffset) % bufferLength;
343
+
344
+ if (numChannels == 1u || u.pingPong == 0.0f) {
345
+ // Standard Delay
346
+ for (var c = 0u; c < numChannels; c = c + 1u) {
347
+ let sampleIndex = c * numSamples + index;
348
+ var inputVal = inputChannels[sampleIndex];
349
+ if (is_nan_or_inf(inputVal)) {
350
+ inputVal = 0.0f;
351
+ }
352
+
353
+ let stateOffset = c * bufferLength + 1u;
354
+ var delayedSample = state[stateOffset + readIndex];
355
+ if (is_nan_or_inf(delayedSample)) {
356
+ delayedSample = 0.0f;
357
+ }
358
+
359
+ var nextStateVal = inputVal + delayedSample * effFeedback;
360
+ if (is_nan_or_inf(nextStateVal)) {
361
+ nextStateVal = 0.0f;
362
+ }
363
+ state[stateOffset + writeIndex] = clamp(nextStateVal, -4.0f, 4.0f);
364
+
365
+ var outVal = effDry * inputVal + effWet * delayedSample;
366
+ if (is_nan_or_inf(outVal)) {
367
+ outVal = 0.0f;
368
+ }
369
+ if (outVal > 1.0f) {
370
+ outVal = 1.0f;
371
+ } else if (outVal < -1.0f) {
372
+ outVal = -1.0f;
373
+ } else if (outVal > 0.9f) {
374
+ outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f);
375
+ } else if (outVal < -0.9f) {
376
+ outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f);
377
+ }
378
+
379
+ outputChannels[sampleIndex] = outVal;
380
+ }
381
+ } else {
382
+ // Stereo Ping-Pong Delay
383
+ var sampleL = inputChannels[index];
384
+ if (is_nan_or_inf(sampleL)) { sampleL = 0.0f; }
385
+
386
+ var sampleR = inputChannels[numSamples + index];
387
+ if (is_nan_or_inf(sampleR)) { sampleR = 0.0f; }
388
+
389
+ var delayedL = state[1u + readIndex];
390
+ if (is_nan_or_inf(delayedL)) { delayedL = 0.0f; }
391
+
392
+ var delayedR = state[bufferLength + 1u + readIndex];
393
+ if (is_nan_or_inf(delayedR)) { delayedR = 0.0f; }
394
+
395
+ // Ping-pong cross-feedback
396
+ var nextL = sampleL + delayedR * effFeedback;
397
+ var nextR = sampleR + delayedL * effFeedback;
398
+ if (is_nan_or_inf(nextL)) { nextL = 0.0f; }
399
+ if (is_nan_or_inf(nextR)) { nextR = 0.0f; }
400
+
401
+ state[1u + writeIndex] = clamp(nextL, -4.0f, 4.0f);
402
+ state[bufferLength + 1u + writeIndex] = clamp(nextR, -4.0f, 4.0f);
403
+
404
+ var outL = effDry * sampleL + effWet * delayedL;
405
+ var outR = effDry * sampleR + effWet * delayedR;
406
+ if (is_nan_or_inf(outL)) { outL = 0.0f; }
407
+ if (is_nan_or_inf(outR)) { outR = 0.0f; }
408
+
409
+ if (outL > 1.0f) { outL = 1.0f; } else if (outL < -1.0f) { outL = -1.0f; }
410
+ else if (outL > 0.9f) { outL = 0.9f + 0.1f * tanh((outL - 0.9f) / 0.1f); }
411
+ else if (outL < -0.9f) { outL = -0.9f + 0.1f * tanh((outL + 0.9f) / 0.1f); }
412
+
413
+ if (outR > 1.0f) { outR = 1.0f; } else if (outR < -1.0f) { outR = -1.0f; }
414
+ else if (outR > 0.9f) { outR = 0.9f + 0.1f * tanh((outR - 0.9f) / 0.1f); }
415
+ else if (outR < -0.9f) { outR = -0.9f + 0.1f * tanh((outR + 0.9f) / 0.1f); }
416
+
417
+ outputChannels[index] = outL;
418
+ outputChannels[numSamples + index] = outR;
419
+ }
420
+
421
+ writeIndex = (writeIndex + 1u) % bufferLength;
422
+ }
423
+
424
+ state[0] = f32(writeIndex);
425
+ }
426
+ `;
427
+ const delayAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
428
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Audio Delay.");
429
+ const op = virtualMedia.operation || {};
430
+ const inputs = op.inputs || {};
431
+ const numChannels = channels.length;
432
+ if (numChannels === 0) return;
433
+ const numSamples = channels[0].length;
434
+ if (numSamples === 0) return;
435
+ const delaySec = Math.max(.001, Math.min(typeof op.delayTime === "number" ? op.delayTime : .25, 5));
436
+ const feedbackInternal = Math.max(0, Math.min(typeof op.feedback === "number" ? op.feedback : .4, .95));
437
+ const wetInternal = Math.max(0, Math.min(typeof op.wet === "number" ? op.wet : .3, 1));
438
+ const dryInternal = Math.max(0, Math.min(typeof op.dry === "number" ? op.dry : 1, 1));
439
+ const pingPongInternal = typeof op.pingPong === "boolean" ? op.pingPong : false;
440
+ const isHandleConnected = (handleIdKey) => {
441
+ const handleId = op[handleIdKey];
442
+ if (typeof handleId !== "string" || !handleId) return false;
443
+ const input = inputs[handleId];
444
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
445
+ };
446
+ const hasDelayTimeSig = isHandleConnected("delayTimeHandleId") ? 1 : 0;
447
+ const hasFeedbackSig = isHandleConnected("feedbackHandleId") ? 1 : 0;
448
+ const hasWetSig = isHandleConnected("wetHandleId") ? 1 : 0;
449
+ const hasDrySig = isHandleConnected("dryHandleId") ? 1 : 0;
450
+ const bufferLength = Math.ceil(5 * sampleRate);
451
+ const nodeId = op.id || "audio-delay";
452
+ const frame = ctx.frame ?? 0;
453
+ const fps = ctx.fps ?? 24;
454
+ const stateFloatCount = numChannels * bufferLength + 1;
455
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, DELAY_SHADER_TEMPLATE, () => [
456
+ sampleRate,
457
+ delaySec,
458
+ feedbackInternal,
459
+ wetInternal,
460
+ dryInternal,
461
+ pingPongInternal ? 1 : 0,
462
+ bufferLength,
463
+ hasDelayTimeSig,
464
+ hasFeedbackSig,
465
+ hasWetSig,
466
+ hasDrySig,
467
+ 0,
468
+ frame,
469
+ numSamples,
470
+ numChannels,
471
+ 0
472
+ ], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, void 0, void 0, PARAM_ORDER$4);
473
+ };
474
+ var renderers_default$5 = defineRenderer({ audioProcessor: delayAudioProcessor });
475
+
476
+ //#endregion
477
+ //#region ../../nodes/node-audio-fade/dist/renderers-HxDOLxTs.mjs
478
+ const PARAM_ORDER$3 = ["fadeInDuration", "fadeOutDuration"];
479
+ const FADE_SHADER_TEMPLATE = () => `
480
+ struct Uniforms {
481
+ sampleRate : f32,
482
+ fadeInDuration : f32,
483
+ fadeOutDuration : f32,
484
+ fadeInCurve : f32,
485
+ fadeOutCurve : f32,
486
+ clipDuration : f32,
487
+ hasFadeInDurationSig : f32,
488
+ hasFadeOutDurationSig : f32,
489
+ baseTime : f32,
490
+ frame : f32,
491
+ numSamples : f32,
492
+ numChannels : f32,
493
+ pad1 : f32,
494
+ pad2 : f32,
495
+ pad3 : f32,
496
+ pad4 : f32,
497
+ };
498
+
499
+ @group(0) @binding(0) var<uniform> u : Uniforms;
500
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
501
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
502
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>; // state[0] = samplesProcessed
503
+ @group(0) @binding(4) var<storage, read> fadeInDurationSignal : array<f32>;
504
+ @group(0) @binding(5) var<storage, read> fadeOutDurationSignal : array<f32>;
505
+
506
+ fn applyCurve(p: f32, curve: f32) -> f32 {
507
+ let clampedP = max(0.0f, min(p, 1.0f));
508
+ if (curve == 1.0f) { // exponential
509
+ return clampedP * clampedP;
510
+ }
511
+ if (curve == 2.0f) { // scurve
512
+ return clampedP * clampedP * (3.0f - 2.0f * clampedP);
513
+ }
514
+ return clampedP; // linear
515
+ }
516
+
517
+ fn tanh(x: f32) -> f32 {
518
+ let exp2x = exp(2.0f * x);
519
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
520
+ }
521
+
522
+ fn is_nan_or_inf(v: f32) -> bool {
523
+ return (v != v) || (abs(v) > 3.402823466e+38f);
524
+ }
525
+
526
+ @compute @workgroup_size(1)
527
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
528
+ if (gid.x != 0u) { return; }
529
+
530
+ let numSamples = u32(u.numSamples);
531
+ let numChannels = u32(u.numChannels);
532
+
533
+ for (var index = 0u; index < numSamples; index = index + 1u) {
534
+ let t = u.baseTime + f32(index) / u.sampleRate;
535
+ var gain = 1.0f;
536
+
537
+ var effFadeIn = u.fadeInDuration;
538
+ if (u.hasFadeInDurationSig > 0.5f) {
539
+ effFadeIn = fadeInDurationSignal[index];
540
+ }
541
+ effFadeIn = clamp(effFadeIn, 0.0f, 60.0f);
542
+
543
+ var effFadeOut = u.fadeOutDuration;
544
+ if (u.hasFadeOutDurationSig > 0.5f) {
545
+ effFadeOut = fadeOutDurationSignal[index];
546
+ }
547
+ effFadeOut = clamp(effFadeOut, 0.0f, 60.0f);
548
+
549
+ if (effFadeIn > 0.0f && t < effFadeIn) {
550
+ let p = t / effFadeIn;
551
+ gain = gain * applyCurve(p, u.fadeInCurve);
552
+ }
553
+
554
+ if (effFadeOut > 0.0f && t > u.clipDuration - effFadeOut) {
555
+ let p = (u.clipDuration - t) / effFadeOut;
556
+ gain = gain * applyCurve(p, u.fadeOutCurve);
557
+ }
558
+
559
+ gain = clamp(gain, 0.0f, 1.0f);
560
+
561
+ for (var c = 0u; c < numChannels; c = c + 1u) {
562
+ let sampleIndex = c * numSamples + index;
563
+ var inVal = inputChannels[sampleIndex];
564
+ if (is_nan_or_inf(inVal)) {
565
+ inVal = 0.0f;
566
+ }
567
+
568
+ var outVal = inVal * gain;
569
+ if (is_nan_or_inf(outVal)) {
570
+ outVal = 0.0f;
571
+ }
572
+
573
+ if (outVal > 1.0f) {
574
+ outVal = 1.0f;
575
+ } else if (outVal < -1.0f) {
576
+ outVal = -1.0f;
577
+ } else if (outVal > 0.9f) {
578
+ outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f);
579
+ } else if (outVal < -0.9f) {
580
+ outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f);
581
+ }
582
+
583
+ outputChannels[sampleIndex] = outVal;
584
+ }
585
+ }
586
+
587
+ // Dummy reference to keep state binding active
588
+ state[0] = state[0];
589
+ }
590
+ `;
591
+ const getCurveCode = (curve) => {
592
+ if (curve === "exponential") return 1;
593
+ if (curve === "scurve") return 2;
594
+ return 0;
595
+ };
596
+ const fadeAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
597
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Audio Fade.");
598
+ const op = virtualMedia.operation || {};
599
+ const inputs = op.inputs || {};
600
+ const numChannels = channels.length;
601
+ if (numChannels === 0) return;
602
+ const numSamples = channels[0].length;
603
+ if (numSamples === 0) return;
604
+ const fadeInDuration = Math.max(0, typeof op.fadeInDuration === "number" ? op.fadeInDuration : 0);
605
+ const fadeOutDuration = Math.max(0, typeof op.fadeOutDuration === "number" ? op.fadeOutDuration : 0);
606
+ const fadeInCurve = typeof op.fadeInCurve === "string" ? op.fadeInCurve : "linear";
607
+ const fadeOutCurve = typeof op.fadeOutCurve === "string" ? op.fadeOutCurve : "linear";
608
+ const clipDuration = (virtualMedia.metadata?.durationMs ?? 0) / 1e3;
609
+ let fadeIn = fadeInDuration;
610
+ let fadeOut = fadeOutDuration;
611
+ if (fadeIn + fadeOut > clipDuration) {
612
+ const half = clipDuration / 2;
613
+ fadeIn = Math.min(fadeIn, half);
614
+ fadeOut = Math.min(fadeOut, half);
615
+ }
616
+ const isHandleConnected = (handleIdKey) => {
617
+ const handleId = op[handleIdKey];
618
+ if (typeof handleId !== "string" || !handleId) return false;
619
+ const input = inputs[handleId];
620
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
621
+ };
622
+ const hasFadeInDurationSig = isHandleConnected("fadeInDurationHandleId") ? 1 : 0;
623
+ const hasFadeOutDurationSig = isHandleConnected("fadeOutDurationHandleId") ? 1 : 0;
624
+ const nodeId = op.id || "audio-fade";
625
+ const frame = ctx.frame ?? 0;
626
+ const fps = ctx.fps ?? 24;
627
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, FADE_SHADER_TEMPLATE, (chunkTimeSec) => {
628
+ const startFrame = typeof op.startFrame === "number" ? op.startFrame : frame;
629
+ const localTimeSec = ctx.elapsedMs !== void 0 ? ctx.elapsedMs / 1e3 + (chunkTimeSec - frame / fps) : Math.max(0, chunkTimeSec - startFrame / fps);
630
+ return [
631
+ sampleRate,
632
+ fadeIn,
633
+ fadeOut,
634
+ getCurveCode(fadeInCurve),
635
+ getCurveCode(fadeOutCurve),
636
+ clipDuration,
637
+ hasFadeInDurationSig,
638
+ hasFadeOutDurationSig,
639
+ localTimeSec,
640
+ frame,
641
+ numSamples,
642
+ numChannels,
643
+ 0,
644
+ 0,
645
+ 0,
646
+ 0
647
+ ];
648
+ }, 16, 1, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, void 0, void 0, PARAM_ORDER$3);
649
+ };
650
+ var renderers_default$4 = defineRenderer({ audioProcessor: fadeAudioProcessor });
651
+
652
+ //#endregion
653
+ //#region ../../nodes/node-audio-noise-gate/dist/renderers-Dn1K3bcD.mjs
654
+ const PARAM_ORDER$2 = [
655
+ "threshold",
656
+ "attack",
657
+ "hold",
658
+ "release",
659
+ "range"
660
+ ];
661
+ const NOISE_GATE_SHADER_TEMPLATE = () => `
662
+ struct Uniforms {
663
+ sampleRate : f32,
664
+ threshold : f32,
665
+ attack : f32,
666
+ hold : f32,
667
+ release : f32,
668
+ closedGain : f32,
669
+ windowLength : f32,
670
+ hasThresholdSig : f32,
671
+ hasAttackSig : f32,
672
+ hasHoldSig : f32,
673
+ hasReleaseSig : f32,
674
+ hasRangeSig : f32,
675
+ baseTime : f32,
676
+ frame : f32,
677
+ numSamples : f32,
678
+ numChannels : f32,
679
+ };
680
+
681
+ @group(0) @binding(0) var<uniform> u : Uniforms;
682
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
683
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
684
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>;
685
+ @group(0) @binding(4) var<storage, read> thresholdSignal : array<f32>;
686
+ @group(0) @binding(5) var<storage, read> attackSignal : array<f32>;
687
+ @group(0) @binding(6) var<storage, read> holdSignal : array<f32>;
688
+ @group(0) @binding(7) var<storage, read> releaseSignal : array<f32>;
689
+ @group(0) @binding(8) var<storage, read> rangeSignal : array<f32>;
690
+
691
+ fn log10(x: f32) -> f32 {
692
+ return log(x) * 0.4342944819032518f;
693
+ }
694
+
695
+ fn tanh(x: f32) -> f32 {
696
+ let exp2x = exp(2.0f * x);
697
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
698
+ }
699
+
700
+ fn is_nan_or_inf(v: f32) -> bool {
701
+ return (v != v) || (abs(v) > 3.402823466e+38f);
702
+ }
703
+
704
+ @compute @workgroup_size(1)
705
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
706
+ if (gid.x != 0u) { return; }
707
+
708
+ let numSamples = u32(u.numSamples);
709
+ let numChannels = u32(u.numChannels);
710
+ let windowLength = u32(u.windowLength);
711
+
712
+ var gateState = u32(state[0]); // 0=CLOSED, 1=ATTACK, 2=OPEN, 3=HOLD, 4=RELEASE
713
+ if (is_nan_or_inf(state[0])) { gateState = 0u; }
714
+
715
+ var currentGain = state[1];
716
+ if (is_nan_or_inf(currentGain)) { currentGain = 0.0f; }
717
+
718
+ var gainAtTransition = state[2];
719
+ if (is_nan_or_inf(gainAtTransition)) { gainAtTransition = 0.0f; }
720
+
721
+ var attackSamplesLeft = i32(state[3]);
722
+ var holdSamplesLeft = i32(state[4]);
723
+ var releaseSamplesLeft = i32(state[5]);
724
+ var writeIndex = u32(state[6]);
725
+ var sumSquares = state[7];
726
+ if (is_nan_or_inf(sumSquares)) { sumSquares = 0.0f; }
727
+
728
+ let sampleRate = u.sampleRate;
729
+
730
+ for (var index = 0u; index < numSamples; index = index + 1u) {
731
+ // 1. Dynamic Parameter Modulation & Anti-Explosion Clamping
732
+ var effThreshold = u.threshold;
733
+ if (u.hasThresholdSig > 0.5f) {
734
+ effThreshold = thresholdSignal[index];
735
+ }
736
+ effThreshold = clamp(effThreshold, -120.0f, 0.0f);
737
+
738
+ var effAttack = u.attack;
739
+ if (u.hasAttackSig > 0.5f) {
740
+ effAttack = attackSignal[index];
741
+ }
742
+ effAttack = clamp(effAttack, 0.0001f, 5.0f);
743
+
744
+ var effHold = u.hold;
745
+ if (u.hasHoldSig > 0.5f) {
746
+ effHold = holdSignal[index];
747
+ }
748
+ effHold = clamp(effHold, 0.001f, 10.0f);
749
+
750
+ var effRelease = u.release;
751
+ if (u.hasReleaseSig > 0.5f) {
752
+ effRelease = releaseSignal[index];
753
+ }
754
+ effRelease = clamp(effRelease, 0.01f, 10.0f);
755
+
756
+ var effRange = u.closedGain;
757
+ if (u.hasRangeSig > 0.5f) {
758
+ let rDb = clamp(rangeSignal[index], -120.0f, 0.0f);
759
+ effRange = select(0.0f, pow(10.0f, rDb / 20.0f), rDb > -96.0f);
760
+ }
761
+ effRange = clamp(effRange, 0.0f, 1.0f);
762
+
763
+ // 2. Peak & RMS Window Calculation
764
+ var peak = 0.0f;
765
+ for (var c = 0u; c < numChannels; c = c + 1u) {
766
+ var val = abs(inputChannels[c * numSamples + index]);
767
+ if (is_nan_or_inf(val)) { val = 0.0f; }
768
+ if (val > peak) {
769
+ peak = val;
770
+ }
771
+ }
772
+
773
+ let inputPowerSq = peak * peak;
774
+ let oldestSq = state[8u + writeIndex];
775
+ state[8u + writeIndex] = inputPowerSq;
776
+ sumSquares = max(0.0f, sumSquares - oldestSq + inputPowerSq);
777
+ writeIndex = (writeIndex + 1u) % windowLength;
778
+
779
+ if (writeIndex == 0u) {
780
+ var sum = 0.0f;
781
+ for (var j = 0u; j < windowLength; j = j + 1u) {
782
+ sum = sum + state[8u + j];
783
+ }
784
+ sumSquares = sum;
785
+ }
786
+
787
+ let rms = sqrt(sumSquares / f32(windowLength));
788
+ let db = 20.0f * log10(max(rms, 1e-9f));
789
+
790
+ let signalAboveThreshold = db >= effThreshold;
791
+
792
+ if (signalAboveThreshold) {
793
+ if (gateState == 0u || gateState == 4u) {
794
+ gateState = 1u; // ATTACK
795
+ gainAtTransition = currentGain;
796
+ var samples = i32(round(effAttack * sampleRate));
797
+ if (samples <= 0) { samples = 1; }
798
+ attackSamplesLeft = samples;
799
+ } else if (gateState == 3u) {
800
+ gateState = 2u; // OPEN
801
+ currentGain = 1.0f;
802
+ }
803
+ } else {
804
+ if (gateState == 2u || gateState == 1u) {
805
+ gateState = 3u; // HOLD
806
+ var samples = i32(round(effHold * sampleRate));
807
+ if (samples <= 0) { samples = 1; }
808
+ holdSamplesLeft = samples;
809
+ }
810
+ }
811
+
812
+ if (gateState == 0u) {
813
+ currentGain = effRange;
814
+ } else if (gateState == 2u) {
815
+ currentGain = 1.0f;
816
+ } else if (gateState == 1u) {
817
+ var totalAttackSamples = i32(round(effAttack * sampleRate));
818
+ if (totalAttackSamples <= 0) { totalAttackSamples = 1; }
819
+ let progress = 1.0f - f32(attackSamplesLeft) / f32(totalAttackSamples);
820
+ currentGain = gainAtTransition + progress * (1.0f - gainAtTransition);
821
+
822
+ attackSamplesLeft = attackSamplesLeft - 1;
823
+ if (attackSamplesLeft <= 0) {
824
+ gateState = 2u;
825
+ currentGain = 1.0f;
826
+ }
827
+ } else if (gateState == 3u) {
828
+ currentGain = 1.0f;
829
+ holdSamplesLeft = holdSamplesLeft - 1;
830
+ if (holdSamplesLeft <= 0) {
831
+ gateState = 4u; // RELEASE
832
+ gainAtTransition = 1.0f;
833
+ var samples = i32(round(effRelease * sampleRate));
834
+ if (samples <= 0) { samples = 1; }
835
+ releaseSamplesLeft = samples;
836
+ }
837
+ } else if (gateState == 4u) {
838
+ var totalReleaseSamples = i32(round(effRelease * sampleRate));
839
+ if (totalReleaseSamples <= 0) { totalReleaseSamples = 1; }
840
+ let progress = 1.0f - f32(releaseSamplesLeft) / f32(totalReleaseSamples);
841
+ currentGain = gainAtTransition + progress * (effRange - gainAtTransition);
842
+
843
+ releaseSamplesLeft = releaseSamplesLeft - 1;
844
+ if (releaseSamplesLeft <= 0) {
845
+ gateState = 0u;
846
+ currentGain = effRange;
847
+ }
848
+ }
849
+
850
+ currentGain = clamp(currentGain, 0.0f, 1.0f);
851
+
852
+ for (var c = 0u; c < numChannels; c = c + 1u) {
853
+ let sampleIndex = c * numSamples + index;
854
+ var inVal = inputChannels[sampleIndex];
855
+ if (is_nan_or_inf(inVal)) { inVal = 0.0f; }
856
+
857
+ var outVal = inVal * currentGain;
858
+ if (is_nan_or_inf(outVal)) { outVal = 0.0f; }
859
+
860
+ if (outVal > 1.0f) {
861
+ outVal = 1.0f;
862
+ } else if (outVal < -1.0f) {
863
+ outVal = -1.0f;
864
+ } else if (outVal > 0.9f) {
865
+ outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f);
866
+ } else if (outVal < -0.9f) {
867
+ outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f);
868
+ }
869
+
870
+ outputChannels[sampleIndex] = outVal;
871
+ }
872
+ }
873
+
874
+ state[0] = f32(gateState);
875
+ state[1] = currentGain;
876
+ state[2] = gainAtTransition;
877
+ state[3] = f32(attackSamplesLeft);
878
+ state[4] = f32(holdSamplesLeft);
879
+ state[5] = f32(releaseSamplesLeft);
880
+ state[6] = f32(writeIndex);
881
+ state[7] = sumSquares;
882
+ }
883
+ `;
884
+ const noiseGateAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
885
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Noise Gate.");
886
+ const op = virtualMedia.operation || {};
887
+ const inputs = op.inputs || {};
888
+ const threshold = typeof op.threshold === "number" ? op.threshold : -40;
889
+ const attack = typeof op.attack === "number" ? op.attack : .005;
890
+ const hold = typeof op.hold === "number" ? op.hold : .05;
891
+ const release = typeof op.release === "number" ? op.release : .1;
892
+ const range = typeof op.range === "number" ? op.range : -80;
893
+ const closedGain = range <= -96 ? 0 : 10 ** (range / 20);
894
+ const isHandleConnected = (handleIdKey) => {
895
+ const handleId = op[handleIdKey];
896
+ if (typeof handleId !== "string" || !handleId) return false;
897
+ const input = inputs[handleId];
898
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
899
+ };
900
+ const hasThresholdSig = isHandleConnected("thresholdHandleId") ? 1 : 0;
901
+ const hasAttackSig = isHandleConnected("attackHandleId") ? 1 : 0;
902
+ const hasHoldSig = isHandleConnected("holdHandleId") ? 1 : 0;
903
+ const hasReleaseSig = isHandleConnected("releaseHandleId") ? 1 : 0;
904
+ const hasRangeSig = isHandleConnected("rangeHandleId") ? 1 : 0;
905
+ const numChannels = channels.length;
906
+ if (numChannels === 0) return;
907
+ const numSamples = channels[0].length;
908
+ if (numSamples === 0) return;
909
+ const windowLength = Math.max(1, Math.round(.01 * sampleRate));
910
+ const nodeId = op.id || "audio-noise-gate";
911
+ const frame = ctx.frame ?? 0;
912
+ const fps = ctx.fps ?? 24;
913
+ const stateFloatCount = 8 + windowLength;
914
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, NOISE_GATE_SHADER_TEMPLATE, () => [
915
+ sampleRate,
916
+ threshold,
917
+ attack,
918
+ hold,
919
+ release,
920
+ closedGain,
921
+ windowLength,
922
+ hasThresholdSig,
923
+ hasAttackSig,
924
+ hasHoldSig,
925
+ hasReleaseSig,
926
+ hasRangeSig,
927
+ 0,
928
+ frame,
929
+ numSamples,
930
+ numChannels
931
+ ], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, void 0, void 0, PARAM_ORDER$2);
932
+ };
933
+ var renderers_default$3 = defineRenderer({ audioProcessor: noiseGateAudioProcessor });
934
+
935
+ //#endregion
936
+ //#region ../../nodes/node-audio-parametric-eq/dist/renderers-CI8P2akC.mjs
937
+ const PARAM_ORDER$1 = [
938
+ "frequency",
939
+ "gain",
940
+ "q"
941
+ ];
942
+ const PARAMETRIC_EQ_SHADER_TEMPLATE = () => `
943
+ struct Uniforms {
944
+ sampleRate : f32,
945
+ numBands : f32,
946
+ frequency : f32,
947
+ gain : f32,
948
+ q : f32,
949
+ bandType : f32,
950
+ hasFrequencySig : f32,
951
+ hasGainSig : f32,
952
+ hasQSig : f32,
953
+ baseTime : f32,
954
+ frame : f32,
955
+ numSamples : f32,
956
+ numChannels : f32,
957
+ pad1 : f32,
958
+ pad2 : f32,
959
+ pad3 : f32,
960
+ };
961
+
962
+ @group(0) @binding(0) var<uniform> u : Uniforms;
963
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
964
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
965
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>;
966
+ @group(0) @binding(4) var<storage, read> frequencySignal : array<f32>;
967
+ @group(0) @binding(5) var<storage, read> gainSignal : array<f32>;
968
+ @group(0) @binding(6) var<storage, read> qSignal : array<f32>;
969
+
970
+ const PI: f32 = 3.141592653589793f;
971
+
972
+ fn tanh(x: f32) -> f32 {
973
+ let exp2x = exp(2.0f * x);
974
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
975
+ }
976
+
977
+ fn is_nan_or_inf(v: f32) -> bool {
978
+ return (v != v) || (abs(v) > 3.402823466e+38f);
979
+ }
980
+
981
+ @compute @workgroup_size(1)
982
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
983
+ if (gid.x != 0u) { return; }
984
+
985
+ let numSamples = u32(u.numSamples);
986
+ let numChannels = u32(u.numChannels);
987
+ let sampleRate = u.sampleRate;
988
+ let bandType = u.bandType; // 0=peak, 1=lowShelf, 2=highShelf, 3=lowPass, 4=highPass, 5=notch
989
+
990
+ let biquadStateOffset = 4u;
991
+
992
+ for (var c = 0u; c < numChannels; c = c + 1u) {
993
+ let offset = biquadStateOffset + c * 4u;
994
+ var x1 = state[offset];
995
+ var x2 = state[offset + 1u];
996
+ var y1 = state[offset + 2u];
997
+ var y2 = state[offset + 3u];
998
+
999
+ if (is_nan_or_inf(x1)) { x1 = 0.0f; }
1000
+ if (is_nan_or_inf(x2)) { x2 = 0.0f; }
1001
+ if (is_nan_or_inf(y1)) { y1 = 0.0f; }
1002
+ if (is_nan_or_inf(y2)) { y2 = 0.0f; }
1003
+
1004
+ for (var index = 0u; index < numSamples; index = index + 1u) {
1005
+ let sampleIndex = c * numSamples + index;
1006
+ var x = inputChannels[sampleIndex];
1007
+ if (is_nan_or_inf(x)) { x = 0.0f; }
1008
+
1009
+ // 1. Dynamic Parameter Signal Resolution & Clamping
1010
+ var effFreq = u.frequency;
1011
+ if (u.hasFrequencySig > 0.5f) {
1012
+ effFreq = frequencySignal[index];
1013
+ }
1014
+ effFreq = clamp(effFreq, 20.0f, sampleRate / 2.0f - 1.0f);
1015
+
1016
+ var effQ = u.q;
1017
+ if (u.hasQSig > 0.5f) {
1018
+ effQ = qSignal[index];
1019
+ }
1020
+ effQ = clamp(effQ, 0.01f, 10.0f);
1021
+
1022
+ var effGain = u.gain;
1023
+ if (u.hasGainSig > 0.5f) {
1024
+ effGain = gainSignal[index];
1025
+ }
1026
+ effGain = clamp(effGain, -24.0f, 24.0f);
1027
+
1028
+ if (bandType == 0.0f && abs(effGain) < 1e-5f) {
1029
+ // Bypass filter if peak gain is zero
1030
+ outputChannels[sampleIndex] = x;
1031
+ continue;
1032
+ }
1033
+
1034
+ // 2. Biquad Coefficients Calculation
1035
+ let w0 = (2.0f * PI * effFreq) / sampleRate;
1036
+ let cos_w0 = cos(w0);
1037
+ let sin_w0 = sin(w0);
1038
+ let A = clamp(pow(10.0f, effGain / 40.0f), 0.25f, 4.0f);
1039
+ let alpha = sin_w0 / (2.0f * effQ);
1040
+
1041
+ var b0 = 0.0f; var b1 = 0.0f; var b2 = 0.0f;
1042
+ var a0 = 0.0f; var a1 = 0.0f; var a2 = 0.0f;
1043
+
1044
+ if (bandType == 0.0f) { // peak
1045
+ b0 = 1.0f + alpha * A;
1046
+ b1 = -2.0f * cos_w0;
1047
+ b2 = 1.0f - alpha * A;
1048
+ a0 = 1.0f + alpha / A;
1049
+ a1 = -2.0f * cos_w0;
1050
+ a2 = 1.0f - alpha / A;
1051
+ } else if (bandType == 1.0f) { // lowShelf
1052
+ let sqrtA = sqrt(A);
1053
+ let twoSqrtAAlpha = 2.0f * sqrtA * alpha;
1054
+ b0 = A * (A + 1.0f - (A - 1.0f) * cos_w0 + twoSqrtAAlpha);
1055
+ b1 = 2.0f * A * (A - 1.0f - (A + 1.0f) * cos_w0);
1056
+ b2 = A * (A + 1.0f - (A - 1.0f) * cos_w0 - twoSqrtAAlpha);
1057
+ a0 = A + 1.0f + (A - 1.0f) * cos_w0 + twoSqrtAAlpha;
1058
+ a1 = -2.0f * (A - 1.0f + (A + 1.0f) * cos_w0);
1059
+ a2 = A + 1.0f + (A - 1.0f) * cos_w0 - twoSqrtAAlpha;
1060
+ } else if (bandType == 2.0f) { // highShelf
1061
+ let sqrtA = sqrt(A);
1062
+ let twoSqrtAAlpha = 2.0f * sqrtA * alpha;
1063
+ b0 = A * (A + 1.0f + (A - 1.0f) * cos_w0 + twoSqrtAAlpha);
1064
+ b1 = -2.0f * A * (A - 1.0f + (A + 1.0f) * cos_w0);
1065
+ b2 = A * (A + 1.0f + (A - 1.0f) * cos_w0 - twoSqrtAAlpha);
1066
+ a0 = A + 1.0f - (A - 1.0f) * cos_w0 + twoSqrtAAlpha;
1067
+ a1 = 2.0f * (A - 1.0f - (A + 1.0f) * cos_w0);
1068
+ a2 = A + 1.0f - (A - 1.0f) * cos_w0 - twoSqrtAAlpha;
1069
+ } else if (bandType == 3.0f) { // lowPass
1070
+ b0 = (1.0f - cos_w0) / 2.0f;
1071
+ b1 = 1.0f - cos_w0;
1072
+ b2 = (1.0f - cos_w0) / 2.0f;
1073
+ a0 = 1.0f + alpha;
1074
+ a1 = -2.0f * cos_w0;
1075
+ a2 = 1.0f - alpha;
1076
+ } else if (bandType == 4.0f) { // highPass
1077
+ b0 = (1.0f + cos_w0) / 2.0f;
1078
+ b1 = -(1.0f + cos_w0);
1079
+ b2 = (1.0f + cos_w0) / 2.0f;
1080
+ a0 = 1.0f + alpha;
1081
+ a1 = -2.0f * cos_w0;
1082
+ a2 = 1.0f - alpha;
1083
+ } else if (bandType == 5.0f) { // notch
1084
+ b0 = 1.0f;
1085
+ b1 = -2.0f * cos_w0;
1086
+ b2 = 1.0f;
1087
+ a0 = 1.0f + alpha;
1088
+ a1 = -2.0f * cos_w0;
1089
+ a2 = 1.0f - alpha;
1090
+ }
1091
+
1092
+ if (abs(a0) < 1e-9f) {
1093
+ outputChannels[sampleIndex] = x;
1094
+ continue;
1095
+ }
1096
+
1097
+ let b0_norm = b0 / a0;
1098
+ let b1_norm = b1 / a0;
1099
+ let b2_norm = b2 / a0;
1100
+ let a1_norm = a1 / a0;
1101
+ let a2_norm = a2 / a0;
1102
+
1103
+ var y = b0_norm * x + b1_norm * x1 + b2_norm * x2 - a1_norm * y1 - a2_norm * y2;
1104
+ if (is_nan_or_inf(y)) { y = 0.0f; }
1105
+
1106
+ // Biquad State History Update with Soft Anti-Explosion Clamping
1107
+ x2 = x1;
1108
+ x1 = x;
1109
+ y2 = clamp(y1, -4.0f, 4.0f);
1110
+ y1 = clamp(y, -4.0f, 4.0f);
1111
+
1112
+ // Output Soft Clipping
1113
+ if (y > 1.0f) {
1114
+ y = 1.0f;
1115
+ } else if (y < -1.0f) {
1116
+ y = -1.0f;
1117
+ } else if (y > 0.9f) {
1118
+ y = 0.9f + 0.1f * tanh((y - 0.9f) / 0.1f);
1119
+ } else if (y < -0.9f) {
1120
+ y = -0.9f + 0.1f * tanh((y + 0.9f) / 0.1f);
1121
+ }
1122
+
1123
+ outputChannels[sampleIndex] = y;
1124
+ }
1125
+
1126
+ state[offset] = x1;
1127
+ state[offset + 1u] = x2;
1128
+ state[offset + 2u] = y1;
1129
+ state[offset + 3u] = y2;
1130
+ }
1131
+ }
1132
+ `;
1133
+ const getBandTypeVal = (type) => {
1134
+ if (type === "peak") return 0;
1135
+ if (type === "lowShelf") return 1;
1136
+ if (type === "highShelf") return 2;
1137
+ if (type === "lowPass") return 3;
1138
+ if (type === "highPass") return 4;
1139
+ if (type === "notch") return 5;
1140
+ return 0;
1141
+ };
1142
+ const parametricEqAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
1143
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Parametric EQ.");
1144
+ const op = virtualMedia.operation || {};
1145
+ const inputs = op.inputs || {};
1146
+ const config = {
1147
+ type: typeof op.type === "string" && [
1148
+ "lowShelf",
1149
+ "highShelf",
1150
+ "peak",
1151
+ "lowPass",
1152
+ "highPass",
1153
+ "notch"
1154
+ ].includes(op.type) ? op.type : "peak",
1155
+ frequency: typeof op.frequency === "number" ? op.frequency : 1e3,
1156
+ frequencyHandleId: typeof op.frequencyHandleId === "string" ? op.frequencyHandleId : null,
1157
+ gain: typeof op.gain === "number" ? op.gain : 0,
1158
+ gainHandleId: typeof op.gainHandleId === "string" ? op.gainHandleId : null,
1159
+ q: typeof op.q === "number" ? op.q : 1,
1160
+ qHandleId: typeof op.qHandleId === "string" ? op.qHandleId : null
1161
+ };
1162
+ const isHandleConnected = (handleIdKey) => {
1163
+ const handleId = op[handleIdKey];
1164
+ if (typeof handleId !== "string" || !handleId) return false;
1165
+ const input = inputs[handleId];
1166
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
1167
+ };
1168
+ const hasFrequencySig = isHandleConnected("frequencyHandleId") ? 1 : 0;
1169
+ const hasGainSig = isHandleConnected("gainHandleId") ? 1 : 0;
1170
+ const hasQSig = isHandleConnected("qHandleId") ? 1 : 0;
1171
+ const numChannels = channels.length;
1172
+ if (numChannels === 0) return;
1173
+ const numSamples = channels[0].length;
1174
+ if (numSamples === 0) return;
1175
+ const nodeId = op.id || "audio-parametric-eq";
1176
+ const frame = ctx.frame ?? 0;
1177
+ const fps = ctx.fps ?? 24;
1178
+ const stateFloatCount = 4 + numChannels * 4;
1179
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, PARAMETRIC_EQ_SHADER_TEMPLATE, () => [
1180
+ sampleRate,
1181
+ 1,
1182
+ config.frequency,
1183
+ config.gain,
1184
+ config.q,
1185
+ getBandTypeVal(config.type),
1186
+ hasFrequencySig,
1187
+ hasGainSig,
1188
+ hasQSig,
1189
+ 0,
1190
+ frame,
1191
+ numSamples,
1192
+ numChannels,
1193
+ 0,
1194
+ 0,
1195
+ 0
1196
+ ], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, void 0, void 0, PARAM_ORDER$1);
1197
+ };
1198
+ var renderers_default$2 = defineRenderer({ audioProcessor: parametricEqAudioProcessor });
1199
+
1200
+ //#endregion
1201
+ //#region ../../nodes/node-audio-reverb/dist/renderers-Dz30LBKg.mjs
1202
+ const PARAM_ORDER = [
1203
+ "roomSize",
1204
+ "damping",
1205
+ "wet",
1206
+ "dry",
1207
+ "width"
1208
+ ];
1209
+ const REVERB_SHADER_TEMPLATE = () => `
1210
+ struct Uniforms {
1211
+ sampleRate : f32,
1212
+ roomSize : f32,
1213
+ damping : f32,
1214
+ wet : f32,
1215
+ dry : f32,
1216
+ width : f32,
1217
+ hasRoomSizeSig : f32,
1218
+ hasDampingSig : f32,
1219
+ hasWetSig : f32,
1220
+ hasDrySig : f32,
1221
+ hasWidthSig : f32,
1222
+ baseTime : f32,
1223
+ frame : f32,
1224
+ numSamples : f32,
1225
+ numChannels : f32,
1226
+ pad1 : f32,
1227
+ };
1228
+
1229
+ @group(0) @binding(0) var<uniform> u : Uniforms;
1230
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
1231
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
1232
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>;
1233
+ @group(0) @binding(4) var<storage, read> roomSizeSignal : array<f32>;
1234
+ @group(0) @binding(5) var<storage, read> dampingSignal : array<f32>;
1235
+ @group(0) @binding(6) var<storage, read> wetSignal : array<f32>;
1236
+ @group(0) @binding(7) var<storage, read> drySignal : array<f32>;
1237
+ @group(0) @binding(8) var<storage, read> widthSignal : array<f32>;
1238
+
1239
+ fn tanh(x: f32) -> f32 {
1240
+ let exp2x = exp(2.0f * x);
1241
+ return (exp2x - 1.0f) / (exp2x + 1.0f);
1242
+ }
1243
+
1244
+ fn is_nan_or_inf(v: f32) -> bool {
1245
+ return (v != v) || (abs(v) > 3.402823466e+38f);
1246
+ }
1247
+
1248
+ @compute @workgroup_size(1)
1249
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
1250
+ if (gid.x != 0u) { return; }
1251
+
1252
+ let numSamples = u32(u.numSamples);
1253
+ let numChannels = u32(u.numChannels);
1254
+ let fixedgain: f32 = 0.015f;
1255
+
1256
+ var bufIndices: array<u32, 24>;
1257
+ var filterStores: array<f32, 24>;
1258
+ var sizes: array<u32, 24>;
1259
+ var startOffsets: array<u32, 24>;
1260
+
1261
+ for (var f = 0u; f < 24u; f = f + 1u) {
1262
+ let metaOffset = f * 4u;
1263
+ sizes[f] = u32(state[metaOffset]);
1264
+ startOffsets[f] = u32(state[metaOffset + 1u]);
1265
+ bufIndices[f] = u32(state[metaOffset + 2u]);
1266
+ var fs = state[metaOffset + 3u];
1267
+ if (is_nan_or_inf(fs)) { fs = 0.0f; }
1268
+ filterStores[f] = fs;
1269
+ }
1270
+
1271
+ if (numChannels == 1u) {
1272
+ for (var index = 0u; index < numSamples; index = index + 1u) {
1273
+ // Dynamic parameter signal evaluation
1274
+ var effRoomSize = u.roomSize;
1275
+ if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }
1276
+ effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);
1277
+
1278
+ var effDamping = u.damping;
1279
+ if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }
1280
+ effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;
1281
+
1282
+ var effWet = u.wet;
1283
+ if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }
1284
+ effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;
1285
+
1286
+ var effDry = u.dry;
1287
+ if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }
1288
+ effDry = clamp(effDry, 0.0f, 2.0f);
1289
+
1290
+ var x = inputChannels[index];
1291
+ if (is_nan_or_inf(x)) { x = 0.0f; }
1292
+ let inputVal = x * fixedgain;
1293
+
1294
+ var combSum = 0.0f;
1295
+ for (var j = 0u; j < 8u; j = j + 1u) {
1296
+ let size = sizes[j];
1297
+ let startOffset = startOffsets[j];
1298
+ let bufferIndex = bufIndices[j];
1299
+ var fs = filterStores[j];
1300
+
1301
+ var output = state[startOffset + bufferIndex];
1302
+ if (is_nan_or_inf(output)) { output = 0.0f; }
1303
+
1304
+ fs = output * (1.0f - effDamping) + fs * effDamping;
1305
+ var nextVal = inputVal + fs * effRoomSize;
1306
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1307
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1308
+
1309
+ bufIndices[j] = (bufferIndex + 1u) % size;
1310
+ filterStores[j] = fs;
1311
+ combSum = combSum + output;
1312
+ }
1313
+
1314
+ var revOut = combSum;
1315
+ for (var j = 0u; j < 4u; j = j + 1u) {
1316
+ let f = 8u + j;
1317
+ let size = sizes[f];
1318
+ let startOffset = startOffsets[f];
1319
+ let bufferIndex = bufIndices[f];
1320
+
1321
+ var bufOut = state[startOffset + bufferIndex];
1322
+ if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }
1323
+
1324
+ var nextVal = revOut + bufOut * 0.5f;
1325
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1326
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1327
+
1328
+ let output = bufOut - revOut;
1329
+ bufIndices[f] = (bufferIndex + 1u) % size;
1330
+ revOut = output;
1331
+ }
1332
+
1333
+ var outVal = x * effDry + revOut * effWet;
1334
+ if (is_nan_or_inf(outVal)) { outVal = 0.0f; }
1335
+
1336
+ if (outVal > 1.0f) { outVal = 1.0f; }
1337
+ else if (outVal < -1.0f) { outVal = -1.0f; }
1338
+ else if (outVal > 0.9f) { outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f); }
1339
+ else if (outVal < -0.9f) { outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f); }
1340
+
1341
+ outputChannels[index] = outVal;
1342
+ }
1343
+ } else {
1344
+ for (var index = 0u; index < numSamples; index = index + 1u) {
1345
+ // Dynamic parameter signal evaluation
1346
+ var effRoomSize = u.roomSize;
1347
+ if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }
1348
+ effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);
1349
+
1350
+ var effDamping = u.damping;
1351
+ if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }
1352
+ effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;
1353
+
1354
+ var effWet = u.wet;
1355
+ if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }
1356
+ effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;
1357
+
1358
+ var effDry = u.dry;
1359
+ if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }
1360
+ effDry = clamp(effDry, 0.0f, 2.0f);
1361
+
1362
+ var effWidth = u.width;
1363
+ if (u.hasWidthSig > 0.5f) { effWidth = widthSignal[index]; }
1364
+ effWidth = clamp(effWidth, 0.0f, 1.0f);
1365
+
1366
+ let wet1 = effWet * (effWidth / 2.0f + 0.5f);
1367
+ let wet2 = effWet * ((1.0f - effWidth) / 2.0f);
1368
+
1369
+ var inputL = inputChannels[index];
1370
+ if (is_nan_or_inf(inputL)) { inputL = 0.0f; }
1371
+ var inputR = inputChannels[numSamples + index];
1372
+ if (is_nan_or_inf(inputR)) { inputR = 0.0f; }
1373
+
1374
+ let inputVal = (inputL + inputR) * fixedgain;
1375
+
1376
+ // Left channel (Filters 0-11)
1377
+ var combSumL = 0.0f;
1378
+ for (var j = 0u; j < 8u; j = j + 1u) {
1379
+ let size = sizes[j];
1380
+ let startOffset = startOffsets[j];
1381
+ let bufferIndex = bufIndices[j];
1382
+ var fs = filterStores[j];
1383
+
1384
+ var output = state[startOffset + bufferIndex];
1385
+ if (is_nan_or_inf(output)) { output = 0.0f; }
1386
+
1387
+ fs = output * (1.0f - effDamping) + fs * effDamping;
1388
+ var nextVal = inputVal + fs * effRoomSize;
1389
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1390
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1391
+
1392
+ bufIndices[j] = (bufferIndex + 1u) % size;
1393
+ filterStores[j] = fs;
1394
+ combSumL = combSumL + output;
1395
+ }
1396
+ var revOutL = combSumL;
1397
+ for (var j = 0u; j < 4u; j = j + 1u) {
1398
+ let f = 8u + j;
1399
+ let size = sizes[f];
1400
+ let startOffset = startOffsets[f];
1401
+ let bufferIndex = bufIndices[f];
1402
+
1403
+ var bufOut = state[startOffset + bufferIndex];
1404
+ if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }
1405
+
1406
+ var nextVal = revOutL + bufOut * 0.5f;
1407
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1408
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1409
+
1410
+ let output = bufOut - revOutL;
1411
+ bufIndices[f] = (bufferIndex + 1u) % size;
1412
+ revOutL = output;
1413
+ }
1414
+
1415
+ // Right channel (Filters 12-23)
1416
+ var combSumR = 0.0f;
1417
+ for (var j = 0u; j < 8u; j = j + 1u) {
1418
+ let f = 12u + j;
1419
+ let size = sizes[f];
1420
+ let startOffset = startOffsets[f];
1421
+ let bufferIndex = bufIndices[f];
1422
+ var fs = filterStores[f];
1423
+
1424
+ var output = state[startOffset + bufferIndex];
1425
+ if (is_nan_or_inf(output)) { output = 0.0f; }
1426
+
1427
+ fs = output * (1.0f - effDamping) + fs * effDamping;
1428
+ var nextVal = inputVal + fs * effRoomSize;
1429
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1430
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1431
+
1432
+ bufIndices[f] = (bufferIndex + 1u) % size;
1433
+ filterStores[f] = fs;
1434
+ combSumR = combSumR + output;
1435
+ }
1436
+ var revOutR = combSumR;
1437
+ for (var j = 0u; j < 4u; j = j + 1u) {
1438
+ let f = 20u + j;
1439
+ let size = sizes[f];
1440
+ let startOffset = startOffsets[f];
1441
+ let bufferIndex = bufIndices[f];
1442
+
1443
+ var bufOut = state[startOffset + bufferIndex];
1444
+ if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }
1445
+
1446
+ var nextVal = revOutR + bufOut * 0.5f;
1447
+ if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }
1448
+ state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);
1449
+
1450
+ let output = bufOut - revOutR;
1451
+ bufIndices[f] = (bufferIndex + 1u) % size;
1452
+ revOutR = output;
1453
+ }
1454
+
1455
+ var outL = inputL * effDry + revOutL * wet1 + revOutR * wet2;
1456
+ var outR = inputR * effDry + revOutR * wet1 + revOutL * wet2;
1457
+ if (is_nan_or_inf(outL)) { outL = 0.0f; }
1458
+ if (is_nan_or_inf(outR)) { outR = 0.0f; }
1459
+
1460
+ if (outL > 1.0f) { outL = 1.0f; } else if (outL < -1.0f) { outL = -1.0f; }
1461
+ else if (outL > 0.9f) { outL = 0.9f + 0.1f * tanh((outL - 0.9f) / 0.1f); }
1462
+ else if (outL < -0.9f) { outL = -0.9f + 0.1f * tanh((outL + 0.9f) / 0.1f); }
1463
+
1464
+ if (outR > 1.0f) { outR = 1.0f; } else if (outR < -1.0f) { outR = -1.0f; }
1465
+ else if (outR > 0.9f) { outR = 0.9f + 0.1f * tanh((outR - 0.9f) / 0.1f); }
1466
+ else if (outR < -0.9f) { outR = -0.9f + 0.1f * tanh((outR + 0.9f) / 0.1f); }
1467
+
1468
+ outputChannels[index] = outL;
1469
+ outputChannels[numSamples + index] = outR;
1470
+ }
1471
+ }
1472
+
1473
+ for (var f = 0u; f < 24u; f = f + 1u) {
1474
+ let metaOffset = f * 4u;
1475
+ state[metaOffset + 2u] = f32(bufIndices[f]);
1476
+ state[metaOffset + 3u] = filterStores[f];
1477
+ }
1478
+ }
1479
+ `;
1480
+ const leftCombLengths = [
1481
+ 1116,
1482
+ 1188,
1483
+ 1277,
1484
+ 1356,
1485
+ 1422,
1486
+ 1491,
1487
+ 1557,
1488
+ 1617
1489
+ ];
1490
+ const leftAllpassLengths = [
1491
+ 556,
1492
+ 441,
1493
+ 341,
1494
+ 225
1495
+ ];
1496
+ const reverbAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {
1497
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Reverb.");
1498
+ const op = virtualMedia.operation || {};
1499
+ const inputs = op.inputs || {};
1500
+ const numChannels = channels.length;
1501
+ if (numChannels === 0) return;
1502
+ const numSamples = channels[0].length;
1503
+ if (numSamples === 0) return;
1504
+ const roomSizeInternal = Math.max(0, Math.min(typeof op.roomSize === "number" ? op.roomSize : .5, .98));
1505
+ const dampingInternal = Math.max(0, Math.min(typeof op.damping === "number" ? op.damping : .5, 1));
1506
+ const wetInternal = Math.max(0, Math.min(typeof op.wet === "number" ? op.wet : .3, 1));
1507
+ const dryInternal = Math.max(0, Math.min(typeof op.dry === "number" ? op.dry : 1, 1));
1508
+ const widthInternal = Math.max(0, Math.min(typeof op.width === "number" ? op.width : 1, 1));
1509
+ const isHandleConnected = (handleIdKey) => {
1510
+ const handleId = op[handleIdKey];
1511
+ if (typeof handleId !== "string" || !handleId) return false;
1512
+ const input = inputs[handleId];
1513
+ return Boolean(input?.connectionValid && input.outputItem && (input.outputItem.type === "Signal" || input.outputItem.type === "Number"));
1514
+ };
1515
+ const hasRoomSizeSig = isHandleConnected("roomSizeHandleId") ? 1 : 0;
1516
+ const hasDampingSig = isHandleConnected("dampingHandleId") ? 1 : 0;
1517
+ const hasWetSig = isHandleConnected("wetHandleId") ? 1 : 0;
1518
+ const hasDrySig = isHandleConnected("dryHandleId") ? 1 : 0;
1519
+ const hasWidthSig = isHandleConnected("widthHandleId") ? 1 : 0;
1520
+ const scale = sampleRate / 44100;
1521
+ const nodeId = op.id || "audio-reverb";
1522
+ const frame = ctx.frame ?? 0;
1523
+ const fps = ctx.fps ?? 24;
1524
+ const sizesL = [];
1525
+ const sizesR = [];
1526
+ leftCombLengths.forEach((len) => {
1527
+ sizesL.push(Math.round(len * scale));
1528
+ });
1529
+ leftCombLengths.forEach((len) => {
1530
+ sizesR.push(Math.round((len + 23) * scale));
1531
+ });
1532
+ leftAllpassLengths.forEach((len) => {
1533
+ sizesL.push(Math.round(len * scale));
1534
+ });
1535
+ leftAllpassLengths.forEach((len) => {
1536
+ sizesR.push(Math.round((len + 23) * scale));
1537
+ });
1538
+ const totalMetadataFloats = 96;
1539
+ let totalDelaySamples = 0;
1540
+ const filterSizes = [...sizesL, ...sizesR];
1541
+ for (const size of filterSizes) totalDelaySamples += size;
1542
+ const stateFloatCount = totalMetadataFloats + totalDelaySamples;
1543
+ const metaData = new Float32Array(totalMetadataFloats);
1544
+ let currentOffset = totalMetadataFloats;
1545
+ for (let f = 0; f < 24; f++) {
1546
+ const size = filterSizes[f];
1547
+ metaData[f * 4] = size;
1548
+ metaData[f * 4 + 1] = currentOffset;
1549
+ metaData[f * 4 + 2] = 0;
1550
+ metaData[f * 4 + 3] = 0;
1551
+ currentOffset += size;
1552
+ }
1553
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, REVERB_SHADER_TEMPLATE, () => [
1554
+ sampleRate,
1555
+ roomSizeInternal,
1556
+ dampingInternal,
1557
+ wetInternal,
1558
+ dryInternal,
1559
+ widthInternal,
1560
+ hasRoomSizeSig,
1561
+ hasDampingSig,
1562
+ hasWetSig,
1563
+ hasDrySig,
1564
+ hasWidthSig,
1565
+ 0,
1566
+ frame,
1567
+ numSamples,
1568
+ numChannels,
1569
+ 0
1570
+ ], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, metaData, void 0, PARAM_ORDER);
1571
+ };
1572
+ var renderers_default$1 = defineRenderer({ audioProcessor: reverbAudioProcessor });
1573
+
1574
+ //#endregion
1575
+ //#region ../../nodes/node-stereo-panning/dist/renderers-Csm8iHs7.mjs
1576
+ const PANNING_SHADER_TEMPLATE = () => `
1577
+ struct Uniforms {
1578
+ sampleRate : f32,
1579
+ pan : f32,
1580
+ baseTime : f32,
1581
+ frame : f32,
1582
+ numSamples : f32,
1583
+ numChannels : f32,
1584
+ pad1 : f32,
1585
+ pad2 : f32,
1586
+ };
1587
+
1588
+ @group(0) @binding(0) var<uniform> u : Uniforms;
1589
+ @group(0) @binding(1) var<storage, read> inputChannels : array<f32>;
1590
+ @group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;
1591
+ @group(0) @binding(3) var<storage, read_write> state : array<f32>;
1592
+
1593
+ @compute @workgroup_size(1)
1594
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
1595
+ if (gid.x != 0u) { return; }
1596
+
1597
+ let numSamples = u32(u.numSamples);
1598
+ let numChannels = u32(u.numChannels);
1599
+
1600
+ let clampedPan = max(-1.0f, min(1.0f, u.pan));
1601
+ var leftGain = 1.0f;
1602
+ var rightGain = 1.0f;
1603
+
1604
+ if (clampedPan < 0.0f) {
1605
+ rightGain = 1.0f + clampedPan;
1606
+ } else if (clampedPan > 0.0f) {
1607
+ leftGain = 1.0f - clampedPan;
1608
+ }
1609
+
1610
+ for (var index = 0u; index < numSamples; index = index + 1u) {
1611
+ if (numChannels > 1u) {
1612
+ outputChannels[index] = inputChannels[index] * leftGain;
1613
+ outputChannels[numSamples + index] = inputChannels[numSamples + index] * rightGain;
1614
+ } else {
1615
+ outputChannels[index] = inputChannels[index] * leftGain;
1616
+ }
1617
+ }
1618
+
1619
+ // Dummy reference to prevent state binding from being optimized out
1620
+ state[0] = state[0];
1621
+ }
1622
+ `;
1623
+ const stereoPanningAudioProcessor = async (channels, sampleRate, node, ctx) => {
1624
+ if (!ctx?.device) throw new Error("GPUDevice is required for WebGPU Stereo Panning.");
1625
+ const pan = node.operation?.pan ?? 0;
1626
+ const clampedPan = Math.max(-1, Math.min(1, pan));
1627
+ if (channels.length === 0) return;
1628
+ const numSamples = channels[0].length;
1629
+ if (numSamples === 0) return;
1630
+ if (channels.length === 1) {
1631
+ const rightChannel = new Float32Array(numSamples);
1632
+ rightChannel.set(channels[0]);
1633
+ channels.push(rightChannel);
1634
+ }
1635
+ const numChannels = channels.length;
1636
+ const nodeId = node.operation?.id || "stereo-panning";
1637
+ const frame = ctx.frame ?? 0;
1638
+ const fps = ctx.fps ?? 24;
1639
+ await WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, node, frame, fps, PANNING_SHADER_TEMPLATE, () => [
1640
+ sampleRate,
1641
+ clampedPan,
1642
+ 0,
1643
+ frame,
1644
+ numSamples,
1645
+ numChannels,
1646
+ 0,
1647
+ 0
1648
+ ], 8, 1, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs);
1649
+ };
1650
+ var renderers_default = defineRenderer({ audioProcessor: stereoPanningAudioProcessor });
1651
+
1652
+ //#endregion
1653
+ //#region src/audio/synth/sfx.ts
1654
+ const DEFAULT_DURATION_SEC = {
1655
+ whoosh: .7,
1656
+ riser: 2,
1657
+ impact: 1.8,
1658
+ downshifter: 1.2,
1659
+ glitch: .35
1660
+ };
1661
+ /** Small, fast, seedable PRNG returning values in [-1, 1). */
1662
+ function createNoise(seed) {
1663
+ let state = (seed ^ 2654435769) >>> 0;
1664
+ return () => {
1665
+ state = state + 1831565813 >>> 0;
1666
+ let t = state;
1667
+ t = Math.imul(t ^ t >>> 15, t | 1);
1668
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1669
+ return ((t ^ t >>> 14) >>> 0) / 4294967296 * 2 - 1;
1670
+ };
1671
+ }
1672
+ /** Chamberlin state-variable filter; returns the band-pass output of one sample. */
1673
+ var BandPass = class {
1674
+ low = 0;
1675
+ band = 0;
1676
+ process(input, freq, q, sampleRate) {
1677
+ const f = 2 * Math.sin(Math.PI * Math.min(freq, sampleRate * .45) / sampleRate);
1678
+ this.low += f * this.band;
1679
+ const high = input - this.low - q * this.band;
1680
+ this.band += f * high;
1681
+ return this.band;
1682
+ }
1683
+ };
1684
+ /** Exponential sweep from `a` to `b` as `x` moves 0 → 1. */
1685
+ const expSweep = (a, b, x) => a * (b / a) ** x;
1686
+ /** Fast rise, longer fall: a bell centred at `peak` in 0..1. */
1687
+ const bell = (x, peak) => x < peak ? Math.sin(x / peak * (Math.PI / 2)) ** 2 : Math.cos((x - peak) / (1 - peak) * (Math.PI / 2)) ** 2;
1688
+ function whoosh(n, sr, noise, reverse) {
1689
+ const left = new Float32Array(n);
1690
+ const right = new Float32Array(n);
1691
+ const filter = new BandPass();
1692
+ for (let i = 0; i < n; i++) {
1693
+ const x = i / n;
1694
+ const sweep = reverse ? 1 - x : x;
1695
+ const env = bell(x, reverse ? .35 : .6);
1696
+ const s = filter.process(noise(), expSweep(250, 7e3, sweep), .35, sr) * env * .9;
1697
+ const pan = (reverse ? 1 - x : x) * 2 - 1;
1698
+ left[i] = s * Math.sqrt((1 - pan) / 2) * 1.4;
1699
+ right[i] = s * Math.sqrt((1 + pan) / 2) * 1.4;
1700
+ }
1701
+ return [left, right];
1702
+ }
1703
+ function riser(n, sr, noise, reverse) {
1704
+ const left = new Float32Array(n);
1705
+ const right = new Float32Array(n);
1706
+ const air = new BandPass();
1707
+ let phase = 0;
1708
+ for (let i = 0; i < n; i++) {
1709
+ const x = i / n;
1710
+ const sweep = reverse ? 1 - x : x;
1711
+ const env = reverse ? (1 - x) ** 1.5 : x ** 2.2;
1712
+ const freq = expSweep(110, 880, sweep);
1713
+ phase += 2 * Math.PI * freq / sr;
1714
+ const s = ((Math.sin(phase) + .5 * Math.sin(phase * 2) + .25 * Math.sin(phase * 3)) * .3 + air.process(noise(), expSweep(300, 9e3, sweep), .5, sr) * 1.2) * env * .6;
1715
+ left[i] = s;
1716
+ right[i] = s;
1717
+ }
1718
+ return [left, right];
1719
+ }
1720
+ function impact(n, sr, noise) {
1721
+ const left = new Float32Array(n);
1722
+ const right = new Float32Array(n);
1723
+ const burst = new BandPass();
1724
+ let phase = 0;
1725
+ for (let i = 0; i < n; i++) {
1726
+ const t = i / sr;
1727
+ const freq = 38 + 62 * Math.exp(-t * 14);
1728
+ phase += 2 * Math.PI * freq / sr;
1729
+ const sub = Math.sin(phase) * Math.exp(-t * 2.6);
1730
+ const body = burst.process(noise(), 900 * Math.exp(-t * 6) + 120, .6, sr) * Math.exp(-t * 9) * 1.6;
1731
+ const click = i < 96 ? noise() * (1 - i / 96) * .5 : 0;
1732
+ const s = (sub * 1.1 + body + click) * .85;
1733
+ const tail = noise() * Math.exp(-t * 5) * .08;
1734
+ left[i] = s + tail;
1735
+ right[i] = s - tail;
1736
+ }
1737
+ return [left, right];
1738
+ }
1739
+ function glitch(n, sr, noise) {
1740
+ const left = new Float32Array(n);
1741
+ const right = new Float32Array(n);
1742
+ const slice = Math.max(1, Math.round(sr * .022));
1743
+ let held = 0;
1744
+ let gate = 1;
1745
+ let crush = 1;
1746
+ for (let i = 0; i < n; i++) {
1747
+ if (i % slice === 0) {
1748
+ gate = noise() > -.2 ? 1 : 0;
1749
+ crush = 2 + Math.floor((noise() + 1) * 6);
1750
+ }
1751
+ if (i % crush === 0) held = noise() > 0 ? 1 : -1;
1752
+ const env = 1 - i / n;
1753
+ const s = held * gate * env * .45;
1754
+ left[i] = s;
1755
+ right[i] = gate ? -s * .6 : 0;
1756
+ }
1757
+ return [left, right];
1758
+ }
1759
+ /**
1760
+ * Renders one trigger to stereo PCM positioned at `atBar` on the score timeline.
1761
+ * `atBar` is where the effect *starts*; a riser meant to land on a cut starts
1762
+ * `durationSec / secondsPerBar` bars before it.
1763
+ */
1764
+ function renderSfx(trigger, ctx) {
1765
+ const durationSec = trigger.durationSec ?? DEFAULT_DURATION_SEC[trigger.type];
1766
+ const sr = ctx.sampleRate;
1767
+ const n = Math.max(1, Math.round(durationSec * sr));
1768
+ const noise = createNoise(ctx.seed + n);
1769
+ let channels;
1770
+ switch (trigger.type) {
1771
+ case "whoosh":
1772
+ channels = whoosh(n, sr, noise, false);
1773
+ break;
1774
+ case "riser":
1775
+ channels = riser(n, sr, noise, false);
1776
+ break;
1777
+ case "downshifter":
1778
+ channels = riser(n, sr, noise, true);
1779
+ break;
1780
+ case "impact":
1781
+ channels = impact(n, sr, noise);
1782
+ break;
1783
+ case "glitch":
1784
+ channels = glitch(n, sr, noise);
1785
+ break;
1786
+ }
1787
+ const gain = trigger.volume ?? .8;
1788
+ for (const channel of channels) for (let i = 0; i < n; i++) channel[i] = Math.tanh((channel[i] ?? 0) * gain);
1789
+ return {
1790
+ startSample: Math.max(0, Math.round(trigger.atBar * ctx.secondsPerBar * sr)),
1791
+ channels
1792
+ };
1793
+ }
1794
+ /** Adds rendered effects into `target` in place, clipping to its length. */
1795
+ function mixSfxInto(target, effects) {
1796
+ for (const fx of effects) for (let c = 0; c < 2; c++) {
1797
+ const dst = target[c];
1798
+ const src = fx.channels[c];
1799
+ if (!dst || !src) continue;
1800
+ const limit = Math.min(src.length, dst.length - fx.startSample);
1801
+ for (let i = 0; i < limit; i++) {
1802
+ const val = dst[fx.startSample + i];
1803
+ const s = src[i];
1804
+ if (val !== void 0 && s !== void 0) dst[fx.startSample + i] = val + s;
1805
+ }
1806
+ }
1807
+ }
1808
+ /**
1809
+ * Soft-knee limiter for sums that may exceed full scale (impacts stacked on a
1810
+ * loud mix): transparent below the knee, asymptotically bounded by 1.0 above it.
1811
+ */
1812
+ function softLimit(target, knee = .7, ceiling = .97) {
1813
+ const headroom = ceiling - knee;
1814
+ for (const channel of target) for (let i = 0; i < channel.length; i++) {
1815
+ const x = channel[i] ?? 0;
1816
+ const a = Math.abs(x);
1817
+ if (a > knee) channel[i] = Math.sign(x) * (knee + headroom * Math.tanh((a - knee) / headroom));
1818
+ }
1819
+ }
1820
+
1821
+ //#endregion
1822
+ //#region src/audio/synth/wav-encoder.ts
1823
+ /**
1824
+ * Encodes stereo Float32Array PCM channels (-1.0 to +1.0) into a standard 16-bit RIFF/WAVE Buffer.
1825
+ */
1826
+ function encodeStereoWav(channels, options = {}) {
1827
+ const sampleRate = options.sampleRate ?? 48e3;
1828
+ const numChannels = 2;
1829
+ const left = channels[0];
1830
+ const right = channels[1];
1831
+ const numSamples = Math.min(left.length, right.length);
1832
+ const blockAlign = numChannels * 2;
1833
+ const byteRate = sampleRate * blockAlign;
1834
+ const dataSize = numSamples * blockAlign;
1835
+ const totalSize = 44 + dataSize;
1836
+ const buffer = Buffer.alloc(totalSize);
1837
+ buffer.write("RIFF", 0, "ascii");
1838
+ buffer.writeUInt32LE(36 + dataSize, 4);
1839
+ buffer.write("WAVE", 8, "ascii");
1840
+ buffer.write("fmt ", 12, "ascii");
1841
+ buffer.writeUInt32LE(16, 16);
1842
+ buffer.writeUInt16LE(1, 20);
1843
+ buffer.writeUInt16LE(numChannels, 22);
1844
+ buffer.writeUInt32LE(sampleRate, 24);
1845
+ buffer.writeUInt32LE(byteRate, 28);
1846
+ buffer.writeUInt16LE(blockAlign, 32);
1847
+ buffer.writeUInt16LE(16, 34);
1848
+ buffer.write("data", 36, "ascii");
1849
+ buffer.writeUInt32LE(dataSize, 40);
1850
+ let offset = 44;
1851
+ for (let i = 0; i < numSamples; i++) {
1852
+ const sl = Math.max(-1, Math.min(1, left[i] ?? 0));
1853
+ const intL = sl < 0 ? sl * 32768 : sl * 32767;
1854
+ buffer.writeInt16LE(Math.round(intL), offset);
1855
+ offset += 2;
1856
+ const sr = Math.max(-1, Math.min(1, right[i] ?? 0));
1857
+ const intR = sr < 0 ? sr * 32768 : sr * 32767;
1858
+ buffer.writeInt16LE(Math.round(intR), offset);
1859
+ offset += 2;
1860
+ }
1861
+ return buffer;
1862
+ }
1863
+
1864
+ //#endregion
1865
+ export { renderers_default as a, renderers_default$3 as c, renderers_default$6 as d, softLimit as i, renderers_default$4 as l, mixSfxInto as n, renderers_default$1 as o, renderSfx as r, renderers_default$2 as s, encodeStereoWav as t, renderers_default$5 as u };
1866
+ //# sourceMappingURL=audio-C4kiPbrV.mjs.map