136 lines
5.8 KiB
C++
136 lines
5.8 KiB
C++
// Standalone tests for reasampler::insert_plan — no REAPER, no framework.
|
|
// The insert shell is DAW-bound; this covers the one genuinely pure piece: the
|
|
// InsertMedia `mode` bitmask arithmetic. The bits are load-bearing for the
|
|
// "no silent time-stretch" invariant, so the assertions here are the checkable
|
|
// proof that the forbidden stretch bit is never set and that native-length insert
|
|
// carries no tempo bits.
|
|
|
|
#include "../src/core/capture/insert_plan.h"
|
|
|
|
#include <cstdio>
|
|
#include <initializer_list>
|
|
|
|
using namespace reasampler;
|
|
using namespace reasampler::capture;
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
// The tempo bits, mirrored here so the tests assert against literal expected
|
|
// values independently of the .cpp's private constants (a test that reused the
|
|
// implementation's constants would be tautological).
|
|
constexpr int STRETCH_FIT = 4;
|
|
constexpr int MATCH_1X = 8;
|
|
constexpr int MATCH_HALF = 16;
|
|
constexpr int MATCH_DBL = 32;
|
|
constexpr int NO_PITCH = 64;
|
|
|
|
static void testDefaultIsCurrentTrackNativeLength() {
|
|
// Defaults: current track (base 0), no conform, pitch preserved.
|
|
InsertOptions opts;
|
|
const int mode = computeInsertMode(opts);
|
|
CHECK(mode == 0); // base 0 only, no other bits
|
|
CHECK((mode & STRETCH_FIT) == 0); // never stretch-to-time-sel
|
|
CHECK((mode & MATCH_1X) == 0); // no tempo bits at native length
|
|
CHECK((mode & MATCH_HALF) == 0);
|
|
CHECK((mode & MATCH_DBL) == 0);
|
|
CHECK((mode & NO_PITCH) == 0);
|
|
}
|
|
|
|
static void testCurrentTrackBaseIsZero() {
|
|
InsertOptions opts;
|
|
opts.target = InsertTarget::CurrentTrack;
|
|
const int mode = computeInsertMode(opts);
|
|
CHECK((mode & 3) == 0); // base 0 = add to current track
|
|
CHECK((mode & STRETCH_FIT) == 0);
|
|
CHECK((mode & (MATCH_1X | MATCH_HALF | MATCH_DBL | NO_PITCH)) == 0);
|
|
}
|
|
|
|
static void testConform1xSetsOnlyMatchBit() {
|
|
InsertOptions opts; // current track base 0 (default)
|
|
opts.conform = TempoConform::Ratio1x;
|
|
const int mode = computeInsertMode(opts);
|
|
CHECK((mode & MATCH_1X) == MATCH_1X); // the 1x match bit is set
|
|
CHECK((mode & 3) == 0); // base target: current track (0)
|
|
CHECK((mode & STRETCH_FIT) == 0); // still never the stretch bit
|
|
CHECK((mode & (MATCH_HALF | MATCH_DBL)) == 0); // no other ratio bits
|
|
CHECK((mode & NO_PITCH) == 0); // pitch preserved by default
|
|
}
|
|
|
|
static void testConformHalfAndDoubleRatios() {
|
|
InsertOptions half;
|
|
half.conform = TempoConform::RatioHalf;
|
|
CHECK((computeInsertMode(half) & MATCH_HALF) == MATCH_HALF);
|
|
CHECK((computeInsertMode(half) & (MATCH_1X | MATCH_DBL)) == 0);
|
|
|
|
InsertOptions dbl;
|
|
dbl.conform = TempoConform::RatioDouble;
|
|
CHECK((computeInsertMode(dbl) & MATCH_DBL) == MATCH_DBL);
|
|
CHECK((computeInsertMode(dbl) & (MATCH_1X | MATCH_HALF)) == 0);
|
|
}
|
|
|
|
static void testPreservePitchGatesTheNoPitchBit() {
|
|
// preservePitch=false only takes effect when a tempo match is active.
|
|
InsertOptions conformShiftPitch;
|
|
conformShiftPitch.conform = TempoConform::Ratio1x;
|
|
conformShiftPitch.preservePitch = false;
|
|
CHECK((computeInsertMode(conformShiftPitch) & NO_PITCH) == NO_PITCH);
|
|
|
|
InsertOptions conformKeepPitch;
|
|
conformKeepPitch.conform = TempoConform::Ratio1x;
|
|
conformKeepPitch.preservePitch = true;
|
|
CHECK((computeInsertMode(conformKeepPitch) & NO_PITCH) == 0);
|
|
|
|
// preservePitch=false with NO conform must NOT set the pitch bit (nothing to
|
|
// pitch-shift; the bit is meaningless and would be spurious).
|
|
InsertOptions noConformNoPitch;
|
|
noConformNoPitch.conform = TempoConform::None;
|
|
noConformNoPitch.preservePitch = false;
|
|
CHECK((computeInsertMode(noConformNoPitch) & NO_PITCH) == 0);
|
|
CHECK(computeInsertMode(noConformNoPitch) == 0); // base 0 (current track), nothing else
|
|
}
|
|
|
|
static void testDefaultActionIsCurrentTrackBase0() {
|
|
// The default InsertOptions must compute base 0 (current track) so that both
|
|
// insert actions (default + conform) target the user's selected track(s), not
|
|
// a new track. This is the wired M6 insert-target change.
|
|
InsertOptions defaultOpts;
|
|
CHECK(defaultOpts.target == InsertTarget::CurrentTrack);
|
|
CHECK((computeInsertMode(defaultOpts) & 3) == 0); // base 0
|
|
|
|
InsertOptions conformOpts;
|
|
conformOpts.conform = TempoConform::Ratio1x;
|
|
CHECK((computeInsertMode(conformOpts) & 3) == 0); // conform variant also base 0
|
|
}
|
|
|
|
static void testStretchBitNeverSetAcrossAllOptions() {
|
|
// Exhaustively enumerate every option combination and assert the forbidden
|
|
// stretch-to-time-selection bit is absent from every computed mode. This is
|
|
// the machine-checkable form of the "no silent time-stretch" invariant.
|
|
const InsertTarget targets[] = {InsertTarget::NewTrack, InsertTarget::CurrentTrack};
|
|
const TempoConform conforms[] = {TempoConform::None, TempoConform::Ratio1x,
|
|
TempoConform::RatioHalf, TempoConform::RatioDouble};
|
|
for (InsertTarget t : targets)
|
|
for (TempoConform c : conforms)
|
|
for (bool pp : {true, false}) {
|
|
InsertOptions o;
|
|
o.target = t; o.conform = c; o.preservePitch = pp;
|
|
CHECK((computeInsertMode(o) & kStretchToTimeSelBit) == 0);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
testDefaultIsCurrentTrackNativeLength();
|
|
testCurrentTrackBaseIsZero();
|
|
testConform1xSetsOnlyMatchBit();
|
|
testConformHalfAndDoubleRatios();
|
|
testPreservePitchGatesTheNoPitchBit();
|
|
testDefaultActionIsCurrentTrackBase0();
|
|
testStretchBitNeverSetAcrossAllOptions();
|
|
|
|
if (g_fail == 0) std::printf("insert_plan: all tests passed\n");
|
|
else std::printf("insert_plan: %d FAILED\n", g_fail);
|
|
return g_fail ? 1 : 0;
|
|
}
|