Commit Graph

76 Commits

Author SHA1 Message Date
shawn 2a74c8e114 Sync NAM engine block_size with JACK period on profile switch
- Add FastNAMHost.set_block_size() — reloads current model with
  new block_size, keeping the engine in sync with JACK's period
- Wire into POST /api/audio/profile so NAM engine restarts with
  correct frame count when switching between standard/low/stable
- Prevents sample-alignment drift when JACK period changes but
  NAM engine stays at old block_size (was causing passthrough)
2026-06-13 22:26:27 -04:00
shawn fa787debd1 Add stable audio profile: period=512 (10.6ms) for Pi 4B stability 2026-06-13 22:24:06 -04:00
shawn 11c62868cf Add UX & API test plan — 10 sections, 60 tests covering block toggle, param updates, preset CRUD, audio profile switching, volume/bypass, WebSocket broadcasts, UI visuals, edge cases, and channel isolation 2026-06-13 22:20:20 -04:00
shawn e7a778d33d Delete all presets everywhere
- Remove factory presets (151 files across 30 banks, 5 channels)
- Remove default_captures (5 NAM preset mappings)
- Disable factory preset installation on boot (if False)
- Clean from local dev repo, pedal project copy, and pedal runtime
2026-06-13 21:10:21 -04:00
shawn 67ffc3252f Fix UnboundLocalError in MIDI handler when pyserial is missing
- Remove serial.SerialException from except clause since 'serial'
  is not defined when import serial fails, causing UnboundLocalError
- Catch only ImportError and OSError
2026-06-13 21:05:21 -04:00
shawn 2aefa5303d Add /api/audio/profile endpoints for runtime latency profile switching
- GET /api/audio/profiles — list available profiles with params
- GET /api/audio/profile — current profile + jack_running + xrun_count
- POST /api/audio/profile {profile: 'standard'|'low'} — switch profile,
  restart JACK server + JACK audio client atomically
- Adds audio_system + jack_audio refs to WebServerDeps
- Rollback on JACK restart failure
2026-06-13 20:47:35 -04:00
shawn c0c0ffe4f2 fix: toggle_block now sets bypass=not enabled - was leaving bypass=True, blocking FX 2026-06-13 20:40:11 -04:00
shawn 1b250cd5a3 fix: critical DSP bugs causing crackles/pops and clipping
1. NAM_AMP: removed double process() call (was processing through C++
   engine twice per block, corrupting internal state), removed os.system()
   debug logging on every audio callback (caused JACK xruns on RPi 4),
   fixed level*2.0 input clipping to proper 0.0-1.0 gain drive

2. Added np.clip() to all wet/dry mix returns across 9+ effect types
   (delay, reverb, chorus, flanger, phaser, ring mod, envelope filter,
   rotary, formant, ping-pong, multi-tap, reverse, shimmer reverb)

3. Clipped all delay feedback write paths (digital, analog, ping-pong,
   multi-tap, reverse, tape echo) to prevent delay-line runaway

4. Added output clipping to _process_mono and _process_4cm master volume

5. Fixed analog delay tanh(*0.5)*2.0 -> tanh(*0.5) (was re-expanding
   after soft-clip, reintroducing digital distortion)
2026-06-13 20:35:38 -04:00
shawn a43e65b81f fix: delay time param was 0.35 (seconds) instead of 350 (ms) — inaudible
- Changed delay time from 0.35ms to 350ms for audible delay
- Increased reverb mix 0.25→0.35 and decay 0.35→0.4
- Increased delay mix 0.25→0.35 and feedback 0.2→0.3
- Both reverb and delay now enabled+active by default (bypass=false)
2026-06-13 20:20:25 -04:00
shawn 103d36a14b cleanup: remove separate ui repo dependency, ui-dist is canonical 2026-06-14 00:05:06 +00:00
shawn 9e3ab9d53b fix: NAM amp level param now controls input drive, JACK period 256, toggle API matching 2026-06-14 00:01:46 +00:00
shawn 9fade4a2d2 fix: add missing API endpoints for volume POST, bypass/toggle, block params, block toggle; fix JACK 1.9.22 cmd args; fix Scarlett 2i2 channel count 2026-06-13 21:09:13 +00:00
shawn cd2ed273e9 fix: handle NaN output_level in state gathering to prevent crash 2026-06-13 12:24:32 -04:00
shawn 6e07aa2b38 fix: NAM SlimmableContainer (A2) model loading with correct weight import
Complete rewrite of _build_nam_model and _build_wavenet to handle:
- SlimmableContainer (A2): pick submodel by quality, recurse into WaveNet
- WaveNet: proper dilated convs with causal padding, 1x1 layer1x1 post-conv
- Linear: simple pass-through with gain/bias

