Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# `plugin-internals`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `plugin-internals` module owns the **boundary guards + parameter validation helpers** consumed by `AbstractAudioFxPlugin` and any other plugin author who wants to keep their `setup` / `process` / parameter-mutation methods tight. Splitting these out of the abstract class file keeps `audio-fx-plugin.ts` focused on lifecycle orchestration (parameter schedules, latency listeners, preset application) while concentrating the dense `if/throw` validation in a small standalone surface that future plugin base classes (a more permissive `LooseAudioFxPlugin` for prototyping, a benchmark-only `BenchmarkPlugin` that skips half the checks) can reuse without inheriting from the full `AbstractAudioFxPlugin`.
Every helper takes `pluginId` as an argument and propagates it to the `AudioFxError.context.pluginId` payload so consumers grepping for which plugin produced a given error can pattern-match against it directly.
## Features
- `assertNotAborted(signal, pluginId, messageKey)` — throws `AUDIO_FX.ABORTED` when `signal` is aborted. No-op otherwise. The plugin's `setup` method uses this twice: once before any allocation and once after any in-flight async work.
- `assertChannelMatch(input, output, expectedInput, expectedOutput, pluginId)` — throws `AUDIO_FX.CHANNEL_MISMATCH` with full channel-count context when either buffer disagrees with the resolved input / output channel counts. Source plugins pass `expectedInput === 0`; fixed-output plugins (e.g. the sampler) pass their declared output count.
- `requireParameter(parameterIndex, id, pluginId)` — returns the parameter record or throws `AUDIO_FX.UNKNOWN_PARAMETER`.
- `requireAutomatable(parameterIndex, id, pluginId)` — returns the parameter after asserting it is automatable; throws `AUDIO_FX.PARAMETER_NOT_AUTOMATABLE` when `automatable: false`.
## Usage: Basic Example
```ts
import {
assertChannelMatch,
requireParameter,
} from "@suaveplan/audio-fx-plugin-base/base";
class MyPlugin extends AbstractAudioFxPlugin {
public mutateParameter(id: string, value: number): void {
const param = requireParameter(this.parameterIndex, id, this.metadata.id);
// ... use param.min / param.max ...
}
protected processBlock(input: Float32Array[], output: Float32Array[], ctx: BlockContext): void {
assertChannelMatch(
input,
output,
this.config!.channels,
this.config!.channels,
this.metadata.id
);
// ... DSP ...
}
}
```
## Usage: Advanced Example
```ts
import {
assertChannelMatch,
assertNotAborted,
requireAutomatable,
requireParameter,
} from "@suaveplan/audio-fx-plugin-base/base";
class CustomPlugin implements AudioFxPlugin {
private readonly parameterIndex: ReadonlyMap<string, PluginParameter>;
private readonly schedules: Map<string, ParameterSchedule>;
public async setup(config: ProcessorConfig, _chain: ChainContext, opts?: { signal?: AbortSignal }): Promise<void> {
assertNotAborted(opts?.signal, this.metadata.id, "custom.setup-pre-aborted");
await this.loadResources(opts?.signal);
assertNotAborted(opts?.signal, this.metadata.id, "custom.setup-mid-aborted");
this.config = config;
}
public setParameterCurve(id: string, curve: ParameterCurve): void {
const param = requireAutomatable(this.parameterIndex, id, this.metadata.id);
const sched = this.schedules.get(id)!;
// ... schedule with clamped target against param.min / param.max ...
}
public process(input: Float32Array[], output: Float32Array[], ctx: BlockContext): void {
assertChannelMatch(
input,
output,
this.config!.channels,
this.config!.channels,
this.metadata.id
);
// ... DSP ...
}
}
```
## API Reference
- `assertNotAborted(signal: AbortSignal | undefined, pluginId: string, messageKey: string): void`.
- `assertChannelMatch(input, output, expectedInput: number, expectedOutput: number, pluginId: string): void`.
- `requireParameter(parameterIndex, id: string, pluginId: string): PluginParameter`.
- `requireAutomatable(parameterIndex, id: string, pluginId: string): PluginParameter`.
## Implementation Notes
Every helper rejects with `AudioFxError` carrying the offending value in `context` so consumers can render UI without grepping the error message. The `pluginId` is always carried because production logs frequently aggregate errors across plugins and matching by `metadata.id` is the canonical resolution path.
`assertChannelMatch` accepts `ReadonlyArray<unknown>` rather than `Float32Array[]` so it can be called with either input / output buffers without forcing the caller to widen their types. The helper only reads `.length` from each array; the actual buffer contents are irrelevant to the channel-count check.
`requireAutomatable` deliberately calls `requireParameter` rather than duplicating the parameter lookup. The cost is one extra function call per scheduled curve / transport event, which is negligible compared to the schedule evaluation itself. This shared lookup keeps the two helpers' error messages and `context` payloads aligned without copy-paste.
`assertNotAborted` accepts the `messageKey` from the caller rather than baking in a fixed key so the plugin's `setup` can distinguish "pre-aborted" from "mid-aborted" in its logs while still using a single helper.