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

7.1 KiB

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

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

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<void>
  onMIDIMessage(message: MIDIMessageEvent): void
}
// + MaschineConfig, MaschineEvents, MaschinePreset, MaschineState, PadState, SequencerState, SampleSlotState

TraktorS4Plugin

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

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.