Files
2026-05-27 23:29:53 +01:00

8.3 KiB

Extend audio-plugins-fx with the enterprise extras

Package: @suaveplan/audio-plugins-fx Status: PROPOSED Type: feature

Why

The in-flight enterprise-audio-plugins-fx change shipped 19 plugins matching the genesis-original spec: EQ, Filter, Compressor, Limiter, Gate, Reverb, Delay, Chorus, Phaser, Flanger, Tremolo, AutoFilter, Distortion, Sampler, Synthesizer, PitchShifter, Spatial, Mixer, StereoWidener. The enterprise spec at ../suaveplan/enterprise/audio-plugins-fx.md lists five further plugins that the main-branch package ships but genesis does not yet cover: AudioAnalyzer, MicrophoneSimulationPlugin, GLaDOSPlugin, GameAudioPlugin, and AudioPlayerPlugin. These plugins drive the React audio surface (visualisers, voice-effect demos), the React audio player, and the game-audio composite used by the game packages. Without them in genesis, downstream packages cannot be rebuilt at parity with main.

This change adds the five plugins on their own subpaths so consumers can tree-shake to exactly the surface they need, keeps every plugin universal-tier under the same AudioFxPlugin contract that the existing 19 already satisfy, and routes any browser-only fast paths through the optional createAudioNode / getWorkletProcessor hooks declared in the base contract.

What changes

  • ADDED: ./audio-analyzer subpath exporting AudioAnalyzerPlugin — tap-point analyser exposing getFrequencyData(), getWaveformData(), async getters for tempo, pitch, spectralFeatures, energy, key, plus an onAnalysisUpdate(callback) subscription and applyPreset(name) for music | speech | percussion | ambient. Pass-through processing on the audio path.
  • ADDED: ./microphone subpath exporting MicrophoneSimulationPlugin and createMicrophonePlugin(options) factory — source plugin (io.inputs[0].count === 0, outputs.count === 1) that synthesises a microphone-coloured signal from white-noise + oscillator + frequency-resonance-distortion + dynamics + wow-and-flutter stages, all configurable via a typed MicrophoneConfig. Includes the structured UI control descriptor (MicrophoneUISection[]) so editors can render the simulation's parameter surface without hard-coding it.
  • ADDED: ./glados subpath exporting GLaDOSPlugin — voice-effect chain combining pitch shift, formant shift, robotic intensity, distortion, reverb, and low-pass filtering with named presets (glados-mainline | glados-test-chamber | glados-cake-deception | wheatley | cave-johnson | robotic-generic). Insert plugin (io.inputs[0].count === outputs[0].count === "preserve").
  • ADDED: ./game subpath exporting GameAudioPlugin — composite plugin for game audio: spatial sources (createSpatialSource, position/orientation/Doppler/occlusion), music-layer mixing (addMusicLayer, crossfadeMusicLayers), and proximity voice chat (enableProximityVoiceChat, addVoiceChatParticipant, distance-based attenuation, bandwidth optimisation tier). Effects bus with blendable voice effects.
  • ADDED: ./audio-player subpath exporting AudioPlayerPlugin — source plugin that decodes an audio asset from a URL into a Float32Array PCM buffer (universal path) or routes through HTMLAudioElement.captureStream() (browser path via createAudioNode). Exposes load(url, { signal? }), play(), pause(), stop(), seek(time), setVolume, setLoop, setPlaybackRate, getState().
  • MODIFIED: package vite.config.ts and package.json to declare the five new subpath exports.
  • MODIFIED: openspec/specs/audio-plugins-fx/spec.md — folded delta adds five new Requirement blocks, each with at least one Verified by: line pointing at a real test path.

