Commit graph

457 commits

Author SHA1 Message Date
monkey-w1n5t0n
a109e0299d docs: sync MAP.md (manifold/ + vcv/ sections) and ALIGNMENT.md 2026-06-28 04:14:30 +02:00
monkey-w1n5t0n
517bd2f987 feat(firmware): wire Explore-and-place glue to hardware buttons + selftest
Map enter/exit-explore, reroll, nudge, begin-place, commit-place to the
peripheral buttons via the shared FeedbackController. (Not compiled here —
no arduino-cli; Placing-audition static_output hook is a flagged follow-up.)
2026-06-28 04:14:30 +02:00
monkey-w1n5t0n
fbc68ebfab feat(vcv): evolve module to 8x16 + LED rings + token palette + WS-OSC bridge
8 inputs x 16 outputs; per-output LED ring widget (drawLayer+nvgArc); palette
from frontend tokens; OSC bridge verbs for bidirectional browser training;
vendored self-contained iml.hpp (retired nisps-core); compiles against Rack
SDK 2.6.4. See SPEC.md BUILD DELTAS.
2026-06-28 04:14:30 +02:00
monkey-w1n5t0n
0d8179d6d5 feat(manifold): dock restructure, settings, backends, shared-core feedback, particle stage
Top Mode selector + 5 centred drawers; Settings (monochrome icons, input-map
shape, corner radius); MIDI+OSC backends w/ named presets; rewire feedback to
the shared C++ core (explore-and-place); ParticleStage + flow-field; engine
base-URL + glue-load fixes. nisps.wasm synced from core build.
2026-06-28 04:14:30 +02:00
monkey-w1n5t0n
22efb1c411 feat(nisps/ml): crystallise Explore-and-place into shared FeedbackController
Add FeedbackMode::ExploreAndPlace + Idle/Exploring/Placing state machine
(no-heap, deterministic nisps::Rng): enter/exit_explore, reroll, nudge, undo,
begin_place, commit_place, cancel_place + on_down/on_up browser policy. Wire
the nisps_ml_feedback_* C API + EXPORTED_FUNCTIONS, extend parity Stage 5d.
Fix set_mode(3) falling back to Avoid. Native ctest 4/4; parity native==WASM
within 1e-5 (max delta 2.4e-7). Rebuilt nisps.{js,wasm}.
2026-06-28 04:14:12 +02:00
monkey-w1n5t0n
24057e7b5f docs(redesign): Manifold research findings + design specs + build plan
Phase-1 audits (feedback-behaviour, engine-surface, design/Manifold), the
judge-panelled rl-feedback-design, dock/backends/inputs specs, the
upstream-firmware survey, the Manifold design mirror, and BUILD-PLAN.md
(resume anchor + locked decisions).
2026-06-28 04:14:12 +02:00
monkey-w1n5t0n
f41d6ea1b7 chore: untrack retired .beads runtime files (beads→ergo/Holon) 2026-06-28 04:14:12 +02:00
monkey-w1n5t0n
19b7f7eee8 feat(manifold): add convertible React front-end + simplify console chrome
Adds the Manifold app (manifold/) — the convertible-mode React front-end on
the real NISPS ML+audio engine, served live at meml.lnfinitemonkeys.org/next/.

Console chrome trimmed per UI pass:
- drop mode label + subtitle from the top-left overlay (keep MEMLNaut wordmark)
- remove the composite split preset/ratio readout (top-centre)
- remove the OUTPUT corner tag above the bars
- remove the A/B compare toggle from the verdict cluster
- remove the follow button + input/noise readout (bottom-left)
- remove AltitudeNav focus switcher (bottom-right)
2026-06-28 03:28:45 +02:00
monkey-w1n5t0n
9ddf7f0bd5 chore(docs): remove retired bd/beads block from CLAUDE.md — task tracking is ergo now (bd retired 2026-06-15) 2026-06-25 05:04:39 +02:00
monkey-w1n5t0n
f2562176fc fix(playground/audio): compile AudioWorklet via ?worker&url
addModule() was handed new URL('./nisps-processor.ts', import.meta.url), which
Vite copies as raw TypeScript (served as video/mp2t) instead of compiling it —
that pattern only bundles for new Worker(). Import the worklet with
?worker&url so Vite emits compiled, hashed .js. Fixes the synth engine failing
to load on meml.lnfinitemonkeys.org.
2026-06-16 22:19:24 +02:00
monkey-w1n5t0n
3c67d86019 chore: untrack retired .beads (moved to ~/.beads-graveyard; beads→Holon 2026-06-15) 2026-06-16 00:22:26 +02:00
w1n5t0n
2b939bb812 chore(beads): switch to embedded Dolt mode, ignore runtime files; add specs README; bump memllib
- .beads/metadata.json: dolt_mode server -> embedded
- .beads/.gitignore: ignore interactions.jsonl, push-state, locks, .env, credential key, corrupt backups
- untrack now-ignored .beads/interactions.jsonl (runtime)
- docs/specs/README.md: tiered specs corpus overview
- src/memllib: bump submodule pointer
2026-06-14 10:06:47 -10: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
e45822e5a2 merge stream 11: verification infrastructure (meml-x06) 2026-04-29 19:50:55 +03:00
w1n5t0n
3138701100 Stream 11: verification infrastructure (golden vectors + parity + lint + CI + Playwright migration)
Agent ran out of API credits before committing. Files staged + committed by orchestrator. Coverage:
- tests/cpp/ml_golden_vectors.cpp: fixed-seed regression tests for MLP determinism
- tests/cpp/engine_impulse.cpp: white-noise impulse responses with binary baseline
- tests/cpp/parity_check.cpp + parity_wasm.mjs + parity_diff.mjs: native vs WASM bit-equivalence
- scripts/build-cpp-tests.sh, lint-cpp.sh, parity-check.sh, run-all-tests.sh
- playground/tests/e2e/{ml-engine,modes,persistence,ui-interactions}.spec.ts (+helpers)
- .github/workflows/ci.yml

