Commit graph

33 commits

Author SHA1 Message Date
monkey-w1n5t0n
8c249ea8af ci: restore verification — reachable submodule pin, codegen + WASM freshness gates
Phase 0 of the 2026-07 simplification audit (plan §1). CI has been 100% red on
main since 2026-07-13 and every "gates green" claim since rested on local runs.

- S7 / critic gap 2: push memllib `feat/nisps-core-swap` (3 commits incl. the
  pin b37fc53) to monkey-w1n5t0n/memllib and repoint .gitmodules at the fork.
  Those commits existed on exactly one disk; `git ls-remote` now resolves the
  pin, so `submodules: recursive` checkout and fresh clones work again. Drops
  the compensating unreachable-pin error paragraph in build-firmware-arch.sh.
- S24 / S31: the manifold-tests job regenerates from schemas/, runs the codegen
  golden test, and fails on a dirty diff — the "schema changes ship with both
  generated outputs" rule is now enforced rather than assumed.
- S32: a WASM freshness gate runs the parity harness against the *committed*
  manifold/public/nisps.{js,wasm} before the CI rebuild overwrites it. That
  artifact is what the webhook ships to production, so a stale commit now fails
  loudly instead of shipping.
- critic gap 3 / operator decision §7.4: the VPS webhook
  (~/.config/webhooks/meml-deploy.sh, not in this repo) waits for the `CI`
  workflow to conclude success on the pushed SHA before building. Fail-closed;
  MEML_SKIP_CI_GATE=1 for an emergency hand-deploy. Verified the gate query
  returns `failure` for fa37047, i.e. it would have blocked that deploy.
- S31: corrected run-all-tests.sh's false "single command CI invokes" header.

Docs moved with the code: ALIGNMENT defect 1 deleted (resolved) and the rest
renumbered; MAP.md's unreachable-pin warning replaced with the fork pin and a
pointer to the §7.5 vendoring decision; ONBOARDING documents the deploy gate
and the tracked-WASM-ships-to-prod hazard; plan §1 marked burned down.

Gates: scripts/run-all-tests.sh ALL GREEN (ctest 4/4, parity 1273 floats within
1e-5, lint, typecheck, 33 Playwright specs).
2026-07-21 11:57:32 +02:00
monkey-w1n5t0n
fd0aee2354 docs(audit): simplification audit 2026-07 — recon findings, phased plan, ALIGNMENT rewrite
66-agent adversarially-verified audit vs the five-bullet one-core vision.
113 findings: CI red since 2026-07-13 (memllib pin on no remote), ungated
deploys, unshared mode layer, no curated/advanced split, dead-mass inventory.
Recon: docs/specs/recon/simplification-audit-2026-07.md (immutable).
Plan: docs/specs/plans/simplification-plan.md (proposal; phases gated on
operator adoption, §7 decisions). ALIGNMENT rewritten; MAP flatly-false
lines fixed (phantom MEMLCelium-upstream entry, exploration.ts, daisysp
non-submodule, pre-P5 sentence, perf-attr claims); MAIN registry updated.
2026-07-21 01:24:35 +02:00
monkey-w1n5t0n
1b69254de2 feat(vcv): reunify module onto core MLP — thin iml.hpp adapter (P6)
Replace the vendored runtime MLP in vcv/src/iml.hpp (DetRng + 3D-weight-store
MLP + Dataset + IML) with a THIN, Rack-free adapter over the shared core:
nisps::ml::MLPCore<nisps::ml::DynamicStorage> (8->[16,24,16]->16, the P2 dynamic
case), nisps::Rng, and the core MLP's own FIFO dataset. Behaviour changes from
the vendored approximation to core-exact firmware/WASM semantics.

- MEMLNaut.cpp: staged/pending weight buffers and patch JSON now use the core's
  flat [weights..][biases..] vector (nisps::IML<float>::Weights); patch version
  bumped to 3. Double-buffer / single-writer threading discipline unchanged.
- New ctest tests/cpp/test_vcv_iml_parity.cpp: seeded train/infer/move_weights
  session through the adapter is memcmp-equal to a bare MLPCore<DynamicStorage>.
- Docs: vcv-module.md delta #5 marked CLOSED (2026-07-18); MAP.md vcv/ updated.

