6.4 KiB
6.4 KiB
Tasks: enterprise-browser-audio-controllers
Change ID: enterprise-browser-audio-controllers
Package: @suaveplan/audio-controllers
Target path: browser/audio/audio-controllers
Prerequisite packages on Verdaccio: @suaveplan/audio-engine, @suaveplan/error, @suaveplan/types, @suaveplan/events
1. Scaffold
- Create
browser/audio/audio-controllers/directory tree per layout inproposal.md. - Write
package.jsonat version0.1.0:"sideEffects": falseexports: top-level"."plus subpaths for each controller and sequencerdependencies:@suaveplan/audio-engine workspace:^,@suaveplan/error workspace:^,@suaveplan/types workspace:^,@suaveplan/events workspace:^,standardized-audio-context catalog:browser,zod catalog:forms,@opentelemetry/api catalog:observability,tslib catalog:builddevDependencies:vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,@suaveplan/testing workspace:^,@playwright/test catalog:testing
- Write
tsconfig.jsonandvitest.config.ts(pool: forks).
2. Error codes (src/error-codes.ts)
- Define
MidiControllerErrorCode:MIDI_UNAVAILABLE,MIDI_PERMISSION_DENIED,MIDI_DEVICE_DISCONNECTED,MIDI_SYSEX_REJECTED,CONTROLLER_PRESET_INVALID,SAMPLE_LOADER_FAILED. - Export
registerMidiControllerErrorCodes()and call eagerly.
3. Base class (src/base/midi-controller-plugin/)
- Implement abstract
MidiControllerPlugin extends AudioPlugin:requestMIDIAccess(sysex?: boolean): callsnavigator.requestMIDIAccess; throws typedMIDI_UNAVAILABLE/MIDI_PERMISSION_DENIEDon failure.onStateChange(event: MIDIConnectionEvent): detects disconnect / reconnect, emits typed events, auto-rebinds.flushLEDs(updates: LEDUpdate[]): diff engine with per-frame byte budget.defineMappings(mappings: CCMapping[]): CC-learn DSL storage.- Protected
EventEmitter<MidiControllerEventMap>member — all subclasses use it.
- Write
midi-controller-plugin.test.ts: permission flow, hot-plug disconnect + reconnect, LED flush budget. - Write
midi-controller-plugin.md.
4. Shared sequencer (src/sequencer/sequencer/)
- Implement
Sequencer: audio-clock-synced step grid, tempo (BPM), step on/off, playback start/stop/reset. - Clock source: inject via constructor (accepts
AudioContextforcurrentTime-based scheduling, notsetInterval). - Write
sequencer.test.ts,sequencer.md.
5. KontrolS88Plugin (src/plugins/kontrol-s88/)
mapping.ts: CC/note-number → parameter map, type-safe DSL.state.ts:KeyState[],TransportStatemachine.arpeggio.ts:ArpModeimplementations (up/down/random/chord), clock-synced via sharedSequencer.scale.ts:ScaleModequantizer (major, minor, chromatic, etc.), chord voicing.led.ts: per-key LED feedback, batch flush.preset.ts:KontrolS88PresetZod schema, serialize/deserialize.kontrol-s88.ts: plugin orchestrator, hooks intoMidiControllerPluginbase.kontrol-s88.test.ts: MIDI byte → state, state → MIDI out, velocity curve, scale transform, arpeggio clock, preset round-trip, hot-plug disconnect + reconnect.kontrol-s88.md.
6. MaschinePlugin (src/plugins/maschine/)
mapping.ts: pad/CC/button mapping.state.ts: pad grid state, group states.sequencer.ts: 16-step sequencer per pad using sharedSequencer.sample-loader.ts: realSampleLoaderinterface — no placeholder; loads viaAudioContext.decodeAudioData.preset.ts:MaschinePresetZod schema.maschine.ts: plugin orchestrator. Event system wired viaEventEmitter— no placeholder comments.maschine.test.ts: pad velocity, step grid, sample load, preset round-trip.maschine.md.
7. TraktorS4Plugin (src/plugins/traktor-s4/)
mapping.ts: CC/button mapping per channel.deck.ts:DeckState, load/play/pause/cue/sync, pitch/tempo.mixer.ts: channel volumes, EQ per channel, master gain.led.ts: LED ring/button feedback.preset.ts:TraktorS4PresetZod schema.traktor-s4.ts: plugin orchestrator. Event system wired — no placeholder comments.traktor-s4.test.ts: crossfader, deck sync, mixer state, preset round-trip.traktor-s4.md.
8. Learn mode (src/learn/learn-mode/)
- Implement
LearnMode: listens for MIDI CC events, stores custom remapping, persists via injectedStorageAdapter. - Default adapter: in-memory; inject
@suaveplan/storage-browserfor durable persistence. - Write
learn-mode.test.ts,learn-mode.md.
9. SysEx fuzz test (tests/integration.test.ts)
- Write a property-based test (fast-check) that feeds malformed SysEx byte arrays to
onMIDIMessageon each controller and verifies the plugin does not crash and emits a typedMIDI_SYSEX_REJECTEDevent or safely ignores.
10. Playwright E2E (tests/e2e/)
- Write E2E spec using a virtual MIDI device (Web MIDI API mock via Playwright page context) for each controller:
- KontrolS88: MIDI note-on →
KeyStateupdate, arpeggio output, LED feedback. - Maschine: pad velocity → step sequencer trigger.
- TraktorS4: crossfader CC → mixer state.
- KontrolS88: MIDI note-on →
- Verify hot-plug: simulate device disconnect + reconnect event.
11. OTel metrics
- Wire in
MidiControllerPluginbase:- Counter
midi.messages_in_total{controller},midi.messages_out_total{controller} - Counter
led.frames_total{controller} - Histogram
midi.input_to_audio_latency_ms{controller}
- Counter
12. Docs
- Write
.mdfor every exported module (≥200 words, API reference, examples). - Write
README.mdwith controller matrix table (every supported CC/note/pad per device), preset-pack list, MIDI permission guidance.
13. Verify and publish
bunx turbo lint typecheck test --filter=@suaveplan/audio-controllers— green.bunx turbo test:coverage --filter=@suaveplan/audio-controllers— 100%.bun run docs:check --filter=@suaveplan/audio-controllers— zero errors.- No
TODO, placeholder,any,ts-ignore,vi.mock,.skipinsrc/. - Playwright E2E green for all three controllers.
bun scripts/publish.ts --package @suaveplan/audio-controllers.- Archive to
openspec/archive/2026-04-24-enterprise-browser-audio-controllers/.