docs(phase-12): spec waveform-visualizer generalization + NowPlayingHero rewire
Generalize the Mix-only WebGL lava visualizer into one release-cardinal WaveformVisualizer serving Mix detail, all Release Detail pages, and the home NowPlaying card. Four waves; flags the non-Mix datum-resolution call.
This commit is contained in:
@@ -254,6 +254,79 @@ Sequenced as **eight waves**; the critical path is `11.A → 11.B → 11.C → 1
|
||||
|
||||
---
|
||||
|
||||
## Phase 12 — Waveform Visualizer Generalization + NowPlayingHero Rewire
|
||||
|
||||
Take the landed Mix waveform visualizer (the WebGL2 lava renderer + its seven-knob controls, Phase 10
|
||||
reframe) and **make it the one release-cardinal visualizer** — serving Mix detail, all Release Detail
|
||||
pages, *and* the home-page NowPlaying card — instead of a Mix-only backdrop forked three ways. **Two
|
||||
deliverables, one engine, DRY/SOLID the explicit ask.** Full design, the extraction analysis, the datum
|
||||
decision, wave decomposition, and open questions: `product-notes/phase-12-waveform-visualizer-generalization.md`.
|
||||
|
||||
**Central finding (verified read, 2026-06-17): the engine is already release-cardinal below the surface.**
|
||||
`MixWaveformVisualizer`'s bridge keys on `ReleaseEntryKey` + `TrackId` (not Mix); the renderer is a pure
|
||||
function of a loudness datum + duration; the controls/state are renderer-agnostic. The *only* genuinely
|
||||
Mix-coupled surface is (1) the datum **fetch** (`GET api/release/{entryKey}/mix/waveform` 404s unless
|
||||
`Medium == Mix`) and (2) the high-res datum **source** (the `mix-waveforms` vault, Mix-only). Everything
|
||||
else is just *named* `Mix*`. So "generalize from Mix to all releases" is a **rename + a data-source
|
||||
generalization, not a rebuild** — the renderer, bridge, controls, read-only contract all carry forward
|
||||
from the Phase 10 reframe unchanged.
|
||||
|
||||
**Crucial data fact (verified): non-Mix releases are not a data gap.** *Every* uploaded track already gets
|
||||
a **512-bucket** waveform profile (`UnifiedTrackService.UploadAsync` → `waveform-profiles` vault, the
|
||||
datum the player-bar `WaveformSeeker` consumes). Mixes *additionally* get a duration-derived **high-res**
|
||||
datum (~333 samples/sec, `mix-waveforms` vault, CMS-triggered). So the only question for non-Mix is
|
||||
*resolution*, not existence — and §8a frames that as the one real product call (recommend: serve the
|
||||
existing 512-bucket profile for ambient non-Mix backdrops in v1; high-res-for-all is a roadmap upgrade).
|
||||
|
||||
**Deliverable 2 — NowPlayingHero overhaul.** `NowPlayingCard.razor` today animates **20 hardcoded
|
||||
CSS-bounce bars** with no audio coupling (the "stochastic" visualizer). Replace them with the *same*
|
||||
`WaveformVisualizer`, mounted inside the existing player cascade and pointed at "whatever is playing right
|
||||
now" — so the home card shows the **real** waveform of the live track, Mix or not. The payoff of the
|
||||
generalization: the NowPlaying card is *just another host* of the one engine, not a fork. The one genuine
|
||||
engineering wrinkle is that the renderer assumes full-viewport (`position: fixed; inset: 0`,
|
||||
clip-to-footer) and the card needs it container-relative — recommend a `Fill` mode parameter (spec §6c).
|
||||
|
||||
**Design discipline.** Rename the engine to its abstraction (`MixWaveformVisualizer` → `WaveformVisualizer`,
|
||||
etc.) — a `Mix`-named component on a Cut page is a lie that cements the wrong model. Variance rides
|
||||
**composition** (a new optional `Backdrop` slot on `ReleaseDetailScaffold`; per-host control suppression),
|
||||
never a `switch (medium)` in the engine (memory *One source, multiple views*; scaffold's "variance rides a
|
||||
slot, never a flag" idiom, Phase 9 §5.3). The lava controls stay a **Mix affordance by default**;
|
||||
Cut/Session (if they get a backdrop) mount it controls-suppressed as ambient — the seam supports controls-
|
||||
everywhere later with no engine change (memory *Design for adaptability up front*).
|
||||
|
||||
Sequenced as **four waves**: `12.A → 12.B → (12.C ‖ 12.D)`.
|
||||
|
||||
- **12.A — Rename to the abstraction (mechanical, no behavior change).** `Mix*` → `Waveform*` across the
|
||||
five C#/Razor files + the TS module + its import path + the DI registration. **Load-bearing
|
||||
prerequisite** — every later wave references the generalized names. Acceptance: Mix detail identical;
|
||||
diff is identifiers only.
|
||||
- **12.B — Generalize the datum fetch + endpoint.** New unauthenticated `GET
|
||||
api/release/{entryKey}/waveform` resolving the **best-available** datum (high-res for Mix-with-datum;
|
||||
512-bucket per-track profile otherwise); `IReleaseDataService.GetReleaseWaveform`; bridge calls it
|
||||
instead of `GetMixWaveform`. **Depends on 12.A.** Direction A (recommended) needs no decision to start;
|
||||
the high-res-for-all alternative (Direction B) does — §8a.
|
||||
- **12.C — `Backdrop` slot on `ReleaseDetailScaffold` + mount on detail pages.** Promote the full-bleed /
|
||||
foreground-stacking / dynamic-footer-clip pattern into the scaffold as an optional `Backdrop` slot; Mix
|
||||
re-expresses its current mount through it; Cut mounts the controls-suppressed ambient backdrop; Session
|
||||
mounts directly (it doesn't compose the scaffold — spec §3e) **if** §8b opts non-Mix in. **Depends on
|
||||
12.B.**
|
||||
- **12.D — NowPlayingHero rewire.** Replace the synthetic bars with a contained, controls-suppressed
|
||||
`<WaveformVisualizer>` driven by the live cascaded player; add the `Fill`/container-sizing mode (spec
|
||||
§6c). **Depends on 12.A + 12.B; independent of 12.C** (different host). Acceptance: home card shows the
|
||||
real playing-track waveform, at-rest when nothing plays; no synthetic bars remain.
|
||||
|
||||
**Open product decisions (spec §8, need Daniel before the dependent wave):** (a) **non-Mix datum
|
||||
resolution** — existing 512-bucket per-track profile (Direction A, recommended, zero new compute) vs.
|
||||
high-res-for-all (Direction B, new CMS/API/content + backfill); gates 12.B's *richness*, blocks nothing if
|
||||
A. (b) **Do Cut/Session get a backdrop, and with controls?** — recommend ambient-on-all-media /
|
||||
controls-Mix-only; note even Mix-only still wants 12.A/B/D for the NowPlaying win. (c) **What is a
|
||||
multi-track Cut's waveform?** — recommend first track by `TrackNumber` for v1; only bites if (b) gives Cut
|
||||
a backdrop. (d) **NowPlaying container-sizing + home-page perf** — staff-engineer-owned (`Fill` mode;
|
||||
`isPlaying`-gated rAF means an idle home page pays nothing), flagged so a lava lamp on the landing page is
|
||||
no surprise.
|
||||
|
||||
---
|
||||
|
||||
## Working with this file
|
||||
|
||||
- **Add items by extending an existing phase first**; only create a new phase when the addition genuinely doesn't fit any of 1–5. Phase numbers are organisational, not sequencing.
|
||||
|
||||
Reference in New Issue
Block a user