(meml-x06)
2026-04-29 19:50:55 +03:00
w1n5t0n
a4d3b3a8b5 merge stream 10: a-immersive feature parity wired (meml-5wg) 2026-04-29 19:48:12 +03:00
54821fffd0 playground/stream-10: wire phase 1-4 features into modes (meml-5wg)
Builds the SolidJS-side feature parity with the legacy a-immersive
playground. Every Phase 1–4 feature called out in
.local/recon/04-playground.md now flows through the runtime → stores →
primitives pipeline.

Phase 1 (compound axes / zoom / drawer / trail):
  - control-routing.ts: resolves Boldness/Memory/Precision into
    input/output/exploration store writes via createEffect.
  - SettingsDrawer (new): tabbed sections for Input / Training /
    Exploration / Output / Per-param overrides / Advanced. Per-param
    ParamEditor list wires through modeStore.setOverride.
  - JoyMap navigator added to ModeShell; tap-to-return + long-press
    region pin handlers wired into the runtime.
  - Anchor mode toggle (auto/sticky/center) exposed via drawer.

Phase 2 (pinning / history / A/B):
  - features/snapshots.ts: bridge sessionStore push/pop to actual
    Float32Array weights. Auto-snapshot before train/randomize/thumbs-down.
  - Undo button + long-press snapshot list popup wired in ModeShell.
  - A/B capture/toggle/accept/revert in SettingsDrawer.
  - features/overrides.ts buildPinMask() composes override `pinned`
    flags + sessionStore param pins; passed to mlStore.moveWeights.
  - Region pinning long-press → addRegionPin + auto-snapshot tag
    "pinned baseline".

Phase 3 (refinement / exploration):
  - features/heatmap-sampler.ts: 16x16 grid via mlStore.inferBatch,
    three colour modes, throttled 5/sec, refreshed on ml.trained /
    ml.delta_update bus events.
  - exploration-store.ts (new): RL noise level, floor/cap/growth/decay,
    spread, learning rate, weight decay, auto-explore, pressure.
  - Auto-explore timer in mode-runtime drives moveWeights at user
    interval, zoom-scaled intensity.
  - Pressure feedback: window pointerdown/up timestamps mapped to
    explorationStore.setPressure; feeds growNoise/decayNoise.

Phase 4 (output / persistence / polish):
  - features/weight-health.ts: histogram, status, per-layer L2 deltas,
    vanishing/exploding/converged classification.
  - features/session-preset.ts: full state capture/restore +
    base64url URL sharing; main.tsx applies on load.
  - SettingsDrawer Advanced tab renders WeightHealth, GradientFlow,
    LayerStats, Heatmap, session preset save/load/share.

Override application:
  - features/overrides.ts applies per-param mute/freeze/curve/range
    between MLP outputs and the engine. Mode runtime exposes
    `paramOutputs` (length = schema.params) which all modes now bind to
    OutputDisplay so ranges and mutes are visible.
  - Freeze flags push into outputStore.freezeMask reactively.

Mic input:
  - features/mic-input.ts: getUserMedia + AnalyserNode-derived
    {energy, brightness, pitch, aperiodicity}. Mode runtime feeds
    them into channels 2..(input_size-1) when active. ModeShell shows
    a Mic toggle for audio_in modes.

Debug probe (probe.ts):
  - Synchronous bypass for snapshot, A/B, pins, overrides, axes,
    spread, output freeze, heatmap, weight health, session presets,
    URL params, bus emit/on. Untracked reads/writes throughout to
    avoid SolidJS reactivity surprises in tests.

