cacophony 0.25.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +47 -16
  2. package/dist/bus.d.ts +5 -4
  3. package/dist/cacophony.d.ts +52 -155
  4. package/dist/effects.d.ts +121 -117
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.mjs +1060 -995
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/mediaStream.d.ts +25 -1
  11. package/dist/worklets.d.ts +58 -0
  12. package/docs/assets/navigation.js +1 -1
  13. package/docs/assets/search.js +1 -1
  14. package/docs/classes/AudioCache.html +7 -7
  15. package/docs/classes/BiquadEffect.html +2 -2
  16. package/docs/classes/Bus.html +32 -31
  17. package/docs/classes/Cacophony.html +86 -165
  18. package/docs/classes/DynamicsEffect.html +8 -5
  19. package/docs/classes/FdnReverbEffect.html +8 -5
  20. package/docs/classes/FoaDecoder.html +15 -16
  21. package/docs/classes/FoaDecoderEffect.html +8 -0
  22. package/docs/classes/Group.html +13 -13
  23. package/docs/classes/ImpulseResponseEffect.html +10 -0
  24. package/docs/classes/KWeightingFilter.html +3 -3
  25. package/docs/classes/LoudnessMeter.html +11 -11
  26. package/docs/classes/MediaStreamPlayback.html +24 -18
  27. package/docs/classes/MediaStreamSound.html +20 -19
  28. package/docs/classes/MicrophonePlayback.html +3 -3
  29. package/docs/classes/ModulatedDelayEffect.html +6 -5
  30. package/docs/classes/PhaserEffect.html +8 -5
  31. package/docs/classes/Playback.html +36 -36
  32. package/docs/classes/ReverbEffect.html +8 -5
  33. package/docs/classes/ShareEffect.html +2 -2
  34. package/docs/classes/Sound.html +33 -33
  35. package/docs/classes/Synth.html +21 -21
  36. package/docs/classes/SynthGroup.html +2 -2
  37. package/docs/classes/TremoloEffect.html +6 -5
  38. package/docs/classes/TruePeakDetector.html +7 -7
  39. package/docs/classes/WaveshaperEffect.html +6 -5
  40. package/docs/functions/encodeMonoToFoaSN3D.html +1 -1
  41. package/docs/functions/integratedLoudness.html +1 -1
  42. package/docs/functions/integratedUngatedLoudness.html +1 -1
  43. package/docs/functions/loudnessRange.html +1 -1
  44. package/docs/functions/timeStretch.html +1 -1
  45. package/docs/functions/timeStretchChannels.html +1 -1
  46. package/docs/functions/truePeakDb.html +1 -1
  47. package/docs/hierarchy.html +1 -1
  48. package/docs/index.html +17 -4
  49. package/docs/interfaces/AudioBuffer.html +2 -2
  50. package/docs/interfaces/AudioBufferSourceNode.html +4 -4
  51. package/docs/interfaces/AudioEventCallbacks.html +1 -1
  52. package/docs/interfaces/AudioListener.html +2 -2
  53. package/docs/interfaces/AudioNode.html +3 -3
  54. package/docs/interfaces/AudioParam.html +2 -2
  55. package/docs/interfaces/AudioWorklet.html +2 -2
  56. package/docs/interfaces/AudioWorkletNode.html +4 -4
  57. package/docs/interfaces/BaseContext.html +2 -2
  58. package/docs/interfaces/BaseSound.html +2 -2
  59. package/docs/interfaces/BiquadCoefficients.html +2 -2
  60. package/docs/interfaces/BiquadFilterNode.html +4 -4
  61. package/docs/interfaces/BuiltEffectGraph.html +10 -0
  62. package/docs/interfaces/CacheErrorEvent.html +2 -2
  63. package/docs/interfaces/CacheHitEvent.html +2 -2
  64. package/docs/interfaces/CacheMissEvent.html +2 -2
  65. package/docs/interfaces/CacophonyEffect.html +2 -2
  66. package/docs/interfaces/ChannelMergerNode.html +3 -3
  67. package/docs/interfaces/ChannelSplitterNode.html +3 -3
  68. package/docs/interfaces/ConvolverNode.html +26 -0
  69. package/docs/interfaces/DynamicsOptions.html +7 -7
  70. package/docs/interfaces/FadeStartEvent.html +2 -2
  71. package/docs/interfaces/FdnReverbOptions.html +6 -6
  72. package/docs/interfaces/FoaDecoderOptions.html +2 -2
  73. package/docs/interfaces/GainNode.html +3 -3
  74. package/docs/interfaces/GlobalPlaybackEvent.html +2 -2
  75. package/docs/interfaces/ImpulseResponseOptions.html +9 -0
  76. package/docs/interfaces/LoadingCompleteEvent.html +2 -2
  77. package/docs/interfaces/LoadingErrorEvent.html +2 -2
  78. package/docs/interfaces/LoadingProgressEvent.html +2 -2
  79. package/docs/interfaces/LoadingStartEvent.html +2 -2
  80. package/docs/interfaces/LoudnessChannelInput.html +2 -2
  81. package/docs/interfaces/LoudnessReading.html +5 -5
  82. package/docs/interfaces/MediaElementSourceNode.html +3 -3
  83. package/docs/interfaces/MediaStreamAudioSourceNode.html +3 -3
  84. package/docs/interfaces/MediaStreamSoundOptions.html +13 -2
  85. package/docs/interfaces/ModulatedDelayOptions.html +8 -8
  86. package/docs/interfaces/OfflineOptions.html +2 -2
  87. package/docs/interfaces/OscillatorNode.html +4 -4
  88. package/docs/interfaces/PannerNode.html +3 -3
  89. package/docs/interfaces/PhaserOptions.html +7 -7
  90. package/docs/interfaces/PlayOptions.html +2 -2
  91. package/docs/interfaces/PlaybackErrorEvent.html +2 -2
  92. package/docs/interfaces/ReverbOptions.html +13 -13
  93. package/docs/interfaces/RuntimeOptions.html +2 -2
  94. package/docs/interfaces/SoundCleanupHoldings.html +2 -2
  95. package/docs/interfaces/SoundErrorEvent.html +2 -2
  96. package/docs/interfaces/StereoPannerNode.html +3 -3
  97. package/docs/interfaces/TimeStretchOptions.html +5 -5
  98. package/docs/interfaces/TremoloOptions.html +5 -5
  99. package/docs/interfaces/WaveshaperOptions.html +5 -5
  100. package/docs/modules.html +7 -0
  101. package/docs/types/BaseAudioEvents.html +1 -1
  102. package/docs/types/BuiltEffect.html +1 -0
  103. package/docs/types/BusConnectionTarget.html +1 -1
  104. package/docs/types/CacheEventCallback.html +1 -1
  105. package/docs/types/CacophonyEvents.html +1 -1
  106. package/docs/types/ErrorEventCallback.html +1 -1
  107. package/docs/types/FadeType.html +1 -1
  108. package/docs/types/HrtfPannerOptions.html +1 -1
  109. package/docs/types/ImpulseResponseSource.html +1 -0
  110. package/docs/types/LoadingEventCallback.html +1 -1
  111. package/docs/types/LoopCount.html +1 -1
  112. package/docs/types/LoudnessChannel.html +1 -1
  113. package/docs/types/Orientation.html +1 -1
  114. package/docs/types/PanCloneOverrides.html +1 -1
  115. package/docs/types/PanType.html +1 -1
  116. package/docs/types/PlaybackEvents.html +1 -1
  117. package/docs/types/Position.html +1 -1
  118. package/docs/types/SoundEvents.html +1 -1
  119. package/docs/types/SoundType.html +1 -1
  120. package/docs/types/SourceNode.html +1 -1
  121. package/docs/types/SynthEvents.html +1 -1
  122. package/docs/types/ThreeDOptions.html +1 -1
  123. package/docs/variables/CHANNEL_WEIGHTS.html +1 -1
  124. package/docs/variables/K_WEIGHTING_STAGE1_48K.html +1 -1
  125. package/docs/variables/K_WEIGHTING_STAGE2_48K.html +1 -1
  126. package/package.json +2 -2