Weight import matches NAM export order:
  rechannel -> per-layer(conv w+b, input_mixer w, layer1x1 w+b) -> head_rechannel w+b -> optional head -> head_scale

Also fixes MOCK_PRESETS in frontend to match actual factory preset names.
2026-06-13 12:21:53 -04:00
shawn 42be40f698 fix: replace nam package import with local init_from_nam
The PyPI 'nam' package (v0.0.3) is Neural Additive Models for tabular
data — completely different from Neural Audio Model (guitar amp modeling).
Implemented a local _init_from_nam() that builds a WaveNet NAM model
from the Core ML .nam JSON format. Uninstalling the wrong package.
2026-06-13 12:06:11 -04:00
shawn 80485186af fix: activate preset on boot after restore_state()
restore_state() loads the last state from disk but doesn't call
activate() on the pipeline, so the NAM model was never loaded
on boot and no audio passed through. Added select() call with
the restored bank/program values so the pipeline loads the
preset chain including the NAM model.
2026-06-13 11:58:38 -04:00
shawn d917d4b20d docs(server): add comment about UI_DIST_DIR env var on pedal 2026-06-13 11:32:54 -04:00
shawn 8e2b090eaf fix(server): prioritize pi-multifx-pedal-ui/dist over src/web/ui-dist for Helix UX
Reordered UI_DIST_DIR candidates so pi-multifx-pedal-ui/dist (the
correct Helix-style pedal UI) is checked first. The old priority
put src/web/ui-dist first, which allowed wrong-build deploys to
mask the real UI.

Also add UI_DIST_DIR env var to systemd service for explicit path.
2026-06-13 11:31:39 -04:00
shawn 659e53766a fix(bass-presets): move factory presets to channel subdirs + per-channel install
Root cause: factory presets for bass (banks 17-20) sat in the root
factory dir alongside guitar presets, and install_factory_presets()
used rglob() which picked up ALL presets into the GUITAR channel.

Changes:
- Move bank_17-20 from presets/factory/ → presets/factory/bass/
  (matching the existing keys/, vocals/, backing_tracks/ layout)
- Fix install_factory_presets() to scan channel-specific subdirectory:
  GUITAR scans root bank_* dirs, all others scan <channel>/bank_*
- Fix main.py to iterate over all 5 channels when installing factory
  presets (was only installing into GUITAR)
- Import Channel enum in main.py

Tests: 56/56 preset manager tests pass. Integration verified:
- GUITAR: 68 presets (banks 0-16)
- BASS: 16 presets (banks 17-20, SansAmp/Darkglass/Ampeg SVT)
- KEYS: 12 presets (banks 21-23)
- VOCALS: 12 presets (banks 24-26)
- BACKING_TRACKS: 12 presets (banks 27-29)
2026-06-13 10:23:57 -04:00
shawn 575535762c fix: bank display showing 1 instead of 0 + build fixes
- Removed 'e.bank||1' falsy trap (was converting bank 0 to 1)
- Fixed TypeScript errors: cp guard, unused vars, path aliases
- Added base: '/ui/' to vite config for correct asset paths
- Bank persistence already implemented (localStorage pedal-footswitch-bank)
- Rebuilt and deployed to src/web/ui-dist/
2026-06-13 10:18:33 -04:00
shawn 82f687323c t_926ea4a1: fix midi import path and channel-aware preset routing
- Fix ModuleNotFoundError: 'from presets.types' -> 'from src.presets.types'
- Share same PresetManager for both channels (it routes internally)
- Pass channel param to list_banks() and load() so bass queries
  read from bass/ subdirectory, not guitar/
