00afa9ba2e
Line numbers rot silently. Ten were already broken, including all eight Precision invariants pointers. Vendor citations left alone.
177 lines
18 KiB
Markdown
177 lines
18 KiB
Markdown
# DAW verification — post-1.0 work on `dev`
|
||
|
||
Checks for Θ, Ξ, Ψ, Ε, and Ρ work that no unit test can close. Build **Release**, install into
|
||
`UserPlugins/`, restart REAPER. Panel tail toggle = **None**, project rate 48000, unless a check says otherwise.
|
||
|
||
## Precision invariants
|
||
|
||
- [ ] Dry offline item capture of a 2 s range, re-inserted at its source position, inverted against the source — reads silence (`CLAUDE.md` §"Precision invariants" — "Null test")
|
||
- [ ] Run the identical offline capture request twice — the two files are byte-identical on disk (`CLAUDE.md` §"Precision invariants" — "Bit-identical repeats")
|
||
- [ ] After any capture, source items and tracks are unchanged: fader, pan, mute, FX bypass, selection (`CLAUDE.md` §"Precision invariants" — "Non-destructive")
|
||
- [ ] After a realtime capture, the temp track is gone and every source track's routing is back as it was (`CLAUDE.md` §"Precision invariants" — "Non-destructive")
|
||
- [ ] Capture 10.000–12.000 s — card reads 2.000 s / 96000 frames, no leading or trailing silence (`CLAUDE.md` §"Precision invariants" — "Exact bounds")
|
||
- [ ] With an FX on the source track: item scope does NOT carry it, track scope does (`CLAUDE.md` §"Precision invariants" — "Capture FX scope")
|
||
- [ ] Track scope on a child track with FX, gain, and pan set on the parent and master — neither colors the capture (`CLAUDE.md` §"Precision invariants" — "Capture FX scope")
|
||
- [ ] Save, move the whole project folder elsewhere, reopen — every card still resolves and auditions (`CLAUDE.md` §"Precision invariants" — "Relative paths only")
|
||
|
||
## The decisive observation
|
||
|
||
- [ ] **Run first.** `docs/verify-track-scope-multitrack.md` §3 by hand, and count the files REAPER writes (`docs/TODO.md` §"The `&128` multi-track output shape is still DAW-unobserved", `docs/COMPLETED.md` §"Ψ-W3-T1", `docs/COMPLETED.md` §"None of the seven is DAW-verified")
|
||
- [ ] Two files confirms Ψ-W1-T1 and Ψ-W3-T1 at once; **one summed file invalidates both** — stop and report, the refusal is costing a capture 1.0.0 accepted (`docs/COMPLETED.md` §"None of the seven is DAW-verified")
|
||
- [ ] Then walk the rest of `docs/verify-track-scope-multitrack.md` (§1–§2, §4–§7) for the multi-track refusal itself (`docs/COMPLETED.md` §"Ψ-W3-T1")
|
||
|
||
## Capture range and bounds
|
||
|
||
- [ ] Over an item much longer than the selection: item scope × time selection, and item scope × razor — each lands exactly the window, not the whole item (`docs/COMPLETED.md` §"Ψ-W1-T1")
|
||
- [ ] Same source: track scope × time selection, and track scope × razor — same exact window (`docs/COMPLETED.md` §"Ψ-W1-T1")
|
||
- [ ] One razor-union case (two disjoint areas, one track) — lands the requested window, no `ReaSampler capture failed:` line (`docs/COMPLETED.md` §"Ψ-W1-T1")
|
||
- [ ] Capture an item whose extent already equals the window — still lands, unchanged (the byte-identity regression floor) (`docs/COMPLETED.md` §"Ψ-W1-T1")
|
||
- [ ] **The millisecond floor — SETTLED, nothing to re-run for `TailMode::None`.** The floor lives in the custom-time-bounds field (`RENDER_BOUNDSFLAG=0`), not in the render engine. Two live 48 kHz `TailMode::None` renders on `RENDER_BOUNDSFLAG=2` (time selection, handed over via `GetSet_LoopTimeRange`) came back exact — 97627 frames against 97627 — the second over a window whose START carried a sub-millisecond remainder, with no floored model of that window able to reproduce the count. Time selection is now the only bounds mode a capture can reach; the console verdict line and the `RENDER_STARTPOS`/`ENDPOS` read-back probe that answered this are gone. Full observation: `src/core/capture/render_settings.h`'s `kRenderBoundsTimeSelection`
|
||
- [ ] **Still open — Auto and Manual tail.** `checkRenderedBounds` judges `TailMode::None` only (Auto/Manual add frames by design), so the settled result covers those two by INFERENCE, not observation, and the inference rests on an unverified PREMISE too: that the (retired) floor applied to the bounds identically across all three tail modes, and that all three now hand the window over the same way. Neither is measured — both live short renders that settled the bounds mode were `TailMode::None`; no Auto or Manual capture has been observed at all. **On Auto/Manual, the ONLY automatic check left is the 0-byte gate (`checkRenderedFileNotEmpty`)** — there is no automatic bounds signal for those two modes at all until this bullet is closed by hand. What would establish it: repeat an off-grid-start capture at **Manual** over a source that is loud right to the window's end, and check the landed file's frames against window + `tailMs` — a floored edge shows up in that count. **Auto** cannot be checked by count (it trims trailing silence), so it needs the null test by ear/inversion against the source instead
|
||
- [ ] `[verify — DAW]` A tail is assumed to render PAST the window end — the SDK header (`:3048`) confirms only that `RENDER_TAILMS` is a length in ms, not that it extends past the end. If that assumption is wrong, a tail capture is silently SHORTER than its window with no detector at all. Report whether either tail capture comes up short against the source
|
||
- [ ] A refused render is kept for diagnosis at `<project folder>/reasampler_refused/` (the refusal line names the path; a failed move leaves it unindexed in the bank folder and says so). Delete the folder when done — nothing in the bank references it
|
||
- [ ] **If a capture is refused for a short render**, report the refusal line verbatim. A message naming `floored to the millisecond` means the floor is back on a mode measured escaping it; a shortfall of one or two frames with no such sentence may be the gate's own edge-convention tolerance rather than the render (`render_window.h`'s `renderHonoredBounds`)
|
||
|
||
## Names and channels
|
||
|
||
- [ ] Capture from a named track — the card reads `<Track> MM-DD HHMM`; capture again the same minute and the second carries an ordinal (`docs/COMPLETED.md` §"Ψ-W2-T1")
|
||
- [ ] Capture from an unnamed track, and from a multi-item selection — both readable, `+N` present on the multi (`docs/COMPLETED.md` §"Ψ-W2-T1")
|
||
- [ ] Load a named capture into ReaSampler 9000 — the same name shows there (`docs/COMPLETED.md` §"Ψ-W2-T1")
|
||
- [ ] The card label stays legible over its scrim at every card size (`docs/COMPLETED.md` §"Ψ-W2-T1")
|
||
- [ ] Capture a dead-center mono source — the `.wav` is roughly half the size of the equivalent stereo capture (`docs/COMPLETED.md` §"Ψ-W2-T2")
|
||
- [ ] Insert that collapsed file on a stereo track and null it against the source — confirms REAPER sums a 1-channel item at unity (`docs/COMPLETED.md` §"Ψ-W2-T2")
|
||
- [ ] Capture a true-stereo source — stays 2-channel, and both it and the collapsed file load into the instrument correctly (`docs/COMPLETED.md` §"Ψ-W2-T2")
|
||
|
||
## Mode switching
|
||
|
||
- [ ] Solo tracks in Arrange, switch to Design, solo different tracks, switch back — each mode restores its own solo set verbatim (`docs/COMPLETED.md` §"Ψ-W1-T2")
|
||
- [ ] Attempt a mode switch while the transport is playing, then while recording — both refuse, visibly (`docs/COMPLETED.md` §"Ψ-W1-T2")
|
||
- [ ] Click the footer mode segment, save, reopen the project — the mode persisted (`docs/COMPLETED.md` §"Ψ-W1-T2")
|
||
|
||
## Actions and drops
|
||
|
||
- [ ] Add the import action to a Media Explorer toolbar and fire it from there — it imports (`docs/COMPLETED.md` §"Ψ-W1-T3")
|
||
- [ ] Fire the existing Main-section import binding — still works (`docs/COMPLETED.md` §"Ψ-W1-T3")
|
||
- [ ] Unload/reload (restart REAPER) — no duplicate Media Explorer entry in the action list (`docs/COMPLETED.md` §"Ψ-W1-T3")
|
||
- [ ] Drag one card across the arrange, over an FX window, over the TCP/MCP and back — cue changes per surface, every transition reverses (`docs/COMPLETED.md` §"Ψ-W1-T4")
|
||
- [ ] Drag fast, and drag onto a narrow TCP — target class still resolves; no release anywhere in REAPER is a silent no-op (`docs/COMPLETED.md` §"Ψ-W1-T4")
|
||
- [ ] Drop a single card into the arrange — an item lands at the pointer's track and time (`docs/COMPLETED.md` §"Ψ-W1-T4")
|
||
- [ ] Drag-out to an external app twenty-plus times in a row — audio arrives every time; this is a soak, a single pass is not a gate (`docs/COMPLETED.md` §"Θ-W1-T2" — "Neither acceptance criterion has actually been met yet")
|
||
- [ ] Drop a capture onto an FX container — the instrument loads with that capture (`docs/COMPLETED.md` §"Θ-W1-T2" — "Neither acceptance criterion has actually been met yet")
|
||
|
||
## Bank packages
|
||
|
||
- [ ] **Run in full.** `docs/verify-package-transfer.md` — the whole cross-machine
|
||
export/import round trip: writes-one-file, the transfer itself, re-importing the
|
||
same file never overwrites, the round trip back to the source, the too-new /
|
||
truncated / mid-payload-corruption refusals (each an exact string), the
|
||
unsaved-project refusals, and drag-and-drop (`docs/COMPLETED.md` §"Ε-W3-T1")
|
||
- [ ] Force a degraded tracking ledger and confirm the import refuses **before the
|
||
file picker opens**: save a project with a bank, close REAPER, edit the saved
|
||
`.rpp`'s `owned_files` ext-state value inside its `<REASAMPLER ...>` block — corrupt
|
||
the JSON for the `Unreadable` case, or bump `"v":2` to `"v":3` for the
|
||
`FutureVersion` case — reopen the project, then run *ReaSampler: import bank
|
||
package (.rsbank)*. Read off: the console prints the ledger-refusal block and no
|
||
file dialog ever appears (`origin_ledger.h`'s `LedgerStatus` and `ledgerDegraded`,
|
||
`package_import_action.cpp`'s `ledgerPermits`)
|
||
- [ ] Export dialog: type a destination name with no extension, then again over a
|
||
name that already carries a different one (e.g. `mybank.bak`) — read off whether
|
||
`GetUserFileName` appended `.rsbank` itself or ReaSampler's own re-append produced
|
||
the double-extension result (`mybank.bak.rsbank`) the code expects
|
||
(`src/shell/package/CLAUDE.md` §"Gotchas" — "The re-append is suffix-blind")
|
||
- [ ] Both the export and the import file dialogs open in front of REAPER's main
|
||
window, not behind it — `GetUserFileName` takes no owner window
|
||
(`src/shell/package/CLAUDE.md` §"Gotchas" — "`GetUserFileName` also takes no owner window")
|
||
- [ ] With a ReaSampler 9000 instance's editor open on the destination project
|
||
(Browse view visible), import a `.rsbank` from the docked panel — the browser
|
||
reflects the new bank without closing or reopening the editor (the bank-generation
|
||
bump, `session.h`'s `bumpBankGeneration`, polled by the instrument at
|
||
`processor_reload.cpp`'s `pollBankSync`)
|
||
- [ ] Drag two or more `.rsbank` files onto the docked panel in one drop — each lands
|
||
as its OWN new bank, never merged into one, and if the tracking ledger is degraded
|
||
the refusal prints ONCE for the whole drop rather than once per file
|
||
(`panel_window.cpp`'s `handleDropFiles`)
|
||
- [ ] Kill REAPER (or the process) partway through an import so a partial bank file
|
||
is stranded under its real name in the bank folder, then re-run the same import
|
||
into the same project — read off what happens. Whether the import verb should
|
||
pre-clean that stale debris is an open question, not yet decided
|
||
(`src/shell/package/CLAUDE.md` §"Gotchas" — "A crash mid-export strands the `.rsbanktmp` sibling")
|
||
|
||
## The resample bake
|
||
|
||
- [ ] Bake a dialed sound — the banked file sounds like what the editor was playing (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Not demonstrated")
|
||
- [ ] Bake the result twice more — iteration composes, nothing is lost per pass (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Not demonstrated")
|
||
- [ ] Save and reopen after a bake — the instance still points at the baked capture (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Not demonstrated")
|
||
- [ ] Confirm no bake put an item in the arrange, and the superseded file is still on disk (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Not demonstrated")
|
||
- [ ] Bake from an instance in a background project tab — refuses rather than writing into the wrong bank (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Two behaviors worth recording")
|
||
- [ ] Load the VST with the extension not installed — the resample affordance reads unavailable, not silently lossy (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Two behaviors worth recording")
|
||
- [ ] After a bake: instance is in Trigger with start point reset, channel mode and preview velocity survived (`docs/COMPLETED.md` §"Ξ-W2-T1" — "Reset-scope classifications made at review")
|
||
- [ ] Gate mode + active sustain loop — "Bake Hold" appears within ~500 ms, its label fits its cell, its travel is duration-ordered (`docs/COMPLETED.md` §"Ξ-W3-T1")
|
||
|
||
## Render in place
|
||
|
||
- [ ] Fire *Render track in place* over a track with a range selected — solo the source
|
||
and the new sibling track, invert one track's polarity, and confirm silence. This is
|
||
Ρ's own trust anchor: the placement is the null test performed automatically
|
||
(`docs/product/render-in-place.md` §"DAW-verification obligations",
|
||
`docs/COMPLETED.md` §"Phase Ρ — Render in place")
|
||
- [ ] Render in place from three source positions in turn — a normal mid-folder track, a
|
||
track that is last in its folder, and a folder-parent track — each time confirm the new
|
||
sibling track lands at the same nesting level as the source and that the folder bus
|
||
feeds (or bypasses) it correctly. Then, to settle whether `TrackList_AdjustWindows(false)`
|
||
is actually needed: comment out that call in `render_in_place.cpp`'s
|
||
`RunRenderTrackInPlace` (it was added preemptively, answering a question the spec had
|
||
left open rather than one the code confirmed), rebuild, and repeat the folder-parent
|
||
case — if nesting still displays correctly with the call removed, it can be dropped in a
|
||
follow-up (`docs/product/render-in-place.md` §"DAW-verification obligations")
|
||
- [ ] Render in place from a dead-centre (channel-identical) source — confirm the placed
|
||
item is mono, and confirm it plays back at the same perceived level the stereo source
|
||
did before the render. This is root `CLAUDE.md`'s existing mono-summing
|
||
`[verify — DAW]`, promoted to load-bearing because Ρ is the first path that places a
|
||
collapsed render into the mix automatically (`docs/product/render-in-place.md`
|
||
§"DAW-verification obligations")
|
||
- [ ] Fire Render track in place once from Arrange and once from Design. From Arrange:
|
||
confirm the source track parks and the result track is visible and in the mix. From
|
||
Design: confirm the source stays on the bench, the result track is parked too, then
|
||
switch to Arrange and confirm the result track appears in the source's place. **In
|
||
both cases wait out at least one panel timer tick before checking membership** — that
|
||
is the check that catches a missing explicit-tag-wins filter or an untagged item,
|
||
either of which silently reverses the ruling that the result track is always an
|
||
Arrange member (`docs/product/render-in-place.md` §"Mode transitions — the source
|
||
parks, the result goes to Arrange")
|
||
- [ ] Render in place, then press Ctrl-Z once — confirm the new track and its item are
|
||
both gone, the source track's folder depth is restored, the rendered file itself is
|
||
still on disk, and the source track is still tagged Design (`docs/product/render-in-place.md`
|
||
§"DAW-verification obligations")
|
||
- [ ] Render in place from a named source, then run it again over the resulting (already
|
||
`Capture `-prefixed) track — confirm the name does not stack a second prefix, and
|
||
confirm the new sibling's colour matches the source's (the colour clone has no unit
|
||
coverage at all). Repeat once from an unnamed source and confirm the result reads
|
||
`Capture Track N` (`docs/product/render-in-place.md` §"DAW-verification obligations";
|
||
the name-composition logic itself — apart from the live colour clone and the real
|
||
`GetTrackName`/`P_NAME` round trip — is unit-tested in `tests/test_capture_name.cpp`)
|
||
- [ ] Save a project into a folder whose recording path is set away from the default
|
||
(Project Settings → Media → Path), then Render track in place — confirm the rendered
|
||
file lands in that configured recording path, not the project folder itself
|
||
(`render_in_place.cpp`'s `RunRenderTrackInPlace`, `GetProjectPathEx`)
|
||
- [ ] Render in place, save the project, and reopen it — confirm the result track (which
|
||
carries an explicit `kArrangeModeId` membership record, unlike the shipped
|
||
tag-selected-tracks action which never writes one) behaves identically, in every
|
||
mode-switch and visibility check, to an ordinary untagged Arrange track. The JSON
|
||
round-trip itself is unit-tested (`tests/test_view_mode_model.cpp`); this is the
|
||
live-view half that isn't (`docs/product/render-in-place.md` §"Mode transitions — the
|
||
source parks, the result goes to Arrange")
|
||
|
||
## Instrument migration
|
||
|
||
- [ ] Open a project saved before the zone retirement — the instance reopens on its first zone and sounds the same (`docs/COMPLETED.md` §"Θ-W1-T1")
|
||
- [ ] Such an instance with implicit channel mode + a stereo capture reopens **Stereo** — confirm that is acceptable by ear (`docs/COMPLETED.md` §"Θ-W1-T1")
|
||
|
||
## Look and feel
|
||
|
||
- [ ] Sign off by eye in a live editor window: knob arcs, needles, envelope splines, waveform outline (`docs/COMPLETED.md` §"Θ-W7-T1", `docs/COMPLETED.md` §"Θ-W6-T1" — "Antialiasing pass")
|
||
- [ ] Same pass for legibility: text sizes, arc weight, and whether the waveform stroke thickens the docked panel (`docs/COMPLETED.md` §"Θ-W6-T1", `docs/COMPLETED.md` §"Θ-W6-T1" — "Sizing")
|
||
- [ ] Piano strip at the 840 px default — keys tile uniformly, the 37 px end gutters read as acceptable (`docs/COMPLETED.md` §"Θ-W2-T3")
|
||
- [ ] Resize the editor across several widths — gutters stay symmetric, no key width jumps (`docs/COMPLETED.md` §"Θ-W2-T3")
|
||
- [ ] Set host/OS scaling to 150% then 200% — record how the strip and the AA strokes actually look (`docs/TODO.md` §"High-DPI host scaling is unverified (distinct from the antialiasing audit)", `docs/COMPLETED.md` §"Θ-W2-T3" — "Width uniformity is guaranteed in client pixels only")
|