package/README.md CHANGED
@@ -350,9 +350,9 @@ API documented below. The Bus class supersedes the older user-built
350
350
  A `Bus` is a named summing node with its own filter chain and per-edge
351
351
  gain on outgoing connections. Sounds and synths route to buses via
352
352
  `routeTo`; buses can carry rich effects (not just BiquadFilter) by
353
- adding a `CacophonyEffect` to their filter chain. The built-in
354
- `cacophony.createReverb()` returns a DattorroReverb effect ready to drop
355
- into a bus.
353
+ adding a `CacophonyEffect` to their filter chain. The built-in
354
+ `cacophony.createReverb()` and `cacophony.createImpulseResponse()` effects
355
+ are ready to drop into a bus.
356
356
 
357
357
  ```typescript
358
358
  // 1. Create a named bus
@@ -393,29 +393,60 @@ the same node as `cacophony.globalGainNode`, so the existing
393
393
  transparently. Routing a sound to `cacophony.master` (or never calling
394
394
  `routeTo`) sends it through the master path.
395
395
 
396
- ### Bus-to-bus routing with per-edge gain
396
+ ### Bus-to-bus routing with per-edge gain
397
397
 
398
398
  ```typescript
399
399
  const groupBus = cacophony.createBus('group');
400
400
  const sendBus = cacophony.createBus('aux');
