Prove the render bounds at the boundary they cross, and name a short render whose count is exactly a millisecond-floored window

No truncation exists on our side of that boundary, so the read-back is the
only evidence available for whether REAPER kept the window — and it fires on
every tail mode, where only None was ever judged.
This commit is contained in:
2026-08-02 14:28:03 -04:00
parent e589addc54
commit 292d14d14c
6 changed files with 246 additions and 6 deletions
+29 -2
View File
@@ -1,9 +1,12 @@
#pragma once
// render_window — pure frame arithmetic for a capture's requested window: the
// frame count a project-time range occupies, and whether a render whose bounds
// come from the selected items' own extent already prints that window.
// frame count a project-time range occupies, whether a render whose bounds come
// from the selected items' own extent already prints that window, and the two
// diagnostics that say where a short render lost its frames.
// NO REAPER types; unit-tested by tests/test_render_window.cpp.
#include <string>
namespace reasampler::capture {
// Frames the [startSeconds, endSeconds) window occupies at `sampleRate`. Both
@@ -42,4 +45,28 @@ bool itemExtentPrintsWindow(double reqStart, double reqEnd,
double itemStart, double itemEnd,
int sampleRate);
// --- Diagnostics: where a short render lost its frames ------------------------
// The frames this window would hold if its END were resolved on a whole-millisecond
// grid, floored, instead of exactly. Two live short renders (48 kHz, TailMode::None)
// matched this count to the frame, which is the entire reason it exists.
//
// A COINCIDENCE OF COUNTS, not a claim about how anything resolved the end: nothing
// renders from this number and no capture path asks for it. Whole-millisecond values
// are recognized within a nanosecond, because a decimal millisecond is not always one
// in binary (0.029 * 1000 lands just below 29) and a bare floor would drop a
// millisecond from a window already on the grid. A nanosecond is far under one frame
// at any rate we render, so a real sub-millisecond remainder still floors.
long long msFlooredEndFrameCount(double startSeconds, double endSeconds,
int sampleRate);
// The sentence a capture prints when the render bounds it handed REAPER did not read
// back unchanged — the requested window, what came back, and both frame counts at
// `sampleRate` (omitted when the rate is unknown). EMPTY when both edges read back
// bit-identical, which is the only answer proving the request crossed into REAPER
// intact; a caller prints this only when it is non-empty.
std::string describeBoundsDrift(double reqStart, double reqEnd,
double storedStart, double storedEnd,
int sampleRate);
} // namespace reasampler::capture