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
+143 -2
View File
@@ -1,13 +1,14 @@
// Standalone tests for reasampler::render_window — no REAPER, no framework.
// Covers the bounds-equality number (a window's exact frame count at the project
// rate), the verdict the offline backend refuses a capture on, and the predicate
// rate), the verdict the offline backend refuses a capture on, the predicate
// that decides whether REAPER's selected-items render source can express a
// requested window at all.
// requested window at all, and the two short-render diagnostics.
#include "../src/core/capture/render_window.h"
#include <cmath>
#include <cstdio>
#include <string>
using namespace reasampler::capture;
@@ -15,6 +16,10 @@ static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
static bool contains(const std::string& haystack, const std::string& needle) {
return haystack.find(needle) != std::string::npos;
}
// --- frameCountFor: the bounds equality, stated as a number ------------------
static void testFrameCountIsExactNotRounded() {
@@ -226,6 +231,131 @@ static void testMultiItemUnionExtent() {
CHECK(!itemExtentPrintsWindow(1.0, 4.0, 1.0, 9.0, 48000));
}
// --- msFlooredEndFrameCount: the shape both live short renders had ------------
static void testMillisecondFlooredEndReproducesBothShortRenders() {
// Both DAW observations, as arithmetic. 48 kHz, TailMode::None, start at 0: the
// requested window's count, and the count its end floored to the millisecond
// holds — which is what each render actually printed.
CHECK(frameCountFor(0.0, 4.067797, 48000) == 195254);
CHECK(msFlooredEndFrameCount(0.0, 4.067797, 48000) == 195216);
CHECK(frameCountFor(0.0, 4.067797, 48000) -
msFlooredEndFrameCount(0.0, 4.067797, 48000) == 38);
CHECK(frameCountFor(0.0, 1.655172, 48000) == 79448);
CHECK(msFlooredEndFrameCount(0.0, 1.655172, 48000) == 79440);
CHECK(frameCountFor(0.0, 1.655172, 48000) -
msFlooredEndFrameCount(0.0, 1.655172, 48000) == 8);
}
static void testTheSixDecimalDisplayDidNotCreateTheEffect() {
// Both reported ends were printed to six decimals by the refusal. Each is one 4/4
// bar — at 59 BPM and at 145 BPM — so the full-precision doubles behind them are
// 240/59 and 240/145. Same counts either way: the display rounding is not what
// produces the shortfall.
CHECK(frameCountFor(0.0, 240.0 / 59.0, 48000) == 195254);
CHECK(msFlooredEndFrameCount(0.0, 240.0 / 59.0, 48000) == 195216);
CHECK(frameCountFor(0.0, 240.0 / 145.0, 48000) == 79448);
CHECK(msFlooredEndFrameCount(0.0, 240.0 / 145.0, 48000) == 79440);
}
static void testWindowAlreadyOnTheMillisecondGridLosesNothing() {
// The "sometimes it works" case: a bar at 120 BPM is exactly 2 s.
CHECK(msFlooredEndFrameCount(0.0, 2.0, 48000) == frameCountFor(0.0, 2.0, 48000));
// The binary-representation trap a bare floor would fall into. The premise, not an
// outcome: 1.007 s is a whole millisecond that really does land BELOW 1007 ms in
// double, so flooring it without a tolerance drops a millisecond from a window
// already on the grid.
CHECK(1.007 * 1000.0 < 1007.0);
CHECK(frameCountFor(0.0, 1.007, 48000) == 48336);
CHECK(msFlooredEndFrameCount(0.0, 1.007, 48000) == 48336);
// Same end reached from a non-zero start, so nothing here rests on the window
// beginning at 0.
CHECK(msFlooredEndFrameCount(0.5, 1.007, 48000) ==
frameCountFor(0.5, 1.007, 48000));
}
static void testOneFrameOfRemainderStillFloors() {
// The whole-millisecond tolerance must sit far below a frame, or it would swallow
// the very remainder this diagnostic exists to find. One frame at 48 kHz is 20.8 us
// — four orders of magnitude above the nanosecond tolerance.
const double oneFrame = 1.0 / 48000.0;
CHECK(frameCountFor(0.0, 1.0 + oneFrame, 48000) == 48001);
CHECK(msFlooredEndFrameCount(0.0, 1.0 + oneFrame, 48000) == 48000);
}
static void testMillisecondFloorAt44100WhereAMillisecondIsNotWholeFrames() {
// 44.1 kHz: a millisecond is 44.1 frames, so a floored end cannot be described as
// dropping a whole number of frames — the count still resolves exactly.
CHECK(frameCountFor(0.0, 0.0105, 44100) == 463);
CHECK(msFlooredEndFrameCount(0.0, 0.0105, 44100) == 441);
// And a window that IS on the millisecond grid there is untouched, even though its
// edge is not on a frame boundary.
CHECK(frameCountFor(0.0, 0.010, 44100) == 441);
CHECK(msFlooredEndFrameCount(0.0, 0.010, 44100) == 441);
}
static void testASubMillisecondStartWouldNotHideItself() {
// Both observations started at 0.000000s, the one value that hides a start-side
// truncation. A window whose START carries a sub-millisecond remainder counts from
// that exact start...
const double start = 1.0001724, end = 2.0001724;
CHECK(frameCountFor(start, end, 48000) == 48000);
// ...so a start floored to the millisecond would print a DIFFERENT count — 8 frames
// more, the same remainder the second observation lost off its end. A start-side
// truncation is therefore visible to the same frame-count gate, not silent.
CHECK(frameCountFor(1.000, end, 48000) == 48008);
CHECK(!renderHonoredBounds(frameCountFor(start, end, 48000),
frameCountFor(1.000, end, 48000)));
}
// --- describeBoundsDrift: the read-back's verdict ------------------------------
static void testBoundsThatReadBackUnchangedDescribeNothing() {
// The answer that proves the request crossed into REAPER intact — including for a
// window whose end is nowhere near a millisecond boundary.
CHECK(describeBoundsDrift(0.0, 4.067797, 0.0, 4.067797, 48000).empty());
CHECK(describeBoundsDrift(1.0001724, 2.0001724, 1.0001724, 2.0001724, 48000).empty());
}
static void testADriftedEndNamesBothWindowsAndBothCounts() {
const std::string s =
describeBoundsDrift(0.0, 4.067797, 0.0, 4.067, 48000);
CHECK(!s.empty());
// Both counts as literals from the DAW observation, not re-derived from the same
// functions the sentence was built with.
CHECK(contains(s, "195254")); // what the request asks for
CHECK(contains(s, "195216")); // what the drifted window would hold
CHECK(contains(s, "48000 Hz"));
}
static void testTheReportPrintsEnoughDigitsToShowTheDrift() {
// A report whose two numbers print identically is evidence of nothing. Two ends a
// single ULP apart — far under the sixth decimal a shorter rendering would stop at
// — must still read as two different numbers.
const double asked = 4.067797;
const double stored = std::nextafter(asked, 5.0);
const std::string s = describeBoundsDrift(0.0, asked, 0.0, stored, 48000);
CHECK(!s.empty());
CHECK(!contains(s, "4.067797s, read back [0s, 4.067797s)"));
}
static void testADriftedStartIsCaughtToo() {
// The edge both observations could not test.
const std::string s = describeBoundsDrift(1.0001724, 2.0, 1.000, 2.0, 48000);
CHECK(!s.empty());
CHECK(contains(s, "1.0001724"));
}
static void testAnUnknownRateStillReportsTheDriftWithoutFrames() {
// A project that never pinned a rate reads 0. The drift is still worth saying; a
// frame count over an unknown rate is not.
const std::string s = describeBoundsDrift(0.0, 4.067797, 0.0, 4.067, 0);
CHECK(!s.empty());
CHECK(!contains(s, "frames"));
}
int main() {
testFrameCountIsExactNotRounded();
testFrameCountIsADifferenceOfIndicesNotADuration();
@@ -244,6 +374,17 @@ int main() {
testSubFrameDriftStillPrintsTheSameFrames();
testUnknownRateFallsBackToExactEquality();
testMultiItemUnionExtent();
testMillisecondFlooredEndReproducesBothShortRenders();
testTheSixDecimalDisplayDidNotCreateTheEffect();
testWindowAlreadyOnTheMillisecondGridLosesNothing();
testOneFrameOfRemainderStillFloors();
testMillisecondFloorAt44100WhereAMillisecondIsNotWholeFrames();
testASubMillisecondStartWouldNotHideItself();
testBoundsThatReadBackUnchangedDescribeNothing();
testADriftedEndNamesBothWindowsAndBothCounts();
testTheReportPrintsEnoughDigitsToShowTheDrift();
testADriftedStartIsCaughtToo();
testAnUnknownRateStillReportsTheDriftWithoutFrames();
if (g_fail) { std::printf("%d check(s) FAILED\n", g_fail); return 1; }
std::printf("render_window: all checks passed\n");