# OpenSpec Change: enterprise-audio-controllers - **Change ID**: enterprise-audio-controllers - **Package**: `@suaveplan/audio-controllers` - **Category**: browser - **Target path**: `packages/audio/audio-controllers` - **Status**: PROPOSED - **Date**: 2026-05-14 - **Design**: `./design.md` - **Spec delta**: `./specs/audio-controllers/spec.md` --- ## Summary Deliver `@suaveplan/audio-controllers` on the genesis branch from scratch at enterprise quality. MIDI hardware controller integrations for the genesis monorepo. Built on `@suaveplan/audio-core` and `@suaveplan/audio-transport` for audio-clock-synced sequencing. Provides a universal `MidiControllerPlugin` base class with hot-plug support, LED feedback, CC-mapping DSL, and learn mode. Controllers run in the browser (Web MIDI API) but their sequencer/arpeggio output feeds into the universal `Transport` and `TimelineEngine`, so patterns can be rendered offline or on the server. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The old `enterprise-browser-audio-controllers` was browser-only and not tied to the universal transport. --- ## Parity Checklist (vs upstream `../suaveplan/packages/browser/audio-controllers/`) | Capability | Upstream | Migrated | Notes | |---|:---:|:---:|---| | `KontrolS88Plugin` (88-key weighted keyboard) | ✅ | ✅ | Same surface; now consumes `audio-transport` for scale-aware arpeggios instead of `setInterval`. | | Scale modes (`major`, `minor`, `chromatic`, …) | ✅ | ✅ | Unchanged enum. | | Arpeggiator | ✅ | ⬆️ | Now transport-synced via `audio-transport` `Transport.schedule`; upstream used `setInterval`. | | `KontrolS88Preset` save / load | ✅ | ✅ | Now Zod-validated through the package's own preset schemas. | | Pitch-bend / mod-wheel CC mapping | ✅ | ✅ | Same mapping; routed through the new `bindParameter` helper for FX surfaces. | | LED feedback / `flushLEDs` | ✅ | ✅ | Same surface; batched per requirement "MIDI input and output mapping". | | `MaschinePlugin` (16-pad + step sequencer + sample slots) | ✅ | ✅ | Step sequencer now drives off `Sequencer`/`Transport`; sample loading accepts `AbortSignal`. | | `TraktorS4Plugin` (4-channel DJ controller) | ✅ | ✅ | Same surface; deck-sync now reads transport tempo from `ChainContext.ambient.transport` when hosted in a chain. | | Web MIDI access via `navigator.requestMIDIAccess` | ✅ | ⬆️ | Now reached only through the injected `MidiAccessAdapter` seam; tests inject fake-device fixtures rather than mocking the platform. | | Connection-state FSM | ⚠ ad-hoc | ⬆️ | Now `@suaveplan/fsm`-driven with explicit `disconnected/connecting/connected/error` and graceful state recovery on reconnect. | | Learn mode for CC mapping | – | ⬆️ | Genesis addition. | | Plugin base class (`AudioPlugin`) | ✅ via `audio-engine` | ⬆️ | Replaced by `MidiControllerPlugin` base whose plugins integrate with `@suaveplan/audio-plugins-fx`'s `AudioFxPlugin` for parameter binding via `bindParameter`. | | `*.types.ts` files duplicating implementation declarations | ⚠ smell | ⬆️ | Consolidated; one types file per feature module. | | Unit tests / coverage | ⚠ one `.spec.ts` only | ⬆️ | 100/100/100/100 across every module with real fake-device fixtures (no mocks). | | Storybook coverage | – | – | n/a — controllers are non-visual. | No 🚫 rows. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/audio-core` (`workspace:^`) | `AudioDocument`, `BlockProcessor` | | `@suaveplan/audio-transport` (`workspace:^`) | `Transport`, `TransportTime` | | `@suaveplan/events` (`workspace:^`) | Typed event bus for controller events | | `@suaveplan/error` (`workspace:^`) | `AudioControllersError` base class; `AUDIO_CONTROLLER.*` codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike` | | `@suaveplan/opentelemetry` (`workspace:^`) | OTel spans, counters, histograms | | `zod` (`catalog:forms`) | Zod schemas for option types | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`). --- ## Public API ### Core types ```ts abstract class MidiControllerPlugin { readonly metadata: ControllerMetadata async requestAccess(sysex?: boolean): Promise onStateChange(event: MIDIConnectionEvent): void flushLEDs(updates: LEDUpdate[]): void defineMappings(mappings: CCMapping[]): void enterLearnMode(): void exitLearnMode(): void on(event: K, listener: ControllerEventMap[K]): void off(event: K, listener: ControllerEventMap[K]): void readonly sequencer: Sequencer initialize(): Promise destroy(): Promise } class Sequencer { constructor(transport: Transport) setTempo(bpm: number): void setSteps(steps: boolean[]): void setStep(index: number, active: boolean): void start(): void stop(): void reset(): void onStep(index: number, callback: () => void): void } class KontrolS88Plugin extends MidiControllerPlugin { setScale(mode: ScaleMode): void setArpMode(mode: ArpMode): void setPitchBend(value: number): void setModWheel(value: number): void sendLED(keyIndex: number, color: LEDColor): void } class MaschinePlugin extends MidiControllerPlugin { setPadVelocity(padIndex: number, velocity: number): void setStep(padIndex: number, stepIndex: number, active: boolean): void loadSample(slotIndex: number, sampleId: string): Promise } class TraktorS4Plugin extends MidiControllerPlugin { loadDeck(deckIndex: number, track: DeckTrack): void setCrossfader(value: number): void setChannelVolume(channel: number, value: number): void } ``` --- ## New additions for genesis 1. **Universal sequencer base** — `Sequencer` uses `@suaveplan/audio-transport` for sample-accurate scheduling, not `setInterval`. 2. **Transport-synced arpeggiator** — arpeggio patterns are `Transport` event subscribers, renderable offline. 3. **Hot-plug support** — `onStateChange` handles connect/disconnect with graceful state recovery. 4. **CC mapping DSL** — declarative control-change to parameter mapping with learn mode. 5. **LED feedback** — bidirectional MIDI for controller LED/state updates. 6. **OTel instrumentation** — spans for MIDI message handling, sequencer step timing. 7. **Property-based tests** — random step patterns → correct sequence, random CC values → mapped parameters. 8. **Benchmark suite** — MIDI message throughput, sequencer scheduling latency. --- ## File layout ```text packages/audio/audio-controllers/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts base/ midi-controller-plugin.ts midi-controller-plugin.md midi-controller-plugin.types.ts midi-controller-plugin.test.ts index.ts sequencer/ sequencer.ts sequencer.md sequencer.types.ts sequencer.test.ts index.ts arpeggiator/ arpeggiator.ts arpeggiator.md arpeggiator.test.ts index.ts learn/ learn-mode.ts learn-mode.md learn-mode.test.ts index.ts plugins/ kontrol-s88/ kontrol-s88.ts, kontrol-s88.md, kontrol-s88.test.ts mapping.ts, state.ts, scale.ts, led.ts, preset.ts index.ts maschine/ maschine.ts, maschine.md, maschine.test.ts mapping.ts, state.ts, sample-loader.ts, preset.ts index.ts traktor-s4/ traktor-s4.ts, traktor-s4.md, traktor-s4.test.ts mapping.ts, deck.ts, mixer.ts, led.ts, preset.ts index.ts __benchmarks__/ midi.bench.ts tests/ integration.test.ts e2e/ kontrol-s88.e2e.test.ts maschine.e2e.test.ts traktor-s4.e2e.test.ts ``` --- ## Impact **Breaking changes**: none — fresh build, package does not yet exist on genesis. **Prerequisites**: `@suaveplan/audio-core`, `@suaveplan/audio-transport`, `@suaveplan/events`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/opentelemetry`, `@suaveplan/fsm` published to Verdaccio. ### Affected dependents No dependents in this batch (verified by `bunx turbo ls --filter=...@suaveplan/audio-controllers`). `@suaveplan/audio-controllers` is a leaf integration package — it is consumed directly by application code (`@suaveplan/react-audio*`, game audio surfaces), none of which exist on genesis in this 13-change audio batch. Each future consumer is created against this package's spec delta, not retrofitted. Once landed, any subsequent change to this package's public surface MUST run `bunx turbo test --filter=...@suaveplan/audio-controllers` per `openspec/AGENTS.md` §13 step 10.