Commit Graph

54 Commits

Author SHA1 Message Date
shawn 5dc769ad30 fix: move NAM block size sync before JACK client restart
The profile change handler was calling nam_host.set_block_size() AFTER
jack_client.start(), which means the JACK process callback started firing
with the new buffer size while the old NAM subprocess was still running
at the old block size. The pipe read/write byte counts desynchronized,
causing NAMEngineProcess.process() to return audio_block as passthrough
(direct input / DI signal) — every subsequent callback showed dry audio.

Fix: reload the NAM engine with the correct block size BEFORE restarting
the JACK client. Now when the process callback fires, the NAM subprocess
is already ready with the matching block size.
2026-06-17 21:07:48 +00:00
shawn 1b2747abb1 fix: audio params timeout - increase API timeout for JACK restart
The setAudioParams call restarts JACK which takes up to 10 seconds.
The default API timeout was 6s, causing the request to abort and
leaving JACK in a broken state.

- Changed setAudioParams to use API._fetch directly with timeout:15000
- Also calls loadAudioParams() after success to refresh dropdown values
2026-06-16 20:02:57 -04:00
shawn 177d412c84 tone3000: IR downloads, model detail, arch filter, metadata
- NAM/IR tab toggle in Downloads overlay
- Architecture filter (All/Nano/Feather/Standard) - filters via
  Supabase architecture_version column
- Model detail overlay: tap a search result to see full description,
  gear info, author, pack, architecture, thumbnail before downloading
- IR search + install with .meta.json metadata saving
- Backend: search_models/get_trending_models accept arch param
- Backend: tone_gear field added to model search response
2026-06-16 19:59:38 -04:00
shawn 5cdc751312 tone3000 metadata capture: save .meta.json alongside downloaded models
- UI now sends full Tone3000 metadata on install (id, author,
  architecture, pack_name, description, thumbnail, etc.)
- Search results show size, architecture, and pack name
- Install endpoint saves a .meta.json file next to each .nam with:
  author, architecture, description, pack, thumbnail, download date
- Enables offline browsing of model provenance
2026-06-16 19:49:23 -04:00
shawn a37e381c4e backing tracks: add source selector + bluealsa in device list
- Audio devices endpoint now includes Bluetooth Audio (bluealsa) and
  other software PCM devices from aplay -L
- Added GET/POST /api/backing-source endpoints with JACK port routing
- Backing Track Source dropdown in Audio section (None, Bluetooth
  Audio, or any JACK capture port found)
- Selecting a source routes it into fx_in:input_2 via jack_connect
- Also supports bt_audio:capture_1/2 from existing JACK bridge
2026-06-16 19:20:30 -04:00
shawn 334e035cde ch output: per-channel output routing dropdowns
- Added GET/POST /api/channel-output endpoints for per-channel
  output routing (playback_1 / playback_2 / both)
- Added output dropdowns to CH 1 and CH 2 cards in Audio Interface
- Added setChOutput() and loadChOutput() JS functions
- Updated connect_fx_ports to read per-channel output config
2026-06-16 19:18:30 -04:00
shawn e324d9007b fix: loadAudioDevices now correctly accesses devs.devices array
Also simplified device auto-selection to match by trailing device
number (e.g. 'hw:USB,0' → finds option ending in ',0').
2026-06-16 19:15:51 -04:00
shawn 427c77cbe7 fix: loadAudioDevices accesses .devices from response object
API.listAudioDevices() returns {devices: [...]} not the array
directly. The response also uses card numbers hw:X,Y format.
2026-06-16 19:14:51 -04:00
shawn 93610e4d98 audio interface: separate input/output device selectors
- Added Output Device dropdown alongside Input Device
- loadAudioDevices() now populates both dropdowns from the API and
  selects current values
- Separated setAudioInputDevice / setAudioOutputDevice functions
  (each calls API with only its respective field)
- Added input_device / output_device to GET /api/audio/profile response
- Removed the old combined setAudioDevice function
2026-06-16 19:13:25 -04:00
shawn 1530aa266f audio: replace profiles with direct sample rate + buffer size controls
- Removed audio profile grid and preset cards
- Added two dropdowns: Sample Rate (44.1/48/96/192 kHz) and Buffer
  Size (64-2048 frames)
- New loadAudioParams() reads current rate+period from API
- New setAudioParams() POSTs {period, rate} directly on change
- Cleaned up unused CSS (.audio-profile-grid, .ap-card, .ap-key, etc.)
- Removed unused audioProfiles variable
2026-06-16 19:11:21 -04:00
shawn ea75c41745 settings: nav sticks to top with bigger buttons
- Moved .settings-nav out of .overlay-body so it stays pinned below
  the header when scrolling
- Buttons bumped to 12px font, 8px 16px padding, 38px min-height
- Added gap, padding, background and border-bottom to nav strip
2026-06-16 19:03:20 -04:00
shawn f6fe3b2523 settings: add horizontal section nav bar
Adds a scrollable row of section tabs at the top of the settings
overlay: Network · Audio · Interface · Bluetooth · System.
Each tab scrolls smoothly to its section and highlights as active.
2026-06-16 18:57:53 -04:00
shawn 753de36dc2 serve on port 80 — pedal.local works without :8080
Changed DEFAULT_PORT to 80 so the UI is accessible at
http://pedal.local/ directly (no port needed).

