capture: name the render source in the exact-bounds refusal, and put its one-frame tolerance under test
The tolerance is unchanged and now derived, not assumed: frameCountFor lands in {floor(L), ceil(L)}, so a non-frame-aligned window can never miss by more than a frame. Naming the source is what tells a self-bounding render from a short one.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
@@ -63,6 +64,28 @@ static void testRealtimeIsUnsupportedOffline() {
|
||||
CHECK(!renderSettingsFor(SourceMode::Realtime, 1.0).supported);
|
||||
}
|
||||
|
||||
static void testEveryRenderSourceIsNameable() {
|
||||
// The bounds refusal quotes this label to say WHICH render source missed the
|
||||
// window, so every mode must name itself and no two may read alike.
|
||||
const SourceMode all[] = {
|
||||
SourceMode::MasterMix, SourceMode::TimeSelection, SourceMode::SelectedTracks,
|
||||
SourceMode::SelectedItems, SourceMode::RazorArea, SourceMode::Realtime,
|
||||
};
|
||||
for (std::size_t i = 0; i < sizeof(all) / sizeof(all[0]); ++i) {
|
||||
const char* label = renderSourceLabel(all[i]);
|
||||
CHECK(label != nullptr && label[0] != '\0');
|
||||
CHECK(std::strcmp(label, "unknown") != 0);
|
||||
for (std::size_t j = i + 1; j < sizeof(all) / sizeof(all[0]); ++j)
|
||||
CHECK(std::strcmp(label, renderSourceLabel(all[j])) != 0);
|
||||
}
|
||||
// The two the refusal must tell apart: a source that derives its own bounds vs
|
||||
// the time-bounded render.
|
||||
CHECK(std::strcmp(renderSourceLabel(SourceMode::SelectedItems),
|
||||
"selected media items") == 0);
|
||||
CHECK(std::strcmp(renderSourceLabel(SourceMode::SelectedTracks),
|
||||
"selected tracks via master") == 0);
|
||||
}
|
||||
|
||||
// --- tail: TailMode -> RENDER_* mapping (docs/product/capture-tail.md) --------
|
||||
|
||||
static void testTailNoneIsExactBounds() {
|
||||
@@ -395,6 +418,7 @@ int main() {
|
||||
testSelectedItemsSingleFile();
|
||||
testRazorSingleFile();
|
||||
testRealtimeIsUnsupportedOffline();
|
||||
testEveryRenderSourceIsNameable();
|
||||
testTailNoneIsExactBounds();
|
||||
testTailAutoIsSurgicalTrim();
|
||||
testAutoTrimRatioDerivesFromDb();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// 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) and the predicate that decides whether REAPER's selected-items render
|
||||
// source can express a requested window at all.
|
||||
// rate), the verdict the offline backend refuses a capture on, and the predicate
|
||||
// that decides whether REAPER's selected-items render source can express a
|
||||
// requested window at all.
|
||||
|
||||
#include "../src/core/capture/render_window.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace reasampler::capture;
|
||||
@@ -42,6 +44,74 @@ static void testFrameCountRefusesEmptyInvertedAndUnknownRate() {
|
||||
CHECK(frameCountFor(1.0, 2.0, -1) == 0); // rate nonsensical
|
||||
}
|
||||
|
||||
static void testWindowStartingAtExactlyZero() {
|
||||
CHECK(frameCountFor(0.0, 1.0, 48000) == 48000);
|
||||
// The window from the reported blocker: it starts at 0 and its end lands a
|
||||
// quarter of a frame off the grid at 48 kHz.
|
||||
CHECK(frameCountFor(0.0, 4.067797, 48000) == 195254);
|
||||
}
|
||||
|
||||
// --- renderHonoredBounds: the gate's verdict ---------------------------------
|
||||
|
||||
static void testNonFrameAlignedWindowAcceptsEveryEdgeConvention() {
|
||||
// 4.067797 s at 48 kHz is 195254.26 frames — not a frame boundary. A correct
|
||||
// render lands on 195254, and the neighbours a different edge convention would
|
||||
// produce are inside the gate.
|
||||
const long long expected = frameCountFor(0.0, 4.067797, 48000);
|
||||
CHECK(expected == 195254);
|
||||
CHECK(renderHonoredBounds(expected, 195254));
|
||||
CHECK(renderHonoredBounds(expected, 195255));
|
||||
CHECK(renderHonoredBounds(expected, 195253));
|
||||
// The shortfall actually reported from the DAW is 38 frames — far outside any
|
||||
// alignment slack, so it is a render that missed the window, and is refused.
|
||||
CHECK(!renderHonoredBounds(expected, 195216));
|
||||
}
|
||||
|
||||
static void testNonAlignmentCanNeverExceedOneFrame() {
|
||||
// The provable content of the one-frame tolerance: frameCountFor rounds each
|
||||
// edge, so it lands within a frame of the window's real length — and so does a
|
||||
// renderer that floors, ceils or rounds that same length. Sweep both edges over
|
||||
// every eighth of a frame; no pairing may fall outside the gate. The renderer's
|
||||
// count is derived from the length here, independently of frameCountFor.
|
||||
const int rate = 48000;
|
||||
for (int s = 0; s < 8; ++s) {
|
||||
for (int e = 0; e < 8; ++e) {
|
||||
const double start = 3.0 + s / (8.0 * rate);
|
||||
const double end = 7.5 + e / (8.0 * rate);
|
||||
const long long expected = frameCountFor(start, end, rate);
|
||||
const double length = (end - start) * rate;
|
||||
CHECK(renderHonoredBounds(expected,
|
||||
static_cast<long long>(std::floor(length))));
|
||||
CHECK(renderHonoredBounds(expected,
|
||||
static_cast<long long>(std::ceil(length))));
|
||||
CHECK(renderHonoredBounds(expected, std::llround(length)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testWholeItemWideningIsStillRefused() {
|
||||
// The defect the gate was built for: a 1 s window inside a 30 s item printing
|
||||
// the whole item.
|
||||
const long long expected = frameCountFor(5.0, 6.0, 48000);
|
||||
CHECK(expected == 48000);
|
||||
CHECK(!renderHonoredBounds(expected, 30 * 48000));
|
||||
}
|
||||
|
||||
static void testLargeShortfallIsStillRefused() {
|
||||
const long long expected = frameCountFor(0.0, 4.067797, 48000);
|
||||
CHECK(!renderHonoredBounds(expected, 190000));
|
||||
// Two frames is the smallest miss outside the tolerance, in both directions —
|
||||
// the tolerance is one frame and stays one frame.
|
||||
CHECK(!renderHonoredBounds(expected, expected - 2));
|
||||
CHECK(!renderHonoredBounds(expected, expected + 2));
|
||||
}
|
||||
|
||||
static void testEmptyRenderIsRefusedAgainstARealWindow() {
|
||||
// A render that produced nothing is a bounds miss like any other; the backend's
|
||||
// own "did the file parse" guard is what keeps an unreadable render out of here.
|
||||
CHECK(!renderHonoredBounds(48000, 0));
|
||||
}
|
||||
|
||||
// --- itemExtentPrintsWindow: can the selected-items source express this? -----
|
||||
|
||||
static void testRangeInsideItemCannotBeExpressed() {
|
||||
@@ -106,6 +176,12 @@ int main() {
|
||||
testFrameCountIsExactNotRounded();
|
||||
testFrameCountIsADifferenceOfIndicesNotADuration();
|
||||
testFrameCountRefusesEmptyInvertedAndUnknownRate();
|
||||
testWindowStartingAtExactlyZero();
|
||||
testNonFrameAlignedWindowAcceptsEveryEdgeConvention();
|
||||
testNonAlignmentCanNeverExceedOneFrame();
|
||||
testWholeItemWideningIsStillRefused();
|
||||
testLargeShortfallIsStillRefused();
|
||||
testEmptyRenderIsRefusedAgainstARealWindow();
|
||||
testRangeInsideItemCannotBeExpressed();
|
||||
testRangeWiderThanItemCannotBeExpressedEither();
|
||||
testEachEdgeAloneDisqualifies();
|
||||
|
||||
Reference in New Issue
Block a user