Files
product-vamp/TECHNICAL_GUIDE.md
T
khodowany 6da21a2deb
ci / checks (push) Successful in 38m59s
Rendering
2026-09-05 13:31:18 -05:00

8.0 KiB

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 draws title/composer/part metadata, systems, measures, clef/key/time preambles, 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 clefs, noteheads, rests, dots, accidentals, and eighth-note flags aligned to staff-space coordinates. Printed accidentals are derived from the active key signature and per-measure accidental state, so natural signs are shown only when they are musically required.

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:

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.