Killed orphaned old port-80 instance that was running stale code.
2026-06-16 18:45:53 -04:00
shawn 7078217783 nuke all old UIs — templates, React SPA, /v2 mount, legacy routes
Removed:
- src/web/templates/*.html (all 6 Jinja2 template files)
- src/web/ui-dist/ (old React SPA build)
- /v2 duplicate static mount (same as /)
- /ui static mount (old React SPA)

Server.py changes:
- Root handler now directly serves v2 UI (no fallback chain)
- Old template routes (/presets, /models, /irs, /settings) → redirect to /
- Removed Jinja2Templates, TEMPLATES_DIR, TEMPLATE_VERSION, UI_DIST_DIR
2026-06-16 18:45:04 -04:00
shawn 9cc08d16da settings: open as v2 overlay instead of redirecting to old settings page
- Changed both tab bar ⚙ and status bar ⚙ buttons to open the
  inline Settings overlay (openOverlay('Settings')) with data loads
  instead of navigating to /settings (old Bootstrap page)
- The overlay was already fully built — HTML, CSS, JS loaders were all
  present, just never wired up
2026-06-16 18:41:13 -04:00
shawn 93eb8cc4aa Make v2 UI Settings button navigate to /settings page
Both the tab bar ⚙ Settings button and the status bar ⚙ button
in the v2 UI now redirect to /settings instead of opening an
inline overlay. The full settings page has the left sidebar nav
with category views.
2026-06-16 18:35:51 -04:00
shawn 4fa49d46c1 Bump template version to 8 for CSS cache bust 2026-06-16 18:33:03 -04:00
shawn 88ee1e24dc Fix mobile settings sidebar: keep vertical layout instead of horizontal row 2026-06-16 18:32:45 -04:00
shawn 3b1bb43895 Add cache-busting ?v= to template CSS/JS, bump version
All static asset references now include ?v={{ version }} to force
browser to reload on deploy instead of serving cached versions.
Increment TEMPLATE_VERSION in server.py on each deploy.
2026-06-16 18:30:07 -04:00
shawn a7d8a27975 Settings ⚙ button now links to /settings page with left nav sidebar
The React tab bar's Settings button previously showed a minimal inline
component. Now it navigates to /settings which has the full settings
page with left sidebar navigation and category views.
2026-06-16 18:24:32 -04:00
shawn 23a83cbd7b Settings: left sidebar nav with separate page views per category
Replaced horizontal tab bar with a left sidebar navigation
that feels like separate pages:
- Left nav with icons + labels (Connection, Audio, 4CM Routing,
  Network, Bluetooth, About)
- Active category highlighted in accent color
- Each category has its own URL via hash (#connection, #audio, etc.)
- Back/forward browser navigation via popstate handler
- Stacked layout on mobile (sidebar becomes scrollable row)
2026-06-16 18:19:32 -04:00
shawn 29542e196b Fix settings 500: add missing block_id to FXBlock, rebuild UI
- FXBlock was missing block_id field (and __post_init__ auto-generator)
  causing AttributeError on /settings and /api/presets endpoints.
- Rebuilt Helix Vite UI from ui/ source (index-jhtO2NOz.js,
  index-D3aC6klh.css). Replaces older dist files.
2026-06-16 17:45:12 -04:00
shawn 1281bf5943 Restore lost UI dist build (prefers-reduced-motion, updated SVG assets)
Commit faa7bdb was on a local branch that got orphaned during
git reset --hard origin/main. Cherry-picked back its UI changes:
- New Vite build: index-DI0gBBIR.css + index-LMuhdreF.js
- Updated SVG icons for effects (amplifier, analyzer, dial, etc.)
- prefers-reduced-motion in index.html
- Updated bank/presets icons
2026-06-16 17:40:05 -04:00
shawn 2c4d7298ec Fix audio timeout when changing buffer size/sample rate
Two bugs:

1. **Deadlock in set_audio_profile() (root cause of timeout)**
   jack_client.stop() was called BEFORE killing the JACK server.
   jack.Client.deactivate() is a synchronous call that blocks until
   the JACK RT callback thread acknowledges. When ALSA reconfigures
   (period/rate change), the RT thread can stall and deactivate()
   hangs permanently. Reversed order: kill JACK server first (via
   killall jackd) to force-kill the RT thread, then clean up the
   Python client. Same fix applied to device hot-swap path.

2. **Pipeline crash after period change**
   _CombFilter.buf and _AllpassFilter.buf were fixed-size arrays
   at BLOCK_SIZE (256). If JACK restarts with a different period
   (e.g. 512), self.buf[:] = block throws a shape mismatch in the
   RT callback -> audio dies permanently. Made internal buffers
   dynamically resize to match input block size.
2026-06-16 17:15:22 -04:00
shawn 0fa8bd387a NAM hero fills screen, pedal-style FX blocks below 2026-06-15 06:03:46 +00:00
shawn a69aee357c v2 UI at root, instrument type selector, Gitea push 2026-06-15 05:53:11 +00:00
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 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 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 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 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 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 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 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 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 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 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 a31342b478 feat(tonedownload): Tone3000 NAM + IR browser, real anon key, tests
- Tonedownload.py: Tone3000 API client with search/download/cache/enrich
- Server.py: 4 endpoints (search models, search IRs, install model, install IR)
- Models.html + irs.html: Browse Tone3000 tab with search, trending, offline
- Models.js + irs.js: Tone3000 search UI with install tracking via localStorage
- Style.css: Card layout for search results with thumbnail, meta, install btn
- Tests: 47 tests covering data classes, cache, API, search, download, enrichment
- Anon key: Updated from placeholder to real Supabase anon key (verified: API works)
2026-06-09 01:39:12 -04:00