- Verified: bass presets isolate correctly, survive restart
2026-06-13 10:18:07 -04:00
shawn 4b59e4422c fix: 'Bank @' → 'Bank A' (off-by-one in transport mode bank letter gen)
Two issues found and fixed in the deployed UI bundle:

1. String.fromCharCode(64+y) where y is 0-based bank index
   → 64+y gives 64+0=64 = '@' for bank 0
   → Fixed: 65+y gives 65+0=65 = 'A' for bank 0

2. e.bank||1 where falsy 0 falls through to 1
   → Shows 'Bank 1' for bank 0, 'Bank 2' for bank 1
   → Fixed: e.bank||0 so 0 renders as 'Bank 0'

Source code (frontend-react/src/App.jsx) was already clean —
the bug was in a transport/song mode feature that was built
from uncommitted changes then reverted.
2026-06-13 10:17:44 -04:00
shawn 42d0592856 Add factory presets for Keys, Vocals, and Backing Tracks channels
3 banks per channel (9 total, 36 presets):
- Keys: Piano & Electric (bank 21), Synth Pads & Leads (22), Organ & Vintage Keys (23)
- Vocals: Lead Vocals (24), Harmony & Backing (25), Vocal FX (26)
- Backing Tracks: Mix Ready (27), Click & Guide (28), Ambient Pads (29)

Appropriate FX chains per channel — de-esser, HPF/LPF, shimmer_reverb,
stereo_widener, rotary_speaker for vocals and keys; master bus chain
for backing tracks. No IR cab blocks on vocal/backing presets.

Generated by scripts/generate_channel_presets.py
2026-06-13 10:09:25 -04:00
shawn 5c51b3db28 feat(nam): download 10 new A2 models for bass, orange, and other missing categories
Bank 7 — Bass: Ampeg PF20T, Aguilar Tone Hammer 500, GK 800RB (Gallien-Krueger), Aguilar DB751
Bank 7 — Orange: Orange Rocker 30 Dirty + Natural
Bank 7 — Others: Fender Bassman dimed, Hiwatt DR103 Bright + Cornish, Supro Black Magick M3

All SlimmableContainer v0.7.0, ~287-295 KB, verified JSON.
Also fixed Bank 2 Tone3000 storage keys to include .nam extension (Tone3000 changed URL format).
Inventory updated: 54 → 64 total models.
2026-06-13 02:09:56 -04:00
shawn 11c838a27e docs: add 17 NAM rock, high-gain & ambient models to inventory (Banks 4-6) 2026-06-13 01:56:40 -04:00
shawn 04fb12dcdc download: 62 real cabinet IRs from GitHub (fnpngn/IR)
Sources downloaded via git-lfs:
- DYNAX IR pack (Fender, Vox, Marshall, Mesa, Bogner, etc.)
- 1960a Marshall 4x12 SM57/Audix i5 captures (96kHz → 48kHz)
- Allure Pack (vintage 1x10, 1x12, 2x12)
- ML Sound Lab free IRs
- Dauntless IRs (Engl Fireball)
- Master Of Puppets IRs
- Modern Metal Songwriter IRs
- FREE BASS IR Pack
- ML NAM AI Cab.nam bonus model

All IRs converted to 48kHz, 16-bit mono WAV. Inventory updated with
full per-file breakdown. Original 7 synthetic IRs preserved.
2026-06-13 01:54:20 -04:00
shawn e11956881f Add Bank 12: Blues genre presets — SRV, Muddy/Buddy, B.B. King, Gary Moore
Bank 12 (Blues) with 4 factory presets covering 9 legendary blues artists:
- Preset 0: Texas Flood (SRV) — TS808 + Fender Super Reverb
- Preset 1: Mannish Boy (Muddy Waters) — Fender Bassman cranked w/ slapback
- Preset 2: The Thrill (B.B. King) — Twin Reverb clean w/ comp
- Preset 3: Still Got the Blues (Gary Moore) — Marshall crunch w/ delay

