Files
product-vamp/TECHNICAL_GUIDE.md
T
khodowany 4f9a269f85
ci / checks (push) Successful in 38m58s
Initial public khodo snapshot
2026-09-04 19:30:11 -05:00

95 lines
7.9 KiB
Markdown

# Product Vamp Technical Guide
## Repository Shape
Product Vamp follows the Modulus product layout used by the khodo repos:
- `include/vamp_zero` and `src/vamp_zero`: Pi Zero C++23 application.
- `include/vamp_pico` and `src/vamp_pico`: Pi Pico 2 W C++17 firmware.
- `include/vamp_protocol` and `src/vamp_protocol`: shared route and page-command contracts used by both devices.
- `cmake/pico.cmake` and `cmake/zero.cmake`: device-specific build definitions selected by the root `CMakeLists.txt`.
- `include/vamp_style.hpp`, `include/vamp_style/font.hpp`, and `src/vamp_style`: shared color palette, Petaluma font paths, and Pico bitmap font data.
- `data`: authored source score JSON for performance songs and practice exercises.
- `generator`: build-time Python preprocessing for performance arrangements and practice variants.
- `package`: generic bundle inputs, scripts, config, migrations, static font assets, and device install files.
## Zero Runtime
The Zero process can run headless for local/API quick checks or with Luma for the Waveshare Zero-DISP-7A display. The API is plain HTTP on the private AP:
- `GET /api/status`
- `GET /api/remote/display`
- `POST /api/remote/heartbeat`
- `POST /api/remote/next`
- `POST /api/remote/previous`
- `POST /api/remote/select`
Legacy `/api/page/next` and `/api/page/previous` routes are still accepted for simple manual checks.
The AP setup pins NetworkManager to 2.4 GHz channel 6 with WPA2/RSN and CCMP ciphers so the Pico W can join using `CYW43_AUTH_WPA2_AES_PSK`.
On Raspberry Pi OS Lite, the installed display service owns `tty1`, conflicts with `getty@tty1.service`, and starts the Zero container through `product-vamp-start-zero`. The wrapper switches to the display VT when `chvt` is available and runs `vamp-zero` inside the container through `setsid --wait --ctty` with `/dev/tty1` as stdin, giving raylib's DRM backend a real virtual terminal while keeping Docker attached to the long-running display process. SSH remains the normal admin path and `tty2` is enabled as the fallback local console.
At startup the Zero scans `share/product-vamp/songs/*.json` and `share/product-vamp/practice/<type>/<lesson_group>/<exercise>/<key>.json`, parses generated score JSON, imports both catalogs into SQLite with Noto, then reloads runtime menu state from the database. Runtime navigation and rendering use that loaded Noto-backed state instead of rescanning asset folders. The UI supports a top-level mode menu, Performance song/part/score menus, and Practice type/lesson/exercise/key/score menus. Melody practice offers key selection by choosing a pre-generated score variant; chord practice skips key selection and renders the authored score key directly. The score renderer is intentionally local and compact for now: it draws title/composer/part metadata, systems, measures, enlarged chord symbols and section labels, Petaluma SMuFL noteheads/rests/flags/dots/accidentals, simple vector stems/ties, and lyrics. Slash notation is represented explicitly with `notehead: "slash"` in generated JSON.
Petaluma music symbols are placed from SMuFL bounding boxes and stem/flag anchors instead of rough font-size offsets, which keeps noteheads, rests, dots, accidentals, and eighth-note flags aligned to staff-space coordinates.
## Score Generation
The build runs `python3 -m generator` against `data/` and writes build-only assets under the CMake binary tree. Performance source songs become acoustic, electric, and vocal arrangements. Melody practice sources are emitted in all 12 keys at build time. Chord practice sources are emitted once in their authored key. Generated JSON is sanitized so packaged scores do not contain tab positions or fingering fields; guitar playability logic is used only inside the arrangement generator.
Generator checks compare the generated performance summary against `data/expected/performance_summary.json`. The summary stores compatibility invariants such as section layout, chord-label placement, slash-section placement, and vocal timing without committing the generated score files themselves. Refresh that file only when an intentional arrangement-logic change should become the new baseline.
## Pico Runtime
The Pico firmware uses the Pico SDK C++ stack and the `pico2_w` board definition. It initializes the ST7789T3 display over SPI, reads the CST816D touch controller over I2C, joins the Zero AP, polls `/api/remote/display`, and sends HTTP POST commands for gestures.
The display is rotated 90 degrees clockwise into a 320x240 landscape UI. The top status bar uses the shared palette:
- Blue: connecting.
- Yellow: retrying or remote request failed.
- Green: connected to the Zero API.
The body stays blank until a performance or practice score is active, then it shows the active title, selected part or active practice key, and page count. Touch controls are tap for next, double tap for previous, and tap-and-hold for select.
The Pico text renderer uses an antialiased bitmap atlas generated from `package/assets/fonts/petaluma/PetalumaScript.otf`; regenerate it with `python3 tools/generate_pico_font.py` after font or atlas-size changes. The generated atlas includes ASCII kerning pairs so display text such as `VAMP` uses the same spacing as the source font.
The root `CMakeLists.txt` is also the Pico firmware entrypoint when configured with `-DVAMP_PICO_FIRMWARE_PROJECT=ON`. That mode returns before the Zero/Linux targets are declared, so the Pico build can use the embedded ARM toolchain cleanly.
Build defaults can be overridden at package time with CMake cache variables:
- `VAMP_PICO_WIFI_SSID`
- `VAMP_PICO_WIFI_PASSPHRASE`
- `VAMP_ZERO_HOST`
- `VAMP_ZERO_PORT`
## Packaging
Modulus packages the CMake install stage. Product Vamp uses `install_prefix` `.` so the registry artifact is a top-level multi-device bundle rather than a root filesystem overlay.
The ARM64 package target builds:
- `zero/product-vamp-zero-linux-arm64.docker.tar`
- `pico/product-vamp-pico2w.uf2`
- `manifest.json`
Building the full package requires Docker/buildx, the ARM embedded GCC toolchain, and the Pico SDK. CI passes `PICO_SDK_PATH` directly. The generated Modulus Docker image also carries the pinned Pico SDK at `/opt/pico-sdk` for local package builds. The Zero Docker runtime targets Debian trixie to match current Raspberry Pi OS and the ARM64 dependency binaries, and includes `util-linux` for the `setsid --wait --ctty` launch path.
Generated score assets are created during CMake build/package and copied into the Zero image context. Static font assets remain tracked under `package/assets/fonts`.
CI builds the generic artifact with `python3 modulus.py package --version latest`, then `package/scripts/publish_latest_generic_package.py` replaces the Gitea generic package version `latest` and deletes older Product Vamp generic package versions.
## Shared Protocol
The Pico firmware does not link Zero-side Linux application libraries such as Ligo, Luma, or Noto. Those remain Zero-side dependencies: Ligo serves the HTTP API, Luma renders the Zero display, and Noto owns SQLite persistence. Cross-device code should stay in `vamp_protocol` and `vamp_style`: plain C++17 with no OS, allocator-heavy, or Zero-only dependencies.
Luma owns the application shell and UI control tree. The score renderer uses Luma primitives for layout, staff lines, and simple vector strokes, and uses a small score-local Raylib font cache for Petaluma SMuFL glyphs until Luma grows first-class codepoint loading.
## LSP
`.clangd` points native Zero and shared protocol code at `build/build_x86_debug`, and Pico code at `build/build_pico_debug`. Refresh both compile databases with:
```sh
python3 modulus.py lsp
```
The `tooling.lsp` section in `modulus.json` owns the Pico CMake configure. It unsets host compiler variables before configuring the Pico SDK project so clangd sees the embedded ARM toolchain. When running directly outside the generated Modulus Docker image, set `PICO_SDK_PATH` before refreshing the Pico database.