test(nisps/modes): host C++ tests for stream 4 mode layer (meml-beb)
Adds `nisps_modes_tests` executable to nisps/CMakeLists.txt with four TUs:
- `test_mode_concepts.cpp`: 8 `static_assert(Mode<...>)` (concept
satisfaction), plus runtime metadata sanity for each mode (mode_id,
input_channel_count, schema sizes match engine param_count).
- `test_mode_paf_synth.cpp`: end-to-end exercise — setup, set_input,
tick_control, process audio. Verifies idle process is finite, output
bounds [0,1] hold, note_on triggers nonzero audio, input clamping,
and engine/ml accessors round-trip.
- `test_mode_voice_space.cpp`: voice-space round trip for PAFSynth,
ChannelStrip and VerbFX (all dispatched modes). Confirms
out-of-range index is silently ignored.
- `test_mode_breakor_events.cpp`: sequencer event pumping. BreakOr
emits Clock + NoteOn/Off, Elysiamorf emits CC, SoundAnalysisMIDI
converts 8 ML outputs → 8 ControlEvents (CC 0..7) per tick, ring
buffer overflow drops cleanly.
Total: 22 new tests (110 across nisps/), all passing under -Werror
-Wpedantic. Build remains clean.
2026-04-29 15:27:03 +02:00
|
|
|
// tests/cpp/test_mode_concepts.cpp — compile-time check that every
|
|
|
|
|
// concrete mode in nisps/modes/ satisfies the `nisps::Mode` concept.
|
|
|
|
|
//
|
|
|
|
|
// Failure here = compilation failure; if this TU compiles, the assertions
|
|
|
|
|
// have already passed. The runtime test bodies just sanity-check schema
|
|
|
|
|
// metadata (mode_id, input_channel_count, schema sizes match expectations).
|
|
|
|
|
|
|
|
|
|
#include "test_helpers.hpp"
|
|
|
|
|
|
|
|
|
|
#include "../../nisps/modes/breakor.hpp"
|
|
|
|
|
#include "../../nisps/modes/channel_strip.hpp"
|
|
|
|
|
#include "../../nisps/modes/elysiamorf.hpp"
|
|
|
|
|
#include "../../nisps/modes/memlcelium.hpp"
|
|
|
|
|
#include "../../nisps/modes/paf_synth.hpp"
|
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
|
|
|
#include "../../nisps/modes/slp_workshop.hpp"
|
test(nisps/modes): host C++ tests for stream 4 mode layer (meml-beb)
Adds `nisps_modes_tests` executable to nisps/CMakeLists.txt with four TUs:
- `test_mode_concepts.cpp`: 8 `static_assert(Mode<...>)` (concept
satisfaction), plus runtime metadata sanity for each mode (mode_id,
input_channel_count, schema sizes match engine param_count).
- `test_mode_paf_synth.cpp`: end-to-end exercise — setup, set_input,
tick_control, process audio. Verifies idle process is finite, output
bounds [0,1] hold, note_on triggers nonzero audio, input clamping,
and engine/ml accessors round-trip.
- `test_mode_voice_space.cpp`: voice-space round trip for PAFSynth,
ChannelStrip and VerbFX (all dispatched modes). Confirms
out-of-range index is silently ignored.
- `test_mode_breakor_events.cpp`: sequencer event pumping. BreakOr
emits Clock + NoteOn/Off, Elysiamorf emits CC, SoundAnalysisMIDI
converts 8 ML outputs → 8 ControlEvents (CC 0..7) per tick, ring
buffer overflow drops cleanly.
Total: 22 new tests (110 across nisps/), all passing under -Werror
-Wpedantic. Build remains clean.
2026-04-29 15:27:03 +02:00
|
|
|
#include "../../nisps/modes/sound_analysis_midi.hpp"
|
|
|
|
|
#include "../../nisps/modes/verb_fx.hpp"
|
|
|
|
|
#include "../../nisps/modes/xiasri.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace nisps;
|
|
|
|
|
|
|
|
|
|
// ---- Static concept checks (redundant with header-side static_asserts but
|
|
|
|
|
// makes the test surface explicit) ----
|
|
|
|
|
static_assert(Mode<modes::PAFSynthMode>);
|
|
|
|
|
static_assert(Mode<modes::ChannelStripMode>);
|
|
|
|
|
static_assert(Mode<modes::XIASRIMode>);
|
|
|
|
|
static_assert(Mode<modes::VerbFXMode>);
|
|
|
|
|
static_assert(Mode<modes::MEMLCeliumMode>);
|
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
|
|
|
static_assert(Mode<modes::SLPWorkshopMode>);
|
test(nisps/modes): host C++ tests for stream 4 mode layer (meml-beb)
Adds `nisps_modes_tests` executable to nisps/CMakeLists.txt with four TUs:
- `test_mode_concepts.cpp`: 8 `static_assert(Mode<...>)` (concept
satisfaction), plus runtime metadata sanity for each mode (mode_id,
input_channel_count, schema sizes match engine param_count).
- `test_mode_paf_synth.cpp`: end-to-end exercise — setup, set_input,
tick_control, process audio. Verifies idle process is finite, output
bounds [0,1] hold, note_on triggers nonzero audio, input clamping,
and engine/ml accessors round-trip.
- `test_mode_voice_space.cpp`: voice-space round trip for PAFSynth,
ChannelStrip and VerbFX (all dispatched modes). Confirms
out-of-range index is silently ignored.
- `test_mode_breakor_events.cpp`: sequencer event pumping. BreakOr
emits Clock + NoteOn/Off, Elysiamorf emits CC, SoundAnalysisMIDI
converts 8 ML outputs → 8 ControlEvents (CC 0..7) per tick, ring
buffer overflow drops cleanly.
Total: 22 new tests (110 across nisps/), all passing under -Werror
-Wpedantic. Build remains clean.
2026-04-29 15:27:03 +02:00
|
|
|
static_assert(Mode<modes::BreakOrMode>);
|
|
|
|
|
static_assert(Mode<modes::ElysiamorfMode>);
|
|
|
|
|
static_assert(Mode<modes::SoundAnalysisMIDIMode>);
|
|
|
|
|
|
|
|
|
|
// ---- Per-mode metadata ----
|
|
|
|
|
NISPS_TEST(mode_paf_synth_metadata) {
|
|
|
|
|
const auto& s = modes::PAFSynthMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::PAFSynthMode::mode_id() == "paf_synth");
|
|
|
|
|
NISPS_EXPECT(modes::PAFSynthMode::input_channel_count() == 4u);
|
|
|
|
|
NISPS_EXPECT(s.input_size == 4u);
|
|
|
|
|
NISPS_EXPECT(s.output_size == 33u);
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "paf_synth");
|
|
|
|
|
NISPS_EXPECT(s.params.size() == 33u);
|
|
|
|
|
NISPS_EXPECT(s.voice_spaces.size() == 7u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_channel_strip_metadata) {
|
|
|
|
|
const auto& s = modes::ChannelStripMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::ChannelStripMode::mode_id() == "channel_strip");
|
|
|
|
|
NISPS_EXPECT(modes::ChannelStripMode::input_channel_count() == 4u);
|
|
|
|
|
NISPS_EXPECT(s.output_size == 24u);
|
|
|
|
|
NISPS_EXPECT(s.voice_spaces.size() == 6u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_xiasri_metadata) {
|
|
|
|
|
const auto& s = modes::XIASRIMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::XIASRIMode::mode_id() == "xiasri");
|
|
|
|
|
NISPS_EXPECT(s.output_size == 24u);
|
|
|
|
|
NISPS_EXPECT(s.voice_spaces.size() == 1u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_verb_fx_metadata) {
|
|
|
|
|
const auto& s = modes::VerbFXMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::VerbFXMode::mode_id() == "verb_fx");
|
|
|
|
|
NISPS_EXPECT(s.output_size == 47u);
|
|
|
|
|
NISPS_EXPECT(s.voice_spaces.size() == 12u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_memlcelium_metadata) {
|
|
|
|
|
const auto& s = modes::MEMLCeliumMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::MEMLCeliumMode::mode_id() == "memlcelium");
|
|
|
|
|
NISPS_EXPECT(s.output_size == 56u);
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "memlcelium");
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
NISPS_TEST(mode_slp_workshop_metadata) {
|
|
|
|
|
const auto& s = modes::SLPWorkshopMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::SLPWorkshopMode::mode_id() == "slp_workshop");
|
|
|
|
|
NISPS_EXPECT(modes::SLPWorkshopMode::input_channel_count() == 4u);
|
|
|
|
|
NISPS_EXPECT(s.output_size == 56u);
|
|
|
|
|
// Reuses the MEMLCelium engine verbatim.
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "memlcelium");
|
|
|
|
|
NISPS_EXPECT(s.voice_spaces.size() == 1u);
|
|
|
|
|
}
|
|
|
|
|
|
test(nisps/modes): host C++ tests for stream 4 mode layer (meml-beb)
Adds `nisps_modes_tests` executable to nisps/CMakeLists.txt with four TUs:
- `test_mode_concepts.cpp`: 8 `static_assert(Mode<...>)` (concept
satisfaction), plus runtime metadata sanity for each mode (mode_id,
input_channel_count, schema sizes match engine param_count).
- `test_mode_paf_synth.cpp`: end-to-end exercise — setup, set_input,
tick_control, process audio. Verifies idle process is finite, output
bounds [0,1] hold, note_on triggers nonzero audio, input clamping,
and engine/ml accessors round-trip.
- `test_mode_voice_space.cpp`: voice-space round trip for PAFSynth,
ChannelStrip and VerbFX (all dispatched modes). Confirms
out-of-range index is silently ignored.
- `test_mode_breakor_events.cpp`: sequencer event pumping. BreakOr
emits Clock + NoteOn/Off, Elysiamorf emits CC, SoundAnalysisMIDI
converts 8 ML outputs → 8 ControlEvents (CC 0..7) per tick, ring
buffer overflow drops cleanly.
Total: 22 new tests (110 across nisps/), all passing under -Werror
-Wpedantic. Build remains clean.
2026-04-29 15:27:03 +02:00
|
|
|
NISPS_TEST(mode_breakor_metadata) {
|
|
|
|
|
const auto& s = modes::BreakOrMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::BreakOrMode::mode_id() == "breakor");
|
|
|
|
|
NISPS_EXPECT(s.output_size == 56u);
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "breakor");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_elysiamorf_metadata) {
|
|
|
|
|
const auto& s = modes::ElysiamorfMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::ElysiamorfMode::mode_id() == "elysiamorf");
|
|
|
|
|
NISPS_EXPECT(s.output_size == 40u);
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "elysiamorf");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NISPS_TEST(mode_sound_analysis_midi_metadata) {
|
|
|
|
|
const auto& s = modes::SoundAnalysisMIDIMode::param_schema();
|
|
|
|
|
NISPS_EXPECT(modes::SoundAnalysisMIDIMode::mode_id() == "sound_analysis_midi");
|
|
|
|
|
NISPS_EXPECT(modes::SoundAnalysisMIDIMode::input_channel_count() == 10u);
|
|
|
|
|
NISPS_EXPECT(s.input_size == 10u);
|
|
|
|
|
NISPS_EXPECT(s.output_size == 8u);
|
|
|
|
|
NISPS_EXPECT(s.engine_id == "thru");
|
|
|
|
|
}
|