Commit Graph

6 Commits

Author SHA1 Message Date
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 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 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 44cf978873 Wire 4CM routing: JackAudioClient + boot orchestration + set_routing_mode()
- Add JackAudioClient class to src/system/audio.py — real-time JACK
  client owning ports and a process callback that streams audio through
  the DSP pipeline. Handles mono (1ch I/O) and stereo 4CM (2ch I/O).
- Wire PedalApp.boot(): read audio.mode from config, set pipeline
  routing_mode (mono/4cm), create and start JackAudioClient with
  matching channel count.
- Add PedalApp.set_routing_mode(mode) — hot-switch between mono and
  stereo_4cm at runtime: updates pipeline.routing_mode, restarts JACK
  client with new channel count via set_channel_count(), re-connects
  JACK ports.
- Wire shutdown: stop JackAudioClient before MIDI/footswitch.
- Export JackAudioClient from src.system.__init__.
- 53 tests pass (22 integration + 31 audio).
2026-06-08 12:59:47 -04:00
shawn 1c5b728e8b Build: Web UI + mDNS (pedal.local)
FastAPI web server with REST + WebSocket, 5 Jinja2 HTML pages,
mobile-friendly dark theme CSS, page-specific JS controllers,
and Avahi mDNS service advertisement for pedal.local.

Files:
  src/web/server.py          — FastAPI app with 17 REST endpoints + /ws WebSocket
  src/web/__init__.py         — Package init with WebServer, WebServerDeps exports
  src/web/templates/*.html    — Dashboard, Presets, Models, IRs, Settings (base)
  src/web/static/style.css    — Dark mobile-first CSS (80-82% max-width)
  src/web/static/websocket.js — Auto-reconnecting WebSocket manager
  src/web/static/app.js       — Dashboard live updates + REST helpers
  src/web/static/presets.js   — Bank/preset CRUD modal UI
  src/web/static/models.js    — NAM model list/load/upload
  src/web/static/irs.js       — IR file list/load/upload
  etc/avahi/pi-multifx-pedal.service — Avahi service advertisement
  scripts/setup-mdns.sh       — One-shot mDNS setup (avahi, hostname, restart)
  main.py                     — Wire WebServer into boot/shutdown
  requirements.txt            — Add fastapi, uvicorn, jinja2, python-multipart
  tests/test_web.py           — 36 tests (pages, REST, WebSocket, errors)

All 36 tests pass.
2026-06-08 00:57:26 -04:00
shawn c38a7b0fd8 Add main entry point + systemd services + integration tests
New files:
  main.py                   - PedalApp: boots all subsystems in order,
                              wires MIDI/footswitch callbacks, graceful
                              teardown reverses boot order
  src/system/config.py      - YAML config loader with deep-merge
                              (separated to avoid hardware deps)
  src/system/services.py    - systemd unit generator for pedal.service
                              + multi-fx-pedal.target
  scripts/install_service.sh - copies project, creates venv, installs
                              + enables service units
  tests/test_integration.py - 41 tests: boot, routing, display sync,
                              teardown, systemd content, CLI, edge cases

Modified:
  tests/conftest.py         - add project root to sys.path
2026-06-07 23:39:50 -04:00