# OpenSpec Change: enterprise-browser-audio-controllers - **Change ID**: enterprise-browser-audio-controllers - **Package**: `@suaveplan/audio-controllers` - **Category**: browser - **Target path**: `browser/audio/audio-controllers` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/audio-controllers.md` --- ## Summary Deliver `@suaveplan/audio-controllers` on the genesis branch from scratch at enterprise quality. The package ships MIDI hardware controller integrations built on `@suaveplan/audio-engine`: `KontrolS88Plugin` (88-key weighted keyboard, scale modes, arpeggiator, LED feedback), `MaschinePlugin` (16-pad controller, step sequencer, sample slots), and `TraktorS4Plugin` (4-channel DJ controller, crossfader, decks, LED feedback). Each plugin owns its MIDI mapping, state machine, parameters, and presets, integrating with the Web MIDI API and the audio graph. --- ## 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`, `PluginState`, `PluginParameter`, `PluginMetadata` | | `@suaveplan/error` (`workspace:^`) | Typed MIDI/controller errors | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `EventBusLike` | | `@suaveplan/events` (`workspace:^`) | `EventEmitter` for typed per-controller event maps | | `@suaveplan/schema-registry` (`workspace:^`) | Register preset Zod schemas | | `zod` (`catalog:forms`) | Schema for every `*Preset` type | | `@opentelemetry/api` (`catalog:observability`) | MIDI message counters, LED frame histograms | | `standardized-audio-context` (`catalog:browser`) | Web Audio context for audio node creation | | `tslib` (`catalog:build`) | Runtime helpers | Dev deps: `vitest`, `@vitest/coverage-istanbul` (`catalog:testing`), `@suaveplan/testing workspace:^`. --- ## Preserved public API surface (from main branch) ### KontrolS88Plugin ```ts class KontrolS88Plugin extends AudioPlugin { config: KontrolS88Config getPreset(): KontrolS88Preset applyPreset(preset: KontrolS88Preset): void setScale(mode: ScaleMode): void setArpMode(mode: ArpMode): void setPitchBend(value: number): void setModWheel(value: number): void sendLED(keyIndex: number, color: LEDColor): void onMIDIMessage(message: MIDIMessageEvent): void } // + KontrolS88Config, KontrolS88Events, KontrolS88Metrics, KontrolS88Preset, KontrolS88State // + ArpMode, ScaleMode, VelocityCurve, KeyState, TransportState, MIDIKontrolS88Mapping ``` ### MaschinePlugin ```ts class MaschinePlugin extends AudioPlugin { getPreset(): MaschinePreset applyPreset(preset: MaschinePreset): void setPadVelocity(padIndex: number, velocity: number): void setStep(padIndex: number, stepIndex: number, active: boolean): void loadSample(slotIndex: number, sampleId: string): Promise onMIDIMessage(message: MIDIMessageEvent): void } // + MaschineConfig, MaschineEvents, MaschinePreset, MaschineState, PadState, SequencerState, SampleSlotState ``` ### TraktorS4Plugin ```ts class TraktorS4Plugin extends AudioPlugin { getPreset(): TraktorS4Preset applyPreset(preset: TraktorS4Preset): void loadDeck(deckIndex: number, track: DeckTrack): void setCrossfader(value: number): void setChannelVolume(channel: number, value: number): void onMIDIMessage(message: MIDIMessageEvent): void } // + TraktorS4Config, TraktorS4Events, TraktorS4Preset, DeckState, DeckTrack, MixerState ``` --- ## New additions for genesis ### 1. `MidiControllerPlugin` base class Common abstract base class at `src/base/midi-controller-plugin.ts` owning: - Web MIDI `requestMIDIAccess()` with permission model (`MIDIUnavailable`, `MIDIPermissionDenied` typed errors) - Hot-plug: `navigator.requestMIDIAccess().onstatechange` listener; auto-rebind on reconnect; `device:connected` / `device:disconnected` events - LED diff/flush engine with per-frame byte budget - Declarative CC-mapping DSL: `cc(0x10).controls('encoder[0]')` with static type checking - `@suaveplan/events` `EventEmitter` — all three controllers use `EventEmitter<*Events>` instead of `EventTarget` ### 2. Structured errors via `@suaveplan/error` Error codes: `MIDI_UNAVAILABLE`, `MIDI_PERMISSION_DENIED`, `MIDI_DEVICE_DISCONNECTED`, `MIDI_SYSEX_REJECTED`, `CONTROLLER_PRESET_INVALID`, `SAMPLE_LOADER_FAILED`. ### 3. Arpeggiator / sequencer shared primitive `src/sequencer/sequencer.ts` — shared audio-clock-synced arpeggiator/step-sequencer primitive used by both Kontrol arpeggio and Maschine step sequencer. Clocked off the engine's audio clock, not `setInterval`. ### 4. Learn mode `src/learn/learn-mode.ts` — runtime CC-learn UX: listens for MIDI CC input, stores remapping via `@suaveplan/storage-browser` (injected adapter). ### 5. OTel metrics Counters `midi.messages_in_total`, `midi.messages_out_total`, `led.frames_total`, `midi.xrun_total`; histogram `midi.input_to_audio_latency_ms`. ### 6. Preset Zod schemas Zod schemas for `KontrolS88Preset`, `MaschinePreset`, `TraktorS4Preset` registered in `@suaveplan/schema-registry`. ### 7. SysEx safety SysEx handled only when `sysex: true` is requested in `requestMIDIAccess`. Fuzz test for malformed SysEx frames. --- ## File layout ```text browser/audio/audio-controllers/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts base/ midi-controller-plugin/ midi-controller-plugin.ts midi-controller-plugin.md midi-controller-plugin.types.ts midi-controller-plugin.test.ts index.ts plugins/ kontrol-s88/ mapping.ts (CC/note → parameter mapping DSL) state.ts (KeyState, TransportState machine) arpeggio.ts (arpeggiator with scale quantizer) scale.ts (scale/chord transform) preset.ts (KontrolS88Preset Zod schema + serializer) led.ts (LED feedback engine) kontrol-s88.ts (plugin class orchestrator) kontrol-s88.md kontrol-s88.types.ts kontrol-s88.test.ts index.ts maschine/ mapping.ts state.ts sequencer.ts (step-sequencer with step grid) sample-loader.ts (real sample-loader contract, no placeholder) preset.ts maschine.ts maschine.md maschine.types.ts maschine.test.ts index.ts traktor-s4/ mapping.ts deck.ts mixer.ts led.ts preset.ts traktor-s4.ts traktor-s4.md traktor-s4.types.ts traktor-s4.test.ts index.ts sequencer/ sequencer/ sequencer.ts sequencer.md sequencer.test.ts index.ts learn/ learn-mode/ learn-mode.ts learn-mode.md learn-mode.test.ts index.ts tests/ integration.test.ts e2e/ (Playwright specs with virtual MIDI device) ``` --- ## Impact **Downstream**: `@suaveplan/react-audio`, game audio surfaces. **Prerequisites**: `@suaveplan/audio-engine`, `@suaveplan/error`, `@suaveplan/events` published to Verdaccio.