Includes docs/blues-genre-research.md with full gear deep-dives, NAM
model sourcing guide (Tone3000 URLs), chain architectures, and
parameter value rationale for all 9 artists (SRV, B.B. King, Albert King,
Freddie King, Muddy Waters, Buddy Guy, Gary Moore, Joe Bonamassa,
John Mayer).
2026-06-13 00:52:28 -04:00
shawn adb35a730b feat: Add analog and ping_pong delay subtypes under FXType.DELAY
- Refactor _apply_delay into subtype dispatcher (digital/analog/ping_pong/tape)
- Add _apply_analog_delay: BBD-style with one-pole LPF on feedback path
  and subtle saturation (tanh) for warm, darker repeats. tone param
  controls feedback brightness (0.0=dark, 1.0=bright).
- Wire existing _apply_ping_pong_delay and _apply_tape_echo as subtypes
- All existing delay tests (TestDelay, TestPingPongDelay) pass unchanged
- Add TestAnalogDelay: output range, dry passthrough, feedback tail decay,
  tone spectral effect, state initialization, bypass
- Add TestDelaySubtypePingPong: output finite, dry path, state accumulation,
  ping alternation, zero mix passthrough, bypass

Unblocks child task t_14bae7ea (FXBlock subtype field + pipeline dispatch)
2026-06-13 00:41:22 -04:00
shawn bfe1434f75 feat: separate preset banks per channel (guitar/bass)
- Channel enum (GUITAR, BASS) with channel field on Preset dataclass
- Channel-prefixed storage: {root}/{channel}/bank_{bank}/preset_{program}.json
- PresetManager channel awareness: set_channel(), current_channel property
- Per-channel state persistence (channel_state.json per channel dir)
- Legacy migration: flat bank_* dirs auto-migrate to guitar/ on first boot
- All CRUD methods accept optional channel parameter
- API endpoints accept channel param on GET/PUT/DELETE /api/presets
- /api/channel GET/POST for channel switching
- 10 new channel tests (independence, switching, migration, scoping)
- Factory presets install to specified channel
2026-06-12 23:22:47 -04:00
shawn 0a21297f77 t_59dfed5b: Dual DSP Chain feasibility research document 2026-06-12 23:10:32 -04:00
shawn 013588f2ca feat: Tuner — pitch detection + audio mute in pipeline, API endpoint for pitch data 2026-06-12 19:23:38 -04:00
shawn 214f3c13d7 feat: Footswitch Modes — Stomp/Preset/Snapshot/Combo with scribble strips
Implements multi-mode footswitch system for the Helix Stadium-inspired pedal UI:

- Footswitch mode context provider with 4 modes: stomp, preset, snapshot, combo
- ModeSelector in status bar (collapsed cycling mode for mobile)
- Stomp mode: footswitches toggle individual blocks on/off, scribble strips show block name + status
- Preset mode: footswitches show Bank Up/Down + preset selection, scribble strips show preset name + number
- Snapshot mode: footswitches recall 8 snapshots, scribble strips show snapshot name
- Combo mode: first 4 stomp + bank nav + preset select + global bypass
- ScribbleStrip LCD component with LED indicator and double-tap support
- Double-tap gesture hook for secondary actions (tuner, snapshot save, etc.)
- FootswitchBar container with 8 capacitive-touch footswitch buttons and LED rings
- usePedalState hook (WebSocket + API polling, ported from frontend-react/App.jsx)
- Dark pedal theme CSS (Helix-inspired) with responsive mobile layout
- Full App.tsx rewrite with status bar, footswitch bar, tab bar, and all screens
2026-06-12 19:22:50 -04:00
shawn b6dff3c0f3 feat: Snapshots — 8 per preset save/recall
Adds snapshot system to Helix Stadium:

Backend:
- Snapshot data model (8 slots per preset, captures block bypass
  + params + master volume)
- Snapshot API endpoints: list, save, recall, rename, delete
- Integrated into preset serialization/deserialization
- Tracks current_snapshot in state / WebSocket broadcasts

