memlnaut-nisps/nisps/wasm
monkey-w1n5t0n 9e59eb04ce feat(manifold): MIDI + game controller inputs; widen ML net to N-D
Wire the modular input layer into the Console and reshape the browser
engine so input axes are genuine independent dimensions.

Inputs (manifold/src/inputs/):
- gamepad-source: emit press+release edges with standard-mapping labels
  (enables hold-and-move); single/double-stick already present.
- midi-input-source: single-device selection + batch "MIDI Learn"
  (every CC swept while armed becomes an axis); notes stay discrete.
- input-layer: compose() forwards each axis 1:1 (no mean-blend);
  add onReducedInput so the manifold tracks gamepad/MIDI position.
- types: InputAction.phase, InputMode.

Console (manifold/src/console/):
- ConsoleApp: bind gamepad buttons to verdicts (RB up / LB down /
  X randomise / Y nudge / B undo / A-hold reposition); mirror composed
  position onto the manifold.
- Drawers: rebuilt Inputs drawer (source picker, gamepad legend, MIDI
  device picker + batch-learn flow, learned-control meters).

Engine (nisps/wasm, manifold/src/engine):
- DefaultMLP widened MLP<2,..> -> MLP<32,..> (32 = MAX_AXES); each
  active axis gets a dedicated slot, unused slots held at 0 (inert).
  Rebuilt nisps.wasm (playground + manifold).
- spine/engine-api: setInputs writes the full N-D vector (was dropping
  arr[2+]); primary pair keeps the 2-D pipeline; process() re-ticks the
  whole vector via spine.reprocess().

Tests:
- parity_check/parity_wasm: ParityMLP -> 32 inputs, widen example bufs.
- CMakeLists: build parity binary with -ffp-contract=off so native
  matches FMA-free WASM (training amplified the gap past 1e-5).

Inputs dock is still an exclusive picker; mixing toggles, reshape modal,
and the >2-D slider view (inputs-spec.md) are groundwork-laid but not
yet wired. See docs/redesign/midi-gamepad-inputs-worklog.md.
2026-06-28 21:05:48 +02:00
..
bindings.cpp feat(manifold): MIDI + game controller inputs; widen ML net to N-D 2026-06-28 21:05:48 +02:00
README.md feat(playground/wasm): WASM bridge between C++ core and SolidJS playground (meml-tgm) 2026-04-29 16:36:29 +03:00

nisps/wasm

Emscripten target that exposes nisps/ml (MLP) and nisps/engines (audio engines) to the SolidJS playground via a flat C ABI.

This directory is a leaf — it does not export headers for inclusion by other C++ code. The only artifact is bindings.cpp plus the build script that turns it into playground/public/nisps.{wasm,js}.

Building

scripts/build-wasm.sh

Requires emcc (Emscripten). The script defaults to /usr/lib/emscripten/emcc and respects an EMCC env var override.

Output:

  • playground/public/nisps.wasm — the compiled module.
  • playground/public/nisps.js — Emscripten glue (factory function createNispsModule, MODULARIZE=1).

Both files are committed (so the playground works from a fresh clone without a C++ toolchain). Re-run build-wasm.sh after changes to nisps/{core,ml,engines,wasm}.

Architecture limit (read this)

The MLP class template is parametrised on (input_size, hidden1, hidden2, hidden3, output_size). WASM cannot recompile templates at runtime, so this build instantiates exactly ONE configuration:

nisps::ml::MLP<2, 10, 14, 18, 126>

That serves the playground use case (2-D joystick → up to 126 synth parameters). nisps_ml_create() accepts caller-supplied dimensions for forward compatibility but currently ignores them — see comment at the top of bindings.cpp. The schemas in schemas/modes/*.json use up to output_size=126; modes whose output_size is < 126 simply ignore the trailing entries.

To support additional architectures, either:

  1. Compile multiple wasm modules (nisps_small.wasm, nisps_default.wasm, …) and let the playground load the right one based on the active mode.
  2. Add a runtime-shape MLP variant to nisps/ml (heap allocation only at create(); no impact on hot paths).

Both options are deferred to a future stream.

C API surface

See bindings.cpp for the full list. Summary:

Group Functions
ML life nisps_ml_create, nisps_ml_destroy, nisps_ml_reset
ML I/O nisps_ml_set_input, nisps_ml_process, nisps_ml_outputs, nisps_ml_infer_batch
Training nisps_ml_add_example, nisps_ml_train, nisps_ml_eval_loss, nisps_ml_clear_examples, nisps_ml_example_count
Weights nisps_ml_weight_count, nisps_ml_get_weights, nisps_ml_set_weights, nisps_ml_draw_weights, nisps_ml_move_weights
Diag nisps_ml_get_layer_stats, nisps_ml_describe
Engines nisps_engine_create, nisps_engine_destroy, nisps_engine_set_params, nisps_engine_process_block

Engine-id strings follow the C++ engine_id() constexpr accessors: thru, paf_synth, channel_strip, xiasri, verb_fx, memlcelium, breakor, elysiamorf, analysis. Unknown ids fall back to thru (silent passthrough).