Phase S FB1 (r11): Sample-view recomposition — knob deck + elastic full-width hero + curve popup w/ right-click delete + post-mixer master gain (ComponentState v8)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// Standalone tests for reasampler::vst::master_gain — no VST3, no REAPER, no framework. Same
|
||||
// fast assert loop as the sibling pure tests. Assert the FB1 post-mixer gain taper HARD:
|
||||
//
|
||||
// * -inf bottom — norm 0 maps to -infinity dB and TRUE ZERO linear (silence, not an epsilon);
|
||||
// linear <= 0 maps back to norm 0.
|
||||
// * endpoints — norm 1 = +24 dB = masterGainMaxLinear() (~15.849); the finite floor at -60 dB.
|
||||
// * unity — 0 dB round-trips exactly through norm<->dB<->linear (the knob's unity detent
|
||||
// position 60/84 of the travel).
|
||||
// * monotonicity — more norm never means less gain.
|
||||
// * label — "-inf" at the bottom, signed one-decimal dB elsewhere.
|
||||
|
||||
#include "../src/vst/master_gain.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
using namespace reasampler::vst;
|
||||
|
||||
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 near(double a, double b, double eps = 1e-9) { return std::fabs(a - b) <= eps; }
|
||||
|
||||
static void testBottomIsTrueSilence() {
|
||||
CHECK(std::isinf(masterGainDbFromNorm(0.0)) && masterGainDbFromNorm(0.0) < 0.0);
|
||||
CHECK(masterGainLinearFromNorm(0.0) == 0.0); // exactly zero — the -inf contract
|
||||
CHECK(masterGainNormFromLinear(0.0) == 0.0);
|
||||
CHECK(masterGainNormFromLinear(-1.0) == 0.0);
|
||||
CHECK(masterGainNormFromDb(-1e9) == 0.0);
|
||||
}
|
||||
|
||||
static void testEndpoints() {
|
||||
CHECK(near(masterGainDbFromNorm(1.0), kMasterGainMaxDb));
|
||||
CHECK(near(masterGainLinearFromNorm(1.0), masterGainMaxLinear()));
|
||||
CHECK(near(masterGainMaxLinear(), std::pow(10.0, 24.0 / 20.0)));
|
||||
CHECK(near(masterGainNormFromDb(kMasterGainMaxDb), 1.0));
|
||||
CHECK(near(masterGainNormFromLinear(masterGainMaxLinear()), 1.0));
|
||||
// Out-of-range norms clamp.
|
||||
CHECK(near(masterGainDbFromNorm(2.0), kMasterGainMaxDb));
|
||||
CHECK(masterGainLinearFromNorm(-0.5) == 0.0);
|
||||
}
|
||||
|
||||
static void testUnityRoundTrip() {
|
||||
// 0 dB sits at norm -minDb/(maxDb-minDb) = 60/84 of the travel.
|
||||
const double unityNorm = masterGainNormFromDb(0.0);
|
||||
CHECK(near(unityNorm, 60.0 / 84.0, 1e-12));
|
||||
CHECK(near(masterGainDbFromNorm(unityNorm), 0.0, 1e-9));
|
||||
CHECK(near(masterGainLinearFromNorm(unityNorm), 1.0, 1e-9));
|
||||
CHECK(near(masterGainNormFromLinear(1.0), unityNorm, 1e-9));
|
||||
}
|
||||
|
||||
static void testNormLinearRoundTripAcrossTravel() {
|
||||
for (int i = 1; i <= 20; ++i) {
|
||||
const double norm = i / 20.0;
|
||||
const double lin = masterGainLinearFromNorm(norm);
|
||||
CHECK(lin > 0.0);
|
||||
CHECK(near(masterGainNormFromLinear(lin), norm, 1e-9));
|
||||
}
|
||||
}
|
||||
|
||||
static void testMonotonic() {
|
||||
double prev = -1.0;
|
||||
for (int i = 0; i <= 100; ++i) {
|
||||
const double lin = masterGainLinearFromNorm(i / 100.0);
|
||||
CHECK(lin > prev); // strictly increasing over the whole travel (0 at the bottom)
|
||||
prev = lin;
|
||||
}
|
||||
}
|
||||
|
||||
static void testNonFiniteLinearClamps() {
|
||||
CHECK(masterGainNormFromLinear(std::nan("")) == 0.0);
|
||||
CHECK(near(masterGainNormFromLinear(1e9), 1.0)); // above the cap clamps to 1
|
||||
}
|
||||
|
||||
static void testLabels() {
|
||||
char buf[24];
|
||||
formatMasterGainLabel(0.0, buf, sizeof(buf));
|
||||
CHECK(std::strcmp(buf, "-inf") == 0);
|
||||
formatMasterGainLabel(1.0, buf, sizeof(buf));
|
||||
CHECK(std::strcmp(buf, "+24.0dB") == 0);
|
||||
formatMasterGainLabel(masterGainNormFromDb(0.0), buf, sizeof(buf));
|
||||
CHECK(std::strcmp(buf, "+0.0dB") == 0);
|
||||
formatMasterGainLabel(masterGainNormFromDb(-12.0), buf, sizeof(buf));
|
||||
CHECK(std::strcmp(buf, "-12.0dB") == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testBottomIsTrueSilence();
|
||||
testEndpoints();
|
||||
testUnityRoundTrip();
|
||||
testNormLinearRoundTripAcrossTravel();
|
||||
testMonotonic();
|
||||
testNonFiniteLinearClamps();
|
||||
testLabels();
|
||||
if (g_fail) {
|
||||
std::printf("%d FAILURE(S)\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
std::printf("master_gain tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user