Modes:
  - All 8 firmware mode TSX files now use the default SettingsDrawer
    (no per-mode SliderBank scaffolding). They bind OutputDisplay to
    runtime.paramOutputs (post-override) instead of processedOutputs.

Build status:
  - bun run typecheck: clean
  - bun run build: clean
  - dev server smoke: index, /modes, mode-runtime, SettingsDrawer,
    features/* all serve.

Note: bd close meml-5wg failed because Dolt server unreachable from
this worktree. Issue should be closed manually by orchestrator.
2026-04-29 17:33:01 +03:00
w1n5t0n
2bc422a880 merge stream 9: SolidJS modes + ModeShell + runtime (meml-yt7) 2026-04-29 17:07:05 +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
f916344c9b feat(playground/modes): all 9 mode bindings + /modes route (meml-yt7)
Stream 9 (part 2/2) — concrete mode TSX components + routing.

Each firmware mode pairs the generated schema with the appropriate
primary input (XYPad for xy_pad schemas, VirtualJoystick for joystick
schemas) and feeds the runtime's processedOutputs into the shared
SliderBank/OutputDisplay/LossPlot layout. The set:

  - PAFSynthMode (xy_pad, voice spaces)
  - ChannelStripMode (joystick)
  - XIASRIMode (joystick — mic-input wiring deferred to stream 10)
  - VerbFXMode (joystick, voice spaces)
  - MEMLCeliumMode (xy_pad, voice spaces)
  - BreakOrMode (xy_pad)
  - ElysiamorfMode (xy_pad)
  - SoundAnalysisMIDIMode (audio_in fallback to joystick;
      mic capture is a stream-10 task — UI scaffold renders today)
  - C15Mode (browser-only placeholder; bridge not yet ported)

App.tsx adds a /modes route that renders <ModeSwitcher /> + the active
mode's component via Solid <Dynamic />. modes/index.ts exposes the
registry the switcher consumes.
2026-04-29 17:00:56 +03:00
w1n5t0n
ebd5dba4c0 feat(playground/modes): ModeShell + runtime + switcher scaffolding (meml-yt7)
Stream 9 (part 1/2) — shared infrastructure for the per-mode TSX layer.

  - ModeShell.tsx: common chrome (header, voice-space PillToggle, audio
    start/stop, control axes, training controls, drawer, status line).
    Modes only have to author primary input + output JSX.
  - mode-runtime.ts: useModeRuntime(schema) hook. Owns input pipeline,
    WASM ML calls, output pipeline, and a 50ms-throttled engine-host
    bridge. Exposes setInput/processedOutputs/training/audio surfaces
    so mode TSX stays declarative.
  - mode-helpers.ts: schema → SliderBank config + Float32Array →
    slider-range value mapping. Pure functions.
  - ModeSwitcher.tsx: top-of-page <select> wired to modeStore.

ModeShell and the runtime read controlStore (Boldness/Memory/Precision)
to derive learning rate + RL noise cap so axes affect training without
needing per-mode plumbing.
2026-04-29 17:00:43 +03:00
w1n5t0n
3021552a38 merge stream 7: WASM bindings + AudioWorklet (meml-tgm) 2026-04-29 16:38:57 +03:00
w1n5t0n
f26ec923e1 feat(playground/wasm): WASM bridge between C++ core and SolidJS playground (meml-tgm)
Stream 7 wires nisps/ml + nisps/engines into the playground via Emscripten.

Highlights:
- nisps/wasm/bindings.cpp: flat C API per architecture.md §6.2. Fixed-arch
  MLP<2, 10, 14, 18, 126>; engine string→type dispatch table with NoOp
  fallback.
- scripts/build-wasm.sh: emcc invocation, MODULARIZE=1, exports listed
  explicitly; produces playground/public/nisps.{js,wasm}.
- playground/src/ml/wasm-iml.ts: main-thread MLP host (sync inference,
  sync training, RL ops, weights I/O, layer stats, localStorage).
- playground/src/ml/wasm-worker.ts: disposable Web Worker for off-thread
  async training, owns its own WASM instance.
- playground/src/ml/dataset.ts: Float32Array-backed FIFO with sample-weight
  modes (uniform/global/local/combined). Port of legacy dataset.js.
- playground/src/audio/engine-host.ts: AudioContext + AudioWorkletNode
  lifecycle, with start/stop/setEngine/setParams.
- playground/src/audio/worklet/nisps-processor.ts: WASM-loading
  AudioWorkletProcessor that runs engine.process_block per 128-sample
  block. Loads its own WASM instance from main-thread-supplied bytes
  (no fetch in worklet).
- playground/src/stores/ml-store.ts: wired stub methods to WasmIML
  singleton; lazy initialize().
- playground/src/debug/probe.ts: window.__nisps now calls real WasmIML
  via the store; lazy-init on first use.

Verified:
- bash scripts/build-wasm.sh succeeds (94 KB nisps.wasm)
- bun run typecheck OK
- bun run build OK (production bundle)
- vite dev server serves /nisps.{js,wasm} with COOP/COEP

Known limitation: WASM is fixed at one MLP shape. Multi-arch deferred —
documented in nisps/wasm/README.md.
2026-04-29 16:36:29 +03:00
w1n5t0n
4964037da0 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 16:27:03 +03:00
w1n5t0n
429da1d1e4 feat(nisps/modes): all 8 concrete mode bindings (meml-beb)
One header per mode, each ~50-130 LOC built atop ModeBase:

  - `paf_synth.hpp` (4 inputs → 33 outputs, 7 voice spaces, note_on/off)
  - `channel_strip.hpp` (4 → 24, 6 voice spaces)
  - `xiasri.hpp` (4 → 24, 1 'Direct' voice space)
  - `verb_fx.hpp` (4 → 47, 12 voice spaces)
  - `memlcelium.hpp` (4 → 56, dual synth + 2-track sequencer; set_playing/update_bpm)
  - `breakor.hpp` (4 → 56, 8-track ratio sequencer; pumps engine NoteOn/Off/Clock
    events into ControlEvent ring buffer)
  - `elysiamorf.hpp` (4 → 40, 8-track FM-pair MIDI-CC sequencer; pumps CC events)
  - `sound_analysis_midi.hpp` (10 → 8; owns AnalysisEngine for input feature
    extraction PLUS NoOpEngine 'thru' for audio passthrough; ML outputs
    converted to MIDI CC events via on_post_inference; opts out of
    output→engine routing via ModeRoutesOutputsToEngine specialisation)

Every mode satisfies `nisps::Mode` (verified via `static_assert` in each
header). Schema `output_size` is verified against engine `param_count()`
at compile time inside ModeBase.

All hardware-specific glue (MEMLNaut::Instance, pico/util/queue, MIDIInOut,
display widgets, button callbacks) is intentionally absent — that lives in
firmware/glue and playground/src/modes per architecture.md §4.3.
2026-04-29 16:26:49 +03:00
w1n5t0n
b6b4cf7362 feat(nisps/modes): scaffolding for platform-agnostic mode layer (meml-beb)
Adds two foundational pieces shared by every concrete mode:

  - `base.hpp`: defines `nisps::ParamSchema` (the aggregate type the
    `Mode` concept's `param_schema()` returns by const-reference),
    `nisps::ControlEvent`, and `ModeBase<Derived, EngineT, MLPType,
    NInputs>` — a CRTP scaffold absorbing input forwarding, ML
    inference + voice space mapping, engine.set_params() routing, and
    a per-mode ControlEvent ring buffer for MIDI/I2C events.

    `ModeRoutesOutputsToEngine<Derived>` lets a derived mode (e.g.
    SoundAnalysisMIDI) opt out of routing ML outputs to engine
    params when its outputs become MIDI CCs instead.

  - `voice_space.hpp`: `VoiceSpaceEntry` + `make_voice_space_entries()`
    + `find_voice_space()` helpers. Voice-space *mapping code* lives
    inside engines (mirroring firmware); mode layer just selects
    which voice space the engine uses by index/name.

No heap, no virtuals, no Pico/Arduino dependencies; satisfies stream
4 of architecture.md.
2026-04-29 16:26:33 +03:00
w1n5t0n
5cd5041b6a fix: NoOpEngine engine_id → 'thru' to match sound_analysis_midi schema
Stream 3's NoOpEngine used engine_id()=='noop' but the schema for
sound_analysis_midi.json declares engine_id: 'thru' (matches firmware's
ThruAudioApp naming). The class name stays NoOpEngine — it accurately
describes what process() does — but the schema-facing identifier is now
'thru'. BreakOr/Elysiamorf compose NoOpEngine by type, not engine_id, so
they're unaffected.
2026-04-29 16:12:12 +03:00
w1n5t0n
8d0d47b992 feat(nisps/engines): port firmware audio engines to AudioEngine concept (meml-1v6)
Concept-based, no virtual dispatch, per-engine voice spaces as inline
methods. Each engine satisfies nisps::AudioEngine via static_assert.

- NoOpEngine: silent passthrough; used for sequencer-only modes and
  for the SoundAnalysisMIDI mode's audio path.
- PAFSynthEngine (33 params, 7 voice spaces): 4-voice PAF synth with
  detune cascade, ring-mod, sine-shaper, ADSR, feedback delay. note_on/
  note_off interface for MIDI keyboard.
- ChannelStripEngine (24 params, 6 voice spaces): stereo console strip
  (pre-gain/HPF/LPF/2x peak/low-shelf/high-shelf/comp/post-gain). Voice
  spaces: WannabeNeve66, SSL4K, SSL9K, MaleVox, FemaleVox, Neve80
  (stepped-frequency).
- XIASRIEngine (24 params, "Direct" voice space): pitch-shift + 6 allpass
  + 2 comb + 4 delays. Direct NN→param mapping per firmware semantics.
- VerbFXEngine (47 params, 12 voice spaces): 8-band SVF filterbank +
  3-lane dynamic delay + 8-lpcomb/4-allpass Freeverb-style tail with
  cross-fades. All 12 voice spaces ported from voicespaces/VerbFX/*.hpp.
- MEMLCeliumEngine (56 params): 2-track ratio sequencer + dual-voice
  PAF synth (7+7+22+20 layout). Sequencer triggers V0/V1 ADSR.
- BreakOrEngine (56 params): 8-track ratio sequencer; emits NoteOn/
  NoteOff/Clock events via pop_events(span). process() returns silence.
- ElysiamorfEngine (40 params): 8-track FM-pair sequencer; emits CC
  events on CCs {1,2,3,4,5,9,11,12}. Silent audio path.
- AnalysisEngine (0 params, 6 features): port of XiasriAnalysis (pitch
  via zero-crossing, aperiodicity via MAD, log-domain energy + attack
  derivative + brightness ratio). Inputs to ML on SoundAnalysisMIDI mode.

All param_count() values match schemas/modes/*.json output_size.
4074 LOC total. CMake adds nisps_dsp_engine_tests target with 38
passing tests under -Wall -Wextra -Werror -Wpedantic.
2026-04-29 16:09:12 +03:00
w1n5t0n
973455b158 feat(nisps/dsp): lean DSP primitives ported from maximilian (meml-1v6)
Header-only, heap-free, sample-rate-aware DSP primitives for stream 3:
- Biquad (LPF/HPF/BPF/Notch/Peak/LowShelf/HighShelf, denormal flush)
- Delay<N> + DynamicDelay<N> (fixed-length feedback + power-of-two
  ring with fractional read & smoothed offset)
- AllPass / Comb / LpComb (Schroeder-Moorer reverb sections, split per
  role rather than maximilian's one-class-many-roles maxiReverbFilters)
- DCBlocker (one-pole HPF)
- ChamberlinSVF + OnePoleSmoother<NCh> + EnvelopeFollower
- ADSR envelope generator
- SineOsc/SawOsc/SquareOsc, PAFOperator (port of maxiPAFOperator with
  static gauss/cauchy tables), FMOp single-operator FM building block
- PitchShifter<N> granular two-head crossfade (replaces daisysp PitchShifter)

Tests: biquad freq-domain attenuation, delay tap timing, reverb
boundedness, pitch-shifter ratio + finite output. All pass under
-Wall -Wextra -Werror -Wpedantic, C++20.
2026-04-29 16:08:49 +03:00
w1n5t0n
825ed6ad33 feat(nisps/ml): MLP library with fixed-architecture template + spread-aware RL (meml-wmh)
Stream 2 of the clean-slate rewrite: nisps/ml/ replaces src/memlp/ with a
header-only, heap-free MLP that satisfies nisps::core::MLEngine.

Files (nisps/ml/):
- activations.hpp — ReLU (leaky 0.01 for parity), sigmoid, tanh
- loss.hpp — MSE per-sample (fixes meml-ues double-scaling: returns the
  sample's MSE without an extra 1/N multiplication; the training loop
  averages explicitly)
- init.hpp — uniform/Xavier/spread-aware weight init
- training.hpp — gradient clip helper (±10.0 matches legacy)
- rl.hpp — move_weights with per-layer Xavier scaling, weight decay
  (10% * spread), gaussian noise via the deterministic Rng (matches the
  legacy JS sum-of-three-uniforms shape); draw_weights also spread-aware
- stats.hpp — per-layer mean/max/dead/saturating diagnostics
- mlp.hpp — 4-layer (3 hidden + sigmoid output) MLP class with
  std::array-backed weights, biases, gradient accumulators, dataset
  ring buffer (default 128 examples), loss history (default 4096 iters).
  Bias is a separate per-layer parameter — no input-vector mutation.
  Flat get_weights/set_weights layout: weights all layers (row-major,
  layer order), then biases all layers.

Tests (tests/cpp/, all 50 passing under -Wall -Wextra -Werror -Wpedantic):
- test_mlp_init.cpp — deterministic seeding, spread regimes,
  static_assert MLEngine concept satisfied
- test_mlp_inference.cpp — golden hand-computed forward pass match,
  sigmoid output range, set_input bounds
- test_mlp_training.cpp — XOR convergence (loss < 0.01 in <2k iters),
  ring-buffer eviction
- test_mlp_loss.cpp — meml-ues regression test: reported loss equals
  hand-computed average MSE without extra 1/N scaling; sample weights
  honoured
- test_mlp_rl.cpp — move_weights respects output_pin_mask (final-layer
  rows + biases preserved); spread regimes; grad clear after draw_weights
- test_mlp_serialize.cpp — get_weights/set_weights round-trip preserves
  inference exactly; eval_loss is non-mutating; infer_batch matches
  individual inference

Verification:
- Clean build, no warnings
- 50 tests pass (22 prior + 28 new)
- No std::vector / new / malloc in nisps/ml/
- All float literals .f-suffixed in code (comments excepted)
2026-04-29 15:55:43 +03:00
w1n5t0n
55e7bc9654 fix: wire generated C++ schemas to nisps::Curve from core/math.hpp
Stream 5 shipped a temporary local Curve enum (PascalCase) while stream 1
was in flight. Stream 1 has now landed nisps/core/math.hpp with the
authoritative lowercase Curve enum per architecture spec. Generated C++
headers now include core/math.hpp and re-export the enum into the
generated namespace via 'using Curve = ::nisps::Curve;'.

- codegen/generate.ts: emit lowercase enum values + include math.hpp
- regenerated all 8 mode schema headers
- updated golden snapshot to match

Verified all 8 headers compile clean with -std=c++20 -Wall -Wextra.
2026-04-29 15:43:42 +03:00
w1n5t0n
f59b12a056 merge stream 5: schemas + codegen + generated outputs (meml-7k6) 2026-04-29 15:41:44 +03:00
w1n5t0n
8911079212 merge stream 8: SolidJS scaffold + UI primitives (meml-911) 2026-04-29 15:41:40 +03:00
w1n5t0n
48dcf3fc6b merge stream 1: nisps/core foundation (meml-dn7) 2026-04-29 15:41:16 +03:00
w1n5t0n
c3fb63a113 feat(playground): UI primitive library + /dev/primitives showcase
Sixteen primitives at src/primitives/, each with its own .module.css and a
.demo.tsx that wires the primitive to live local state. All primitives
are typed (strict), pointer-event based (touch + mouse), keyboard-accessible
where it makes sense, and free of business logic.

Primitives:
- Slider, SliderBank: value sliders with optional curve mapping;
  collapsible sections.
- VirtualJoystick, XYPad: 2D pointer input, returning [0,1]^2.
- Heatmap: NxN canvas rendering with three color modes
  (luminance/variance/divergence) and a configurable palette.
- OutputDisplay: bar chart of N output values.
- TrainingControls: thumbs-up/down/undo + train/randomise + status.
- Drawer: portal'd slide-in panel from left or right; ESC + scrim close.
- ControlAxis: compound-axis slider (Boldness/Memory/Precision shape)
  with active-preset hint, endpoint labels, double-tap-to-relink.
- ProgressRing: SVG circular progress with optional inline label.
- PillToggle: segmented radio control.
- ParamEditor: min/max/curve/mute/pin/fixedValue editor for a single param.
- JoyMap: zoom minimap with adaptive grid + zoom window, vanishing trail
  with Catmull-Rom spline + tap-to-return, dual concentric noise rings,
  region pin overlays, frozen overlay.
- WeightHealth: 10-bin histogram + dead/saturating/healthy status glow.
- GradientFlow: per-layer bar chart with vanishing/exploding/converged
  color coding.
- LossPlot: log-scale line chart of training loss history.

App.tsx now wires the /dev/primitives route to a lazy-loaded
PrimitivesShowcase that renders all sixteen demos in a responsive grid.
Lazy import keeps the home page bundle ~20 kB while the showcase ships
its own ~42 kB chunk.

Stream 8 of the rewrite (meml-911).
2026-04-29 15:38:55 +03:00
w1n5t0n
add05ea554 feat(playground): solid stores + signal bus
Seven Solid stores wired up around the architecture's reactivity model
(§7.1). Each store uses createStore for object state + createSignal for
Float32Arrays where appropriate; setters mutate the store and schedule a
debounced (200ms) localStorage write through src/stores/persistence.ts.

- bus.ts: typed signal bus with prefix wildcards (ml.*, ui.*, mode.*,
  pin.*, snap.*). Singleton coreBus for app-wide events.
- ml-store.ts: shape final, methods stub-throw "not implemented" until
  stream 7 wires WASM. Outputs and weights are separate Float32Array
  signals so the store proxy doesn't run on every audio-rate tick.
- input-store.ts: full input pipeline config (zoom, anchor, deadzone,
  curve, smoothing, momentum, invert) + persisted live state.
- output-store.ts: globalCurve, smoothing, slewRate, freezeOutput,
  freezeMask. Mask not persisted (engine-specific).
- mode-store.ts: activeModeId + per-mode { paramName → ParamOverride }.
- control-store.ts: Boldness/Memory/Precision compound axes with
  interpolation tables and offset-based overrides (trim-pot model).
  Tables and 6 built-in CONTROL_PRESETS mirror legacy
  js/ui/control-surface.js exactly. interpolateAxis() exposed for
  testing. Stream 10 wires resolveParams() into other stores.
- session-store.ts: ring-buffered snapshot stack (max 20), A/B
  capture/toggle/accept/revert, region pins (max 5), param pins
  (toggle + mask builder), named session presets.
- index.ts: public re-exports for components and modes.

Stream 8 of the rewrite (meml-911).
2026-04-29 15:38:05 +03:00
w1n5t0n
665e224122 feat(playground): input/output pipelines + curve catalog
Pure-TS ports of the legacy input + output pipelines:
- src/input/pipeline.ts: deadzone → circular clamp → zoom → centered
  power curve → EMA smoothing → momentum-as-zoom. Exposed as a
  pure function processInput(raw, cfg, state, dt) so the input-store
  can hold the state. Math is intentionally bit-equivalent to the
  legacy js/ui/input-pipeline.js implementation.
- src/output/pipeline.ts: global power curve → EMA smoothing →
  slew-rate limit → freeze gate (global + per-output mask).
- src/output/curves.ts: named curve catalog (linear/exp/log/square/
  sqrt/sigmoid/cubic/centered_power) — the TS half of the contract
  defined in nisps/core/math.hpp (stream 1). Golden-vector tests
  in stream 11 will keep them in lockstep.

Stream 8 of the rewrite (meml-911).
2026-04-29 15:37:46 +03:00
w1n5t0n
a160b72295 feat(playground): scaffold vite + solid + ts
Fresh playground/ now sits on:
- Vite 5 + vite-plugin-solid + Solid.js 1.9
- TypeScript strict (jsx preserve, target ES2022)
- index.html → main.tsx → App.tsx with a tiny pushState/popstate router
- Design tokens (dark immersive palette, JetBrains Mono) at src/styles/tokens.css
- COOP/COEP headers in vite.config.ts (server + preview) so SharedArrayBuffer
  is available for the C15 + AudioWorklet wiring stream 7 will land.
- Debug-probe stub at src/debug/probe.ts: window.__nisps with placeholder
  methods + __ready=false marker. Stream 10 fills it in. Keeping the install
  point stable from day one means Playwright tests can rely on the global
  existing.

Stores, pipelines, and primitives ride in the next commits.

Stream 8 of the rewrite (meml-911).
2026-04-29 15:37:23 +03:00
w1n5t0n
d41e4c3e9f chore(playground): remove legacy a-immersive playground
Clears the old vanilla-JS / multi-app playground (a-immersive.html,
b-workbench.html, c-journey.html, designs.html, index.html, plus the
js/, css/, c15/, faust/, firmware/, osc-bridge/, wasm/ subtrees and the
serve/test scripts). The fresh SolidJS + Vite scaffold lands in the next
commits per the rewrite plan in .local/architecture.md.

Reference docs (SPEC-controls.md, SPEC-shapeseq.md,
PLAN-solidjs-migration.md, ARCHITECTURE.md, README.md, TODOS.md, devlog/)
have been moved to .local/playground-archive/ (out of git) so future
streams can still consult them.

Stream 8 of the rewrite (meml-911).
2026-04-29 15:35:58 +03:00
w1n5t0n
05f04a90e0 Add generated schema headers (C++) and modules (TS)
Output of \`bun run codegen/generate.ts\` for the 8 mode schemas.
These files are checked in so consumers don't need bun on every
build, but they remain regenerable and byte-identical.

C++ (nisps/modes/generated/):
  schema_types.hpp + 8 <mode_id>_schema.hpp files. All compile
  cleanly with g++ -std=c++20 -Wall -Wextra -fsyntax-only -I nisps.

TS (playground/src/modes/generated/):
  types.ts + index.ts + 8 <mode_id>_schema.ts files. Type-check
  cleanly with tsc --strict.
2026-04-29 15:29:15 +03:00
w1n5t0n
129e28b207 Add codegen tool: schemas -> C++ headers + TS modules
bun-runnable TypeScript script (codegen/generate.ts) that:
- Validates each schemas/modes/*.json against the meta-schema via
  ajv (Draft 2020-12).
- Cross-checks params.length == ml.output_size and
  ml.input_channels.length == ml.input_size.
- Emits constexpr C++ data into nisps/modes/generated/ (one
  schema_types.hpp + one <mode_id>_schema.hpp per mode). Uses
  std::string_view + std::array; no std::vector, no heap, .f
  suffixed float literals (perf contract §3.3).
- Emits TS modules into playground/src/modes/generated/ (one
  types.ts + one <mode_id>_schema.ts + index.ts barrel).
- Idempotent: re-running yields byte-identical output.
- Exits non-zero on schema validation failure.

Reference templates live in codegen/templates/ (not consumed at
codegen time -- for human reviewers).

Golden test (codegen/tests/golden_test.ts) snapshots
paf_synth_schema.{hpp,ts} and verifies regeneration matches the
golden + that a second run is idempotent.

Until stream 1 lands nisps/core/math.hpp, schema_types.hpp ships
its own minimal Curve enum with a TODO marker pointing at the
eventual include.
2026-04-29 15:29:08 +03:00
w1n5t0n
1bb0ec5eed Add mode schemas + meta-schema (Stream 5 / meml-7k6)
JSON Schema Draft 2020-12 meta-schema (schemas/schema.json) plus 8
per-mode schemas authored against the firmware sources:

- paf_synth (33 params, 7 voice spaces)
- channel_strip (24 params, 6 voice spaces)
- xiasri (24 params, direct mapping)
- verb_fx (47 params, 12 voice spaces)
- memlcelium (56 params, sequencer + dual PAF)
- breakor (56 params, 8-track ratio sequencer)
- elysiamorf (40 params, 8-track FM/CC sequencer)
- sound_analysis_midi (8 params, audio-feature -> MIDI CC)

schemas/modes/params_notes.md captures provenance and judgement
calls for each mode.
2026-04-29 15:28:49 +03:00
w1n5t0n
e5bf2aa055 feat: nisps build + host test harness
CMakeLists.txt:
- Native host build by default; Emscripten-target detection plumbed but
  WASM emit deferred to stream 7 (playground build script).
- Header-only INTERFACE library `nisps_core`.
- Host test executable `nisps_core_tests` compiled with -Wall -Wextra
  -Werror -Wpedantic (Chris's rules: clean build is non-negotiable).

tests/cpp/test_helpers.hpp:
- Minimal NISPS_TEST / NISPS_EXPECT / NISPS_EXPECT_NEAR macros, no external
  deps. Rationale documented in-file: Catch2/doctest would add ~10MB and 30s
  for what is currently <100 LOC of test runtime.

22 unit tests covering FixedBuffer (5), RingBuffer (5), Rng (7), math (5).
All green; verified via `cmake --build nisps/build && ./nisps/build/nisps_core_tests`.
2026-04-29 15:22:01 +03:00
w1n5t0n
4f60fc8405 feat: nisps/core foundation — perf, types, concepts, buffers, rng, math
Greenfield C++20 core for the unified firmware+WASM rewrite (architecture.md
streams, meml-dn7). Header-only, platform-agnostic, no heap, no virtual
dispatch.

Components:
- perf.hpp        memory section + inlining macros, RP2040/RP2350-aware,
                  inert on host/Emscripten
- types.hpp       stereosample_t (mirrors firmware AudioDriver API),
                  sample_t/param_t aliases, DriverConfig negotiation struct
- concepts.hpp    MLEngine, AudioEngine, Mode (architecture §4.1-4.3)
- fixed_buffer.hpp  std::array-backed cursor; replaces std::vector in hot paths
- ring_buffer.hpp   SPSC lock-free FIFO, power-of-two capacity, atomic
                    head/tail; replaces pico/util/queue in core
- rng.hpp         xoshiro256+ with splitmix64 seeding, uniform/signed/
                  gaussian-via-3-uniforms (matches legacy MoveWeights shape)
- math.hpp        clamp01, fast_sigmoid (tanh-Padé, ~1.2% max err on [-6,6]),
                  exact_sigmoid, fast_exp, named Curve catalog (linear/exp/
                  log/square/sqrt/sigmoid/cubic) — TypeScript twin lives in
                  playground/src/output/curves.ts (stream 5)

Performance discipline (Chris's rules):
- No heap, no std::vector, no malloc/new in core
- All float literals carry .f suffix
- Memory section attrs syntactically present, inert on non-firmware builds
2026-04-29 15:21:52 +03:00
w1n5t0n
01f6522fd5 docs: capture playground redesign intent from _rewound snapshot
Source: archive/playground-redesign-2026-snapshot branch (commit 287e3a1).
Reference-only — playground is being rewritten in SolidJS, so the redesign's
implementation will not be merged, but its design intent is preserved here
for the rewrite team.
2026-04-29 13:32:39 +03:00
w1n5t0n
d3373a4e44 Update memllib: fix DISLIKE and DRAG audible feedback
DISLIKE now calls joltNetworks() immediately for instant sound change.
DRAG release now outputs savedAction to audio queue immediately rather
than waiting for training to converge.
2026-04-17 23:23:47 -07:00
w1n5t0n
c7da7bf4f7 Improve UF2 bootloader detection 2026-04-16 00:52:39 +09:00
w1n5t0n
25b8f7ca72 Update beads interaction log 2026-04-16 00:46:17 +09:00
w1n5t0n
6b26168a29 Preserve firmware variant capitalization 2026-04-16 00:46:02 +09:00
w1n5t0n
d1c12dd2b1 Fix firmware variant prompt TTY detection 2026-04-16 00:44:52 +09:00