From 61d81220527d19dba51c35eb954eef18d389eac8 Mon Sep 17 00:00:00 2001 From: gionnibgud Date: Thu, 23 Jul 2026 10:31:52 +0200 Subject: [PATCH] Document the rig bindings on the tone_changes wire message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHANGELOG entry for the core rig reader, plus the WS protocol table in CLAUDE.md, which described `tone_changes` as carrying only base + name. While in that row: its time key was documented as `time`, but every producer emits `t` — both the sloppak builder and the legacy XML path. The 3D highway already carries a comment warning readers about exactly this discrepancy. Corrected here rather than left sitting next to the newly-added keys, where a reader would reasonably assume both were equally reliable. Signed-off-by: gionnibgud --- CHANGELOG.md | 14 ++++++++++++++ CLAUDE.md | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6294fd6..a67ef5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- **Core reader for source rigs (feedpak 1.18.0).** A pack can declare what a + MIDI part should sound like by binding a rig; core now reads that binding and + hands it to the client instead of dropping it. Three parts: the + `tone_changes` WS message carries the pack's rig bindings (`base_rig`, and + `rig` per change) alongside the tone names it already sent; the manifest + `rigs:` key loads the pack's rig library (`rigs.json`, spec §7.9) verbatim; + and the binding precedence is resolved per spec §5.1/§5.2 — a manifest + arrangement entry's `tones` replaces the arrangement JSON's **wholesale** + (no field-level merge), while top-level `drum_tones` binds the primary drum + part as the fallback a `type: drums` entry's own `tones` outranks. Core + deliberately stops there: it does not select a realization or apply the + `intent.gm` floor, which belong to whatever actually voices the part. Packs + that bind no rig produce a byte-identical `tone_changes` payload, so existing + consumers are unaffected. - **Opt-in career venue packs (#122)** — higher-tier venue crowd media (`club`, `arena`) is no longer bundled; the app downloads each pack on demand from its release when you reach the venue (sha256-verified), keeping the diff --git a/CLAUDE.md b/CLAUDE.md index 1084f4e..4318635 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -690,7 +690,7 @@ The highway WebSocket at `/ws/highway/{filename}?arrangement={index}` streams th | `anchors` | `{ type, data: [{ time, fret, width }] }` | Fret zoom anchors | | `chord_templates` | `{ type, data: [{ name, frets: [6] }] }` | Named chord shapes | | `lyrics` | `{ type, data: [{ w, t, d }], source }` | Syllables: `w`=word, `t`=time, `d`=duration. `-` joins to previous, `+` = line break. `source` is one of `"xml"`, `"whisperx"`, `"user"` — UI can use it to render an "auto-transcribed" badge for `whisperx`. Sloppaks always include `source` (legacy sloppaks without a `lyrics_source` manifest key default to `"xml"` at load time). Loose folders set it based on which extractor matched. Absent only when no lyrics fired the message at all | -| `tone_changes` | `{ type: 'tone_changes', base, data: [{ time, name }] }` | Optional — tone change events relative to the arrangement base tone; only sent if tones were found | +| `tone_changes` | `{ type: 'tone_changes', base, base_rig?, data: [{ t, name, rig? }] }` | Optional — tone change events relative to the arrangement base tone; only sent if tones were found. Note the time key is **`t`**, not `time` (both the sloppak path and the legacy XML path emit `t`). `base_rig` and each entry's `rig` are the pack's **rig bindings** — ids into [`rigs.json`](https://github.com/got-feedback/feedpak-spec/blob/main/spec/feedpak-v1.md#79-rigsjson) (feedpak §6.9/§7.9), carried through verbatim and **not** resolved by core: selecting a realization and applying the `intent.gm` floor belong to whatever voices the part. Both are **omitted entirely** when the chart binds no rig, so consumers predating the rig model see the payload they always did. | | `notes` | `{ type, data: [{ t, s, f, sus, ho, po, sl, bn, ... }] }` | Single notes | | `chords` | `{ type, data: [{ t, notes: [{ s, f, sus, ... }] }] }` | Chord events | | `phrases` | `{ type, data: [{ start_time, end_time, max_difficulty, levels: [{ difficulty, notes, chords, anchors, handshapes }] }], total }` | Optional — per-phrase difficulty ladder for master-difficulty slider (feedBack#48). Only sent when the source chart carries multi-level phrase data (phrase-aware sloppak). Sent in chunks (`data` is a batch, `total` is the full count across messages) to avoid multi-MB single frames. Absent for GP imports and legacy sloppak; consumers must treat missing message as "single fixed difficulty — slider disabled". |