feat(backends): register VcvBackend in manager + roster; add defaultVcvSpec, feedback forwarding, input-vector streaming
This commit is contained in:
parent
f05669969f
commit
ecb5bf2bb6
3 changed files with 40 additions and 2 deletions
|
|
@ -16,6 +16,8 @@ export { WebMidiBackend } from './midi-backend';
|
|||
export type { MidiBackendConfig } from './midi-backend';
|
||||
export { OscBridgeBackend } from './osc-backend';
|
||||
export type { OscBackendConfig } from './osc-backend';
|
||||
export { VcvBackend } from './vcv-backend';
|
||||
export type { VcvBackendConfig, VcvFeedbackOp } from './vcv-backend';
|
||||
export { NispsOscClient } from './osc-client';
|
||||
export { PassthroughBackend } from './passthrough-backend';
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -21,12 +21,19 @@ import type { BackendId } from '../dock/output-state';
|
|||
import { WebMidiBackend } from './midi-backend';
|
||||
import { OscBridgeBackend } from './osc-backend';
|
||||
import { PassthroughBackend } from './passthrough-backend';
|
||||
import { VcvBackend, type VcvFeedbackOp } from './vcv-backend';
|
||||
|
||||
/** The slice of EngineApi the manager depends on (keeps it decoupled/testable). */
|
||||
export interface ManagerEngine {
|
||||
subscribe(cb: () => void): () => void;
|
||||
routedOutput(): Float32Array | null;
|
||||
audio: { setMuted(muted: boolean): void };
|
||||
/**
|
||||
* Current control input vector (2-D for the fixed 2→N MLP). Optional — when
|
||||
* present the VCV backend streams it to the module so the browser drives the
|
||||
* module's inputs in bridged mode.
|
||||
*/
|
||||
inputVector?(): ReadonlyArray<number>;
|
||||
}
|
||||
|
||||
export class BackendManager {
|
||||
|
|
@ -49,12 +56,17 @@ export class BackendManager {
|
|||
['synth', backends?.synth ?? new PassthroughBackend('synth', 'Built-in Synth — audio plays in the engine')],
|
||||
['particles', backends?.particles ?? new PassthroughBackend('particles', 'Particle visualiser')],
|
||||
['cvgate', backends?.cvgate ?? new PassthroughBackend('cvgate', 'CV / gate (via VCV bridge)')],
|
||||
['vcv', backends?.vcv ?? new PassthroughBackend('vcv', 'VCV bridge')],
|
||||
['vcv', backends?.vcv ?? new VcvBackend()],
|
||||
]);
|
||||
|
||||
// Single subscription to the spine: forward routed → active backend.
|
||||
// Single subscription to the spine: forward routed → active backend. For the
|
||||
// VCV backend we also stream the current input vector each tick so the
|
||||
// browser drives the module's inputs in bridged mode.
|
||||
this.unsub = this.engine.subscribe(() => {
|
||||
if (!this.active) return;
|
||||
if (this.active instanceof VcvBackend && this.engine.inputVector) {
|
||||
this.active.setInputVector(this.engine.inputVector());
|
||||
}
|
||||
const routed = this.engine.routedOutput();
|
||||
if (routed) this.active.send(routed);
|
||||
});
|
||||
|
|
@ -71,6 +83,25 @@ export class BackendManager {
|
|||
return b instanceof OscBridgeBackend ? b : null;
|
||||
}
|
||||
|
||||
vcv(): VcvBackend | null {
|
||||
const b = this.backends.get('vcv');
|
||||
return b instanceof VcvBackend ? b : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward a verdict op to the VCV module's embedded learner over the bridge.
|
||||
* No-op unless the VCV backend is the ACTIVE one — the verdict loop only
|
||||
* trains the module when Mode = VCV (otherwise the browser engine is the
|
||||
* learner). Returns true if it was forwarded.
|
||||
*/
|
||||
forwardFeedback(op: VcvFeedbackOp): boolean {
|
||||
if (this.activeId !== 'vcv') return false;
|
||||
const vcv = this.vcv();
|
||||
if (!vcv) return false;
|
||||
vcv.sendFeedback(op);
|
||||
return true;
|
||||
}
|
||||
|
||||
get(id: BackendId): OutputBackend | undefined {
|
||||
return this.backends.get(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,3 +130,8 @@ export function defaultMidiSpec(index: number): MidiCcSpec {
|
|||
export function defaultOscSpec(name: string): OscSpec {
|
||||
return { path: `/nisps/${name.toLowerCase()}`, rangeMin: 0, rangeMax: 1 };
|
||||
}
|
||||
|
||||
/** Default VCV spec for an output — unipolar 0–10 V by default. */
|
||||
export function defaultVcvSpec(): VcvSpec {
|
||||
return { bipolar: false };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue