111 lines
4.4 KiB
C++
111 lines
4.4 KiB
C++
// Standalone tests for reasampler::instrument::map::trigger_seam — no VST3, no REAPER, no framework.
|
|
// Same fast assert loop as the sibling pure tests.
|
|
//
|
|
// Covers triggerPlayLength: zero play length, startFrame set, startFrame past frameCount,
|
|
// rounding, and the Finding 1 regression (start-point set — the case that was broken before
|
|
// this module existed); plus effectiveLengthFraction, the spline fold every consumer of the
|
|
// span must go through.
|
|
|
|
#include "../src/core/instrument/map/trigger_seam.h"
|
|
|
|
#include <cstdio>
|
|
|
|
using namespace reasampler;
|
|
using namespace reasampler::instrument::map;
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
// --- triggerPlayLength --------------------------------------------------------
|
|
|
|
static void testPlayLengthNoStartPoint() {
|
|
// startFrame == 0 (no start marker): postStart = frameCount.
|
|
// 1000 frames, lengthFraction 1.0 -> 1000.
|
|
CHECK(triggerPlayLength(1.0, 1000, 0) == 1000);
|
|
// 1000 frames, lengthFraction 0.5 -> round(500.0) = 500.
|
|
CHECK(triggerPlayLength(0.5, 1000, 0) == 500);
|
|
// 1000 frames, lengthFraction 0.333 -> round(333.0) = 333.
|
|
CHECK(triggerPlayLength(0.333, 1000, 0) == 333);
|
|
}
|
|
|
|
static void testPlayLengthWithStartPoint() {
|
|
// The Finding 1 regression: startFrame set, play length must be shorter.
|
|
// frameCount=1000, startFrame=200 -> postStart=800.
|
|
// lengthFraction 1.0 -> 800 (NOT 1000 as the pre-fix code produced).
|
|
CHECK(triggerPlayLength(1.0, 1000, 200) == 800);
|
|
// lengthFraction 0.5 -> round(400.0) = 400.
|
|
CHECK(triggerPlayLength(0.5, 1000, 200) == 400);
|
|
}
|
|
|
|
static void testPlayLengthZeroFrameCount() {
|
|
// No decoded audio -> 0.
|
|
CHECK(triggerPlayLength(1.0, 0, 0) == 0);
|
|
}
|
|
|
|
static void testPlayLengthStartFramePastEnd() {
|
|
// startFrame >= frameCount -> postStart clamped to 0 -> play length 0.
|
|
CHECK(triggerPlayLength(1.0, 500, 500) == 0);
|
|
CHECK(triggerPlayLength(1.0, 500, 600) == 0);
|
|
}
|
|
|
|
static void testPlayLengthRounding() {
|
|
// round(0.5) = 1 (round half up via +0.5 truncation: 0.5 + 0.5 = 1.0 -> 1).
|
|
CHECK(triggerPlayLength(0.5, 1, 0) == 1);
|
|
// round(lengthFraction * 3): 0.4 * 3 = 1.2 -> 1.
|
|
CHECK(triggerPlayLength(0.4, 3, 0) == 1);
|
|
// 0.6 * 3 = 1.8 -> 2.
|
|
CHECK(triggerPlayLength(0.6, 3, 0) == 2);
|
|
}
|
|
|
|
// --- effectiveLengthFraction --------------------------------------------------
|
|
|
|
// A drawn contour covers the FULL sample length, so the stored %-knob goes inert. It is not
|
|
// cleared, though — a pre-spline value survives in the record, and every reader that takes it
|
|
// raw plays, draws or bakes a fraction of the take.
|
|
static void testDrawnEnvelopeFoldsTheFractionToOne() {
|
|
PlayParams p;
|
|
p.trigger.lengthFraction = 0.25;
|
|
CHECK(effectiveLengthFraction(p) == 0.25); // staged: the knob is what it says
|
|
|
|
p.ampSpline.mode = EnvMode::Spline;
|
|
CHECK(effectiveLengthFraction(p) == 1.0); // drawn: the whole take
|
|
CHECK(p.trigger.lengthFraction == 0.25); // …and the stored value is untouched
|
|
|
|
// Flipping back restores the stored fraction, which is why the fold cannot be a write.
|
|
p.ampSpline.mode = EnvMode::Staged;
|
|
CHECK(effectiveLengthFraction(p) == 0.25);
|
|
}
|
|
|
|
// The fold reads the SAME predicate the engine's Gate refusal does, gating flags included: a
|
|
// Spline mode on a DISABLED pitch/filter envelope binds no cursor, so it must not fold.
|
|
static void testDisabledEnvelopesDoNotFoldTheFraction() {
|
|
PlayParams p;
|
|
p.trigger.lengthFraction = 0.5;
|
|
|
|
p.pitchSpline.mode = EnvMode::Spline;
|
|
CHECK(effectiveLengthFraction(p) == 0.5); // pitchEnv.enabled is still false
|
|
p.pitchEnv.enabled = true;
|
|
CHECK(effectiveLengthFraction(p) == 1.0);
|
|
|
|
p.pitchEnv.enabled = false;
|
|
p.filterSpline.mode = EnvMode::Spline;
|
|
CHECK(effectiveLengthFraction(p) == 0.5); // filter.enabled is still false
|
|
p.filter.enabled = true;
|
|
CHECK(effectiveLengthFraction(p) == 1.0);
|
|
}
|
|
|
|
int main() {
|
|
testDrawnEnvelopeFoldsTheFractionToOne();
|
|
testDisabledEnvelopesDoNotFoldTheFraction();
|
|
testPlayLengthNoStartPoint();
|
|
testPlayLengthWithStartPoint();
|
|
testPlayLengthZeroFrameCount();
|
|
testPlayLengthStartFramePastEnd();
|
|
testPlayLengthRounding();
|
|
|
|
if (g_fail == 0) std::printf("trigger_seam: all tests passed\n");
|
|
else std::printf("trigger_seam: %d FAILED\n", g_fail);
|
|
return g_fail == 0 ? 0 : 1;
|
|
}
|