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).
This commit is contained in:
parent
2f9cfd1de2
commit
3952c65d75
4 changed files with 70 additions and 1 deletions
|
|
@ -58,7 +58,8 @@ firmware/MEMLNaut-NISPS/
|
||||||
│ ├── midi_io.hpp # MIDI in → mode handlers; drain ControlEvent ring → MIDI UART
|
│ ├── midi_io.hpp # MIDI in → mode handlers; drain ControlEvent ring → MIDI UART
|
||||||
│ ├── mode_select.hpp # type aliases firmware mode name → nisps::modes::*Mode
|
│ ├── mode_select.hpp # type aliases firmware mode name → nisps::modes::*Mode
|
||||||
│ ├── input_router.hpp # wire_inputs() entry point
|
│ ├── input_router.hpp # wire_inputs() entry point
|
||||||
│ └── output_router.hpp # drain_outputs() entry point
|
│ ├── output_router.hpp # drain_outputs() entry point
|
||||||
|
│ └── settings_view.hpp # wire_settings(): TFT/rotary menu (Joystick Dual/Single for 4-in modes)
|
||||||
└── src/{memllib,daisysp,nisps} # symlinks (Arduino-CLI sketch tree convention)
|
└── src/{memllib,daisysp,nisps} # symlinks (Arduino-CLI sketch tree convention)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
1
MAP.md
1
MAP.md
|
|
@ -23,6 +23,7 @@ MEMLNaut-NISPS — Neural Interactive Shaping of Parameter Spaces. One C++20 cod
|
||||||
- `mode_select.hpp` — type aliases mapping firmware mode identifiers to `nisps::modes::*Mode` C++ types. Build script rewrites the active line. Includes the six `MEMLNautModeExtSynth*` external-synth variants (one per device template in `nisps/midi`, e.g. `MEMLNautModeExtSynthSub37`). Also defines the `MEMLNautModeSelfTest` pseudo-variant (tag type) + the `NISPS_ST_*`/`NISPS_ST_CAT` token-paste macros the `.ino` uses to compute `NISPS_SELFTEST`. Note: `src/nisps/` exposes each referenced top-level nisps subdir as a symlink — `midi` was added alongside `core/dsp/engines/ml/modes`.
|
- `mode_select.hpp` — type aliases mapping firmware mode identifiers to `nisps::modes::*Mode` C++ types. Build script rewrites the active line. Includes the six `MEMLNautModeExtSynth*` external-synth variants (one per device template in `nisps/midi`, e.g. `MEMLNautModeExtSynthSub37`). Also defines the `MEMLNautModeSelfTest` pseudo-variant (tag type) + the `NISPS_ST_*`/`NISPS_ST_CAT` token-paste macros the `.ino` uses to compute `NISPS_SELFTEST`. Note: `src/nisps/` exposes each referenced top-level nisps subdir as a symlink — `midi` was added alongside `core/dsp/engines/ml/modes`.
|
||||||
- `selftest.hpp` — standalone guided hardware self-test rig (`SelfTest` variant; no engine/ML). Step-driven state machine on a `SelfTestView`: TFT prompts the operator through every control, auto-advances on detection, encoder-press skips. Ends with optional L/R/BOTH sine-sweep headphone check (core 1 block callback) + MIDI loopback-cable test. Lives firmware-side (touches TFT + raw pins) so it stays out of platform-agnostic `nisps/`.
|
- `selftest.hpp` — standalone guided hardware self-test rig (`SelfTest` variant; no engine/ML). Step-driven state machine on a `SelfTestView`: TFT prompts the operator through every control, auto-advances on detection, encoder-press skips. Ends with optional L/R/BOTH sine-sweep headphone check (core 1 block callback) + MIDI loopback-cable test. Lives firmware-side (touches TFT + raw pins) so it stays out of platform-agnostic `nisps/`.
|
||||||
- `input_router.hpp`, `output_router.hpp` — top-level `wire_inputs()` / `drain_outputs()` entry points.
|
- `input_router.hpp`, `output_router.hpp` — top-level `wire_inputs()` / `drain_outputs()` entry points.
|
||||||
|
- `settings_view.hpp` — `wire_settings(mode)`: adds on-device settings views to the MEMLNaut display carousel (TFT + rotary encoder). Joystick Dual/Single toggle for the 4-input ("two 2-D joystick") modes — "Single" pins ML input channels 2,3 to neutral via `ModeBase::set_input_pinned` (no net rebuild). Registered in the `.ino` after `addSystemInfoView()`.
|
||||||
- `firmware/MEMLNaut-NISPS/src/{memllib,daisysp,nisps}` — symlinks (Arduino-CLI requires sketch-tree includes; preprocessor refuses `..` in headers).
|
- `firmware/MEMLNaut-NISPS/src/{memllib,daisysp,nisps}` — symlinks (Arduino-CLI requires sketch-tree includes; preprocessor refuses `..` in headers).
|
||||||
- `firmware/README.md` — structure + build instructions.
|
- `firmware/README.md` — structure + build instructions.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
#include "glue/midi_io.hpp"
|
#include "glue/midi_io.hpp"
|
||||||
#include "glue/mode_select.hpp"
|
#include "glue/mode_select.hpp"
|
||||||
#include "glue/output_router.hpp"
|
#include "glue/output_router.hpp"
|
||||||
|
#include "glue/settings_view.hpp"
|
||||||
|
|
||||||
// ---- Mode selection ----
|
// ---- Mode selection ----
|
||||||
// Build script rewrites which line is uncommented.
|
// Build script rewrites which line is uncommented.
|
||||||
|
|
@ -141,6 +142,8 @@ void setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MEMLNaut::Instance()->addSystemInfoView();
|
MEMLNaut::Instance()->addSystemInfoView();
|
||||||
|
// Settings menu (e.g. Joystick: Dual/Single for the 4-input modes).
|
||||||
|
nisps_firmware::wire_settings(g_mode);
|
||||||
Serial.println("Finished initialising core 0.");
|
Serial.println("Finished initialising core 0.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
64
firmware/MEMLNaut-NISPS/glue/settings_view.hpp
Normal file
64
firmware/MEMLNaut-NISPS/glue/settings_view.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// glue/settings_view.hpp — on-device settings menu views (TFT + rotary encoder).
|
||||||
|
//
|
||||||
|
// Adds entries to the MEMLNaut display carousel (the same DisplayDriver the
|
||||||
|
// SystemView and SelfTest use). Navigation: rotate to move between views;
|
||||||
|
// press to focus a view; rotate-while-focused to change its value; press
|
||||||
|
// again to unfocus. See src/memllib/.../display/DisplayDriver.hpp.
|
||||||
|
//
|
||||||
|
// Currently provides:
|
||||||
|
// * Joystick: Dual / Single — for the 4-input ("two 2-D joystick") modes,
|
||||||
|
// toggles whether the SECOND joystick (input channels 2,3) feeds the ML
|
||||||
|
// engine. "Single" pins those channels to the neutral value (0.5) via the
|
||||||
|
// platform-agnostic ModeBase pin mechanism, so the network is never
|
||||||
|
// rebuilt and trained state survives toggling. Default is Dual (the 4-D
|
||||||
|
// variant naturally uses both joysticks).
|
||||||
|
//
|
||||||
|
// Only registered for modes whose input arity is exactly 4 (the two-joystick
|
||||||
|
// modes); other modes (e.g. SoundAnalysisMIDI, whose joystick channels are not
|
||||||
|
// 2,3) get no joystick toggle.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "../src/memllib/hardware/memlnaut/MEMLNaut.hpp"
|
||||||
|
#include "../src/memllib/hardware/memlnaut/display/SingleSelectView.hpp"
|
||||||
|
|
||||||
|
namespace nisps_firmware {
|
||||||
|
|
||||||
|
// The second 2-D joystick maps to ML input channels 2 and 3 (joy_z / joy_w).
|
||||||
|
inline constexpr std::size_t kSecondJoystickCh0 = 2u;
|
||||||
|
inline constexpr std::size_t kSecondJoystickCh1 = 3u;
|
||||||
|
|
||||||
|
// Add the settings views for `mode` to the display carousel. Must run AFTER
|
||||||
|
// MEMLNaut::Initialize() (so the display + TFT exist) and after
|
||||||
|
// addSystemInfoView(), on core 0.
|
||||||
|
template <typename ModeT>
|
||||||
|
inline void wire_settings(ModeT& mode) {
|
||||||
|
if constexpr (ModeT::input_channel_count() == 4u) {
|
||||||
|
MEMLNaut* meml = MEMLNaut::Instance();
|
||||||
|
if (!meml || !meml->disp) return;
|
||||||
|
|
||||||
|
// Kept alive for the program lifetime (the carousel holds a shared_ptr
|
||||||
|
// too, but this guarantees ownership regardless of view churn).
|
||||||
|
static std::shared_ptr<SingleSelectView> joystick_view;
|
||||||
|
joystick_view = std::make_shared<SingleSelectView>("Joystick");
|
||||||
|
meml->disp->AddView(joystick_view); // calls Setup() → creates the selector
|
||||||
|
|
||||||
|
// Index 0 = Dual (default, no pinning); index 1 = Single.
|
||||||
|
std::array<String, 2u> options = { String("Dual"), String("Single") };
|
||||||
|
joystick_view->setOptions(std::span<String>(options.data(), options.size()));
|
||||||
|
joystick_view->setNewVoiceCallback([&mode](std::size_t idx) {
|
||||||
|
const bool single = (idx == 1u);
|
||||||
|
mode.set_input_pinned(kSecondJoystickCh0, single);
|
||||||
|
mode.set_input_pinned(kSecondJoystickCh1, single);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
(void)mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nisps_firmware
|
||||||
Loading…
Reference in a new issue