Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
# OpenSpec Change: enterprise-react-audio
- **Change ID**: enterprise-react-audio
- **Package**: `@suaveplan/react-audio`
- **Category**: react
- **Target path**: `packages/react/react-audio`
- **Status**: PROPOSED
- **Date**: 2026-05-15
- **Design**: `./design.md`
- **Spec delta**: `./specs/react-audio/spec.md`
> Replaces the prior 2026-04-24 draft that was flagged `NEEDS-REWRITE` because it targeted the obsolete monolithic `@suaveplan/audio-engine` and predated the universal `audio-core` + `audio-timeline` + `audio-transport` split. See `../REACT_AUDIO_WAVE_REWRITE_COMPLETE.md`.
---
## Summary
Deliver `@suaveplan/react-audio` on genesis from scratch at enterprise quality. The package is the React surface for the unified audio architecture: it composes `@suaveplan/audio-engine-browser` (the `TimelineAdapter` rendering backend), `@suaveplan/audio-fx-plugin-base` (the `AudioFxPlugin` contract + `PluginChain` primitive + `AmbientState`), `@suaveplan/audio-timeline` (`TimelineEngine`), `@suaveplan/audio-transport` (musical clock), and `@suaveplan/audio-controllers` (MIDI binding) into:
1. **A single React context provider** (`AudioProvider`) that owns one engine adapter, one timeline engine, one transport, one project-level bus registry, and one plugin registry for the React subtree below it. Strict-mode safe (double-render does not produce double-engine). Disposable; unmounting the provider tears down the engine cleanly.
2. **A typed hook layer** that exposes the unified architecture without leaking engine types: `useAudioEngine`, `useTimeline`, `useTransport`, `useTrack(id)`, `usePluginChain(trackId)`, `useChainPlugin(trackId, slotIndex)`, `useParameter(plugin, paramId)` (subscribes to the parameter's per-block value with sample-bounded updates), `usePreset(plugin)`, `useBus(name)`, `useAnalyser(trackId | "master")`, `useTransportTime()`, `useEngineState()`, `useMidiController(controllerId)`. Hooks subscribe via the plugin / chain / transport event surfaces; updates are React-batched.
3. **A component library** that maps the contract to UI: `<TrackList>`, `<TrackRow>`, `<PluginChainView>` (drag-to-reorder, drop-to-insert), `<PluginSlot>` (bypass toggle, dry/wet for plugins that expose it, parameter knobs/sliders rendered from `parameterIndex`, preset selector), `<MasterMeter>`, `<TransportControls>` (play/pause/stop/seek/loop/tempo), `<Mixer>` (track strips with gain/pan/mute/solo/sends), `<MicrophoneControls>`, `<GLaDOSControls>`, `<MidiControllerSetup>` (learn-mode panel), `<PresetManager>` (chain snapshot save/restore via Zod-validated JSON).
4. **Zustand stores** for cross-component UI state that does not belong on the engine (panel open/closed, theme, visualization mode, drag state).
5. **First-class accessibility** — every parameter control is keyboard-driven (arrow keys for fine, page-up/down for coarse, shift for ×10, alt for ×0.1), every knob/slider has a labelled `aria-valuetext` with the unit, every chain UI is screen-reader navigable.
6. **Storybook stories** for every component with default + interactive + edge-case states (empty chain, bypassed plugin, mid-load reverb IR, sidechain bound).
7. **100% test coverage** with happy-dom + injected `OfflineAudioContext` per `audio-engine-browser`'s test seam.
The package contains **no DSP, no plugin instance state, no transport scheduler, no parameter automation logic** — every one of those lives below and is consumed by reference. React's job is presentation and event wiring.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build replacing the obsolete pre-tier 2026-04-24 draft. The upstream `react/media/react-audio/` is built on the monolithic `@suaveplan/audio-engine`; its public surface (12 hooks, 14 components, 2 stores) is captured here and re-derived against the unified architecture so prior consumer expectations carry over where they remain meaningful.
---
## Parity Checklist (vs upstream `../suaveplan/packages/react/media/react-audio/`)
| Capability | Upstream | Migrated | Notes |
|---|:---:|:---:|---|
| AudioProvider context | ✅ | ✅ | Now wraps `BrowserTimelineAdapter` + `PluginRegistry` + `BusRegistry` instead of the old monolithic engine. |
| useAudioEngine hook | ✅ | ⬆️ | Now exposes the `TimelineEngine` + `Transport` + chain APIs, not the obsolete `AudioEngine` class. |
| useAudioPlayer | ✅ | ⬆️ | Built on `AudioPlayerPlugin` from `@suaveplan/audio-fx-plugin-audio-player`; reads transport time from `useTransportTime`. |
| useAudioRecorder | ✅ | ✅ | Wraps `MediaRecorder` + the engine's tap-to-bus surface for in-app capture. |
| useAudioVisualizer | ✅ | ✅ | Reads `useAnalyser(trackId)` rather than reaching into the engine. |
| useDragDropQueue | ✅ | ✅ | Reuses the existing drag/drop utilities; unchanged surface. |
| useKeyboardShortcuts | ✅ | ✅ | Unchanged. |
| useMobileGestures | ✅ | ✅ | Unchanged. |
| usePlaylist | ✅ | ✅ | Built on `AudioPlayerPlugin`'s loaded-source queue. |
| usePlugin (single plugin) | ✅ | ✅ | Now over `AudioFxPlugin` from `@suaveplan/audio-fx-plugin-base`. |
| usePluginChain | ✅ | ⬆️ | Now reflects the universal `PluginChain` primitive (insert / remove / move / replace / bypass / snapshot / restore / events). |
| usePresetManager | ✅ | ⬆️ | Plugin-level via `usePreset(plugin)` AND chain-level via `useChainSnapshot(chain)` for FX-rack-style "track presets". |
| useQueue | ✅ | ✅ | Unchanged. |
| audio-store (Zustand) | ✅ | ⬆️ | Slimmer; no playback state (that lives on transport). UI state only. |
| ui-store (Zustand) | ✅ | ✅ | Unchanged. |
| audio-dashboard component | ✅ | ✅ | Re-enabled; tree-shaking proven via subpath export bundle test. |
| audio-player component | ✅ | ✅ | Re-enabled; consumes `AudioPlayerPlugin`. |
| audio-provider component | ✅ | ⬆️ | Now also owns `PluginRegistry` and `BusRegistry`. |
| audio-visualizer | ✅ | ✅ | Same surface; reads `useAnalyser`. |
| basic-player | ✅ | ✅ | Slim variant. |
| premium-player | ✅ | ⬆️ | Adds chain UI for inserting FX plugins from the registry. |
| plugin-chain UI | ✅ | ⬆️ | Drag-to-reorder, drop-to-insert, bypass toggle, parameter rendering from `parameterIndex`. |
| plugin-controls (EQ/reverb/delay/compressor) | ✅ | ✅ | Replaced with generic `<PluginSlot>` that renders any `AudioFxPlugin` from its `parameters` declaration. EQ/reverb/delay/compressor get optional specialised UIs that opt in. |
| plugin-loader UI | ✅ | ⬆️ | Now reads from `PluginRegistry.list()`; supports dynamic registration. |
| mixer | ✅ | ✅ | Built on `MixerPlugin` from `@suaveplan/audio-fx-plugin-mixer`. |
| preset-manager | ✅ | ✅ | Chain-snapshot save/restore. |
| microphone-controls | ✅ | ✅ | Built on `MicrophonePlugin`. |
| vocoder-controls (GLaDOS) | ✅ | ✅ | Built on `GLaDOSPlugin`. |
| MIDI controller setup (NEW) | – | ⬆️ | Genesis addition; learn-mode panel for `MidiControllerPlugin` from `audio-controllers`. |
| Storybook coverage | ⚠ partial | ✅ | Every component gets default / interactive / edge-case stories. |
| Coverage | ~70 % | ✅ | 100/100/100/100 per genesis policy. |
No 🚫 rows.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/audio-engine-browser` (`workspace:^`) | `BrowserTimelineAdapter` + `DefaultBusRegistry` |
| `@suaveplan/audio-timeline` (`workspace:^`) | `TimelineEngine`, `Track`, `Clip` |
| `@suaveplan/audio-transport` (`workspace:^`) | `Transport` musical clock |
| `@suaveplan/audio-fx-plugin-base` (`workspace:^`) | `AudioFxPlugin`, `PluginChain`, `PluginRegistry`, every plugin |
| `@suaveplan/audio-controllers` (`workspace:^`) | `MidiControllerPlugin`, `bindParameter` |
| `@suaveplan/events` (`workspace:^`) | Typed event subscriptions for hook updates |
| `@suaveplan/error` (`workspace:^`) | `defineCodes` + `AudioError` |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
| `@suaveplan/opentelemetry` (`workspace:^`) | OTel spans on lifecycle events |
| `react` (`peerDependency`) | React 19 peer |
| `zustand` (`catalog:state`) | UI store |
| `framer-motion` (`catalog:animation`) | Knob / slider animation |
| `lucide-react` (`catalog:ui`) | Icons |
| `zod` (`catalog:forms`) | Provider-options validation |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `happy-dom`, `@storybook/react`, `@testing-library/react`, `@testing-library/user-event`, `playwright` (all `catalog:testing`).
---
## File layout
```text
packages/react/react-audio/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts (+ .test.ts .md)
provider/
audio-provider.tsx (+ .md .types.ts .test.tsx .stories.tsx)
provider-options.ts (+ .schemas.ts .md .test.ts)
index.ts
hooks/
use-audio-engine.ts (+ .md .test.tsx)
use-timeline.ts (+ .md .test.tsx)
use-transport.ts (+ .md .test.tsx)
use-transport-time.ts (+ .md .test.tsx)
use-engine-state.ts (+ .md .test.tsx)
use-track.ts (+ .md .test.tsx)
use-plugin-chain.ts (+ .md .test.tsx)
use-chain-plugin.ts (+ .md .test.tsx)
use-audio-fx-plugin.ts (+ .md .test.tsx)
use-parameter.ts (+ .md .test.tsx)
use-preset.ts (+ .md .test.tsx)
use-chain-snapshot.ts (+ .md .test.tsx)
use-bus.ts (+ .md .test.tsx)
use-analyser.ts (+ .md .test.tsx)
use-audio-recorder.ts (+ .md .test.tsx)
use-audio-visualizer.ts (+ .md .test.tsx)
use-drag-drop-queue.ts (+ .md .test.tsx)
use-keyboard-shortcuts.ts (+ .md .test.tsx)
use-mobile-gestures.ts (+ .md .test.tsx)
use-playlist.ts (+ .md .test.tsx)
use-queue.ts (+ .md .test.tsx)
use-midi-controller.ts (+ .md .test.tsx)
index.ts
stores/
audio-store.ts (+ .md .test.ts)
ui-store.ts (+ .md .test.ts)
index.ts
components/
transport-controls/ (.tsx .md .test.tsx .stories.tsx)
track-list/ ...
track-row/ ...
plugin-chain-view/ ...
plugin-slot/ ...
plugin-parameter-knob/ ...
plugin-parameter-slider/ ...
preset-selector/ ...
preset-manager/ ...
master-meter/ ...
mixer/ ...
audio-provider-component/ ...
audio-visualizer/ ...
audio-dashboard/ ...
basic-player/ ...
premium-player/ ...
audio-player/ ...
microphone-controls/ ...
vocoder-controls/ ...
plugin-loader/ ...
midi-controller-setup/ ...
a11y/
keyboard-bindings.ts (+ .md .test.ts)
aria-value-text.ts (+ .md .test.ts)
index.ts
__benchmarks__/
hook-rerender.bench.ts
chain-ui.bench.ts
tests/
integration.test.tsx
purity.test.ts
exports.test.ts
bundle-size/
hooks-only.size.test.ts
provider-only.size.test.ts
e2e/
audio.spec.ts
plugin-chain.spec.ts
midi-learn.spec.ts
```
---
## Impact
**Breaking changes**: none — fresh build.
**Prerequisites**: `audio-engine-browser`, `audio-fx-plugin-base` + every `audio-fx-plugin-*` package, `audio-controllers`, `audio-timeline`, `audio-transport`, `events`, `error`, `types`, `opentelemetry` shipped to Verdaccio.
### Affected dependents
- `@suaveplan/react-audio-player` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: separate change `enterprise-react-audio-player` consumes this package's hooks + provider; created against this spec, not retrofitted)
- `@suaveplan/react-audio-stream` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: separate change `enterprise-react-audio-stream` builds on this package's transport hooks; created against this spec)
- `@suaveplan/react-audio-fingerprint` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: separate change `enterprise-react-audio-fingerprint` consumes `useAnalyser`; created against this spec)
- `@suaveplan/r3f-audio` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: separate change `enterprise-gaming-r3f-audio` consumes `useTransport` + the game-audio building blocks from the `@suaveplan/audio-fx-plugin-*` family; created against this spec)
Once landed, any subsequent change to this package's public surface MUST run `bunx turbo test --filter=...@suaveplan/react-audio` per `openspec/AGENTS.md` §13 step 10.