Workstream F core. Pull-based InputSource interface (N axes + discrete actions), three sources (XYPadSource push-based, WebMidiInputSource with learn-map CCs/notes, GamepadSource single/double stick), and an InputLayer that composes active sources into one N-dim vector at the head of the reactive spine. Arity reduction blends >2 axes down to the fixed 2-input WASM head; the true multi-WASM reshape is a documented TODO (inputs-spec). useInputLayer is the React binding (enable/config/status/channel layout).
56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
/**
|
|
* XYPadSource — the existing on-screen XY pad / joystick / manifold drag,
|
|
* wrapped as a 2-axis InputSource.
|
|
*
|
|
* Push-based: the pad's `onMove(x, y)` handler calls `pushAxes(x, y)`, which
|
|
* latches the values. `sample()` reads them back through the standard pull path
|
|
* so the XY pad composes identically with poll-based sources (MIDI/gamepad).
|
|
*
|
|
* This is always available (it's an on-screen widget), so it starts `ready`.
|
|
*/
|
|
import { BaseSource } from './base-source';
|
|
import type { InputSourceKind } from './types';
|
|
|
|
export class XYPadSource extends BaseSource {
|
|
readonly kind: InputSourceKind = 'xy-pad';
|
|
readonly label = 'XY pad';
|
|
|
|
private x = 0.5;
|
|
private y = 0.5;
|
|
|
|
constructor() {
|
|
super({ state: 'ready', message: 'XY pad ready' });
|
|
}
|
|
|
|
axisCount(): number {
|
|
return 2;
|
|
}
|
|
|
|
axisLabels(): string[] {
|
|
return ['X', 'Y'];
|
|
}
|
|
|
|
/** Latch the latest pad position (∈ [0,1]). Called from the pad's onMove. */
|
|
pushAxes(x: number, y: number): void {
|
|
this.x = clamp01(x);
|
|
this.y = clamp01(y);
|
|
}
|
|
|
|
sample(out: Float32Array, offset: number): number {
|
|
out[offset] = this.x;
|
|
out[offset + 1] = this.y;
|
|
return 2;
|
|
}
|
|
|
|
start(): void {
|
|
this.setStatus({ state: 'ready', message: 'XY pad ready' });
|
|
}
|
|
|
|
stop(): void {
|
|
this.setStatus({ state: 'idle', message: 'XY pad off' });
|
|
}
|
|
}
|
|
|
|
function clamp01(v: number): number {
|
|
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
}
|