401
401
  groupBus.connect(sendBus, 0.2); // 20% send: groupBus.output → sendGain(0.2) → sendBus.input
402
- groupBus.disconnect(sendBus); // tears down the sendGain too
403
- ```
404
-
405
- ### Adding custom effects to a bus
406
-
407
- Bus filter chains accept Cacophony-built BiquadFilters and any
408
- `CacophonyEffect`. Raw third-party AudioNodes are rejected unless you
409
- wrap them explicitly with `cacophony.shareEffect(node)` this surfaces
410
- the shared-state intent (the same node will run on every bus that adds it).
402
+ groupBus.disconnect(sendBus); // tears down the sendGain too
403
+ ```
404
+
405
+ ### Native impulse responses
406
+
407
+ `createImpulseResponse()` builds a native `ConvolverNode` effect from an
408
+ `AudioBuffer` or URL. URL-backed IRs are fetched and decoded once per audio
409
+ context and URL; failed loads are evicted so a later call can retry. Convolver
410
+ normalization defaults to `false`, preserving measured IR gain.
411
+
412
+ ```typescript
413
+ // Wet-only send bus: returns a single ConvolverNode internally.
414
+ const chamber = cacophony.createBus('chamber');
415
+ await chamber.addFilter(cacophony.createImpulseResponse('/irs/chamber.wav'));
416
+ vocals.routeTo(chamber, 0.25);
417
+
418
+ // Inline dry/wet graph: exposes dry/wet AudioParams for bus automation.
419
+ const room = cacophony.createImpulseResponse('/irs/room.wav', {
420
+ dry: 0.7,
421
+ wet: 0.3,
422
+ normalize: false,
423
+ });
424
+ const handle = await bus.addFilter(room);
425
+ bus.rampFilterParam(handle, 'wet', 0.6, { duration: 500 });
426
+ ```
427
+
428
+ ### Adding custom effects to a bus
429
+
430
+ Bus filter chains accept Cacophony-built BiquadFilters and any
431
+ `CacophonyEffect`. Raw third-party AudioNodes are rejected unless you
432
+ wrap them explicitly with `cacophony.shareEffect(node)` — this surfaces
433
+ the shared-state intent (the same node will run on every bus that adds it).
434
+ An effect can build either a single `AudioNode` or a `BuiltEffectGraph` with
435
+ separate `input`/`output` endpoints for multi-node processors.
411
436
 