Out of scope

  • Browser-engine integration (@suaveplan/audio-engine-browser wiring createAudioNode into a Web Audio graph) — separate change in that package.
  • React bindings (@suaveplan/react-audio-analyzer-viz, @suaveplan/react-audio-player) — handled in their own packages once these plugins land.
  • The essentia.js optional analyser bridge from main-branch — deferred behind a peerDependenciesMeta.optional declaration in a follow-up; the universal analyser uses the built-in browser-equivalent FFT path only.
  • Worklet hot-reload, oversampling on Compressor/Distortion/PitchShifter, mid/side processing on EQ/Compressor/Reverb, the convolution IR library, parameter-automation lanes beyond the existing universal setParameterCurve surface — all listed in the enterprise plan's §4 enhancements, all deferred to dedicated change proposals so this one stays scoped to "add the missing plugins".
  • The package split into per-plugin packages (split-audio-plugins-fx) — a separate in-flight change; lands after this one once the surface is complete.

Source mapping

REQUIRED. This change derives from ../suaveplan/enterprise/audio-plugins-fx.md plus the implementation under ../suaveplan/packages/browser/audio-plugins-fx/src/plugins/<name>/.

Requirement Source lines Notes
Audio analyzer plugin enterprise/audio-plugins-fx.md §1 line 11; packages/browser/audio-plugins-fx/src/plugins/audio-analyzer/audio-analyzer.ts:28-870 Reworded for universal tier; FFT path stays, essentia bridge deferred
Microphone simulation plugin enterprise/audio-plugins-fx.md §1 line 11; packages/browser/audio-plugins-fx/src/plugins/microphone/microphone.ts:10-490 UI section descriptor preserved verbatim
GLaDOS voice-effect plugin enterprise/audio-plugins-fx.md §1 line 11; packages/browser/audio-plugins-fx/src/plugins/glados/glados.ts:21-700 Preset set expanded per §4.7 (Wheatley, Cave Johnson, robotic-generic)
Game audio plugin enterprise/audio-plugins-fx.md §1 line 11; packages/browser/audio-plugins-fx/src/plugins/game/game-audio.ts:22-820 Composite of spatial + music-layer + proximity-voice surfaces
Audio player plugin enterprise/audio-plugins-fx.md §1 line 11; packages/browser/audio-plugins-fx/src/plugins/audio-player/audio-player.ts:18-413 Universal decode path replaces HTMLAudioElement-only impl from main

Requirements with no source line

Requirement Justification
AbortSignal on every async load entry point Aligns with the genesis-side loadImpulseResponse / loadSample precedent — no main-branch equivalent because main's load(url) ignores abort
Universal-tier purity (no browser globals on the BlockProcessor path) New constraint introduced by the genesis package design; the main-branch plugins assume an AudioContext and are not portable
Zod boundary validation on every plugin's options + preset shape Already required by the existing enterprise-audio-plugins-fx change for the first 19 plugins; this extension keeps the policy consistent

Verification approach

Each new plugin lands with the same five-file co-location pattern the existing 19 already use: <name>.ts (implementation), <name>.types.ts, <name>.test.ts, <name>.md, plus schemas/{schemas.ts, schemas.md, index.ts} and index.ts. Tests run under vitest with no mocks — the audio path uses real Float32Array arithmetic, the URL loaders use data: URLs the same way ReverbPlugin.loadImpulseResponse already does, and any browser-only paths are exercised via happy-dom polyfills.

  • src/plugins/audio-analyzer/audio-analyzer.test.ts — covers analyzer surface (FFT, presets, callback subscription).
  • src/plugins/microphone/microphone.test.ts — covers source-plugin behaviour (output non-silent, options validated, UI section descriptor preserved).
  • src/plugins/glados/glados.test.ts — covers voice-chain wiring, parameter setters, preset application.
  • src/plugins/game/game-audio.test.ts — covers spatial source lifecycle, music-layer crossfade, proximity-voice attenuation.
  • src/plugins/audio-player/audio-player.test.ts — covers load / play / stop / seek / loop / playbackRate, AbortSignal handling.
  • bunx turbo test:coverage --filter=@suaveplan/audio-plugins-fx — must hit 100/100/100/100 on every new module the same way the existing 19 do.

Migration impact

None — additive only. The five new subpath exports do not modify or remove any existing export. Consumers of the existing 19 plugins continue to work without changes. The split-audio-plugins-fx change (in-flight) will relocate every plugin including these five into its own package, but the API surface stays stable across that move.