Initial Commit
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# Superseded — not folded into `openspec/specs/`
|
||||
|
||||
**Date superseded**: 2026-05-15
|
||||
**Superseded by**: `openspec/changes/enterprise-audio-plugins-fx/`
|
||||
|
||||
## Why
|
||||
|
||||
This proposal shipped twelve `AudioPlugin` subclasses built on the now-superseded monolithic `@suaveplan/audio-engine`. It uses the pre-tier `browser/audio/audio-plugins-fx` target path and is browser-only.
|
||||
|
||||
The replacement (`enterprise-audio-plugins-fx`) is universal: every plugin implements `BlockProcessor` from `@suaveplan/audio-core` and optionally `createAudioNode()` for browser-native Web Audio rendering. The same plugin code therefore runs in server, offline, and worklet contexts as well as the browser.
|
||||
|
||||
No code was implemented for this proposal.
|
||||
@@ -0,0 +1,149 @@
|
||||
# OpenSpec Change: enterprise-browser-audio-plugins-fx
|
||||
|
||||
- **Change ID**: enterprise-browser-audio-plugins-fx
|
||||
- **Package**: `@suaveplan/audio-plugins-fx`
|
||||
- **Category**: browser
|
||||
- **Target path**: `browser/audio/audio-plugins-fx`
|
||||
- **Status**: IN PROGRESS
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/audio-plugins-fx.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/audio-plugins-fx` on the genesis branch from scratch at enterprise quality. The package ships twelve `AudioPlugin` subclasses built on `@suaveplan/audio-engine`, each exposed from its own subpath entry point for surgical tree-shaking: EQ (multi-band parametric with presets), Reverb (IR convolver), Delay, Compressor, Pitch Shifter (AudioWorklet), Synthesizer (oscillators/envelopes/voice pool), Audio Analyzer (FFT/tempo/key/spectral), Microphone (simulation), Mixer (channel strips, sends), GLaDOS voice effect, Game Audio (spatial/proximity/music layers), and Audio Player. Together these provide the core DSP surface for the monorepo's audio UI packages.
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
The package does not exist on genesis yet — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/audio-engine` (`workspace:^`) | `AudioPlugin` base class, `AudioGraph`, `AudioWorkletManager`, `PluginRegistry` |
|
||||
| `@suaveplan/error` (`workspace:^`) | Typed errors for DSP failures, preset validation |
|
||||
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, injectable clock |
|
||||
| `@suaveplan/events` (`workspace:^`) | Typed event maps per plugin |
|
||||
| `@suaveplan/schema-registry` (`workspace:^`) | Register preset Zod schemas |
|
||||
| `zod` (`catalog:forms`) | Schema for every `*Preset` type |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | Latency histograms, CPU counters per plugin |
|
||||
| `standardized-audio-context` (`catalog:browser`) | Web Audio context shim |
|
||||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||||
|
||||
Optional peer: `essentia.js` — declared in `optionalDependencies`, loaded via dynamic feature-detected import.
|
||||
|
||||
Dev deps: `vitest`, `@vitest/coverage-istanbul` (`catalog:testing`), `@suaveplan/testing workspace:^`.
|
||||
|
||||
---
|
||||
|
||||
## Preserved public API surface (from main branch)
|
||||
|
||||
Twelve plugin classes with their full config/types/preset surfaces — all preserved verbatim:
|
||||
|
||||
- `EQPlugin` + `EQBand`, `EQPluginConfig`, `EQ_PRESETS`
|
||||
- `ReverbPlugin` + `ReverbType`, `ReverbPluginConfig`, `REVERB_DURATIONS`
|
||||
- `DelayPlugin` + `DelayPluginConfig`
|
||||
- `CompressorPlugin` + `CompressorPluginConfig`
|
||||
- `PitchShifterPlugin` + `PitchShifterConfig`, `PitchShifterPreset`
|
||||
- `SynthesizerPlugin` + oscillator/envelope/voice-pool types, `SynthesizerPreset`
|
||||
- `AudioAnalyzerPlugin` + 9 analysis types (`FFTData`, `BeatInfo`, `KeyInfo`, `SpectralFeatures`, etc.)
|
||||
- `MicrophoneSimulationPlugin` + `createMicrophonePlugin` + 7 mic types, `MicrophonePreset`
|
||||
- `MixerPlugin` + 5 mixer types, `MixerPreset` (with complete sends round-trip)
|
||||
- `GLaDOSPlugin` + 6 GLaDOS types, `GLaDOSPreset`
|
||||
- `GameAudioPlugin` + 9 game audio types, `GameAudioPreset`
|
||||
- `AudioPlayerPlugin` + `AudioPlayerConfig`
|
||||
|
||||
Twelve subpath exports: `./eq`, `./reverb`, `./delay`, `./compressor`, `./pitch-shifter`, `./synthesizer`, `./audio-analyzer`, `./microphone`, `./mixer`, `./glados`, `./game`, `./audio-player`.
|
||||
|
||||
---
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
### 1. Structured errors via `@suaveplan/error`
|
||||
|
||||
Error codes: `DSP_INIT_FAILED`, `DSP_PROCESSING_FAILED`, `PRESET_VALIDATION_FAILED`, `WORKLET_REQUIRED`, `ESSENTIA_UNAVAILABLE`, `SIDECHAIN_UNAVAILABLE`.
|
||||
|
||||
### 2. Mixer sends round-trip
|
||||
|
||||
`MixerPlugin.getPreset()` / `applyPreset()` fully serializes and restores send states (the main-branch TODO is resolved).
|
||||
|
||||
### 3. PitchShifter worklet URL helper + fallback
|
||||
|
||||
Export `resolvePitchShifterWorkletUrl()` helper for Vite/Webpack consumers. Add `ScriptProcessorNode` fallback path for environments without AudioWorklet support.
|
||||
|
||||
### 4. Preset Zod schemas
|
||||
|
||||
Zod schemas for every `*Preset` type registered in `@suaveplan/schema-registry`. Runtime validation on `applyPreset()` calls.
|
||||
|
||||
### 5. `essentia.js` optional dep
|
||||
|
||||
Declared in `optionalDependencies`. Dynamic import with `try/catch` fallback; removes `@ts-expect-error` at call sites. `AudioAnalyzerPlugin` works without essentia.js (no beat/key detection).
|
||||
|
||||
### 6. OTel latency histograms
|
||||
|
||||
Per-plugin histogram `audio.plugin_processing_ms{plugin}`, counter `audio.plugin_cpu_usage{plugin}`.
|
||||
|
||||
### 7. Per-subpath bundle-size snapshots
|
||||
|
||||
Twelve snapshot tests asserting independent tree-shake (importing `./eq` does not pull in `./synthesizer`).
|
||||
|
||||
### 8. Sidechain support + mid/side
|
||||
|
||||
`CompressorPlugin` supports a first-class sidechain input. `EQPlugin` and `CompressorPlugin` expose mid/side processing mode.
|
||||
|
||||
### 9. Preset marketplace entry points
|
||||
|
||||
`@suaveplan/audio-plugins-fx/presets/reverb/cathedrals` (and similar) for optional preset packs shipped as separate lazy entry points.
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
Each plugin follows the five-file co-located pattern. Large plugins are split into `dsp.ts`, `state.ts`, `preset.ts`, `params.ts`, `plugin.ts` sub-modules each ≤ 300 lines. Tests are `.test.ts` (not `.spec.ts`).
|
||||
|
||||
```text
|
||||
browser/audio/audio-plugins-fx/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
error-codes.ts
|
||||
plugins/
|
||||
eq/ (eq-plugin, eq-presets, eq-params, eq-dsp)
|
||||
reverb/ (reverb-plugin, reverb-dsp, reverb-ir, reverb-presets)
|
||||
delay/ (delay-plugin, delay-dsp)
|
||||
compressor/ (compressor-plugin, compressor-dsp, compressor-sidechain)
|
||||
pitch-shifter/ (pitch-shifter-plugin, pitch-shifter-dsp, pitch-shifter-worklet-url, worklet-fallback)
|
||||
synthesizer/ (synthesizer-plugin, oscillator, envelope, voice-pool, synth-presets)
|
||||
audio-analyzer/(audio-analyzer-plugin, fft, beat-detector, key-detector, essentia-bridge)
|
||||
microphone/ (microphone-plugin, mic-simulation, mic-presets)
|
||||
mixer/ (mixer-plugin, channel-strip, mixer-sends, mixer-presets)
|
||||
glados/ (glados-plugin, glados-dsp, glados-presets)
|
||||
game/ (game-audio-plugin, spatial-audio, proximity-voice, music-layers, game-presets)
|
||||
audio-player/ (audio-player-plugin, audio-player-controls)
|
||||
worklets/
|
||||
pitch-shifter.ts (AudioWorklet processor)
|
||||
presets/
|
||||
reverb/
|
||||
cathedrals.ts
|
||||
tests/
|
||||
integration.test.ts
|
||||
bundle-size/
|
||||
eq.size.test.ts
|
||||
(... one per subpath)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impact
|
||||
|
||||
**Downstream**: `@suaveplan/react-audio`, `@suaveplan/react-audio-player`, `@suaveplan/r3f-audio`, game packages.
|
||||
|
||||
**Prerequisites**: `@suaveplan/audio-engine` published to Verdaccio.
|
||||
@@ -0,0 +1,169 @@
|
||||
# Tasks: enterprise-browser-audio-plugins-fx
|
||||
|
||||
**Change ID**: enterprise-browser-audio-plugins-fx
|
||||
**Package**: `@suaveplan/audio-plugins-fx`
|
||||
**Target path**: `browser/audio/audio-plugins-fx`
|
||||
**Prerequisite packages on Verdaccio**: `@suaveplan/audio-engine`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`
|
||||
|
||||
---
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create `browser/audio/audio-plugins-fx/` directory tree per layout in `proposal.md`.
|
||||
- [ ] Write `package.json` at version `0.1.0`:
|
||||
- `"sideEffects": false`
|
||||
- Twelve subpath exports: `./eq`, `./reverb`, `./delay`, `./compressor`, `./pitch-shifter`, `./synthesizer`, `./audio-analyzer`, `./microphone`, `./mixer`, `./glados`, `./game`, `./audio-player`
|
||||
- `dependencies`: `@suaveplan/audio-engine workspace:^`, `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/events workspace:^`, `standardized-audio-context catalog:browser`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`
|
||||
- `optionalDependencies`: `essentia.js` (version from Bun catalog if available, else explicit)
|
||||
- `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `@suaveplan/testing workspace:^`
|
||||
- [ ] Write `tsconfig.json` extending `@suaveplan/typescript-config/library-browser.json`.
|
||||
- [ ] Write `vitest.config.ts` with `pool: 'forks'`, `fileParallelism: false`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Error codes (`src/error-codes.ts`)
|
||||
|
||||
- [ ] Define `AudioFxErrorCode`: `DSP_INIT_FAILED`, `DSP_PROCESSING_FAILED`, `PRESET_VALIDATION_FAILED`, `WORKLET_REQUIRED`, `ESSENTIA_UNAVAILABLE`, `SIDECHAIN_UNAVAILABLE`.
|
||||
- [ ] Export `registerAudioFxErrorCodes()` and call eagerly.
|
||||
|
||||
---
|
||||
|
||||
## 3. EQ plugin (`src/plugins/eq/`)
|
||||
|
||||
- [ ] Implement `EQPlugin` with multi-band parametric EQ, biquad filter nodes, `EQ_PRESETS`.
|
||||
- [ ] Split into `eq-dsp.ts` (filter nodes, freq/gain/Q), `eq-params.ts` (parameter definitions), `eq-presets.ts`, `eq-plugin.ts`.
|
||||
- [ ] Add `EQBand` and `EQPluginConfig` Zod schemas.
|
||||
- [ ] Write tests for each sub-module (≤500 lines each), `eq-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Reverb plugin (`src/plugins/reverb/`)
|
||||
|
||||
- [ ] Implement `ReverbPlugin` with IR convolver + `REVERB_DURATIONS`, dry/wet mix.
|
||||
- [ ] Split into `reverb-dsp.ts`, `reverb-ir.ts` (impulse response loader), `reverb-presets.ts`, `reverb-plugin.ts`.
|
||||
- [ ] Write tests, `reverb-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Delay plugin (`src/plugins/delay/`)
|
||||
|
||||
- [ ] Implement `DelayPlugin` with feedback loop and wet/dry mix.
|
||||
- [ ] Write `delay-plugin.test.ts`, `delay-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Compressor plugin (`src/plugins/compressor/`)
|
||||
|
||||
- [ ] Implement `CompressorPlugin` with standard Web Audio `DynamicsCompressorNode` params.
|
||||
- [ ] Add sidechain input: `CompressorPlugin.setSidechainSource(node)` with `SIDECHAIN_UNAVAILABLE` error when not wired.
|
||||
- [ ] Add mid/side processing mode.
|
||||
- [ ] Write tests, `compressor-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Pitch Shifter plugin (`src/plugins/pitch-shifter/`)
|
||||
|
||||
- [ ] Implement `PitchShifterPlugin` as AudioWorklet host.
|
||||
- [ ] Add `worklets/pitch-shifter.ts` processor.
|
||||
- [ ] Export `resolvePitchShifterWorkletUrl()` helper for Vite/Webpack: `new URL('../worklets/pitch-shifter.js', import.meta.url).href`.
|
||||
- [ ] Add `ScriptProcessorNode` fallback path (in `worklet-fallback.ts`) when AudioWorklet unavailable.
|
||||
- [ ] Write tests for both paths, `pitch-shifter-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Synthesizer plugin (`src/plugins/synthesizer/`)
|
||||
|
||||
- [ ] Implement full `SynthesizerPlugin`: oscillators (sine/square/sawtooth/triangle), ADSR envelope, voice pool (polyphony), filter.
|
||||
- [ ] Split into `oscillator.ts` (≤300), `envelope.ts` (≤300), `voice-pool.ts` (≤300), `synth-presets.ts`, `synthesizer-plugin.ts`.
|
||||
- [ ] Add `SynthesizerPreset` Zod schema.
|
||||
- [ ] Write tests per sub-module, `synthesizer-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Audio Analyzer plugin (`src/plugins/audio-analyzer/`)
|
||||
|
||||
- [ ] Implement `AudioAnalyzerPlugin` with FFT (`AnalyserNode`), tempo BPM estimation, key detection.
|
||||
- [ ] Add `essentia-bridge.ts`: dynamic `import('essentia.js')` with try/catch; export `isEssentiaAvailable()`. When unavailable, key/spectral features return `null`.
|
||||
- [ ] No `@ts-expect-error`; type the dynamic import result with an explicit interface.
|
||||
- [ ] Write tests with and without essentia stub (real interface, not vi.mock), `audio-analyzer-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Microphone plugin (`src/plugins/microphone/`)
|
||||
|
||||
- [ ] Implement `MicrophoneSimulationPlugin` + `createMicrophonePlugin` factory.
|
||||
- [ ] Add `MicrophonePreset` Zod schema.
|
||||
- [ ] Write tests, `microphone-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Mixer plugin (`src/plugins/mixer/`)
|
||||
|
||||
- [ ] Implement `MixerPlugin` with channel strips, sends, routing, master gain.
|
||||
- [ ] Fully implement send-state round-trip in `mixer-sends.ts` — no TODO.
|
||||
- [ ] Add `MixerPreset` Zod schema.
|
||||
- [ ] Split into `channel-strip.ts` (≤300), `mixer-sends.ts` (≤300), `mixer-presets.ts`, `mixer-plugin.ts`.
|
||||
- [ ] Write regression test: `applyPreset(getPreset())` round-trips sends exactly.
|
||||
- [ ] Write `mixer-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 12. GLaDOS plugin (`src/plugins/glados/`)
|
||||
|
||||
- [ ] Implement `GLaDOSPlugin` with voice-effect chain (ring modulator + pitch shift + bitcrusher) and `GLaDOSPreset` options.
|
||||
- [ ] Add Wheatley, Cave Johnson, generic robotic presets.
|
||||
- [ ] Split into `glados-dsp.ts`, `glados-presets.ts`, `glados-plugin.ts`.
|
||||
- [ ] Add `GLaDOSPreset` Zod schema.
|
||||
- [ ] Write tests, `glados-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 13. Game Audio plugin (`src/plugins/game/`)
|
||||
|
||||
- [ ] Implement `GameAudioPlugin`: `PannerNode`-based 3D spatial audio, proximity voice chat (gain-by-distance), music layer system, HUD voice channel.
|
||||
- [ ] Game-audio ducking bus: music auto-ducks when VoIP speaks.
|
||||
- [ ] Split into `spatial-audio.ts`, `proximity-voice.ts`, `music-layers.ts`, `game-audio-plugin.ts`.
|
||||
- [ ] Add `GameAudioPreset` Zod schema.
|
||||
- [ ] Write tests per sub-module, `game-audio-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 14. Audio Player plugin (`src/plugins/audio-player/`)
|
||||
|
||||
- [ ] Implement `AudioPlayerPlugin` with load/play/pause/stop/seek, loop, playback rate, volume.
|
||||
- [ ] Write `audio-player-plugin.test.ts`, `audio-player-plugin.md`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Bundle-size snapshot tests (`tests/bundle-size/`)
|
||||
|
||||
- [ ] One snapshot test per subpath: build the entry point and assert that the emitted chunk does not contain symbols from other plugins (tree-shake isolation). Use `rollup` or `esbuild` in the test script.
|
||||
|
||||
---
|
||||
|
||||
## 16. Integration test (`tests/integration.test.ts`)
|
||||
|
||||
- [ ] Load all twelve plugins into a single `AudioEngine` instance; verify no cross-plugin contamination.
|
||||
- [ ] Preset round-trip test for every plugin with a Zod schema.
|
||||
- [ ] Mixer sends round-trip regression.
|
||||
- [ ] PitchShifter worklet URL helper returns a URL string.
|
||||
|
||||
---
|
||||
|
||||
## 17. Docs
|
||||
|
||||
- [ ] Write `.md` for every plugin module (≥200 words, simple + advanced examples, API reference).
|
||||
- [ ] Write `README.md` with purpose, subpath table, preset schema table, worklet bundling notes, deps table.
|
||||
|
||||
---
|
||||
|
||||
## 18. Verify and publish
|
||||
|
||||
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/audio-plugins-fx` — green.
|
||||
- [ ] `bunx turbo test:coverage --filter=@suaveplan/audio-plugins-fx` — 100% on touched modules.
|
||||
- [ ] `bun run docs:check --filter=@suaveplan/audio-plugins-fx` — zero errors.
|
||||
- [ ] No `TODO`, `FIXME`, `any`, `ts-ignore`, `ts-expect-error`, `vi.mock`, `.skip` in `src/`.
|
||||
- [ ] All twelve subpath tree-shake snapshots green.
|
||||
- [ ] Mixer sends round-trip regression test passes.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/audio-plugins-fx`.
|
||||
- [ ] Archive to `openspec/archive/2026-04-24-enterprise-browser-audio-plugins-fx/`.
|
||||
Reference in New Issue
Block a user