memlnaut-nisps/playground/src/modes/index.ts
w1n5t0n f916344c9b feat(playground/modes): all 9 mode bindings + /modes route (meml-yt7)
Stream 9 (part 2/2) — concrete mode TSX components + routing.

Each firmware mode pairs the generated schema with the appropriate
primary input (XYPad for xy_pad schemas, VirtualJoystick for joystick
schemas) and feeds the runtime's processedOutputs into the shared
SliderBank/OutputDisplay/LossPlot layout. The set:

  - PAFSynthMode (xy_pad, voice spaces)
  - ChannelStripMode (joystick)
  - XIASRIMode (joystick — mic-input wiring deferred to stream 10)
  - VerbFXMode (joystick, voice spaces)
  - MEMLCeliumMode (xy_pad, voice spaces)
  - BreakOrMode (xy_pad)
  - ElysiamorfMode (xy_pad)
  - SoundAnalysisMIDIMode (audio_in fallback to joystick;
      mic capture is a stream-10 task — UI scaffold renders today)
  - C15Mode (browser-only placeholder; bridge not yet ported)

App.tsx adds a /modes route that renders <ModeSwitcher /> + the active
mode's component via Solid <Dynamic />. modes/index.ts exposes the
registry the switcher consumes.
2026-04-29 17:00:56 +03:00

99 lines
2.9 KiB
TypeScript

/**
* Mode registry — maps mode_id (and the special "c15" placeholder) to the
* TSX component that renders it. The ModeSwitcher reads from this list to
* populate its dropdown, and `App.tsx` looks up the active mode here.
*
* Order matters — the switcher renders modes in this order.
*/
import type { Component } from 'solid-js';
import { PAFSynthMode } from './PAFSynthMode';
import { ChannelStripMode } from './ChannelStripMode';
import { XIASRIMode } from './XIASRIMode';
import { VerbFXMode } from './VerbFXMode';
import { MEMLCeliumMode } from './MEMLCeliumMode';
import { BreakOrMode } from './BreakOrMode';
import { ElysiamorfMode } from './ElysiamorfMode';
import { SoundAnalysisMIDIMode } from './SoundAnalysisMIDIMode';
import { C15Mode } from './C15Mode';
export interface ModeRegistration {
/** Stable id; matches the schema's `mode_id` for firmware modes. */
id: string;
/** Human-readable label for the switcher. */
label: string;
/** Short description for the switcher tooltip. */
description: string;
/** TSX component rendering the mode. */
Component: Component;
/** True for browser-only placeholders (currently just C15). */
placeholder?: boolean;
}
export const MODE_REGISTRY: ReadonlyArray<ModeRegistration> = [
{
id: 'paf_synth',
label: 'PAF Synth',
description: 'Phase-aligned formant synth (XY pad).',
Component: PAFSynthMode,
},
{
id: 'channel_strip',
label: 'Channel Strip',
description: 'EQ + dynamics processing channel.',
Component: ChannelStripMode,
},
{
id: 'xiasri',
label: 'XIASRI',
description: 'Audio-reactive verb / pitch engine.',
Component: XIASRIMode,
},
{
id: 'verb_fx',
label: 'Verb FX',
description: 'Reverb / multi-effects unit.',
Component: VerbFXMode,
},
{
id: 'memlcelium',
label: 'MEML Celium',
description: 'Voice + dual-MLP CV/gate via uSEQ.',
Component: MEMLCeliumMode,
},
{
id: 'breakor',
label: 'Breakor',
description: 'Drum / breakbeat synthesis.',
Component: BreakOrMode,
},
{
id: 'elysiamorf',
label: 'Elysiamorf',
description: 'Granular morphing synth.',
Component: ElysiamorfMode,
},
{
id: 'sound_analysis_midi',
label: 'Sound Analysis → MIDI',
description: 'Audio features → MIDI CC routing.',
Component: SoundAnalysisMIDIMode,
},
{
id: 'c15',
label: 'C15 (browser-only)',
description: 'C15 WASM synth. Not yet ported.',
Component: C15Mode,
placeholder: true,
},
];
/** Look up a mode by id, falling back to the first registration. */
export function getModeById(id: string | null): ModeRegistration {
if (!id) return MODE_REGISTRY[0]!;
return MODE_REGISTRY.find((m) => m.id === id) ?? MODE_REGISTRY[0]!;
}
export { ModeShell } from './ModeShell';
export { useModeRuntime } from './mode-runtime';
export type { ModeRuntime } from './mode-runtime';