412
437
  ```typescript
413
438
  const eq = cacophony.createBiquadFilter({ type: 'highshelf', frequency: 4000, gain: 3 });
414
439
  await bus.addFilter(eq);
415
440
 
416
- const sharedWorklet = new AudioWorkletNode(cacophony.context, 'my-fx');
417
- await bus.addFilter(cacophony.shareEffect(sharedWorklet));
418
- ```
441
+ const sharedWorklet = new AudioWorkletNode(cacophony.context, 'my-fx');
442
+ await bus.addFilter(cacophony.shareEffect(sharedWorklet));
443
+ ```
444
+
445
+ FOA binaural decoding is available in both forms. Use
446
+ `createFoaDecoder()` when you want explicit custom wiring through
447
+ `decoder.input` and `decoder.output`; use `createFoaDecoderEffect()` only on a
448
+ dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only filter
449
+ that converts that bus to stereo binaural output.
419
450
 
420
451
  ### Cleaning up
421
452
 
package/dist/bus.d.ts CHANGED
@@ -39,10 +39,10 @@ export declare class Bus {
39
39
  */
40
40
  readonly output: GainNode;
41
41
  private readonly _context;
42
- private readonly _filterNodes;
42
+ private readonly _filterEntries;
43
43
  /**
44
44
  * Filter nodes currently bypassed (skipped in the audible chain). A bypassed
45
- * node stays in {@link _filterNodes} — so {@link filters} order, identity, and
45
+ * node stays in {@link _filterEntries} — so {@link filters} order, identity, and
46
46
  * its live AudioParams are preserved — but {@link _desiredFilterChainEdges}
47
47
  * builds the series chain over the NON-bypassed filters only, wiring the
48
48
  * signal around it. Membership is by node identity.
@@ -273,15 +273,16 @@ export declare class Bus {
273
273
  private _refreshFilters;
274
274
  /**
275
275
  * Compute the desired ordered chain edge list from the current
276
- * `_filterNodes`, skipping any node in {@link _bypassedFilters}: the series
276
+ * `_filterEntries`, skipping any node in {@link _bypassedFilters}: the series
277
277
  * chain is built over the NON-bypassed filters only. With no active (non-
278
278
  * bypassed) filters — whether the bus has no filters at all or every filter is
279
279
  * bypassed — the desired list is `[[input, output]]` (the direct edge);
280
280
  * otherwise `[[input, a1], [a1, a2], ..., [aN, output]]` over the active
281
- * filters `a1..aN`. Bypassed nodes stay in {@link _filterNodes} (and thus in
281
+ * filters `a1..aN`. Bypassed nodes stay in {@link _filterEntries} (and thus in
282
282
  * {@link filters}) but receive no inbound/outbound chain edge.
283
283
  */
284
284
  private _desiredFilterChainEdges;
285
+ private _normalizeBuiltEffect;
285
286
  private _connectFilterChainEdge;
286
287
  private _disconnectFilterChainEdges;
287
288
  private _throwIfDestroyed;
@@ -1,7 +1,7 @@
1
1
  import { Bus } from './bus';
2
2
  import { ICache } from './cache';
3
3
  import { AudioBuffer, AudioListener, AudioNode, AudioWorkletNode, BaseContext, BiquadFilterNode, ChannelMergerNode, ChannelSplitterNode, GainNode, PannerNode } from './context';
4
- import { CacophonyEffect, DynamicsEffect, DynamicsOptions, FdnReverbEffect, FdnReverbOptions, FoaDecoder, FoaDecoderOptions, ModulatedDelayEffect, ModulatedDelayOptions, PhaserEffect, PhaserOptions, ReverbEffect, ReverbOptions, TremoloEffect, TremoloOptions, WaveshaperEffect, WaveshaperOptions } from './effects';
4
+ import { CacophonyEffect, DynamicsEffect, DynamicsOptions, FdnReverbEffect, FdnReverbOptions, FoaDecoder, FoaDecoderEffect, FoaDecoderOptions, ImpulseResponseEffect, ImpulseResponseOptions, ImpulseResponseSource, ModulatedDelayEffect, ModulatedDelayOptions, PhaserEffect, PhaserOptions, ReverbEffect, ReverbOptions, TremoloEffect, TremoloOptions, WaveshaperEffect, WaveshaperOptions } from './effects';
5
5
  import { CacophonyEvents } from './events';
6
6
  import { Group } from './group';
7
7
  import { MediaStreamSound, MediaStreamSoundOptions } from './mediaStream';
@@ -11,6 +11,7 @@ import { ThreeDOptions } from './pannerMixin';
11
11
  import { TimeStretchOptions } from './processors/timestretch-core';
12
12
  import { Sound } from './sound';
13
13
  import { Synth } from './synth';
14
+ import { WorkletModule } from './worklets';
14
15
  export type SoundType = "html" | "streaming" | "buffer" | "oscillator";
15
16
  /**
16
17
  * Represents a 3D position in space.
@@ -148,6 +149,12 @@ export declare class Cacophony {
148
149
  * `loadFoaHrir` calls share a single fetch/decode.
149
150
  */
150
151
  private foaHrirCache;
152
+ /**
153
+ * Per-context, per-URL decoded impulse-response cache. Stores in-flight
154
+ * promises so concurrent effect builds for the same context/URL share the
155
+ * same fetch/decode. Rejected promises are evicted so the caller can retry.
156
+ */
157
+ private impulseResponseCache;
151
158
  private finalizationRegistry;
152
159
  private eventEmitter;
153
160
  private cache;
@@ -253,162 +260,24 @@ export declare class Cacophony {
253
260
  off<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): void;
254
261
  emit<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): void;
255
262
  emitAsync<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): Promise<void>;
256
- loadWorklets(signal?: AbortSignal): Promise<void>;
257
- loadStereoToBFormatWorklet(signal?: AbortSignal): Promise<void>;
258
- /**
259
- * Idempotently registers the `phase-vocoder` AudioWorkletProcessor (peak-based
260
- * pitch-shifter with Identity Phase-Locking, Laroche & Dolson 1999 WASPAA) on
261
- * this context. Safe to call repeatedly — subsequent calls short-circuit via
262
- * the per-context {@link loadedAudioWorklets} set. Cross-context: pass
263
- * `context` so a node built against a bus on a different context loads there.
264
- */
265
- loadPhaseVocoder(signal?: AbortSignal, context?: BaseContext): Promise<void>;
266
- /**
267
- * Constructs a `phase-vocoder` AudioWorkletNode. Caller is expected to have
268
- * loaded the module already (via {@link loadPhaseVocoder} or {@link loadWorklets}).
269
- * Uses the same construct/fallback path as {@link createWorkletNode}. The
270
- * returned node carries a single `pitchFactor` AudioParam (1 = no shift).
271
- */
272
- createPhaseVocoderNode(options?: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
273
- /**
274
- * Idempotently registers the DattorroReverb AudioWorkletProcessor on this
275
- * context. Safe to call repeatedly — subsequent calls short-circuit via
276
- * the {@link loadedAudioWorklets} set used by
277
- * {@link loadAudioWorkletModule}.
278
- *
279
- * @param signal Optional abort signal forwarded to the module load.
280
- * @param context Optional BaseContext to load the worklet on. Defaults to
281
- * the host Cacophony instance's `context`. Supplied so a
282
- * {@link ReverbEffect} added to a bus whose context is NOT this host's
283
- * own (cross-context use) can load the worklet on the right context.
284
- */
285
- loadDattorroReverb(signal?: AbortSignal, context?: BaseContext): Promise<void>;
286
- /**
287
- * Constructs a DattorroReverb AudioWorkletNode. Caller is expected to have
288
- * loaded the module already (via {@link loadDattorroReverb} or by reaching
289
- * here through {@link ReverbEffect.build}). Uses the same construct/fallback
290
- * path as {@link createWorkletNode}.
291
- *
292
- * @param options AudioWorkletNode construction options.
293
- * @param context Optional BaseContext to construct on. Defaults to the
294
- * host Cacophony instance's `context`. See {@link loadDattorroReverb}
295
- * for the cross-context rationale.
296
- */
297
- createDattorroReverbNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
298
- /**
299
- * Idempotently registers the `dynamics` AudioWorkletProcessor (feed-forward
300
- * compressor/limiter/expander/gate, Giannoulis 2012) on this context. Safe to
301
- * call repeatedly — subsequent calls short-circuit via the per-context
302
- * {@link loadedAudioWorklets} set. Cross-context: pass `context` so a
303
- * {@link DynamicsEffect} added to a bus on a different context loads there.
304
- */
305
- loadDynamics(signal?: AbortSignal, context?: BaseContext): Promise<void>;
306
- /**
307
- * Constructs a `dynamics` AudioWorkletNode. Caller is expected to have loaded
308
- * the module already (via {@link loadDynamics} or by reaching here through
309
- * {@link DynamicsEffect.build}). Uses the same construct/fallback path as
310
- * {@link createWorkletNode}.
311
- */
312
- createDynamicsNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
313
- /**
314
- * Idempotently registers the `fdn-reverb` AudioWorkletProcessor (Feedback
315
- * Delay Network reverb — lossless paraunitary Hadamard feedback per Schlecht
316
- * & Habets 2019, per-line absorption T60 per Jot 1991, velvet-noise diffusion
317
- * per Fagerström 2020) on this context. Safe to call repeatedly — subsequent
318
- * calls short-circuit via the per-context {@link loadedAudioWorklets} set.
319
- * Cross-context: pass `context` so an {@link FdnReverbEffect} added to a bus
320
- * on a different context loads there.
321
- */
322
- loadFdnReverb(signal?: AbortSignal, context?: BaseContext): Promise<void>;
323
- /**
324
- * Constructs an `fdn-reverb` AudioWorkletNode. Caller is expected to have
325
- * loaded the module already (via {@link loadFdnReverb} or by reaching here
326
- * through {@link FdnReverbEffect.build}). Uses the same construct/fallback
327
- * path as {@link createWorkletNode}.
328
- */
329
- createFdnReverbNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
330
- /**
331
- * Idempotently registers the `waveshaper` AudioWorkletProcessor (antialiased
332
- * distortion/waveshaper via first-order Antiderivative Antialiasing, Parker,
333
- * Zavalishin & Le Bivic 2016, DAFx-16) on this context. Safe to call
334
- * repeatedly — subsequent calls short-circuit via the per-context
335
- * {@link loadedAudioWorklets} set. Cross-context: pass `context` so a
336
- * {@link WaveshaperEffect} added to a bus on a different context loads there.
337
- */
338
- loadWaveshaper(signal?: AbortSignal, context?: BaseContext): Promise<void>;
339
- /**
340
- * Constructs a `waveshaper` AudioWorkletNode. Caller is expected to have
341
- * loaded the module already (via {@link loadWaveshaper} or by reaching here
342
- * through {@link WaveshaperEffect.build}). Uses the same construct/fallback
343
- * path as {@link createWorkletNode}.
344
- */
345
- createWaveshaperNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
346
263
  /**
347
- * Idempotently registers the `modulated-delay` AudioWorkletProcessor
348
- * Dattorro's unified modulated-delay circuit (JAES 1997, Fig. 36) backing
349
- * delay/chorus/flanger/vibrato/doubling, with Lagrange FIR fractional-delay
350
- * interpolation (Laakso 1996) on this context. Safe to call repeatedly
351
- * subsequent calls short-circuit via the per-context {@link loadedAudioWorklets}
352
- * set. Cross-context: pass `context` so a {@link ModulatedDelayEffect} added to
353
- * a bus on a different context loads there.
264
+ * Eagerly registers every bundled AudioWorklet module on this context. This is
265
+ * OPTIONAL effects load their own worklet lazily in `build`, and the
266
+ * pitch-shift path loads the phase-vocoder on first use. Call this up front to
267
+ * pay the registration cost ahead of time. Idempotent per context (each module
268
+ * short-circuits via the per-context {@link loadedAudioWorklets} set).
354
269
  */
355
- loadModulatedDelay(signal?: AbortSignal, context?: BaseContext): Promise<void>;
356
- /**
357
- * Constructs a `modulated-delay` AudioWorkletNode. Caller is expected to have
358
- * loaded the module already (via {@link loadModulatedDelay} or by reaching here
359
- * through {@link ModulatedDelayEffect.build}). Uses the same construct/fallback
360
- * path as {@link createWorkletNode}.
361
- */
362
- createModulatedDelayNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
363
- /**
364
- * Idempotently registers the `phaser` AudioWorkletProcessor — a classic
365
- * MXR/Univibe-style allpass-cascade phase shifter (Smith STAN-M-21; PASP §8.9:
366
- * a cascade of first-order allpass sections at a common LFO-swept break
367
- * frequency, summed additively with the dry signal to sweep notches) on this
368
- * context. Safe to call repeatedly — subsequent calls short-circuit via the
369
- * per-context {@link loadedAudioWorklets} set. Cross-context: pass `context` so
370
- * a {@link PhaserEffect} added to a bus on a different context loads there.
371
- */
372
- loadPhaser(signal?: AbortSignal, context?: BaseContext): Promise<void>;
373
- /**
374
- * Constructs a `phaser` AudioWorkletNode. Caller is expected to have loaded the
375
- * module already (via {@link loadPhaser} or by reaching here through
376
- * {@link PhaserEffect.build}). Uses the same construct/fallback path as
377
- * {@link createWorkletNode}.
378
- */
379
- createPhaserNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
380
- /**
381
- * Idempotently registers the `tremolo` AudioWorkletProcessor — LFO-driven
382
- * amplitude modulation (a VCA swung by a low-frequency oscillator; standard AM
383
- * theory + Dattorro 1997 p.776 quadrature stereo LFO + Mitcheltree et al.
384
- * DAFx23 LFO framing) on this context. Safe to call repeatedly — subsequent
385
- * calls short-circuit via the per-context {@link loadedAudioWorklets} set.
386
- * Cross-context: pass `context` so a {@link TremoloEffect} added to a bus on a
387
- * different context loads there.
388
- */
389
- loadTremolo(signal?: AbortSignal, context?: BaseContext): Promise<void>;
390
- /**
391
- * Constructs a `tremolo` AudioWorkletNode. Caller is expected to have loaded the
392
- * module already (via {@link loadTremolo} or by reaching here through
393
- * {@link TremoloEffect.build}). Uses the same construct/fallback path as
394
- * {@link createWorkletNode}.
395
- */
396
- createTremoloNode(options: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
397
- /**
398
- * Idempotently registers the `loudness-meter` AudioWorkletProcessor (ITU-R
399
- * BS.1770-5 momentary / short-term / integrated loudness + true-peak) on this
400
- * context. Safe to call repeatedly — subsequent calls short-circuit via the
401
- * per-context {@link loadedAudioWorklets} set. Cross-context: pass `context`
402
- * so a meter tapping a bus on a different context loads there.
403
- */
404
- loadLoudnessMeter(signal?: AbortSignal, context?: BaseContext): Promise<void>;
270
+ loadWorklets(signal?: AbortSignal): Promise<void>;
271
+ loadStereoToBFormatWorklet(signal?: AbortSignal): Promise<void>;
405
272
  /**
406
- * Constructs a `loudness-meter` AudioWorkletNode. Caller is expected to have
407
- * loaded the module already (via {@link loadLoudnessMeter} or {@link createLoudnessMeter}).
408
- * The node is a PASS-THROUGH metering tap (1 input, 1 output) that posts
409
- * momentary/short-term/integrated loudness and true-peak back over its port.
273
+ * The single worklet-effect construction seam ({@link WorkletEffectHost}).
274
+ * Idempotently registers `worklet`'s module on `context` or this host's own
275
+ * context when omitted, honoring the cross-context contract that effects'
276
+ * `build(context)` promises then constructs the AudioWorkletNode with
277
+ * `parameterData`. Every worklet-backed {@link CacophonyEffect} routes through
278
+ * here, as does the phase-vocoder pitch-shift path ({@link Playback.setPitchShift}).
410
279
  */
411
- createLoudnessMeterNode(options?: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
280
+ buildWorkletEffect(worklet: WorkletModule, parameterData: Record<string, number>, context?: BaseContext): Promise<AudioWorkletNode>;
412
281
  /**
413
282
  * Fetches and decodes the bundled order-1 SH-HRIR
414
283
  * (`sh_hrir_order_1.wav`, from Omnitone, Apache-2.0 — see
@@ -426,6 +295,18 @@ export declare class Cacophony {
426
295
  * a different context decodes the HRIR on the right context.
427
296
  */
428
297
  loadFoaHrir(context?: BaseContext): Promise<AudioBuffer>;
298
+ /**
299
+ * Fetches and decodes an impulse response URL on the requested audio context,
300
+ * memoized per context and URL. Buffers decoded by one context are not reused
301
+ * on another context, matching Web Audio's context-bound decode behavior.
302
+ *
303
+ * @param url Impulse-response audio URL.
304
+ * @param context Optional decode context. Defaults to this Cacophony instance.
305
+ * @param signal Optional abort signal for the fetch. Decode itself is not
306
+ * abortable in Web Audio, but an already-aborted signal is honored before
307
+ * decode starts.
308
+ */
309
+ loadImpulseResponseBuffer(url: string, context?: BaseContext, signal?: AbortSignal): Promise<AudioBuffer>;
429
310
  createWorkletNode(name: string, url: string, signal?: AbortSignal, options?: AudioWorkletNodeOptions, context?: BaseContext): Promise<AudioWorkletNode>;
430
311
  createStereoToBFormatNode(signal?: AbortSignal): Promise<AudioWorkletNode>;
431
312
  /**
@@ -439,6 +320,7 @@ export declare class Cacophony {
439
320
  private markWorkletLoadedOn;
440
321
  private loadAudioWorkletModule;
441
322
  private createAbortError;
323
+ private waitForImpulseResponseLoad;
442
324
  private createMediaSound;
443
325
  clearMemoryCache(): void;
444
326
  createOscillator(options: OscillatorOptions, panType?: PanType): Synth;
@@ -529,6 +411,14 @@ export declare class Cacophony {
529
411
  * chain via `bus.addFilter(effect)`.
530
412
  */
531
413
  createReverb(options?: ReverbOptions): ReverbEffect;
414
+ /**
415
+ * Creates a native ConvolverNode impulse-response effect. Pass an AudioBuffer
416
+ * for an already-decoded IR or a URL to fetch/decode through the per-context
417
+ * IR cache. The default is wet-only (`dry: 0`, `wet: 1`) and returns a single
418
+ * ConvolverNode when added to a bus; setting `dry` or non-unity `wet` builds
419
+ * an owned dry/wet endpoint graph exposing `dry` and `wet` automation params.
420
+ */
421
+ createImpulseResponse(source: ImpulseResponseSource, options?: ImpulseResponseOptions): ImpulseResponseEffect;
532
422
  /**
533
423
  * Creates a Feedback Delay Network (FDN) {@link CacophonyEffect} — an
534
424
  * algorithmic reverb with a lossless degree-0 paraunitary Hadamard feedback
@@ -668,8 +558,8 @@ export declare class Cacophony {
668
558
  * Omnitone's WY/ZX 2-stereo-ConvolverNode packing and the bundled order-1
669
559
  * SH-HRIR.
670
560
  *
671
- * It is 4-channel-in / 2-channel-out, so it is NOT a `CacophonyEffect` and is
672
- * NOT added via `bus.addFilter`. Wire it EXPLICITLY using its two endpoints:
561
+ * It is 4-channel-in / 2-channel-out; this method returns the explicit
562
+ * endpoint object for custom graph wiring:
673
563
  * feed FOA into `decoder.input` (4-ch) and route `decoder.output` (2-ch
674
564
  * stereo) downstream:
675
565
  * ```ts
@@ -682,6 +572,13 @@ export declare class Cacophony {
682
572
  * `createStereoToBFormatNode` (the perceptual, approximate stereo-upmix path).
683
573
  */
684
574
  createFoaDecoder(options?: FoaDecoderOptions, context?: BaseContext): Promise<FoaDecoder>;
575
+ /**
576
+ * Creates a bus-filter wrapper around {@link FoaDecoder}. Use this on a
577
+ * dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only
578
+ * filter that converts that bus to stereo binaural output. For custom manual
579
+ * wiring, use {@link createFoaDecoder} instead.
580
+ */
581
+ createFoaDecoderEffect(options?: FoaDecoderOptions): FoaDecoderEffect;
685
582
  /**
686
583
  * Creates an ITU-R BS.1770-5 {@link LoudnessMeter} that TAPS the output of a
687
584
  * target node without altering the audible path. The meter reports momentary