Optional context: BaseContextAudio context to use. If omitted, a fresh AudioContext
is constructed (which on mobile will be suspended until a user
gesture, triggering the auto-unlock path described above).
Optional cache: ICacheOptional cache implementation. Defaults to AudioCache.
Optional runtime configuration including the
autoUnlock opt-out and a createAudioWorkletNode factory override.
Private _busNamed-bus registry. Populated by createBus when a name is supplied; entries are removed by the bus's onDestroy hook.
Private autoplayPrivate cachePrivate createOptional options: AudioWorkletNodeOptionsPrivate eventPrivate finalizationPrivate foaPer-context cache of the decoded order-1 SH-HRIR AudioBuffer used by
FoaDecoder. Keyed on the context (like
loadedAudioWorklets) because decodeAudioData produces a buffer
bound to ONE context's sample rate; a buffer decoded on context A must not
be reused on context B. Stores the in-flight Promise so concurrent
loadFoaHrir calls share a single fetch/decode.
Private impulsePer-context, per-URL decoded impulse-response cache. Stores in-flight promises so concurrent effect builds for the same context/URL share the same fetch/decode. Rejected promises are evicted so the caller can retry.
Private loadedPer-context cache of "module-name has been loaded on this BaseContext".
Keyed on the context itself because AudioWorklet.addModule() registers
the module against ONE context — a module loaded on context A is NOT
loaded on context B. A previous host-scoped Set<string> short-circuited
loadAudioWorkletModule() on name alone, so cross-context
ReverbEffect.build would skip addModule on the new context and
the second construct would throw with no module registered.
The master bus — master.input IS globalGainNode (literally the same
GainNode), so every existing .connect(globalGainNode) call in the
codebase remains correct. cacophony.volume and cacophony.mute still
read and write master.input.gain.value through that alias.
Private prevPrivate suspendReturns true if this instance is backed by an offline audio context (i.e., the context has a startRendering method).
Returns true while the audio context is suspended — i.e. the library
cannot produce sound until the context is resumed (typically by a user
gesture). After the auto-unlock fires (or resume() is called manually),
this becomes false.
Mirrors Howler's Howler.ctx.state === 'suspended' check via the
Howler global's _audioUnlocked. Use this to gate UI that hints to
the user that an interaction is required to start audio.
The single worklet-effect construction seam (WorkletEffectHost).
Idempotently registers worklet's module on context — or this host's own
context when omitted, honoring the cross-context contract that effects'
build(context) promises — then constructs the AudioWorkletNode with
parameterData. Every worklet-backed CacophonyEffect routes through
here, as does the phase-vocoder pitch-shift path (Playback.setPitchShift).
Optional context: BaseContextPrivate canPrivate createCreates a TremoloEffect preset as an AUTO-PAN — a tremolo with
stereoPhase: 180, so the left and right channel gains modulate in
anti-phase (the sound swings hard between the speakers). The 180-deg
per-channel LFO offset is Dattorro's stereo-modulation convention (p.776). A
convenience wrapper over createTremolo; caller options override the
preset.
Creates a new Bus. If name is provided, the bus is registered
so getBus can look it up by name. Anonymous buses (no name) are
not registered and cannot be retrieved by name.
Optional name: stringif a named bus with the same name already exists, or if the reserved name 'master' is used (the master bus is auto-created).
Creates a ModulatedDelayEffect preset as a (white) CHORUS — Dattorro
Table 6 white-chorus knobs blend: 0.7071, feedforward: 1,
feedback: 0.7071 (blend = feedback, feedforward = 1, the negative-feedback
path that minimizes the spectral aberration of a plain dry+wet chorus). A
9 ms tap modulated 4 ms at 0.5 Hz sits in the chorus range (Table 7).
NOTE: Dattorro's white chorus (p.776) combines this negative-feedback path with ALLPASS fractional-delay interpolation. This implementation uses the white-chorus knob settings but substitutes cubic-Lagrange interpolation (Laakso 1996) — transient-safe under modulation (Laakso p.52), trading some high-frequency trough depth versus a true allpass-interpolated white chorus.
Creates a dynamics CacophonyEffect configured as a COMPRESSOR
(ratio > 1 reduces the level of signals above threshold). Implements the
feed-forward design of Giannoulis, Massberg & Reiss 2012. Add the returned
effect to a Bus via bus.addFilter(effect). The same machinery
(gain computer + log-domain smooth-branching detector) backs the limiter
and gate presets below.
Creates a ModulatedDelayEffect preset as a DELAY / ECHO — Dattorro's
unified modulated-delay circuit (JAES 1997, Fig. 36) with the modulation OFF
(depth: 0, rate: 0). Echo knobs (Table 6): full dry blend: 1 plus a
unity wet tap feedforward: 1; feedback defaults to 0 (a single tap) but is
caller-configurable for a regenerating echo (|feedback| < 1, Table 6). The
250 ms tap sits in the echo range (Table 7). All knobs are exposed.
Creates a WaveshaperEffect preset as a DISTORTION — the tanh soft
clipper (shape: 1, F0 = log cosh, Parker 2016 eq.20) with a default drive
of 4 for an audible saturated tone. A convenience wrapper over
createWaveshaper; caller-supplied options override the defaults.
Creates a ModulatedDelayEffect preset as DOUBLING ("double tracking")
— Dattorro Table 6 doubling knobs blend: 0.7071, feedforward: 0.7071,
feedback: 0 (no feedback). A ~20 ms tap (Table 7 doubling nominal) with a
gentle 1 ms / 0.4 Hz wobble fattens a single take into two.
Creates a Feedback Delay Network (FDN) CacophonyEffect — an
algorithmic reverb with a lossless degree-0 paraunitary Hadamard feedback
matrix (Schlecht & Habets 2019), per-delay-line absorption filters setting
the reverberation time T60 (Jot & Chaigne 1991), and multiplication-free
velvet-noise input diffusion (Fagerström et al. 2020). The effect's
build lazily registers the worklet module (no-op if already loaded) and
constructs the AudioWorkletNode with the supplied FdnReverbOptions
as parameterData. Add the returned effect to a Bus via
bus.addFilter(effect).
Creates a ModulatedDelayEffect preset as a FLANGER — Dattorro Table 6
flanger knobs blend: 0.7071, feedforward: 0.7071 (blend = feedforward for
the deepest comb trough), feedback: -0.7071 (negative feedback deepens the
troughs). A short 5 ms tap swept 4 ms at 0.25 Hz gives the classic flange
(Table 7).
Creates a FoaDecoder — a standalone first-order ambisonic (FOA) ->
binaural FORMAT CONVERTER built from native Web Audio nodes (no worklet).
It decodes a 4-channel ACN/SN3D [W, Y, Z, X] signal to headphone stereo
via the per-SH-channel HRIR decode of Ahrens 2022 (eq.31), using
Omnitone's WY/ZX 2-stereo-ConvolverNode packing and the bundled order-1
SH-HRIR.
It is 4-channel-in / 2-channel-out; this method returns the explicit
endpoint object for custom graph wiring:
feed FOA into decoder.input (4-ch) and route decoder.output (2-ch
stereo) downstream:
const decoder = await cacophony.createFoaDecoder();
foaSource.connect(decoder.input);
decoder.output.connect(bus.input); // or context.destination
Build is async (the bundled HRIR is fetched + decoded). Pair it with
encodeMonoToFoaSN3D (clean, physically correct) or with
createStereoToBFormatNode (the perceptual, approximate stereo-upmix path).
Optional context: BaseContextCreates a bus-filter wrapper around FoaDecoder. Use this on a dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only filter that converts that bus to stereo binaural output. For custom manual wiring, use createFoaDecoder instead.
Creates a dynamics CacophonyEffect preset as a downward EXPANDER /
GATE — ratio < 1 so signals BELOW the threshold are pushed further down
(Giannoulis 2012 p.403). The default ratio of 0.1 gives gate-like
downward expansion; pass a ratio closer to 1 for gentler expansion.
Creates a native ConvolverNode impulse-response effect. Pass an AudioBuffer
for an already-decoded IR or a URL to fetch/decode through the per-context
IR cache. The default is wet-only (dry: 0, wet: 1) and returns a single
ConvolverNode when added to a bus; setting dry or non-unity wet builds
an owned dry/wet endpoint graph exposing dry and wet automation params.
Creates a dynamics CacophonyEffect preset as a LIMITER — a
compressor with an effectively infinite ratio so output is clamped at the
threshold (Giannoulis 2012 eqs 18-19). The ratio is fixed to the worklet's
limiter sentinel; caller-supplied ratio is ignored. Other params
(threshold, knee, attack, release, makeup) remain configurable.
Creates an ITU-R BS.1770-5 LoudnessMeter that TAPS the output of a target node without altering the audible path. The meter reports momentary (400 ms), short-term (3 s), and gated integrated loudness in LKFS/LUFS, plus true-peak level in dBTP (Annex 2 4× oversampling).
The tap is a BRANCH: the target's output is connected to the metering
worklet in ADDITION to its existing forward edge, and the worklet's output
is routed through an owned zero-gain sink to keep the branch silent and live.
By default the target is the
master bus output (master.output), measuring everything that reaches the
destination — the integrated-loudness target. Pass a Bus to meter
one bus's output, or any AudioNode to meter that node.
Lazily registers the loudness-meter worklet module (no-op if already
loaded) on the target's context, then constructs the node and branch-taps.
A LoudnessMeter handle exposing the live readings.
Private createOptional options: MediaStreamSoundOptionsCreates a PannerNode with the specified options.
An object containing the options to use when creating the PannerNode.
A new PannerNode instance with the specified options.
const panner = audio.createPanner({
positionX: 0,
positionY: 0,
positionZ: 0,
orientationX: 0,
orientationY: 0,
orientationZ: 0,
});
Creates a PhaserEffect — a classic MXR/Univibe-style phase shifter: a
cascade of first-order allpass sections at a common LFO-swept break frequency
summed additively with the dry signal to sweep notches through the spectrum
(Smith STAN-M-21; PASP §8.9). Two allpass sections per notch (default 4
sections = 2 notches). Add the returned effect to a Bus via
bus.addFilter(effect).
Creates a DattorroReverb CacophonyEffect. The effect's build
lazily registers the worklet module (no-op if already loaded) and
constructs the AudioWorkletNode with the supplied ReverbOptions
as parameterData. Add the returned effect to a Bus's filter
chain via bus.addFilter(effect).
Creates a Sound instance from an AudioBuffer or URL.
Optional soundType: SoundTypeType of sound (Buffer, HTML, Streaming)
Optional panType: PanTypeType of panning (HRTF or stereo)
Optional signal: AbortSignalOptional AbortSignal to cancel the operation
Promise that resolves to a Sound instance
Creates a Sound from the first playable URL in a Howler-style fallback
array. The browser's HTMLAudioElement.canPlayType is queried per
extension; the first source it reports as supported ('probably' or
'maybe') is fetched and decoded. If decoding rejects for a candidate,
the next playable candidate is tried — a 'maybe' can still fail at
decode time.
MIME types are inferred from file extensions: .webm → audio/webm,
.mp3 → audio/mpeg, .ogg → audio/ogg, .wav → audio/wav,
.flac → audio/flac, .m4a → audio/mp4, .aac → audio/aac,
.opus → audio/ogg; codecs=opus.
Cache and loading events fire only for the URL actually fetched.
v1 limitation: only the default 'buffer' sound type participates in
fallback. Passing an array with any non-'buffer' soundType (e.g.
'html', 'streaming', 'oscillator') rejects with a clear "not yet
supported" error.
If the array is empty, if soundType is not 'buffer', or if
no candidate is playable (codec unsupported or every decode failed).
The error message names the URLs that were tried.
Private createOptional signal: AbortSignalCreates a TremoloEffect — LFO-driven amplitude modulation (a VCA
swung by a low-frequency oscillator). The gain swings between (1 - depth)
and 1, never inverting (a true tremolo, not ring modulation). Anchored to
standard AM theory, Dattorro 1997 (p.776) for the quadrature stereo LFO, and
Mitcheltree et al. (DAFx23) for the LFO-driven-effect framing. shape selects
the LFO waveform (0 = sine, 1 = triangle, 2 = square); stereoPhase offsets
the per-channel LFO. Add the returned effect to a Bus via
bus.addFilter(effect).
Creates a ModulatedDelayEffect preset as a VIBRATO — Dattorro Table 6
vibrato knobs blend: 0, feedforward: 1, feedback: 0 (100% wet, no dry
path, no feedback), so only the pitch-modulated delayed signal is heard. A
5 ms tap swept 3 ms at 5 Hz (Table 7).
Creates a WaveshaperEffect — an antialiased distortion/waveshaper
implementing first-order Antiderivative Antialiasing (Parker, Zavalishin &
Le Bivic 2016, DAFx-16): y[n] = (F0(x_n) - F0(x_{n-1}))/(x_n - x_{n-1})
(eq.9), with an f(midpoint) fallback at the 0/0 singularity (eq.10). Ships
two nonlinearities — shape: 0 hard clip (polynomial F0, eq.25), shape: 1
tanh soft clip (F0 = log cosh, eq.20). Carries an inherent 0.5-sample group
delay (eq.17). Add the returned effect to a Bus via
bus.addFilter(effect).
Optional signal: AbortSignalOptional options: AudioWorkletNodeOptionsOptional context: BaseContextThe default audio context for this Cacophony instance (used by FoaDecoder when no explicit context is supplied).
Private isLookup helper: has worklet name been loaded on ctx (per the
per-context loadedAudioWorklets cache).
Private loadOptional signal: AbortSignalOptional context: BaseContextPrivate loadFetches and decodes the bundled order-1 SH-HRIR
(sh_hrir_order_1.wav, from Omnitone, Apache-2.0 — see
src/assets/NOTICE) into an AudioBuffer, memoized per context. The
buffer is the 4-channel (ACN rows W,Y,Z,X) SH-domain HRIR consumed by
FoaDecoder to drive its WY/ZX stereo ConvolverNodes
(Ahrens 2022 eq.31 decode).
The WAV is 48 kHz; decodeAudioData resamples it to the context's sample
rate automatically when they differ (standard Web Audio behavior, which
Omnitone relies on).
Optional context: BaseContextOptional BaseContext to decode on. Defaults to this host's
own context. Supplied so a FoaDecoder added to a bus on
a different context decodes the HRIR on the right context.
Fetches and decodes an impulse response URL on the requested audio context, memoized per context and URL. Buffers decoded by one context are not reused on another context, matching Web Audio's context-bound decode behavior.
Impulse-response audio URL.
Optional context: BaseContextOptional decode context. Defaults to this Cacophony instance.
Optional signal: AbortSignalOptional abort signal for the fetch. Decode itself is not abortable in Web Audio, but an already-aborted signal is honored before decode starts.
Eagerly registers every bundled AudioWorklet module on this context. This is
OPTIONAL — effects load their own worklet lazily in build, and the
pitch-shift path loads the phase-vocoder on first use. Call this up front to
pay the registration cost ahead of time. Idempotent per context (each module
short-circuits via the per-context loadedAudioWorklets set).
Optional signal: AbortSignalPrivate markRecord name as loaded on ctx, lazily allocating the per-context Set.
Remove event listener.
Register event listener.
Cleanup function
Suspends the audio context.
Resolves after the underlying AudioContext transition completes, then
emits the suspend event. If the context is already suspended (per this
instance's view), resolves immediately as a no-op. If the underlying
suspend() call rejects, the rejection is propagated and no event fires.
Internal Resumes the audio context. This method is required to resume the audio context on mobile devices. On desktop, the audio context will automatically resume when a sound is played.
Resolves after the underlying AudioContext transition completes, then
emits the resume event. If the underlying resume() call rejects, the
rejection is propagated and no event fires.
Wraps a raw AudioNode in a CacophonyEffect so it can be added
to a Bus filter chain. By default Bus.addFilter rejects raw
AudioNodes — wrapping via shareEffect is the explicit opt-in that
signals "yes, I understand this single node will be shared across every
bus I add it to."
Renders the offline audio graph to a buffer. Only available when the context has a startRendering method.
Promise that resolves to the rendered AudioBuffer
Error if the context does not support offline rendering
OFFLINE independent time-stretch: change an AudioBuffer's tempo WITHOUT
changing its pitch, returning a NEW AudioBuffer of length ≈
round(buffer.length · factor) at the same sample rate.
Algorithm: Phase Gradient Heap Integration (PGHI) per Zdeněk Průša & Nicki Holighaus, "Phase Vocoder Done Right" (EUSIPCO 2017 / arXiv:2202.07382). The signal is STFT'd, the synthesis phase is reconstructed by integrating the analysis-phase time/frequency gradients along the magnitude ridges (max-heap), then overlap-added at the stretched synthesis hop. No peak picking and no transient detection. Each channel is processed independently.
This is an OFFLINE buffer transform, NOT a real-time worklet: the project's OLA worklet base is unity-rate (analysis hop == synthesis hop, fixed to the 128-sample render quantum) and cannot change the time base in real time. A genuine independent stretch needs analysis hop ≠ synthesis hop, which only the whole-buffer offline path provides.
The source AudioBuffer to time-stretch.
Stretch factor (> 0). > 1 lengthens (slower tempo), < 1
shortens (faster tempo); pitch is preserved either way.
Optional options: TimeStretchOptionsOptional PGHI parameters (fftSize, analysisHop, tol, seed).
A new, time-stretched AudioBuffer.
If the context cannot create buffers, or factor <= 0.
Private waitOptional signal: AbortSignalStatic createCreates a Cacophony instance backed by an OfflineAudioContext. Use this for rendering, bouncing, precomputing processed output, or non-realtime scenarios.
Offline context configuration (channels, length, sampleRate)
Optional cache: ICacheOptional cache implementation
A Cacophony instance backed by OfflineAudioContext
Static Private isReturns true if the error came from AudioContext.decodeAudioData.
Per the Web Audio spec, decode failures throw a DOMException with name
EncodingError. We deliberately do NOT treat plain Error instances as
decode failures: fetch/cache/network errors must propagate so the caller
sees the real cause instead of silently falling through to a different
format. See reports/format-fallback-codex-review.md (Major 1).
Constructs a new Cacophony instance.
If the supplied (or auto-constructed) audio context is in
suspendedstate andruntimeOptions.autoUnlockistrue(default), one-timetouchend/click/keydownlisteners are installed ondocument.bodyso the first user gesture transparently unlocks audio — matching Howler'sautoUnlockbehavior. See RuntimeOptions.autoUnlock for the opt-out and the iOS primer rationale.