+17
-10
@@ -31,13 +31,13 @@ The AP setup pins NetworkManager to 2.4 GHz channel 6 with WPA2/RSN and CCMP cip
|
||||
|
||||
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`, ensures the current SQLite schema with Noto, imports both catalogs, 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 and chord practice both offer key selection by choosing a pre-generated score variant. The score view displays build-time engraved PNG pages. Musical interpretation, spacing, beaming, ties, accidentals, and pagination happen on the build host, not on the Pi. The view retains at most two page textures.
|
||||
At startup the Zero validates the packaged `catalogue.db` application identifier and schema version, opens it read-only through Noto, and loads menu metadata. It never creates or migrates a database, scans score JSON, or repairs metadata. 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 and chord practice both offer key selection by choosing a pre-generated score variant. The score view displays build-time engraved PNG pages. Musical interpretation, spacing, beaming, ties, accidentals, and pagination happen on the build host, not on the Pi. The view retains at most two page textures.
|
||||
|
||||
Runtime catalogues contain immutable metadata shared by navigation, the HTTP service, and the UI. Song groups and practice lessons reference canonical records by index. SQLite retains complete bundles, while cached measure/page counts let menu queries avoid reading musical JSON. Imports process one file at a time in a transaction; failed imports roll back, and unmanaged rows remain untouched. Legacy metadata is backfilled one score at a time from an indexed pending set. External score-JSON changes invalidate cached metadata; imports restore their already-validated metadata within the transaction. Selecting a score loads only that bundle asynchronously, discarding stale results after navigation; the view keeps at most two page textures.
|
||||
Runtime catalogues contain immutable metadata shared by navigation, the HTTP service, and the UI. Song groups and practice lessons reference canonical records by index. SQLite retains complete bundles, while cached measure/page counts let menu queries avoid reading musical JSON. Python constructs the complete database before deployment; metadata and embedded JSON are published together. There are no custom rows, runtime imports, metadata-repair triggers, or persisted page-state tables. Selecting a score loads only that bundle asynchronously, discarding stale results after navigation; the view keeps at most two page textures.
|
||||
|
||||
Practice IDs retain their existing hyphen-joined format. Generation and runtime scanning reject distinct paths that produce the same exercise or variant ID, reporting both paths before replacing assets or importing rows. For example, `chord/one-two/three/c.json` and `chord/one/two-three/c.json` cannot coexist; rename one lesson or exercise to give it a distinct ID.
|
||||
Practice IDs retain their existing hyphen-joined format. Generation and catalogue compilation reject distinct paths that produce the same exercise or variant ID, reporting both paths before publishing the generated bundle. For example, `chord/one-two/three/c.json` and `chord/one/two-three/c.json` cannot coexist; rename one lesson or exercise to give it a distinct ID.
|
||||
|
||||
`--once` (also `runtime.exit_after_startup`) is a headless readiness check: it imports and validates the catalogue and referenced page assets, checks runtime-directory writes and the configured server bind, and exits without opening audio/display devices or publishing a readiness heartbeat. Page checks include containment, checksums, CPU-only PNG decoding, and exact 976x484 dimensions. The configured heartbeat destination must be absent or a regular file, never a directory, symlink, or special file. Runtime writes and readiness probes use exclusive sibling temporary files; readiness leaves an existing heartbeat unchanged and removes its probes. `--health-check` instead probes the running service's read-only `/api/health` endpoint using the configured timeout. A stopped/unresponsive service or an invalid response fails the check; it does not create or repair files. `--check-config` remains configuration-only.
|
||||
`--once` (also `runtime.exit_after_startup`) is a headless readiness check: it checks database integrity and validates the catalogue and referenced page assets, checks runtime-directory writes and the configured server bind, and exits without opening audio/display devices or publishing a readiness heartbeat. Page checks include containment, checksums, CPU-only PNG decoding, and exact 976x484 dimensions. The configured heartbeat destination must be absent or a regular file, never a directory, symlink, or special file. Runtime writes and readiness probes use exclusive sibling temporary files; readiness leaves an existing heartbeat unchanged and removes its probes. `--health-check` instead probes the running service's read-only `/api/health` endpoint using the configured timeout. A stopped/unresponsive service or an invalid response fails the check; it does not create or repair files. `--check-config` remains configuration-only.
|
||||
|
||||
Pico networking progresses without blocking touch sampling. Navigation commands use a bounded 16-entry FIFO ahead of background status polls; a full queue reports busy. Commands are not automatically retried because a timed-out request may already have executed. Background polls wait one second after completion before trying again.
|
||||
|
||||
@@ -47,7 +47,7 @@ Zero and Pico share one gesture state machine: a second release within 220 ms of
|
||||
|
||||
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 and chord practice sources are emitted in all 12 keys at build time. Generated JSON keeps notation metadata while removing generator-only optional chord markers. The catalogue produces three bundles per song and twelve per practice exercise; source counts are discovered, not fixed in code.
|
||||
|
||||
Practice sources live at `data/practice/<chord|melody>/<lesson_group>/<exercise>.xml`. For example, `chord/001_three_note_shell_voicings/001-cycles.xml` generates `practice/chord/001_three_note_shell_voicings/001-cycles/{c,db,d,eb,e,f,gb,g,ab,a,bb,b}.json` and corresponding page directories. Keep numeric prefixes zero-padded to the same width for menu ordering. Lesson folders determine lesson labels; MusicXML work/movement titles determine exercise labels, falling back to the filename stem when absent. Repeated titles do not merge exercises: identity includes the practice type, lesson folder, and normalized filename stem. Invalid nesting, unknown practice types, and colliding normalized filenames fail generation. Adding or renaming a source needs no registry edit; the next build discovers it, replaces obsolete generated paths, and runtime catalogue import removes obsolete managed database entries.
|
||||
Practice sources live at `data/practice/<chord|melody>/<lesson_group>/<exercise>.xml`. For example, `chord/001_three_note_shell_voicings/001-cycles.xml` generates `practice/chord/001_three_note_shell_voicings/001-cycles/{c,db,d,eb,e,f,gb,g,ab,a,bb,b}.json` and corresponding page directories. Keep numeric prefixes zero-padded to the same width for menu ordering. Lesson folders determine lesson labels; MusicXML work/movement titles determine exercise labels, falling back to the filename stem when absent. Repeated titles do not merge exercises: identity includes the practice type, lesson folder, and normalized filename stem. Invalid nesting, unknown practice types, and colliding normalized filenames fail generation. Adding or renaming a source needs no registry edit; the next build discovers it, replaces obsolete generated paths, and builds a fresh database containing only the current sources.
|
||||
|
||||
The pipeline is MuseScore -> uncompressed `.xml` -> musical model -> automatic arranger/transposer -> semantic JSON plus engraved pages. Export MusicXML 3.1 or 4.0, `score-partwise`, with one part and one staff. Independent rhythmic voices remain separate; simultaneous chord tones retain their own spelling, written duration, and tie links. The packaged music describes each transformed part or practice variant, not an archival copy of the source XML.
|
||||
|
||||
@@ -128,7 +128,7 @@ At runtime the shared on-demand score loader serves both display and audio. A ba
|
||||
|
||||
`audio.playback_enabled` defaults to false and replaces the old inert `track_enabled` setting. The old config name is accepted for migration, but conflicting values fail. Playback suppresses the metronome without changing its stored toggle. Authored tempo marks remain notation; the menu BPM is constant throughout a session. Selecting a practice variant or performance part starts a new session. Manual page changes never restart or seek. Natural completion, including trailing silence/release, returns to the corresponding key/part menu with its selection preserved. Leaving the score cancels playback; stale load/completion results cannot close a newer score. Errors stay in the score view and `/api/status` rather than being mistaken for completion. Existing bundles without `playback` still display but require regeneration to play.
|
||||
|
||||
`--once` additionally validates referenced audio without opening display/audio hardware. Runtime packages contain WAVs alongside pages/JSON; the source XML and Python dependencies remain build-only.
|
||||
`--once` additionally validates referenced audio without opening display/audio hardware. Runtime packages contain WAVs alongside pages and the SQLite catalogue; the source XML and Python dependencies remain build-only.
|
||||
|
||||
### Engraving Boundary
|
||||
|
||||
@@ -140,17 +140,17 @@ The former backend-specific four-voice, duration, unequal-tone and tuplet restri
|
||||
|
||||
`audit.py` checks the final layout against the musical model: timeline and fragment coverage, exact fragment durations, actual glyph bounds, pitch positions, visible note primitives, required accidentals, ties, lyrics, harmonies, tempos and spans. It rejects out-of-page geometry and collisions among unrelated noteheads, accidentals and same-lane text. Staff, ledger, stem, beam and tie contacts are intentional. This is not a proof of publication-quality engraving for every possible MusicXML document; notation changes need targeted semantic, geometry and reviewed image tests. The writer's XML round-trip audit is retained separately in the test suite.
|
||||
|
||||
Bundles keep schema version 3 and an unchanged `music` object. Presentation identifies `Vamp Python` version `1.0.0`; its style fingerprint includes the style settings, both font files, font-specific metadata and the glyph-name mapping. Runtime remains engine-agnostic, so valid existing schema-v3 bundles need no database migration.
|
||||
Bundles keep schema version 3 and an unchanged `music` object. Presentation identifies `Vamp Python` version `1.0.0`; its style fingerprint includes the style settings, both font files, font-specific metadata and the glyph-name mapping. The musical bundle version is separate from the catalogue database version; runtime accepts only the supported packaged catalogue schema.
|
||||
|
||||
Pages are 976x484 pixels with 12-pixel staff spacing, 28-pixel horizontal margins and 18-pixel vertical margins. Dense measures continue across systems/pages; sustained notes become exact, tied presentation fragments and rests become exact rest fragments. These fragments never change the semantic JSON. Irreducible simultaneous content that cannot fit a fixed page fails contextually rather than being clipped or shrunk. Blank pages, incorrect dimensions, and ink reaching the page edge are rejected. Runtime letterboxing preserves aspect ratio. Luma's `load_texture_uncached()` lets Product Vamp retain at most two textures without a hidden strong asset-cache reference. The UI reports missing/corrupt assets instead of falling back to the removed renderer. Runtime validates bundle metadata, paths, dimensions, and page checksums; the build validates musical semantics.
|
||||
|
||||
### Validation And Migration
|
||||
|
||||
Generation validates sources and complete staged output before replacing the previous asset directory. Replacement is always complete, including when the compatibility `--clean` flag is omitted; deleted sources cannot leave stale JSON, pages, or audio. CMake tracks the input list as well as file contents, so additions and removals regenerate the catalogue. Runtime packages contain semantic JSON, PNG pages, generated PCM WAVs, and the OTF UI text font, not MusicXML, MuseScore, or Python dependencies. Python test discovery runs model/import/export tests; the normal asset build additionally qualifies and audits every catalogue score.
|
||||
Generation validates sources and complete staged output before replacing the previous asset directory. Replacement is always complete, including when the compatibility `--clean` flag is omitted; deleted sources cannot leave stale JSON, pages, or audio. CMake tracks the input list as well as file contents, so additions and removals regenerate the catalogue. Runtime packages contain `catalogue.db` with embedded semantic JSON, PNG pages, generated PCM WAVs, and the OTF UI text font, not MusicXML, MuseScore, or Python dependencies. Python test discovery runs model/import/export tests; the normal asset build additionally qualifies and audits every catalogue score.
|
||||
|
||||
Diagnostics identify the file, part, measure, and unsupported element or invalid relationship. Examples include `part P1, measure 4: unsupported musical element <slur>`, `tie stop has no contiguous matching start`, and `optional-chord marker must identify exactly one harmony`.
|
||||
|
||||
The six compact source files were replaced, not retained as a second authoring format. Existing catalogue rows refresh from regenerated JSON at startup. Custom database rows (`catalog_managed = 0`) are retained, including ID conflicts, and are never automatically deleted to resolve a schema mismatch. Custom legacy JSON must be regenerated as a schema-v3 bundle with page assets; the loader reports a regeneration error instead of interpreting old ticks heuristically.
|
||||
The six compact source files were replaced, not retained as a second authoring format. Installation replaces the entire catalogue, rather than retaining or migrating rows from older releases. Legacy data must be authored as supported MusicXML and regenerated; the runtime never upgrades old databases.
|
||||
|
||||
References: [MusicXML durations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/duration/), [tuplets](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/tuplet/), [degree alterations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/degree-alter/), and [SMuFL accidentals](https://smufl.formats.music/latest/tables/standard-accidentals-12-edo.html).
|
||||
|
||||
@@ -192,6 +192,14 @@ Building the full package requires Docker/buildx, the ARM embedded GCC toolchain
|
||||
|
||||
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`.
|
||||
|
||||
`generator/catalogue.py` uses Python's standard-library SQLite module to construct `catalogue.db` after musical, playback, and engraving validation. The database carries application ID `0x56414D50` and catalogue schema version 1, with complete score bundles and menu metadata. Sorted insertion and the absence of wall-clock timestamps make repeated builds reproducible with the same toolchain. It is closed and integrity-checked before publication, without journal/WAL sidecars. Individual JSON files remain available in the build tree for inspection, but both CMake installation and Docker image assembly exclude them.
|
||||
|
||||
The default `database.path` is `/usr/share/product-vamp/catalogue.db`; development configuration points at the native generated-assets database. Stored source paths are logical, asset-relative locations, not build-host filenames. Runtime resolves them against `runtime.assets_path` and rejects traversal and escaping symlinks. The JSON file itself need not exist: its parent identifies the score's page directory. Normal startup checks catalogue compatibility; `--once` additionally runs SQLite's integrity probe and checks all page/audio files. Missing, corrupt, or incompatible catalogues fail rather than creating an empty library. `--ensure-database` is no longer a supported command.
|
||||
|
||||
Every run of `zero/install-zero.sh` is a clean installation. It stages configuration/scripts, verifies the manifest's Docker tag against the incoming archive's image digest, and probes that image before stopping Product Vamp. It then clears the owned `/etc/product-vamp`, `/var/lib/product-vamp`, `/run/product-vamp`, and obsolete host `/usr/share/product-vamp` trees, including old SQLite sidecars. The installed container uses assets from its image and no longer mounts a writable state database. Startup reads the verified image ID from `/etc/product-vamp/image-id`, rather than depending on a mutable Docker tag. The installer replaces its scripts and systemd units, removes their old overrides, recreates only the `product-vamp-control` NetworkManager profile, and requires a successful service health check before announcing success.
|
||||
|
||||
Configuration, AP credentials, and all prior catalogue data are intentionally reset to bundle defaults. There is no automatic backup or migration. Installation through the control AP can disconnect the session; use the local console or independent networking. Preflight failure leaves existing application files untouched and restores the previous image tag; replacement failure stops Product Vamp and reports the error. Rerunning the installer is the recovery path. Ordinary boots/restarts do not run cleanup. Unrelated networks, OS data, containers, and system logs remain untouched. Cleanup rejects symlink/mount targets, never follows paths from old configuration, and removes only identified obsolete Product Vamp images not used by another container.
|
||||
|
||||
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
|
||||
@@ -209,4 +217,3 @@ 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user