first commit
This commit is contained in:
31
docs/media/audio-fx/audio-fx-plugin-delay/README.md
Normal file
31
docs/media/audio-fx/audio-fx-plugin-delay/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# @suaveplan/audio-fx-plugin-delay - delay plugin for the @suaveplan/audio-fx-plugin-* family.
|
||||
|
||||
delay plugin for the @suaveplan/audio-fx-plugin-* family.
|
||||
|
||||
Member of the `@suaveplan/audio-fx-plugin-*` family. Universal (browser / server / offline / worklet identical). Depends on `@suaveplan/audio-fx-plugin-base` for the `AudioFxPlugin` contract; declares no other `@suaveplan/audio-fx-plugin-*` deps.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [delay](./src/delay/delay.md) — `DelayPlugin` is the universal stereo delay effect. Per-channel circular delay lines render decaying echoes; bounded fe…
|
||||
- [delay](./src/delay/delay.md) — `DelayPlugin` is the universal stereo delay effect. Per-channel circular delay lines render decaying echoes; bounded fe…
|
||||
- [schemas](./src/schemas/schemas.md) — The `delay.schemas` module owns the Zod boundary guards for `DelayPlugin` — construction options and the plugin-specifi…
|
||||
- [schemas](./src/schemas/schemas.md) — The `delay.schemas` module owns the Zod boundary guards for `DelayPlugin` — construction options and the plugin-specifi…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Composition
|
||||
|
||||
```ts
|
||||
import { PluginChain, DefaultBusRegistry } from "@suaveplan/audio-fx-plugin-base/chain";
|
||||
import { /* Plugin */ } from "@suaveplan/audio-fx-plugin-delay";
|
||||
|
||||
const plugin = new /* Plugin */();
|
||||
const chain = new PluginChain({
|
||||
buses: new DefaultBusRegistry(),
|
||||
plugins: [plugin],
|
||||
});
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Replaces the `./delay` subpath of the bundled `@suaveplan/audio-plugins-fx`.
|
||||
72
docs/media/audio-fx/audio-fx-plugin-delay/src/delay/delay.md
Normal file
72
docs/media/audio-fx/audio-fx-plugin-delay/src/delay/delay.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# `delay`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
@@ -0,0 +1,64 @@
|
||||
# `schemas`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The `delay.schemas` module owns the Zod boundary guards for `DelayPlugin` — construction options and the plugin-specific preset payload. Every public input runs through these schemas before the plugin touches any state; failures surface as `AUDIO_FX.INVALID_OPTIONS` / `AUDIO_FX.INVALID_PRESET` with the Zod issue list in `context.issues`.
|
||||
|
||||
Keeping the schemas in a sibling folder follows the package-wide convention: hosts can import the schemas directly, run their own `safeParse` against UI input, and surface field-level errors before constructing a `DelayPlugin`.
|
||||
|
||||
## Features
|
||||
|
||||
- `DelayPluginOptionsSchema` — every field optional, ranged to match the plugin's parameter declarations: `delayMs ∈ [1, 2000]`, `feedback ∈ [0, 0.95]`, `wet`/`dry ∈ [0, 1]`, `syncBeats ∈ [1/16, 8]`.
|
||||
- `DelayPresetDataSchema` — required `pingPong` and `bpmSync` booleans. Continuous parameters (delayMs, feedback, wet, dry, syncBeats) ride the universal `parameters` map in the `PresetSnapshot` envelope.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { DelayPluginOptionsSchema } from "@suaveplan/audio-fx-plugin-delay";
|
||||
|
||||
const parsed = DelayPluginOptionsSchema.safeParse(formValues);
|
||||
if (!parsed.success) {
|
||||
return showFieldErrors(parsed.error.issues);
|
||||
}
|
||||
const delay = new DelayPlugin(parsed.data);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
DelayPluginOptionsSchema,
|
||||
DelayPresetDataSchema,
|
||||
} from "@suaveplan/audio-fx-plugin-delay";
|
||||
|
||||
// Server-side validation of an untrusted preset payload from cloud
|
||||
// sync before applyPreset. The plugin would catch the same issue,
|
||||
// but the host wants a friendlier error before a plugin instance is
|
||||
// constructed.
|
||||
const envelope = parseChainSnapshot(payload);
|
||||
for (const entry of envelope.plugins.filter((p) => p.pluginType === "delay")) {
|
||||
const result = DelayPresetDataSchema.safeParse(entry.preset.data);
|
||||
if (!result.success) {
|
||||
return reject("Delay preset is corrupt.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `DelayPluginOptionsSchema` — `z.ZodObject` matching `DelayPluginOptions`. All fields optional and ranged.
|
||||
- `DelayPresetDataSchema` — `z.ZodObject` matching `DelayPresetData`. Both fields required.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Numeric fields use `.finite()` so `NaN` and `±Infinity` are rejected at the boundary; the per-block delay-line writes would otherwise propagate non-finite values into every subsequent block, and `feedback` close to `1` already lives at the edge of numerical stability.
|
||||
|
||||
`feedback` is `.max(0.95)` rather than the maximum the IIR can theoretically sustain (`1.0 − ε`) because at `0.95` the signal still has substantial perceived decay over many seconds; allowing values closer to `1` invites runaway under any automation lane that drifts.
|
||||
|
||||
`delayMs` is `.max(2000)` to match the line-size allocation in `delay.ts`; values beyond that would be silently clamped at the play-side anyway, so rejecting them at the boundary surfaces the limit clearly.
|
||||
|
||||
`syncBeats` ranges from `0.0625` (1/16 note) to `8` (two whole notes) so a tempo-locked delay covers everything from a tight slap-back to a long ambient wash.
|
||||
Reference in New Issue
Block a user