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 16:05:38 +02:00
|
|
|
// firmware/glue/peripherals.hpp — Joystick, buttons, toggles → mode input
|
|
|
|
|
// channels.
|
|
|
|
|
//
|
|
|
|
|
// The MEMLNaut hardware exposes:
|
|
|
|
|
// - 3 analog joystick axes (X, Y, Z) via `setJoyXCallback` / `Y` / `Z`,
|
|
|
|
|
// each delivering a float in [0, 1].
|
|
|
|
|
// - Up to 5 rotary pots (RVGain1, RVZ1, RVY1, RVX1, ADC3) — same callback
|
|
|
|
|
// shape.
|
|
|
|
|
// - 4 momentary buttons (MomA1/2, MomB1/2) and 5 toggles (TogA1/2, TogB1/2,
|
|
|
|
|
// JoySW). Buttons fire `void()` on press; toggles fire `void(bool)` on
|
|
|
|
|
// edge.
|
|
|
|
|
//
|
|
|
|
|
// The `Mode` concept's input channels are abstract floats in [0, 1]. Each
|
|
|
|
|
// concrete mode's schema names its channels (e.g. paf_synth: joy_x, joy_y,
|
|
|
|
|
// joy_z, joy_w). The glue maps the *first N* analog inputs onto channels
|
|
|
|
|
// `[0, N)` — that's the trivial mapping that matches every concrete mode's
|
|
|
|
|
// schema today (joystick first, optional 4D extra pot for joy_w).
|
|
|
|
|
//
|
2026-06-28 23:44:57 +02:00
|
|
|
// Buttons/toggles drive the SHARED ExploreAndPlace lifecycle. The authoritative
|
|
|
|
|
// per-control mapping is documented at the binding site below; in summary:
|
|
|
|
|
// MomA1 (TA up) : enter / exit explore (toggle)
|
|
|
|
|
// MomA2 (TA down) : like — freeze current scratchpad output (begin place)
|
|
|
|
|
// MomB1 (MA up) : Exploring → reroll scratchpad; Idle → grab (reposition)
|
|
|
|
|
// MomB2 (MA down) : Exploring → nudge scratchpad; Repositioning → drop+train
|
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
|
|
|
// TogB1 : Jolt — held continuous weight morph (up=morph, down=freeze)
|
2026-06-28 23:44:57 +02:00
|
|
|
// TogB2 : commit place (rising-edge) — store +1 example + train
|
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
|
|
|
// RVX1 : exploration amount (Ornstein-Uhlenbeck output walk)
|
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 16:05:38 +02:00
|
|
|
//
|
2026-06-28 04:14:30 +02:00
|
|
|
// The button block below wires the SHARED ExploreAndPlace lifecycle
|
|
|
|
|
// (nisps/ml/feedback.hpp, the same core the browser drives via WASM) to the
|
|
|
|
|
// hardware buttons. See the per-button mapping at the binding site.
|
|
|
|
|
//
|
|
|
|
|
// FOLLOW-UP (needs a firmware build to verify — no arduino-cli here): while the
|
|
|
|
|
// controller is PLACING, the audition should hold feedback.static_output()
|
|
|
|
|
// (the frozen vector) instead of running fresh inference. That requires a hook
|
|
|
|
|
// in the control path (tick_control / audio_driver) to consult
|
|
|
|
|
// feedback.static_output() before ml().process() — not yet wired here because
|
|
|
|
|
// the controller lives in this translation unit. Wiring the buttons is the
|
|
|
|
|
// deliverable; the audition-hold is a small follow-up.
|
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 16:05:38 +02:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
#include "../src/nisps/core/perf.hpp"
|
2026-06-28 04:14:30 +02:00
|
|
|
#include "../src/nisps/ml/feedback.hpp"
|
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 16:05:38 +02:00
|
|
|
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
|
|
|
|
|
|
|
|
|
namespace nisps_firmware {
|
|
|
|
|
|
2026-06-28 04:14:30 +02:00
|
|
|
// Salt matching nisps/wasm/bindings.cpp so the firmware FeedbackController's
|
|
|
|
|
// per-instance Rng stream is independent of (and reproducible against) the
|
|
|
|
|
// MLP's inference/move RNG — same discipline as the browser.
|
|
|
|
|
inline constexpr std::uint64_t kFeedbackSalt = 0xFEEDBACC0DEull;
|
|
|
|
|
|
|
|
|
|
// Firmware undo-ring depth for ExploreAndPlace (rl-feedback-design §2.2: WASM
|
|
|
|
|
// D=4, firmware D=2 — SRAM budget).
|
|
|
|
|
inline constexpr std::size_t kFirmwareUndoDepth = 2u;
|
|
|
|
|
|
refactor(firmware): delete vendored daisysp and the input_router layer
Phase 1 group 8 (S8, ST2, L14).
- S8: the vendored src/daisysp tree (96 files, 41 .cpp compiled into every
firmware build) and its sketch-tree symlink. Zero consumers — nisps replaced
daisysp's PitchShifter with a custom granular implementation. Corrected
firmware/README.md and setup-firmware-toolchain.sh, which called it a live
submodule. The attribution comment in nisps/dsp/pitch_shift.hpp stays: it is
an honest provenance note about a port, not a dependency.
Noted while verifying: src/memllib/examples/KassiaAudioApp includes
../../daisysp/..., but examples/ is not symlinked into the sketch tree and is
never compiled by the firmware build, so the deletion stands.
- ST2: input_router.hpp was a zero-logic speculative layer with one consumer;
the .ino now calls bind_peripherals directly.
- L14: peripherals.hpp — deleted kAnalogInputCount, PeripheralBindings and the
unused spread local, and extracted the duplicated commit-and-train block into
one helper. The audit's suggested commit_and_train(mode, feedback) signature
could NOT be behaviour-identical: repositioning() implies placing() (both
live in ExploreState::Placing), so dispatching on feedback state inside the
helper would silently reroute a TogB2 press mid-reposition from commit_place
to commit_reposition, which differs (no snapshot restore, clears
reposition_). Implemented as commit_and_train(mode, feedback, bool
reposition) with the branch decided at the call site, preserving behaviour.
Firmware is not compiled by any gate yet (that arrives with the PlatformIO
migration, plan §5/S9), so this group is verified by reading and grep only.
Gates: run-all-tests.sh ALL GREEN.
2026-07-21 12:49:25 +02:00
|
|
|
// Shared commit path for the two "commit the held output at the CURRENT
|
|
|
|
|
// joystick input" gestures (MomB2 drop-reposition, TogB2 commit-place).
|
|
|
|
|
// Captures the joystick input BEFORE the commit (commit_place restores the
|
|
|
|
|
// real net), runs the caller-named commit, then stores the +1 example
|
|
|
|
|
// (input → committed output) and warm-start trains — the CALLER owns
|
|
|
|
|
// training. The commit is picked by the call site, not by feedback state:
|
|
|
|
|
// dispatching on repositioning() here would change what TogB2 does when
|
|
|
|
|
// pressed mid-reposition (today it takes the commit_place path).
|
|
|
|
|
template <typename Mode, typename FB>
|
|
|
|
|
inline void commit_and_train(Mode& mode, FB& feedback, bool reposition) {
|
|
|
|
|
// Capture the current joystick input — the DESTINATION position.
|
|
|
|
|
const auto in = mode.input_channels();
|
|
|
|
|
std::array<float, Mode::ML::kInput> features{};
|
|
|
|
|
for (std::size_t i = 0; i < Mode::ML::kInput; ++i) {
|
|
|
|
|
features[i] = (i < in.size()) ? in[i] : 0.f;
|
|
|
|
|
}
|
|
|
|
|
if (reposition) {
|
|
|
|
|
feedback.commit_reposition(); // no weight restore (net never set aside)
|
|
|
|
|
} else {
|
|
|
|
|
feedback.commit_place(mode.ml()); // restores the real net
|
|
|
|
|
}
|
|
|
|
|
const auto label = feedback.committed_output(); // valid post-commit
|
|
|
|
|
if (!label.empty()) {
|
|
|
|
|
mode.ml().add_example(
|
|
|
|
|
std::span<const float>(features.data(), Mode::ML::kInput), label);
|
|
|
|
|
(void)mode.ml().train();
|
|
|
|
|
}
|
|
|
|
|
}
|
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 16:05:38 +02:00
|
|
|
|
|
|
|
|
// Wire all hardware → mode bindings. Must be called *after*
|
|
|
|
|
// MEMLNaut::Initialize() (so MEMLNaut::Instance() is valid). The Mode
|
|
|
|
|
// reference must outlive the program (it's a static in the .ino).
|
|
|
|
|
template <typename Mode>
|
|
|
|
|
inline void bind_peripherals(Mode& mode) {
|
|
|
|
|
auto* meml = MEMLNaut::Instance();
|
|
|
|
|
if (!meml) return;
|
|
|
|
|
|
|
|
|
|
// ---- Analog inputs → mode input channels ----
|
|
|
|
|
meml->setJoyXCallback([&mode](float v) { mode.set_input(0u, v); });
|
|
|
|
|
meml->setJoyYCallback([&mode](float v) { mode.set_input(1u, v); });
|
|
|
|
|
meml->setJoyZCallback([&mode](float v) { mode.set_input(2u, v); });
|
|
|
|
|
// Fourth analog input: use ADC3 (the spare). Modes that don't expose a
|
|
|
|
|
// joy_w channel just don't see it (set_input drops out-of-range idx).
|
|
|
|
|
meml->setADC3Callback([&mode](float v) { mode.set_input(3u, v); });
|
|
|
|
|
|
|
|
|
|
// ---- Rotary pots ----
|
|
|
|
|
// Reuse RVGain1 as a "tempo" knob for sequencer modes. The mode is free
|
|
|
|
|
// to ignore via the same channel-bounds drop. RVGain1 is also used by
|
|
|
|
|
// InterfaceRL legacy as an audio output volume knob — for now we hand
|
|
|
|
|
// it to the ML interface for non-sequencer modes by setting the output
|
|
|
|
|
// master volume directly via AudioDriver.
|
|
|
|
|
meml->setRVGain1Callback([](float v) {
|
|
|
|
|
AudioDriver::SetMasterVolume(v);
|
|
|
|
|
});
|
|
|
|
|
|
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
|
|
|
// RVX1 → exploration amount. Drives the Ornstein-Uhlenbeck random walk
|
|
|
|
|
// added to the mode's output vector (nisps/ml/ou_noise.hpp, ported from
|
|
|
|
|
// upstream InterfaceRL). 0 = off (inert); turning it up makes the sound
|
|
|
|
|
// slowly roam so likes/dislikes can steer the net. Available on every
|
|
|
|
|
// mode via ModeBase::set_explore_intensity.
|
|
|
|
|
meml->setRVX1Callback([&mode](float v) {
|
|
|
|
|
mode.set_explore_intensity(v);
|
|
|
|
|
});
|
|
|
|
|
|
2026-06-28 04:14:30 +02:00
|
|
|
// ---- Buttons / toggles → ExploreAndPlace lifecycle (SHARED C++ core) ----
|
|
|
|
|
//
|
|
|
|
|
// The same nisps::ml::FeedbackController that runs in the browser (via
|
|
|
|
|
// WASM) drives the Idle → Exploring → Placing → Idle state machine here.
|
|
|
|
|
// The HARDWARE button mapping differs from the browser's on_down/on_up
|
|
|
|
|
// default policy — firmware maps its buttons to the GRANULAR core methods:
|
|
|
|
|
//
|
|
|
|
|
// MomA1 (TA up) : down — enter explore (Idle→Exploring) /
|
|
|
|
|
// exit explore (Exploring→Idle) TOGGLE
|
2026-06-28 23:44:57 +02:00
|
|
|
// MomB1 (MA up) : STATE-GATED — Exploring: reroll the scratchpad;
|
|
|
|
|
// Idle: GRAB (begin reposition — freeze the
|
|
|
|
|
// current trained-net output to carry it).
|
|
|
|
|
// MomB2 (MA down) : STATE-GATED — Exploring: nudge (small perturbation);
|
|
|
|
|
// Repositioning: DROP (commit the carried output
|
|
|
|
|
// at the current joystick input + train).
|
2026-06-28 04:14:30 +02:00
|
|
|
// MomA2 (TA down) : like — begin place (Exploring→Placing, freeze output)
|
|
|
|
|
// TogB2 (rising) : up — commit place (Placing→Idle) at the CURRENT
|
|
|
|
|
// joystick input, then store the +1 example and
|
|
|
|
|
// train (caller owns training). Outside Placing,
|
|
|
|
|
// a plain train().
|
|
|
|
|
//
|
|
|
|
|
// The controller is a function-local static so it lives for the whole
|
|
|
|
|
// program (like the mode). It is seeded off the same salt as the browser.
|
|
|
|
|
using FB = nisps::ml::FeedbackController<typename Mode::ML, kFirmwareUndoDepth>;
|
|
|
|
|
static FB feedback(static_cast<std::uint64_t>(0xC0FFEEu) ^ kFeedbackSalt);
|
|
|
|
|
feedback.set_mode(nisps::ml::FeedbackMode::ExploreAndPlace, mode.ml());
|
|
|
|
|
|
|
|
|
|
// MomA1: enter/exit explore toggle.
|
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 16:05:38 +02:00
|
|
|
meml->setMomA1Callback([&mode]() {
|
2026-06-28 04:14:30 +02:00
|
|
|
if (feedback.explore_state() == nisps::ml::ExploreState::Idle) {
|
|
|
|
|
feedback.enter_explore(mode.ml(), mode.param_schema().default_spread);
|
|
|
|
|
} else {
|
|
|
|
|
feedback.exit_explore(mode.ml());
|
|
|
|
|
}
|
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 16:05:38 +02:00
|
|
|
});
|
|
|
|
|
|
2026-06-28 23:44:57 +02:00
|
|
|
// MomB1: STATE-GATED.
|
|
|
|
|
// Exploring → reroll the scratchpad (a fresh random sound).
|
|
|
|
|
// Idle → GRAB: begin a reposition. Freezes the output currently heard
|
|
|
|
|
// from the trained net so the user can carry it to a new input
|
|
|
|
|
// location (the 4D variant has no joystick button, so the
|
|
|
|
|
// grab/drop gesture lives on this momentary toggle). The real
|
|
|
|
|
// net is NOT set aside — only the heard output is frozen.
|
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 16:05:38 +02:00
|
|
|
meml->setMomB1Callback([&mode]() {
|
2026-06-28 23:44:57 +02:00
|
|
|
switch (feedback.explore_state()) {
|
|
|
|
|
case nisps::ml::ExploreState::Exploring:
|
|
|
|
|
feedback.reroll(mode.ml(), mode.param_schema().default_spread);
|
|
|
|
|
break;
|
|
|
|
|
case nisps::ml::ExploreState::Idle:
|
|
|
|
|
feedback.begin_reposition(mode.ml()); // process + capture + hold
|
|
|
|
|
break;
|
|
|
|
|
case nisps::ml::ExploreState::Placing:
|
|
|
|
|
break; // already holding — ignore
|
|
|
|
|
}
|
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 16:05:38 +02:00
|
|
|
});
|
|
|
|
|
|
2026-06-28 23:44:57 +02:00
|
|
|
// MomB2: STATE-GATED.
|
|
|
|
|
// Exploring → nudge the scratchpad (small bounded perturbation).
|
|
|
|
|
// Repositioning → DROP: commit the carried output at the CURRENT joystick
|
|
|
|
|
// input, store the +1 example (new input → carried output)
|
|
|
|
|
// and warm-start train. This is the "move an existing
|
|
|
|
|
// positive example to a new position" gesture.
|
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 16:05:38 +02:00
|
|
|
meml->setMomB2Callback([&mode]() {
|
2026-06-28 23:44:57 +02:00
|
|
|
if (feedback.explore_state() == nisps::ml::ExploreState::Exploring) {
|
|
|
|
|
feedback.nudge(mode.ml(), 0.05f);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (feedback.repositioning()) {
|
refactor(firmware): delete vendored daisysp and the input_router layer
Phase 1 group 8 (S8, ST2, L14).
- S8: the vendored src/daisysp tree (96 files, 41 .cpp compiled into every
firmware build) and its sketch-tree symlink. Zero consumers — nisps replaced
daisysp's PitchShifter with a custom granular implementation. Corrected
firmware/README.md and setup-firmware-toolchain.sh, which called it a live
submodule. The attribution comment in nisps/dsp/pitch_shift.hpp stays: it is
an honest provenance note about a port, not a dependency.
Noted while verifying: src/memllib/examples/KassiaAudioApp includes
../../daisysp/..., but examples/ is not symlinked into the sketch tree and is
never compiled by the firmware build, so the deletion stands.
- ST2: input_router.hpp was a zero-logic speculative layer with one consumer;
the .ino now calls bind_peripherals directly.
- L14: peripherals.hpp — deleted kAnalogInputCount, PeripheralBindings and the
unused spread local, and extracted the duplicated commit-and-train block into
one helper. The audit's suggested commit_and_train(mode, feedback) signature
could NOT be behaviour-identical: repositioning() implies placing() (both
live in ExploreState::Placing), so dispatching on feedback state inside the
helper would silently reroute a TogB2 press mid-reposition from commit_place
to commit_reposition, which differs (no snapshot restore, clears
reposition_). Implemented as commit_and_train(mode, feedback, bool
reposition) with the branch decided at the call site, preserving behaviour.
Firmware is not compiled by any gate yet (that arrives with the PlatformIO
migration, plan §5/S9), so this group is verified by reading and grep only.
Gates: run-all-tests.sh ALL GREEN.
2026-07-21 12:49:25 +02:00
|
|
|
commit_and_train(mode, feedback, /*reposition=*/true);
|
2026-06-28 23:44:57 +02:00
|
|
|
}
|
2026-06-28 04:14:30 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// MomA2: like → begin place. Freezes the current scratchpad output so the
|
|
|
|
|
// user can move the joystick to choose WHERE to place it.
|
|
|
|
|
meml->setMomA2Callback([&mode]() {
|
|
|
|
|
feedback.begin_place(mode.ml());
|
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 16:05:38 +02:00
|
|
|
});
|
|
|
|
|
|
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
|
|
|
// TogB1: Jolt — held continuous weight morph (nisps/ml/jolt.hpp, ported
|
|
|
|
|
// from upstream InterfaceRL). Flip up to start morphing a scatter of
|
|
|
|
|
// weights live; flip down to freeze them (the change is permanent) and
|
|
|
|
|
// let the learning rate ramp gently back in. A toggle (not a momentary)
|
|
|
|
|
// because Jolt is a HELD gesture and momentary buttons only fire on
|
|
|
|
|
// press. Available on every mode via ModeBase::jolt_press/jolt_release.
|
|
|
|
|
meml->setTogB1Callback([&mode](bool state) {
|
|
|
|
|
if (state) mode.jolt_press();
|
|
|
|
|
else mode.jolt_release();
|
|
|
|
|
});
|
|
|
|
|
|
2026-06-28 04:14:30 +02:00
|
|
|
// TogB2 (rising): up → commit place at the current joystick input, then
|
|
|
|
|
// store the +1 example (input → placed output) and train. Outside Placing,
|
|
|
|
|
// just train (legacy behaviour).
|
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 16:05:38 +02:00
|
|
|
meml->setTogB2Callback([&mode](bool state) {
|
2026-06-28 04:14:30 +02:00
|
|
|
if (!state) return;
|
|
|
|
|
if (feedback.placing()) {
|
refactor(firmware): delete vendored daisysp and the input_router layer
Phase 1 group 8 (S8, ST2, L14).
- S8: the vendored src/daisysp tree (96 files, 41 .cpp compiled into every
firmware build) and its sketch-tree symlink. Zero consumers — nisps replaced
daisysp's PitchShifter with a custom granular implementation. Corrected
firmware/README.md and setup-firmware-toolchain.sh, which called it a live
submodule. The attribution comment in nisps/dsp/pitch_shift.hpp stays: it is
an honest provenance note about a port, not a dependency.
Noted while verifying: src/memllib/examples/KassiaAudioApp includes
../../daisysp/..., but examples/ is not symlinked into the sketch tree and is
never compiled by the firmware build, so the deletion stands.
- ST2: input_router.hpp was a zero-logic speculative layer with one consumer;
the .ino now calls bind_peripherals directly.
- L14: peripherals.hpp — deleted kAnalogInputCount, PeripheralBindings and the
unused spread local, and extracted the duplicated commit-and-train block into
one helper. The audit's suggested commit_and_train(mode, feedback) signature
could NOT be behaviour-identical: repositioning() implies placing() (both
live in ExploreState::Placing), so dispatching on feedback state inside the
helper would silently reroute a TogB2 press mid-reposition from commit_place
to commit_reposition, which differs (no snapshot restore, clears
reposition_). Implemented as commit_and_train(mode, feedback, bool
reposition) with the branch decided at the call site, preserving behaviour.
Firmware is not compiled by any gate yet (that arrives with the PlatformIO
migration, plan §5/S9), so this group is verified by reading and grep only.
Gates: run-all-tests.sh ALL GREEN.
2026-07-21 12:49:25 +02:00
|
|
|
commit_and_train(mode, feedback, /*reposition=*/false);
|
2026-06-28 04:14:30 +02:00
|
|
|
} else {
|
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 16:05:38 +02:00
|
|
|
(void)mode.ml().train();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace nisps_firmware
|