Files
skills/docs/media/audio-fx/audio-fx-plugin-delay/src/delay/delay.md
2026-05-29 08:20:56 +01:00

3.9 KiB
Raw Blame History

delay

(no submodules)

Purpose

DelayPlugin is the universal stereo delay effect. Per-channel circular delay lines render decaying echoes; bounded feedback (clamped at 0.95) prevents the recursion from diverging regardless of automation; an optional ping-pong mode cross-feeds the two channels for that classic alternating-side bounce; and a transport-BPM sync mode derives the delay length from the live tempo on the chain's ambient state so a 1/4-note delay always stays tempo-locked.

The plugin extends AbstractAudioFxPlugin, so it runs identically inside the Web Audio engine, the server renderer, and the worklet wrapper. Delay state is per-channel and cleared in place by reset(). With wet === 0 the plugin short-circuits to dry * input, satisfying the spec's "fully-dry mix is transparent" guarantee.

Features

  • Up-to-2 s per-channel delay line, sized at setup and never re-allocated.
  • Seven parameters: delayMs, feedback (0–0.95), wet, dry, pingPong, bpmSync, syncBeats.
  • Ping-pong mode (stereo only): the L delay reads R's feedback and vice versa for an alternating-side bounce; mono input silently falls back to regular delay.
  • BPM sync: when bpmSync >= 0.5, delay length = 60_000 · syncBeats / tempo ms, with tempo read from ChainContext.ambient.transport.
  • wet === 0 early-return path: output equals dry * input exactly, bit-transparent when dry === 1.
  • Bounded feedback so the IIR recursion cannot blow up.

Usage: Basic Example

import { DelayPlugin } from "@suaveplan/audio-fx-plugin-delay";

const delay = new DelayPlugin({
    initialDelayMs: 250,
    initialFeedback: 0.4,
    initialWet: 0.3,
    initialDry: 0.7,
});
chain.add(delay);

Usage: Advanced Example

import { DelayPlugin } from "@suaveplan/audio-fx-plugin-delay";

// Tempo-locked 1/4-note ping-pong delay for stereo guitar.
const delay = new DelayPlugin({
    initialBpmSync: true,
    initialSyncBeats: 1,
    initialFeedback: 0.6,
    initialPingPong: true,
    initialWet: 0.5,
    initialDry: 0.7,
});
await delay.setup({ sampleRate: 48_000, channels: 2, blockSize: 512 }, ctx);

// Sweep the delay length from 1/4 → 1/8 over four bars via automation.
delay.setParameterCurve("syncBeats", { type: "linear", value: 0.5, duration: 8 });

API Reference

  • DelayPlugin (class extends AbstractAudioFxPlugin)
    • new DelayPlugin(options?: DelayPluginOptions) — throws AUDIO_FX.INVALID_OPTIONS on malformed options.
    • Inherited parameter accessors for delayMs, feedback, wet, dry, pingPong, bpmSync, syncBeats.
  • DelayPluginOptions — initial parameter values and mode flags.
  • DelayPresetData — { pingPong, bpmSync }; the continuous parameters round-trip via the universal parameters map.

Implementation Notes

The non-ping-pong path is the standard "comb" structure: line[w] = input + feedback · line[r]; out = dry · input + wet · line[r]. The read index r is (w + lineSize − delaySamples) % lineSize, computed once per sample because delaySamples may move with automation. The line size is fixed at the maximum allowed (2 s · sampleRate) so changing delayMs between blocks costs nothing.

Ping-pong cross-feeds the feedbacks: lineL[w] = inL + feedback · lineR[r] and symmetrically for R. The dry path stays in-channel. This is the classic Eventide-style ping-pong topology; an impulse on L produces a bounce L → R → L → R with each bounce attenuated by feedback.

BPM sync uses 60_000 / tempo · syncBeats to convert beats to milliseconds. The plugin reads tempo from ChainContext.ambient.transport.tempo each block, so a tempo automation lane updates the delay length continuously without per-tempo recompute on the plugin side.

Feedback is clamped to 0.95 by the parameter declaration. Higher values are mathematically unstable for an IIR delay; the clamp keeps the plugin safe under any automation lane the user can write.