# maschine _(no submodules)_ ## Purpose `MaschinePlugin` is the concrete `MidiControllerPlugin` subclass for Native Instruments Maschine: a 16-pad grid with per-pad velocity, a 16-step sequencer row per pad, and slot-based sample loading. It inherits the universal MIDI lifecycle from the base and adds the device-specific surface declared by the spec: `setPadVelocity`, `setStep`, and `loadSample`. Sample loading is delegated to an injectable `SampleLoader` so the package stays universal and side-effect-free. ## Features - 16 pads × 16 steps internal grid. - Per-pad velocity table (0–127) with `MIDIOutput` echo. - `loadSample(slot, id, { signal })` honours `AbortSignal` both before and after the loader resolves. - Range-checked pad index, step index, velocity, and slot index. - Sample registry exposed via `getSample(slotIndex)`. ## Usage: Basic ```ts const maschine = new MaschinePlugin({ adapter, transport }); await maschine.initialize(); maschine.setPadVelocity(3, 96); maschine.setStep(3, 0, true); await maschine.loadSample(3, "kick-808"); ``` ## Usage: Advanced ```ts const ctl = new AbortController(); const maschine = new MaschinePlugin({ adapter, transport, sampleLoader: async (id, signal) => loadFromDrive(id, signal), }); await maschine.initialize({ signal: ctl.signal }); maschine.setPadVelocity(0, 127); maschine.setStep(0, 0, true); maschine.setStep(0, 4, true); maschine.setStep(0, 8, true); maschine.setStep(0, 12, true); await maschine.loadSample(0, "kick-808", { signal: ctl.signal }); ``` ## API Reference - `new MaschinePlugin({ adapter, transport, sampleLoader? })`. - `setPadVelocity(padIndex, velocity)` / `getPadVelocity(padIndex)`. - `setStep(padIndex, stepIndex, active)` / `getStep(padIndex, stepIndex)`. - `loadSample(slotIndex, sampleId, { signal? })` → `Promise`. - `getSample(slotIndex)` → `string | undefined`. ## Implementation Notes `loadSample` performs a pre-await abort check, awaits the loader, then re-checks the signal at the next await boundary — the spec's "mid-operation abort" guarantee. Any failure inside the loader is re-thrown as `AUDIO_CONTROLLERS.DEVICE_ERROR` carrying the original error as `cause`, so consumers can introspect transport-layer failures without re-implementing error mapping. The default loader is a no-op suitable for unit-test scenarios; production callers wire in their own.