docs: resolve Phase 18 OQ7 seek-index granularity to 0.5s buckets
This commit is contained in:
@@ -3,8 +3,9 @@
|
||||
Product spec. Status: **design / framing — open questions RESOLVED (Daniel, 2026-06-23); implementation-ready.**
|
||||
Author: product-designer. Date: 2026-06-23. **No code has been written by this doc.**
|
||||
|
||||
> **Resolution pass (Daniel, 2026-06-23).** OQ1–OQ6 are resolved (see §6 — each marked RESOLVED, kept
|
||||
> visible per file convention). Two resolutions reshaped the spec materially: (a) the listener quality
|
||||
> **Resolution pass (Daniel, 2026-06-23).** OQ1–OQ7 are resolved (see §6 — each marked RESOLVED, kept
|
||||
> visible per file convention; OQ7 — seek-index granularity — set to **0.5 s buckets**). Two resolutions
|
||||
> reshaped the spec materially: (a) the listener quality
|
||||
> selection lives inside a **new public-site Settings menu surface** (not a bare app-bar control) — §4 +
|
||||
> §4a; and (b) Daniel rejected the "approximate page-interpolation" seek hand-wave outright — **VBR-safe
|
||||
> *accurate* seeking is now a first-class part of the architecture** (a precomputed seek-index artifact +
|
||||
@@ -329,15 +330,17 @@ estimated.
|
||||
fixed-width records is the natural shape (e.g. a `uint64 granulepos` + `uint64 byteOffset` per entry);
|
||||
the exact encoding is staff-engineer's, but it should be a **compact binary blob**, fetched once and
|
||||
parsed into a typed array client-side.
|
||||
- **Granularity vs. size (the one real tuning knob).** One entry per Ogg page is the most precise but
|
||||
largest; an Ogg page is typically a few KB of audio (~tens of ms to a few hundred ms), so a 1-hour mix
|
||||
could be tens of thousands of pages. Recommend a **coarser bucket: one index entry per ~1–2 seconds of
|
||||
audio** (snap each bucket boundary to the *nearest enclosing page start*, so every indexed offset is
|
||||
still an exact page boundary). At ~1 s granularity a 1-hour mix is ~3,600 entries × 16 bytes ≈ **~58 KB**
|
||||
— a trivial one-time fetch, and 1 s seek resolution is more than fine (the decoder re-syncs to the exact
|
||||
page within the bucket anyway — see the client flow). **Per-page precision is the fallback if 1 s buckets
|
||||
ever prove too coarse**, at a larger index. The number is staff-engineer's call; the *shape* (precomputed
|
||||
exact granule→byte, bucketed, snapped to page starts) is fixed.
|
||||
- **Granularity vs. size — RESOLVED: 0.5 s (half-second) buckets (Daniel, 2026-06-23).** One entry per
|
||||
Ogg page is the most precise but largest; an Ogg page is typically a few KB of audio (~tens of ms to a
|
||||
few hundred ms), so a 1-hour mix could be tens of thousands of pages. The chosen bucket is **one index
|
||||
entry per 0.5 seconds of audio** (snap each bucket boundary to the *nearest enclosing page start*, so
|
||||
every indexed offset is still an exact page boundary). At 0.5 s granularity a 1-hour mix is
|
||||
~7,200 entries × 16 bytes ≈ **~115 KB** — still a trivial one-time fetch, and 0.5 s seek resolution is
|
||||
finer than required (the decoder re-syncs to the exact page within the bucket anyway — see the client
|
||||
flow — so the in-bucket trim is *sub-half-second*, tighter than the earlier ~1–2 s recommendation).
|
||||
**Per-page precision remains the fallback if 0.5 s buckets ever prove too coarse**, at a larger index.
|
||||
The bucket size is now fixed; the *shape* (precomputed exact granule→byte, bucketed, snapped to page
|
||||
starts) is unchanged.
|
||||
- **Sidecar, not embedded (recommended).** Store the index as a **third derived artifact** alongside the
|
||||
Opus bytes and the waveform datum — the same "derived artifacts get their own vault" pattern this phase
|
||||
already uses (S2 / `track-opus`; the `track-waveforms` precedent). Keep it a separate vault resource
|
||||
@@ -395,8 +398,8 @@ With the sidecar (`OpusSeekData` = setup header + granule→byte index) fetched
|
||||
page start, the stream is immediately Ogg-sync-aligned.
|
||||
4. **Fine re-sync within the bucket.** The granule of the first decoded page tells the decoder the *exact*
|
||||
time it landed at (≤ the bucket granularity ahead of `t`); the scheduler trims/positions to land
|
||||
playback at `t` precisely. With ~1 s buckets the trim is sub-second; with per-page granularity it is
|
||||
near-zero. **Either way the listener lands at the correct time, not approximately** (AC9).
|
||||
playback at `t` precisely. With 0.5 s buckets the trim is sub-half-second; with per-page granularity it
|
||||
is near-zero. **Either way the listener lands at the correct time, not approximately** (AC9).
|
||||
|
||||
#### D. Composition with Phase 21 windowed refill
|
||||
|
||||
@@ -581,9 +584,9 @@ the "one source, multiple views" / design-for-adaptability discipline applied to
|
||||
|
||||
## 6. Open questions — RESOLVED (Daniel, 2026-06-23)
|
||||
|
||||
All six original open questions are resolved. Kept visible per file convention, each with the decision and
|
||||
the section that now carries it. One new open question (OQ7) is raised by the seek-model design; it is a
|
||||
narrow tuning/scoping call, not a blocker.
|
||||
All seven open questions are resolved. Kept visible per file convention, each with the decision and
|
||||
the section that now carries it. OQ7 (raised by the seek-model design) is a narrow tuning call, now set to
|
||||
0.5 s buckets.
|
||||
|
||||
- **OQ1 — Selection UX — RESOLVED: global, via a Settings *menu* (not a bare app-bar control).** Daniel:
|
||||
*"Global is perfect, but we need a menu system for settings, don't just slap the quality control directly
|
||||
@@ -612,15 +615,15 @@ narrow tuning/scoping call, not a blocker.
|
||||
track is lossless-only until its Opus finishes — accepted, and now made visible rather than implicit.
|
||||
`[RESOLVED — §3.1a]`
|
||||
|
||||
**New open question raised by the seek-model design (§3.4a) — narrow, non-blocking:**
|
||||
**New open question raised by the seek-model design (§3.4a) — RESOLVED:**
|
||||
|
||||
- **OQ7 — Seek-index granularity (tuning, leans implementation).** The seek index trades precision against
|
||||
size: per-Ogg-page (most precise, largest) vs. coarser time buckets snapped to page starts. Recommend
|
||||
**~1–2 s buckets** (~58 KB for a 1-hour mix at 1 s; the decoder fine-re-syncs within the bucket so seek
|
||||
*accuracy* is unaffected — only the in-bucket trim distance changes). This is largely staff-engineer's
|
||||
call at implementation; flagged because the *number* is a deliberate choice and Daniel may have a feel
|
||||
for acceptable index size vs. in-bucket trim. Does **not** block — the shape (precomputed exact
|
||||
granule→byte, page-snapped) is fixed regardless of the bucket size. `[Daniel steer — not a blocker]`
|
||||
- **OQ7 — Seek-index granularity — RESOLVED: 0.5 s (half-second) buckets (Daniel, 2026-06-23).** The seek
|
||||
index trades precision against size: per-Ogg-page (most precise, largest) vs. coarser time buckets snapped
|
||||
to page starts. Daniel set the bucket at **0.5 s** (finer than the ~1–2 s the spec had recommended):
|
||||
~7,200 entries × 16 bytes ≈ **~115 KB** for a 1-hour mix — still a trivial one-time fetch. The decoder
|
||||
fine-re-syncs within the bucket so seek *accuracy* is unaffected; at 0.5 s the in-bucket trim is
|
||||
sub-half-second, tighter than before. The shape (precomputed exact granule→byte, page-snapped) is
|
||||
unchanged. `[RESOLVED — §3.4a A]`
|
||||
|
||||
---
|
||||
|
||||
@@ -652,7 +655,7 @@ narrow tuning/scoping call, not a blocker.
|
||||
the client fetches and parses it once on track load (into `OpusSeekData`) before issuing any seek.
|
||||
- **AC9 (the seek-accuracy criterion) — an Opus seek lands at the *correct* time, not approximately.**
|
||||
Seeking to time `t` in an Opus stream resolves via the precomputed index and lands playback at `t`
|
||||
(within the fine-resync tolerance — sub-second at the recommended bucket granularity), **measurably
|
||||
(within the fine-resync tolerance — sub-half-second at the chosen 0.5 s bucket granularity), **measurably
|
||||
accurate**, not a `byteRate`/interpolation estimate. Verifiable: seek to a known marker (e.g. a downbeat
|
||||
at a known timestamp) and confirm playback resumes there, not seconds off. This holds **without** the
|
||||
full PCM decoded in memory (composes with Phase 21).
|
||||
|
||||
Reference in New Issue
Block a user