Closes vcv-module.md delta #5.
2026-07-18 13:01:28 +02:00
monkey-w1n5t0n
161ce155e4 feat(manifold): merge P5 — MF_MODES from schema truth; per-mode engine dims real 2026-07-18 12:46:24 +02:00
monkey-w1n5t0n
28e394ddf3 docs+ci: P5 doc sync; codegen golden wired into run-all-tests stage 5 2026-07-18 12:45:45 +02:00
monkey-w1n5t0n
6c499e6826 feat(manifold): P5.2/P5.3 — derive MF_MODES from schema truth + per-mode engine dims
Schema-backed modes in console/model.ts are now DERIVED from the codegen
schemas in src/modes/generated/ (source of truth): real param names/groups/
count, plus each mode's ml net shape (MFMode.ml) and schema engine_id. A thin
manifold OVERLAY supplies only label/glyph/ModeClass/input/ordering. New
browser-viable modes xiasri + slp_workshop get derived entries; schema-less
visualizer + c15 stay hand-written on DEFAULT_MODE_ML. Schema min/max/default/
label/curve surface as engine-unit metadata (schemaMin/... on MFParam) without
touching the 0..1 routing semantics.

Switching instrument mode reshapes the runtime-shaped WASM net to the mode's
schema ml config (ConsoleApp effect keyed on [engine, modeId]; no confirm
modal). Boot lands paf_synth dims (4->[10,10,14]->33) once WASM is ready. The
P2.3 axis-count reshape offer still reads the engine's live inputSize and does
not spuriously prompt on a mode switch.

Adds schema-modes.spec.ts (P5 gate): drives switches via a new window.__mf
debug seam and asserts describe() dims, getWeights count, output length/bounds,
and UI param count FROM the imported schemas; spot-checks trainAsync after a
switch. Updates reshape/probe-api/geo-dislike specs to assert from the boot
mode schema instead of the retired fixed 32/126 shape.

All gates green: typecheck, unit (9), build, e2e (33).
2026-07-18 12:45:06 +02:00
monkey-w1n5t0n
bd35432797 docs(plan): P4 burned down — pipelines/curves in core, browser firmware-exact
MAP: nisps/pipeline entry + manifold engine listing; ALIGNMENT: curve-maths
unification note; plan gate evidence incl. the proven-inherent f32 momentum
drift and parity stage 7.
2026-07-18 12:23:14 +02:00
monkey-w1n5t0n
1f4513802f docs: P3 software burned down — ADR §8 ALIGNMENT updates, MAP feedback/replay entries
- ALIGNMENT: retract the 2026-06-18 'geometric push not ported' accepted
  divergence (it IS ported); record the two deliberate divergences
  (useRandom via nisps::Rng; synchronous per-press SGD vs upstream's
  shuffled TrainBatch — behavioural parity with firmware, 1e-5 native↔WASM)
- MAP: nisps/ml gains replay/geo_push/warm_start; feedback is storage-
  policied with Geometric default + Diffuse legacy
