first commit
This commit is contained in:
98
docs/media/audio/audio-engine-server/src/fx/plugin-chain.md
Normal file
98
docs/media/audio/audio-engine-server/src/fx/plugin-chain.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# PluginChain + BusRegistry + SidechainPublisher
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`PluginChain` owns the ordered list of FX plugins attached to a single
|
||||
track, the per-chain ambient state, the chain-level bypass flag, and the
|
||||
effective plugin-delay-compensation latency for that track. `DefaultBusRegistry`
|
||||
is the project-level registry passed to every chain via
|
||||
`ChainContext.buses`; `SidechainPublisher` is the handle returned by
|
||||
`BusRegistry.publish`. Together they implement plugin slot 5 of the audio
|
||||
family's three-layer model — the "FX slot list" that the
|
||||
`ServerTimelineAdapter` hosts on each track. Every plugin runs through its
|
||||
`BlockProcessor` `process()` path; no `createAudioNode` or worklet path is
|
||||
used on the server.
|
||||
|
||||
## Features
|
||||
|
||||
- Ordered plugin list with `add` / `remove` / `setPluginBypass`.
|
||||
- Chain-level bypass that short-circuits `process` while preserving the
|
||||
hosted plugins.
|
||||
- Ambient state propagation — `updateAmbient` merges a patch into
|
||||
`ChainContext.ambient` and fires every plugin's `onAmbient` hook before
|
||||
the next block runs.
|
||||
- Plugin-delay-compensation via `getEffectiveLatencySamples()`; the chain
|
||||
emits `latency-changed` whenever the sum of un-bypassed plugin latencies
|
||||
changes so the host engine can recompute per-track PDC.
|
||||
- Typed event surface (`plugin-added`, `plugin-removed`, `bypass-changed`,
|
||||
`ambient-changed`, `latency-changed`) with `on(event, listener)`
|
||||
returning an unsubscribe closure.
|
||||
- `DefaultBusRegistry` — `publish` returns a `SidechainPublisher`,
|
||||
`subscribe` notifies listeners on registration and on every publish /
|
||||
close, and `get` / `list` expose the read surface plugins consume.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { DefaultBusRegistry, PluginChain } from "@suaveplan/audio-engine-server";
|
||||
|
||||
const buses = new DefaultBusRegistry();
|
||||
const chain = new PluginChain({
|
||||
id: "track-1",
|
||||
channels: 2,
|
||||
sampleRate: 48_000,
|
||||
blockSize: 512,
|
||||
buses,
|
||||
});
|
||||
chain.process([new Float32Array(512), new Float32Array(512)], 0);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
const pub = buses.publish("kick", 2);
|
||||
pub.write([kickLeft, kickRight]);
|
||||
|
||||
const offChain = chain.on("latency-changed", ({ effectiveLatencySamples }) => {
|
||||
recomputePerTrackPdc(effectiveLatencySamples);
|
||||
});
|
||||
|
||||
chain.updateAmbient({
|
||||
track: { pan: -0.5 },
|
||||
transport: { state: "playing", time: 1.25 },
|
||||
});
|
||||
|
||||
offChain();
|
||||
pub.close();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class PluginChain` — `add`, `remove`, `setPluginBypass`,
|
||||
`updateAmbient`, `process`, `getEffectiveLatencySamples`, `on`,
|
||||
`dispose`, plus read accessors `id`, `bypassed`, `context`, `buses`,
|
||||
`list`.
|
||||
- `class DefaultBusRegistry implements BusRegistry` — `publish`,
|
||||
`subscribe`, `get`, `list`.
|
||||
- `interface SidechainPublisher` — `bus`, `write`, `close`.
|
||||
- `interface BusRegistry`, `interface ChainContext`,
|
||||
`interface ChainPlugin`, `interface AmbientState`,
|
||||
`interface AmbientStatePatch`, plus the `ChainEvent`/`ChainEventPayload`
|
||||
pair.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The chain re-uses the same `Float32Array[]` block as both `input` and
|
||||
`output` for each plugin's `BlockProcessor.process` call, so each plugin
|
||||
writes into the channel arrays the next plugin reads from. Latency
|
||||
recomputation runs after every mutation (`add` / `remove` /
|
||||
`setPluginBypass`) and only fires `latency-changed` when the effective sum
|
||||
actually changes — repeated no-op mutations stay quiet. The default bus
|
||||
registry stores per-bus payloads as a `Float32Array[]` slot mutated by
|
||||
`SidechainPublisher.write`; `close` drops the slot and notifies every
|
||||
subscribed listener with `undefined` so plugins reading the bus on the
|
||||
next block see silence rather than stale samples.
|
||||
Reference in New Issue
Block a user