Frontend:
- Camera icon in header bar with current snapshot number
- SnapshotPanel as slide-up overlay
- 8 snapshot slots in a horizontal row
- Tap to recall filled snapshot, tap empty slot to save
- Press-and-hold (600ms) to save current state
- Inline name editing on tap
- Discard Edits toggle
- Green flash indicator on save
- Delete button for active snapshot
2026-06-12 19:20:56 -04:00
shawn a1f35a56e3 feat: new preset, looper recorder, IR display improvements
- New Preset button: finds first empty slot, saves with default name
- Looper/Recorder tab: Record/Stop via arecord backend, list captures, play back
- Rig: shows loaded IR name more clearly + quick-link buttons to browse IRs/Models
- Backend: capture/start, capture/stop, captures API endpoints with file serve
2026-06-12 17:24:14 -04:00
shawn 2a30592ab3 fix: all dummy data replaced with real API data
- Status bar: CPU load + sample rate from /api/state, TUNER badge shown immediately
- Rig: optimistic toggle updates (bypass/tuner respond instantly), prominent tuner card
- FX Chain: loads real block params from /api/block-params/{fx_type}, Add Block picker with all FX types
- Record: replaced fake multitrack with real Signal Monitor (live I/O VU, clip detect, level history)
- Backend: added _gather_system_stats() returning cpu_percent and sample_rate
2026-06-12 17:16:03 -04:00
shawn 7498ad55fc fix: real VU meter levels from pipeline instead of random animation
- Added input_level and output_level to /api/state (scaled 0-100)
- React RigScreen now reads real levels from state instead of random walk
- When no audio flows, VU meters show flat (0) instead of fake animation
2026-06-12 17:09:49 -04:00
shawn 32690c293e feat: comprehensive React SPA replacing Jinja2 UI
- New dark UI with 7 tabs: Rig, FX Chain, Models, IRs, Presets, Settings, Record
- All screens wired to live API endpoints
- Real-time state via WebSocket + polling fallback
- Models screen: list/load/unload/upload NAM files + Tone3000 search/install
- IRs screen: list/load/unload/upload IR files + Tone3000 search/install
- Presets screen: browse banks, activate, save, delete
- Settings screen: WiFi scan/connect/hotspot, Bluetooth/MIDI, 4CM routing
- Rig screen: live state, volume, bypass, tuner, signal chain
- FX Chain screen: block params, per-block bypass
- Serves at / instead of /ui
- Built with Vite, ~71KB gzipped
2026-06-12 15:30:03 -04:00
shawn 35d2dc0893 R4: Save Tone3000 downloads with readable filenames instead of internal IDs 2026-06-12 14:24:09 -04:00
shawn 763e152d86 R3: Add Focusrite 2i2 1st gen profile + models/irs escapeHtml fix 2026-06-12 14:20:04 -04:00
shawn 737b123994 R2: Add escapeHtml to app.js for models/irs pages 2026-06-12 14:17:24 -04:00
shawn 77a757cee6 R1: Dashboard 500 fix, Phase 2/7 uncommitted changes
- Fix dashboard 500 when deps disconnected — _gather_state() now returns
  full defaults instead of bare {'connected': False}
- Phase 2: FX param schemas aligned with DSP implementations
- Phase 7: Pipeline routing, preset manager improvements
- Factory presets: cleanup and normalization