- AGENT-REFERENCE: Jolt/OU/dislike TS-math limitation closed
- plan: P3 software marked landed; chokepoint A pending hardware + bug
  10c3e55c (explore wiring linker-GC'd out of PAFSynth ELF, pre-existing)
2026-07-14 04:56:18 +02:00
monkey-w1n5t0n
7e957457cd docs: P2 complete — burn down plan, close ALIGNMENT defect #3 (fixed WASM arity)
- plan P2 marked landed (hardware timing spot-check deferred to the
  chokepoint-B session on physical MEMLNaut)
- ALIGNMENT: defect #3 resolved by the runtime-shaped browser MLP
- MAP/AGENT-REFERENCE/nisps-wasm-README: describe the honoured-dims
  create + reshape ABI; note per-mode dims become schema-real at P5
- fresh wasm artifacts from the gate run
2026-07-14 03:56:04 +02:00
monkey-w1n5t0n
8a19e5b52c refactor(ml)!: P2.1 storage-policy split — MLPCore<Storage>, fixed + dynamic models
Algorithms (forward, backprop/SGD, init, move_weights, diagnostics) now live
once in MLPCore<Storage> (nisps/ml/mlp.hpp). Storage models:

- FixedStorage (storage.hpp): template-sized std::array, zero heap. The
  classic MLP<NIn,H1,H2,H3,NOut,...> is an alias preserving kInput/kHidden*/
  kOutput/kNumLayers/weight_count() constexpr — firmware + bindings + modes
  compile unchanged.
- DynamicStorage (dynamic_storage.hpp): runtime dims, ONE arena allocation
  at construction, nothing per-call. #error under NISPS_TARGET_EMBEDDED
  (new macro in core/perf.hpp); sole lint-cpp.sh heap-allowlist entry, plus
  a lint check that fails if the #error guard disappears.

Verification:
- new ctest test_mlp_storage_parity: fixed↔dynamic BIT-identical across
  init/draw/inference/train(FIFO)/move_weights(pin mask)/eval_loss/
  layer_stats/set_weights/infer_batch/reset; invalid+moved-from inert
- golden ML vectors (pre-refactor constants) pass → bit-stable refactor
- native↔WASM parity PASS, max delta unchanged (2.4e-7)
- chokepoint B compile: PAFSynth .text 122324→122692 (+0.30%, ±1% budget);
  RAM +416B (eval scratch)
- fix: firmware-common.sh used bare 'python' (absent here) → ${PYTHON:-python3}

Part of one-core-engine-refactor P2. nisps_ml_create ABI untouched (P2.2 is
an operator stop-point).
2026-07-13 23:47:03 +02:00
monkey-w1n5t0n
d1f1f45f6b docs(plan): P1 burned down; wire golden fixtures test into bun run test
- manifold package.json test script now includes tests/pipeline-golden.test.ts
- MAP.md: manifold exploration modules + tests/fixtures entries
- ONBOARDING.md §engine: exploration.ts + spine setOutputMorph + P3 swap point
- plan P1 marked landed with gate evidence
2026-07-13 23:30:28 +02:00
monkey-w1n5t0n
0d1a2102eb feat(manifold)!: P1 — retire playground/, manifold is the sole browser app
Playground archived at branch archive/playground-solidjs (tag
playground-solidjs-final) and deleted from main. Retargets:

- run-all-tests.sh stage 5 → manifold (typecheck + bun test + build +
  playwright via non-snap node runner, BUILD-PLAN gotcha)
- ci.yml playground-tests → manifold-tests; osc-bridge.yml → manifold/osc-bridge
  (was already broken: playground/osc-bridge no longer existed)
- codegen: TS emission target removed (returns at P5 → manifold); golden
  test now C++-only; TS emitters retained dormant
- .gitignore: manifold/osc-bridge paths; drop dead playground faust exception
- docs: AGENTS.md gates, README quickstart, MAP.md, ALIGNMENT.md (C15 now
  archive-only — defect #1 updated), AGENT-REFERENCE.md, specs/MAIN.md

Part of docs/specs/plans/one-core-engine-refactor.md P1.
2026-07-13 23:27:56 +02:00
monkey-w1n5t0n
29dc88be3a chore(build): P0 plumbing — WASM build/parity retarget to manifold/public
- build-wasm.sh emits to manifold/public/ (transitional copy to
  playground/public/ until P1 retires the playground)
- parity-check.sh + parity_wasm.mjs read the manifold artifact
- fix stale MLP<2,...> arity in AGENT-REFERENCE.md + nisps/wasm/README.md
- gitignore .claude/worktrees/
- plan one-core-engine-refactor.md: P0 marked landed

Gate: run-all-tests green; parity PASS from manifold artifact (2.4e-7);
manifold builds against freshly-built nisps.wasm.
2026-07-13 23:14:23 +02:00
monkey-w1n5t0n
c936bf75c9 docs(ergo): update task guidance 2026-07-13 22:58:26 +02:00
monkey-w1n5t0n
a9623d71d7 docs(plan): one-core-engine refactor plan — firmware+manifold reunification
Locked (operator 2026-07-13): retire playground/, all algorithms into
nisps/ C++, storage-policy MLP (fixed template on RP2350 / runtime-shaped
in WASM+VCV), codegen serves manifold, VCV last phase. Supersedes
BUILD-PLAN 'multiple WASM modules' MLP decision; fixes stale MLP<2,...>
in MAP.md.
2026-07-13 23:38:05 +03:00
monkey-w1n5t0n
45f3ca5cae docs: restructure design docs into docs/specs (adr/plans/recon), update path references 2026-07-13 23:15:46 +03:00
monkey-w1n5t0n
c986377b4c feat(firmware): reposition gesture — relocate an existing example to a new input
Add a "grab → move → drop" gesture that moves an existing positive
example's output to a new input position, preserving the output. This is
the new core's home for upstream InterfaceRL's drag-store/reposition-commit,
distinct from Explore→Place (which places newly-auditioned scratchpad sounds).

- nisps/ml/feedback.hpp: begin_reposition()/commit_reposition()/repositioning().
  Reuses the Placing state (static_output holds the carried vector) but a
  reposition_ flag makes commit AND the mode-switch teardown SKIP the weight
  restore — the real net is never set aside here, so restoring snapshot_ would
  clobber the live trained net. Guards cancel_place + abort_explore_place.
- firmware glue: state-gate Toggle B. Exploring → reroll/nudge (unchanged);
  Idle → MomB1 grab, MomB2 drop (commit + add_example + train). The 4D variant
  has no joystick button, so the gesture lives on the momentary toggle. Also
  fix a stale top-of-file control-map comment that contradicted the bindings.
- tests: 4 reposition cases (hold without snapshot; commit stores carried
  output with no restore; mode-switch aborts without clobber; begin-only-Idle).
- MAP.md: document the full ExploreAndPlace lifecycle + reposition wiring.

Audio-hold (carrying the sound audibly during the move) remains the existing
unwired static_output() TODO and affects Explore→Place identically.
Firmware compile unverified (no arduino-cli); host tests + lint pass.
2026-06-28 23:45:27 +02:00
monkey-w1n5t0n
2e75194fbb docs(spec): merge the two SLP-Workshop specs into one
Consolidate docs/slp-workshop/SPEC.md into docs/specs/slp-workshop-
firmware.md so the project has a single SLP-Workshop spec. The unified
spec is now two parts:
- Part I (§1-8, shipped/stable): the mode + Jolt & OU-explore gestures.
- Part II (§9-13, planned): the output-mode evolution — Continuous/Rhythm
  stream model, gate sequences, CV/MIDI output config, Manifold UX, and
  the locked operator decisions (2026-06-28).

Frontmatter bumped stable -> evolving (the doc now spans shipped +
planned). Donor docs/slp-workshop/SPEC.md removed; README.md row and
MAP.md ## Specs note updated. No code change.
2026-06-28 23:09:49 +02:00
monkey-w1n5t0n
68f7d681fe Merge remote-tracking branch 'origin/main' into workshop/synth-fw-audit 2026-06-28 22:36:59 +02:00
monkey-w1n5t0n
e28a4b0a3a docs(spec): add SLP-Workshop firmware spec (Jolt + OU-explore gestures)
Self-contained stable reference spec crystallizing the SLP-Workshop
firmware: the MEMLCelium-based mode, the two adaptive-learning gestures
ported from upstream InterfaceRL (Jolt held weight-morph, OU output
walk) with exact constants, the runtime-not-compile-time + inert-by-
default design, the ModeBase integration incl. the GCC -Wstringop-
overflow workaround, control mappings, schema/codegen, and the
browser-parity caveat. Adds the spec to docs/specs/README.md and a
## Specs section to MAP.md per the specs-skill config format.

Refs commits 4e60d01, 57c9ede (merged at 527b8fc).
2026-06-28 22:36:58 +02:00
monkey-w1n5t0n
4656568d4f feat(manifold,firmware): restore uSEQ CV/gate as an Outputs backend
Restores the April-2026 "uSEQ-Celium" functionality (browser → uSEQ
hardware + CV expander over USB Web Serial) as a first-class Manifold
Outputs backend, and re-vendors the RP2040 firmware into the repo.

- protocol v2 (uSEQ-CV): firmware/useq-celium/shared/protocol.h is the
  single source of truth, mirrored by manifold/src/backends/useq-protocol.ts.
  26-byte OUTPUT frame, 11×u16 CV (12-bit) + 3-gate bitfield + XOR; fixed
  topology; host-agnostic so the MEMLNaut RP2350 can emit identical bytes.
  Spec in docs/useq-celium/protocol.md.
- firmware/useq-celium/{main,expander}: PlatformIO RP2040 firmware rewritten
  to v2 from the real April pin maps (expander I2C addr 0x10).
- UseqCvBackend (id cvgate): Web Serial connect/identify/disconnect, 100 Hz
  stream, per-channel dead-zone, gate thresholding; modeled on midi-backend.
  Per-output CvSpec (channel + gateThreshold) on MFParam; config UI in
  OutputsBackendConfig + BackendAdvanced; new "CV / uSEQ" top-dock mode.
- bun-test for the protocol frame layout; MAP.md updated.
2026-06-28 22:30:54 +02:00
monkey-w1n5t0n
c45aff4411 Merge remote-tracking branch 'origin/main' into workshop/synth-fw-audit
# Conflicts:
#	nisps/modes/base.hpp
2026-06-28 22:29:41 +02:00
monkey-w1n5t0n
4e60d0192a feat(slp-workshop): new MEMLCelium-based mode + port Jolt & OU-noise RL learning
New SLP-Workshop firmware variant (Synth Library Portland), built on the
MEMLCelium engine + MLP shape. Ports the two post-fork learning-algorithm
changes from upstream memllib InterfaceRL into the shared nisps/ml core,
runtime-configurable (no compile-time switch), inert by default:

- nisps/ml/jolt.hpp: Jolt — held continuous weight morph over the flat
  weight buffer + post-release LR ramp (kJolt* constants verbatim).
- nisps/ml/ou_noise.hpp: OUNoise<N> — Ornstein-Uhlenbeck exploration walk
  on the output vector (theta=0.02, dt=0.001, kMaxAmplitude=0.65).

Both wired into ModeBase so every mode gains jolt_press/jolt_release/
jolt_lr_scale + set_explore_intensity; gated so existing modes stay
bit-identical (parity + golden tests green). Firmware surfaces them on
TogB1 (Jolt) and RVX1 (explore). New SLPWorkshopMode mode + schema +
codegen; firmware alias + .ino variant; playground mode registration.

Tests: jolt + OU unit tests, ModeBase learning integration incl. an
inert-parity test proving SLP-Workshop == MEMLCelium with features off.
Verified: cpp tests, wasm build, native↔wasm parity, lint, codegen
golden, playground typecheck. Firmware compile/e2e/hardware are
environment-bound (no arduino-cli/submodules/browser here).

Refs ergo 019f0fca.
2026-06-28 22:15:36 +02:00
monkey-w1n5t0n
3952c65d75 feat(firmware): on-device Joystick Dual/Single settings menu
Add glue/settings_view.hpp: wire_settings(mode) registers a "Joystick"
SingleSelectView on the MEMLNaut display carousel (TFT + rotary encoder, same
DisplayDriver as SystemView/SelfTest). For the 4-input two-joystick modes,
"Single" pins ML input channels 2,3 (the second joystick) to neutral via
ModeBase::set_input_pinned — the network is never rebuilt and trained state
survives toggling. Default is Dual. Only registered when input arity == 4.

Called from MEMLNaut-NISPS.ino setup() after addSystemInfoView(). MAP.md +
CLAUDE.md glue listings updated.

NOTE: compile-unverified — no arduino-cli/RP2350 toolchain on this host and no
hardware; needs scripts/build-firmware.sh + a flash test (chokepoint A).
2026-06-28 21:17:51 +02:00
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
monkey-w1n5t0n
5d3785a6a8 feat(firmware): ExternalSynthMIDIMode + 6 device variants
Joystick -> MLP -> MIDI CC for an external hardware synth, using the
compile-time device templates from nisps/midi/generated. ExternalSynthMIDIMode
<const MidiDevice&, NOut> mirrors SoundAnalysisMIDIMode (NoOpEngine,
kRouteOutputsToEngine=false, pushes ControlChange events). A consteval
pick_cc_slots curates which NOut params fill the output slots (prefers musical
params over Bank Select/global). Adds six flashable variants
(MEMLNautModeExtSynth{Sub37,SubPhatty,Pro12,AnalogKeys,Hydrasynth,JD800}) wired
into mode_select.hpp + the .ino variant list + NISPS_ST guards, and a src/nisps/
midi symlink so the sketch tree can reach nisps/midi.

Host-compile-verified under C++20 (incl. via the sketch include path) and
lint-clean; full arduino-cli build + flash is on the hardware (no toolchain here.
2026-06-28 20:13:08 +02:00
monkey-w1n5t0n
9a9b66c5ee feat(midi-devices): canonical external-synth CC templates + dual codegen
Add a durable, committed source of truth for external MIDI synth control:
- synth-midi-cc.json: verified CC maps + provenance/sources (8 devices researched)
- schemas/midi_device.schema.json + schemas/midi_devices/*.json: 6 CC-controllable
  device templates (Moog Sub 37/Sub Phatty, Creamware Pro-12 ASB, Elektron Analog
  Keys, ASM Hydrasynth, Roland JD-800), params keyed {id, cc, label, min, max,
  default, group}.
- codegen/generate-midi-devices.ts (isolated from the mode golden test) emits both
  nisps/midi/generated/midi_devices.hpp (no-heap constexpr, firmware+WASM) and
  manifold/src/midi-devices/generated/ (typed catalogue for the browser).
- codegen/seed-midi-devices.ts: reproducible seed from the research artifact.

Lets a performer pick a device and address its parameters by name (not CC number)
on both the firmware and the Manifold browser engine.
2026-06-28 20:03:17 +02:00
monkey-w1n5t0n
a109e0299d docs: sync MAP.md (manifold/ + vcv/ sections) and ALIGNMENT.md 2026-06-28 04:14:30 +02:00
w1n5t0n
3a8e2b8116 Stream 12: cleanup + docs (delete nisps-core, rewrite MAP/CLAUDE, create ALIGNMENT)
- Delete nisps-core/ (lessons absorbed into nisps/ml; firmware is canonical)
- Rewrite MAP.md to reflect new clean-slate layout (nisps/ + firmware/ + playground/ + schemas/ + codegen/)
- Rewrite CLAUDE.md as new architecture narrative
- Create ALIGNMENT.md with current strategic gaps + open mission questions

(meml-quc)
2026-04-29 19:57:27 +03:00
w1n5t0n
5fc37f760e Stream 6: extract firmware glue under firmware/
Move the Arduino sketch into firmware/MEMLNaut-NISPS/ and bridge the
hardware (memllib) to the platform-agnostic nisps/ library through a
slim glue layer. Delete the legacy root-level *AudioApp.hpp,
modes/MEMLNautMode*.hpp, voicespaces/, IMLInterface.hpp, XiasriAnalysis,
and the src/memlp submodule.

Glue layout (firmware/MEMLNaut-NISPS/glue/):
  audio_driver.hpp - bridge memllib block callback to Mode::process
                     via per-Mode templated trampoline (no virtual dispatch)
  peripherals.hpp  - joystick/pots/buttons -> Mode::set_input + ML primitives
  midi_io.hpp      - MIDI in -> mode.note_on/update_bpm/set_playing,
                     drains mode ControlEvent ring -> MIDI UART
  mode_select.hpp  - using-aliases mapping MEMLNautMode<Name> to
                     nisps::modes::*Mode (build script rewrites the
                     #define MEMLNAUT_MODE_TYPE line)
  input_router.hpp / output_router.hpp - top-level wire/drain entry points

The sketch tree uses src/{memllib,daisysp,nisps} symlinks because
Arduino-CLI rejects ".." in include paths from sketch-tree headers.
mode_select.hpp #undefs Arduino's sq/min/max/abs/round macros before
including nisps headers (some nisps engines use those identifiers as
method names). The audio bridge struct is extern in the header and
defined in the .ino because inline + __not_in_flash section attribute
collide at link time.

Verification: arduino-cli compile succeeds for PAFSynth, ChannelStrip,
and BreakOr (rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3,
-std=gnu++20). Host C++ tests under nisps/build still pass (3 binaries,
110+ tests). Build script (scripts/build-firmware.sh) updated to point
at the new sketch path; mode-rewrite logic unchanged.

Closes meml-gkm.
2026-04-29 17:05:38 +03:00
w1n5t0n
6b26168a29 Preserve firmware variant capitalization 2026-04-16 00:46:02 +09:00
w1n5t0n
39312a0d1f Add firmware variant selection to build scripts 2026-04-16 00:37:58 +09:00
w1n5t0n
314408aba9 Add firmware helper scripts and docs 2026-04-16 00:32:32 +09:00