Fixes dashboard crash on disconnected state (dogfood issue #1)
2026-06-12 14:02:13 -04:00
shawn 1301a8a128 docs: update test plan with Phase 2/7 results, README test count -> 574, close Gitea issues #1 #2 2026-06-12 13:36:39 -04:00
shawn 8e84b0217d Phase 7: Stability test + fix MIDI test signature
- tests/test_stability.py — 1-hour continuous DSP pipeline stability test
  covering all 46 FX types with CPU/memory/NaN monitoring
- tests/test_integration.py — fixed test_midi_cc_maps_expression to use
  correct _on_midi_cc(value, cc_number, channel) signature matching
  the Phase 6 MIDI fix
2026-06-12 13:29:55 -04:00
shawn f8d7db731e P6-R3: Fix MIDI CC callback bug — pass CC number, not MIDI channel
Bug: PedalApp._on_midi_cc() received (value, channel) where channel was
the MIDI channel (0-15) but was treated as the CC number (0-127). The
register_cc callback only passes value + MIDI channel, not the CC number.

Fix: Use a lambda closure in _wire_midi_callbacks to capture the CC
number so _on_midi_cc gets (value, cc_number, channel). Moved expression
pedal (CC#11) check before the presets/pipeline guard so it works even
when no preset is active.

Tests: 21 new integration tests covering:
- CC lambda closure capturing correct CC numbers
- Expression pedal → master volume (CC#11)
- Full parse→dispatch→callback chain for PC and CC
- Preset MIDI mapping lookup by CC number
- Edge cases: min/max values, rapid switching, empty slots
2026-06-12 13:05:21 -04:00
shawn 9a8d42e1b6 P2: Fix FX param schemas to match DSP implementations
- EQ: Renamed low_gain/mid_gain/high_gain to bass/mid/treble + added
  bass_freq/mid_freq/treble_freq/q params matching _apply_eq()
- Chorus: Added 'delay' param matching _apply_chorus()
- Flanger: Added 'delay' param matching _apply_flanger()
- Phaser: Added 'stages' and 'mix' params matching _apply_phaser()
- Tremolo: Added 'shape' param matching _apply_tremolo()
- Delay: Added 'tap_tempo' param matching _apply_delay()
- Reverb: Added 'predelay' param matching _apply_reverb()
- Added missing nam_amp + ir_cab param schemas

Verified: 49/49 FX DSP blocks pass, 227 tests pass
2026-06-12 12:57:40 -04:00
shawn 3e55ea8a9d Fix multi-word query URL encoding for Tone3000 search
Replace spaces with PostgREST wildcards in ilike filter values so
multi-word queries like 'vox ac30' produce valid URLs.

Root cause: PostgREST filter parser doesn't handle spaces URL-encoded
as '+' in query parameter values, returning 400 Bad Request.

Fix: query.replace(' ', '*') converts spaces to PostgREST wildcards
before inserting into ilike.* pattern. 'vox ac30' becomes
ilike.*vox*ac30* -> ILIKE '%vox%ac30%'.

Changes:
- src/system/tonedownload.py: fix search_models() and search_irs()
- tests/test_tonedownload.py: update test_multi_word_query assertion
2026-06-12 12:43:55 -04:00
shawn e890a4aad3 Wire React UI dist mount at /ui/
Add UI_DIST_DIR auto-detection and /ui static mount in the
FastAPI server so the built React frontend is served alongside
the main dashboard.

- UI_DIST_DIR env var override
- Auto-detect common project layouts (frontend-react/dist,
  pi-multifx-pedal-ui/dist)
- Mount with html=True for SPA client-side routing
2026-06-12 03:08:07 -04:00
shawn da3097c2dc docs(presets): add factory-presets.md reference
Document all 3 banks (12 presets) with chain, parameters, and best-use notes.
2026-06-12 03:06:48 -04:00
shawn 9a6cbdf9ac feat(presets): seed default overdrive/delay/reverb/chorus presets
Add Bank 2 'FX Bench' with 4 dedicated factory presets:
- Overdrive Bench: noise_gate + overdrive + volume
- Delay Bench: noise_gate + eq + delay + volume
- Reverb Bench: noise_gate + eq + reverb + volume
- Chorus Bench: noise_gate + eq + chorus + volume

Each preset showcases a single core effect with well-tuned default
parameters derived from the pipeline DSP schemas.

All 46 existing tests pass.
2026-06-12 03:05:56 -04:00
shawn ced339bd18 I2S HAT: add PCB net definitions, net assignments, trace segments, and GND copper pours
- 17 net definitions (GND, +3.3V/+5V, BCK/LRCK/DIN/DOUT, audio signals)
- Net assignments to 35+ pads across all components (PCM1808, PCM5102A, TL072, AMS1117)
- 20 trace segments for I2S bus and audio signal routing
- 4 GND copper pour zones (F.Cu + B.Cu)

Uses string-based S-expression manipulation with proper parenthesis tracking.
2026-06-10 00:47:25 -04:00