|
|
@@ -4,20 +4,34 @@
|
|
|
|
// Covers the ratified reset scope PER PARAMETER, in both directions: every control whose
|
|
|
|
// Covers the ratified reset scope PER PARAMETER, in both directions: every control whose
|
|
|
|
// effect the render printed comes back at its default, and every mapping fact comes back
|
|
|
|
// effect the render printed comes back at its default, and every mapping fact comes back
|
|
|
|
// untouched. Asserted field by field rather than by struct equality on purpose — a
|
|
|
|
// untouched. Asserted field by field rather than by struct equality on purpose — a
|
|
|
|
// whole-struct compare would pass while silently resetting a survivor, or vice versa.
|
|
|
|
// whole-struct compare would pass while silently resetting a survivor, or vice versa. The
|
|
|
|
|
|
|
|
// sweep runs over TWO independently dialed fixtures that share only the survivors, which is
|
|
|
|
|
|
|
|
// what makes it a property of the survivors alone rather than of one input.
|
|
|
|
|
|
|
|
|
|
|
|
#include "../src/core/instrument/bake/bake_reset.h"
|
|
|
|
#include "../src/core/instrument/bake/bake_reset.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The loop's neutral is loop_marks' definition of it, not this test's reading of it: what a
|
|
|
|
|
|
|
|
// reset loop LOOKS like on the band is the thing worth asserting, and only that module says.
|
|
|
|
|
|
|
|
#include "../src/core/instrument/ui/loop_marks.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
|
|
|
|
|
|
|
|
using namespace reasampler;
|
|
|
|
using namespace reasampler;
|
|
|
|
using namespace reasampler::instrument::bake;
|
|
|
|
using namespace reasampler::instrument::bake;
|
|
|
|
using reasampler::instrument::map::InstrumentParams;
|
|
|
|
using reasampler::instrument::map::InstrumentParams;
|
|
|
|
using reasampler::instrument::map::PlaySeconds;
|
|
|
|
using reasampler::instrument::map::PlaySeconds;
|
|
|
|
|
|
|
|
using reasampler::instrument::ui::LoopMarks;
|
|
|
|
|
|
|
|
using reasampler::instrument::ui::StoredLoop;
|
|
|
|
|
|
|
|
using reasampler::instrument::ui::resolveLoopMarks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace note = reasampler::instrument::note;
|
|
|
|
|
|
|
|
namespace filter = reasampler::instrument::engine::filter;
|
|
|
|
|
|
|
|
|
|
|
|
static int g_fail = 0;
|
|
|
|
static int g_fail = 0;
|
|
|
|
|
|
|
|
static const char* g_ctx = "";
|
|
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
|
|
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
std::printf("FAIL [%s] line %d: %s\n", g_ctx, __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
@@ -45,14 +59,18 @@ InstrumentParams dialed() {
|
|
|
|
p.play.trigAhd.decaySeconds = 0.22;
|
|
|
|
p.play.trigAhd.decaySeconds = 0.22;
|
|
|
|
p.play.trigAhd.holdFraction = 0.33;
|
|
|
|
p.play.trigAhd.holdFraction = 0.33;
|
|
|
|
p.play.trigAhd.attackCurve = 1.7;
|
|
|
|
p.play.trigAhd.attackCurve = 1.7;
|
|
|
|
|
|
|
|
p.play.trigAhd.decayCurve = 2.2;
|
|
|
|
p.play.pitchEngine = PitchEngine::Varispeed;
|
|
|
|
p.play.pitchEngine = PitchEngine::Varispeed;
|
|
|
|
p.play.pitchEnv.enabled = true;
|
|
|
|
p.play.pitchEnv.enabled = true;
|
|
|
|
p.play.pitchEnv.peakSemitones = -7.0;
|
|
|
|
p.play.pitchEnv.peakSemitones = -7.0;
|
|
|
|
p.play.pitchEnv.shape.attackSeconds = 0.05;
|
|
|
|
p.play.pitchEnv.shape.attackSeconds = 0.05;
|
|
|
|
|
|
|
|
p.play.pitchEnv.shape.decaySeconds = 0.15;
|
|
|
|
p.play.pitchVelocityCurve = VelocityCurve::linear();
|
|
|
|
p.play.pitchVelocityCurve = VelocityCurve::linear();
|
|
|
|
p.play.playRate = 0.5;
|
|
|
|
p.play.playRate = 0.5;
|
|
|
|
p.play.pitchOffsetSemitones = -7.5;
|
|
|
|
p.play.pitchOffsetSemitones = -7.5;
|
|
|
|
p.play.filter.enabled = true;
|
|
|
|
p.play.filter.enabled = true;
|
|
|
|
|
|
|
|
p.play.filter.settings = filter::FilterSettings{0.3f, 0.8f, 0.1f, 0.7f,
|
|
|
|
|
|
|
|
filter::MorphLaw::HighNotchLow};
|
|
|
|
p.play.filter.modAmount = -0.8;
|
|
|
|
p.play.filter.modAmount = -0.8;
|
|
|
|
p.play.filter.velAmount = 0.6;
|
|
|
|
p.play.filter.velAmount = 0.6;
|
|
|
|
p.play.filter.keyTrack = 1.5;
|
|
|
|
p.play.filter.keyTrack = 1.5;
|
|
|
@@ -62,7 +80,69 @@ InstrumentParams dialed() {
|
|
|
|
p.play.ampSpline.mode = EnvMode::Spline;
|
|
|
|
p.play.ampSpline.mode = EnvMode::Spline;
|
|
|
|
p.play.ampSpline.contour = VelocityCurve::linear();
|
|
|
|
p.play.ampSpline.contour = VelocityCurve::linear();
|
|
|
|
p.play.pitchSpline.mode = EnvMode::Spline;
|
|
|
|
p.play.pitchSpline.mode = EnvMode::Spline;
|
|
|
|
|
|
|
|
p.play.pitchSpline.contour = VelocityCurve::linear();
|
|
|
|
p.play.filterSpline.mode = EnvMode::Spline;
|
|
|
|
p.play.filterSpline.mode = EnvMode::Spline;
|
|
|
|
|
|
|
|
p.play.filterSpline.contour = VelocityCurve::linear();
|
|
|
|
|
|
|
|
p.bakeHold = note::makeDivision(0, note::DivisionModifier::Dotted);
|
|
|
|
|
|
|
|
p.limiterEnabled = true;
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A SECOND dialed instrument, agreeing with the first on the SURVIVORS and disagreeing on
|
|
|
|
|
|
|
|
// every other field — including play mode, which is a chosen neutral rather than a survivor.
|
|
|
|
|
|
|
|
// Both run the same neutral sweep: if the reset ever inverted into "copy the dialed set, then
|
|
|
|
|
|
|
|
// clear a blacklist", a non-survivor would come through and at most one fixture could still
|
|
|
|
|
|
|
|
// land on the defaults. A parameter added later is covered here by moving it in BOTH
|
|
|
|
|
|
|
|
// fixtures, which is the same work as adding it to the sweep.
|
|
|
|
|
|
|
|
InstrumentParams dialedOther() {
|
|
|
|
|
|
|
|
InstrumentParams p;
|
|
|
|
|
|
|
|
p.rootOverride = 43; // survivor — same as dialed()
|
|
|
|
|
|
|
|
p.keyTrack = 0.5; // survivor — same as dialed()
|
|
|
|
|
|
|
|
p.loopOverride = SampleLoop{true, 7, 909};
|
|
|
|
|
|
|
|
p.startPoint = 12;
|
|
|
|
|
|
|
|
p.loopCrossfadeFrames = 64;
|
|
|
|
|
|
|
|
p.velocityCurve = VelocityCurve::rampDown();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
p.play.playMode = PlayMode::Gate;
|
|
|
|
|
|
|
|
p.play.adsr.attackSeconds = 1.4;
|
|
|
|
|
|
|
|
p.play.adsr.holdSeconds = 1.3;
|
|
|
|
|
|
|
|
p.play.adsr.decaySeconds = 1.2;
|
|
|
|
|
|
|
|
p.play.adsr.sustainLevel = 0.7;
|
|
|
|
|
|
|
|
p.play.adsr.releaseSeconds = 1.9;
|
|
|
|
|
|
|
|
p.play.adsr.attackCurve = 0.3;
|
|
|
|
|
|
|
|
p.play.adsr.decayCurve = 3.4;
|
|
|
|
|
|
|
|
p.play.adsr.releaseCurve = 0.6;
|
|
|
|
|
|
|
|
p.play.trigger.lengthFraction = 0.75;
|
|
|
|
|
|
|
|
p.play.trigAhd.attackSeconds = 0.91;
|
|
|
|
|
|
|
|
p.play.trigAhd.decaySeconds = 0.82;
|
|
|
|
|
|
|
|
p.play.trigAhd.holdFraction = 0.13;
|
|
|
|
|
|
|
|
p.play.trigAhd.attackCurve = 0.7;
|
|
|
|
|
|
|
|
p.play.trigAhd.decayCurve = 0.9;
|
|
|
|
|
|
|
|
p.play.pitchEngine = PitchEngine::Preserve;
|
|
|
|
|
|
|
|
p.play.pitchEnv.enabled = true;
|
|
|
|
|
|
|
|
p.play.pitchEnv.peakSemitones = 11.0;
|
|
|
|
|
|
|
|
p.play.pitchEnv.shape.attackSeconds = 0.25;
|
|
|
|
|
|
|
|
p.play.pitchEnv.shape.decaySeconds = 0.35;
|
|
|
|
|
|
|
|
p.play.pitchVelocityCurve = VelocityCurve::rampDown();
|
|
|
|
|
|
|
|
p.play.playRate = 1.75;
|
|
|
|
|
|
|
|
p.play.pitchOffsetSemitones = 3.25;
|
|
|
|
|
|
|
|
p.play.filter.enabled = true;
|
|
|
|
|
|
|
|
p.play.filter.settings = filter::FilterSettings{0.9f, 0.2f, 0.4f, 0.05f,
|
|
|
|
|
|
|
|
filter::MorphLaw::HighNotchLow};
|
|
|
|
|
|
|
|
p.play.filter.modAmount = 0.45;
|
|
|
|
|
|
|
|
p.play.filter.velAmount = -0.35;
|
|
|
|
|
|
|
|
p.play.filter.keyTrack = -0.9;
|
|
|
|
|
|
|
|
p.play.filter.env.attackSeconds = 1.7;
|
|
|
|
|
|
|
|
p.play.filter.trigEnv.decaySeconds = 1.8;
|
|
|
|
|
|
|
|
p.play.filter.velocityCurve = VelocityCurve::rampDown();
|
|
|
|
|
|
|
|
p.play.ampSpline.mode = EnvMode::Spline;
|
|
|
|
|
|
|
|
p.play.ampSpline.contour = VelocityCurve::flat();
|
|
|
|
|
|
|
|
p.play.pitchSpline.mode = EnvMode::Spline;
|
|
|
|
|
|
|
|
p.play.pitchSpline.contour = VelocityCurve::flat();
|
|
|
|
|
|
|
|
p.play.filterSpline.mode = EnvMode::Spline;
|
|
|
|
|
|
|
|
p.play.filterSpline.contour = VelocityCurve::flat();
|
|
|
|
|
|
|
|
p.bakeHold = note::makeDivision(2, note::DivisionModifier::Triplet);
|
|
|
|
|
|
|
|
p.limiterEnabled = true;
|
|
|
|
return p;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -75,22 +155,13 @@ bool sameCurve(const VelocityCurve& a, const VelocityCurve& b) {
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
// THE reset neutral, field by field. Run against every fixture, so the two can never be
|
|
|
|
|
|
|
|
// asserted against two different notions of neutral.
|
|
|
|
int main() {
|
|
|
|
void checkNeutral(const BakeReset& reset) {
|
|
|
|
const InstrumentParams before = dialed();
|
|
|
|
|
|
|
|
const BakeReset reset = resetAfterBake(before);
|
|
|
|
|
|
|
|
const InstrumentParams& after = reset.params;
|
|
|
|
const InstrumentParams& after = reset.params;
|
|
|
|
const InstrumentParams fresh; // the defaults every reset control must land on
|
|
|
|
const InstrumentParams fresh; // the defaults every reset control must land on
|
|
|
|
const PlaySeconds freshPlay;
|
|
|
|
const PlaySeconds freshPlay;
|
|
|
|
|
|
|
|
|
|
|
|
// --- SURVIVE: mapping facts, absent from the printed audio ----------------------
|
|
|
|
|
|
|
|
CHECK(after.rootOverride.has_value());
|
|
|
|
|
|
|
|
CHECK(after.rootOverride == before.rootOverride);
|
|
|
|
|
|
|
|
CHECK(after.keyTrack == before.keyTrack);
|
|
|
|
|
|
|
|
CHECK(after.keyTrack == 0.5); // and it is the dialed value, not the default 1.0
|
|
|
|
|
|
|
|
CHECK(fresh.keyTrack != before.keyTrack); // the fixture really did move it
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: loop points, start point, crossfade ---------------------------------
|
|
|
|
// --- RESET: loop points, start point, crossfade ---------------------------------
|
|
|
|
CHECK(!after.loopOverride.has_value());
|
|
|
|
CHECK(!after.loopOverride.has_value());
|
|
|
|
CHECK(!after.startPoint.has_value());
|
|
|
|
CHECK(!after.startPoint.has_value());
|
|
|
@@ -98,26 +169,17 @@ int main() {
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: the velocity transfer curves ----------------------------------------
|
|
|
|
// --- RESET: the velocity transfer curves ----------------------------------------
|
|
|
|
CHECK(sameCurve(after.velocityCurve, VelocityCurve::flat()));
|
|
|
|
CHECK(sameCurve(after.velocityCurve, VelocityCurve::flat()));
|
|
|
|
CHECK(!sameCurve(after.velocityCurve, before.velocityCurve));
|
|
|
|
|
|
|
|
CHECK(sameCurve(after.play.pitchVelocityCurve, VelocityCurve::zero()));
|
|
|
|
CHECK(sameCurve(after.play.pitchVelocityCurve, VelocityCurve::zero()));
|
|
|
|
CHECK(sameCurve(after.play.filter.velocityCurve, VelocityCurve::zero()));
|
|
|
|
CHECK(sameCurve(after.play.filter.velocityCurve, VelocityCurve::zero()));
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: play mode, to TRIGGER rather than to the struct's Gate default -------
|
|
|
|
// --- RESET: play mode, to TRIGGER rather than to the struct's Gate default -------
|
|
|
|
// The bake's product is a finished one-shot; Trigger plays it back verbatim, Gate would
|
|
|
|
// bake_reset.cpp carries the argument at the assignment.
|
|
|
|
// re-gate its printed release tail and each iteration would truncate the last one's.
|
|
|
|
|
|
|
|
CHECK(after.play.playMode == PlayMode::Trigger);
|
|
|
|
CHECK(after.play.playMode == PlayMode::Trigger);
|
|
|
|
CHECK(freshPlay.playMode == PlayMode::Gate); // and that really is NOT the default
|
|
|
|
CHECK(freshPlay.playMode == PlayMode::Gate); // and that really is NOT the default
|
|
|
|
// The Trigger face it lands on plays the whole file flat: full span, unity throughout.
|
|
|
|
// The Trigger face it lands on plays the whole file flat: full span, unity throughout.
|
|
|
|
CHECK(after.play.trigger.lengthFraction == 1.0);
|
|
|
|
CHECK(after.play.trigger.lengthFraction == 1.0);
|
|
|
|
CHECK(after.play.trigAhd.attackSeconds == 0.0);
|
|
|
|
CHECK(after.play.trigAhd.attackSeconds == 0.0);
|
|
|
|
CHECK(after.play.trigAhd.decaySeconds == 0.0);
|
|
|
|
CHECK(after.play.trigAhd.decaySeconds == 0.0);
|
|
|
|
{
|
|
|
|
|
|
|
|
// …and a GATE-dialed instrument lands there too: this is a reset to a chosen
|
|
|
|
|
|
|
|
// neutral, not the dialed value surviving.
|
|
|
|
|
|
|
|
InstrumentParams gated = dialed();
|
|
|
|
|
|
|
|
gated.play.playMode = PlayMode::Gate;
|
|
|
|
|
|
|
|
CHECK(resetAfterBake(gated).params.play.playMode == PlayMode::Trigger);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: the amp envelope, staged, every stage and every curve exponent ------
|
|
|
|
// --- RESET: the amp envelope, staged, every stage and every curve exponent ------
|
|
|
|
CHECK(after.play.adsr.attackSeconds == freshPlay.adsr.attackSeconds);
|
|
|
|
CHECK(after.play.adsr.attackSeconds == freshPlay.adsr.attackSeconds);
|
|
|
@@ -133,6 +195,7 @@ int main() {
|
|
|
|
CHECK(after.play.trigAhd.decaySeconds == freshPlay.trigAhd.decaySeconds);
|
|
|
|
CHECK(after.play.trigAhd.decaySeconds == freshPlay.trigAhd.decaySeconds);
|
|
|
|
CHECK(after.play.trigAhd.holdFraction == freshPlay.trigAhd.holdFraction);
|
|
|
|
CHECK(after.play.trigAhd.holdFraction == freshPlay.trigAhd.holdFraction);
|
|
|
|
CHECK(after.play.trigAhd.attackCurve == freshPlay.trigAhd.attackCurve);
|
|
|
|
CHECK(after.play.trigAhd.attackCurve == freshPlay.trigAhd.attackCurve);
|
|
|
|
|
|
|
|
CHECK(after.play.trigAhd.decayCurve == freshPlay.trigAhd.decayCurve);
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: pitch engine + pitch envelope ---------------------------------------
|
|
|
|
// --- RESET: pitch engine + pitch envelope ---------------------------------------
|
|
|
|
CHECK(after.play.pitchEngine == freshPlay.pitchEngine);
|
|
|
|
CHECK(after.play.pitchEngine == freshPlay.pitchEngine);
|
|
|
@@ -140,6 +203,7 @@ int main() {
|
|
|
|
CHECK(!after.play.pitchEnv.enabled);
|
|
|
|
CHECK(!after.play.pitchEnv.enabled);
|
|
|
|
CHECK(after.play.pitchEnv.peakSemitones == 0.0);
|
|
|
|
CHECK(after.play.pitchEnv.peakSemitones == 0.0);
|
|
|
|
CHECK(after.play.pitchEnv.shape.attackSeconds == freshPlay.pitchEnv.shape.attackSeconds);
|
|
|
|
CHECK(after.play.pitchEnv.shape.attackSeconds == freshPlay.pitchEnv.shape.attackSeconds);
|
|
|
|
|
|
|
|
CHECK(after.play.pitchEnv.shape.decaySeconds == freshPlay.pitchEnv.shape.decaySeconds);
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: Rate and the baseline Pitch offset -----------------------------------
|
|
|
|
// --- RESET: Rate and the baseline Pitch offset -----------------------------------
|
|
|
|
// Both are processing the bake already printed, so the whitelist leaves them at their
|
|
|
|
// Both are processing the bake already printed, so the whitelist leaves them at their
|
|
|
@@ -150,11 +214,16 @@ int main() {
|
|
|
|
CHECK(after.play.playRate == freshPlay.playRate);
|
|
|
|
CHECK(after.play.playRate == freshPlay.playRate);
|
|
|
|
CHECK(after.play.pitchOffsetSemitones == freshPlay.pitchOffsetSemitones);
|
|
|
|
CHECK(after.play.pitchOffsetSemitones == freshPlay.pitchOffsetSemitones);
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: the filter, including its velocity/key-tracking mod -----------------
|
|
|
|
// --- RESET: the filter, its control positions, and its velocity/key-tracking mod --
|
|
|
|
CHECK(!after.play.filter.enabled);
|
|
|
|
CHECK(!after.play.filter.enabled);
|
|
|
|
CHECK(after.play.filter.modAmount == 0.0);
|
|
|
|
CHECK(after.play.filter.modAmount == 0.0);
|
|
|
|
CHECK(after.play.filter.velAmount == 0.0);
|
|
|
|
CHECK(after.play.filter.velAmount == 0.0);
|
|
|
|
CHECK(after.play.filter.keyTrack == 0.0);
|
|
|
|
CHECK(after.play.filter.keyTrack == 0.0);
|
|
|
|
|
|
|
|
CHECK(after.play.filter.settings.cutoffNorm == freshPlay.filter.settings.cutoffNorm);
|
|
|
|
|
|
|
|
CHECK(after.play.filter.settings.resonanceNorm == freshPlay.filter.settings.resonanceNorm);
|
|
|
|
|
|
|
|
CHECK(after.play.filter.settings.morphNorm == freshPlay.filter.settings.morphNorm);
|
|
|
|
|
|
|
|
CHECK(after.play.filter.settings.driveNorm == freshPlay.filter.settings.driveNorm);
|
|
|
|
|
|
|
|
CHECK(after.play.filter.settings.morphLaw == freshPlay.filter.settings.morphLaw);
|
|
|
|
CHECK(after.play.filter.env.attackSeconds == freshPlay.filter.env.attackSeconds);
|
|
|
|
CHECK(after.play.filter.env.attackSeconds == freshPlay.filter.env.attackSeconds);
|
|
|
|
CHECK(after.play.filter.trigEnv.decaySeconds == freshPlay.filter.trigEnv.decaySeconds);
|
|
|
|
CHECK(after.play.filter.trigEnv.decaySeconds == freshPlay.filter.trigEnv.decaySeconds);
|
|
|
|
|
|
|
|
|
|
|
@@ -165,10 +234,94 @@ int main() {
|
|
|
|
CHECK(after.play.pitchSpline.mode == EnvMode::Staged);
|
|
|
|
CHECK(after.play.pitchSpline.mode == EnvMode::Staged);
|
|
|
|
CHECK(after.play.filterSpline.mode == EnvMode::Staged);
|
|
|
|
CHECK(after.play.filterSpline.mode == EnvMode::Staged);
|
|
|
|
CHECK(sameCurve(after.play.ampSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
CHECK(sameCurve(after.play.ampSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
CHECK(!sameCurve(after.play.ampSpline.contour, before.play.ampSpline.contour));
|
|
|
|
CHECK(sameCurve(after.play.pitchSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
|
|
|
|
CHECK(sameCurve(after.play.filterSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: the bake's own Hold division -----------------------------------------
|
|
|
|
|
|
|
|
// It sizes the render window for the one case that cannot derive one, so the length it
|
|
|
|
|
|
|
|
// chose is in the printed file, and the next bake re-derives from that file.
|
|
|
|
|
|
|
|
CHECK(after.bakeHold == fresh.bakeHold);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: the master-bus limiter enable ----------------------------------------
|
|
|
|
|
|
|
|
// Read the asymmetry recorded at BakeReset before reasoning about this one: it is not
|
|
|
|
|
|
|
|
// the master stage resetting as a unit.
|
|
|
|
|
|
|
|
CHECK(!after.limiterEnabled);
|
|
|
|
|
|
|
|
CHECK(!fresh.limiterEnabled);
|
|
|
|
|
|
|
|
|
|
|
|
// --- RESET: master gain ----------------------------------------------------------
|
|
|
|
// --- RESET: master gain ----------------------------------------------------------
|
|
|
|
CHECK(reset.masterGainLinear == 1.0);
|
|
|
|
CHECK(reset.masterGainLinear == 1.0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
|
|
|
const InstrumentParams before = dialed();
|
|
|
|
|
|
|
|
const BakeReset reset = resetAfterBake(before);
|
|
|
|
|
|
|
|
const InstrumentParams& after = reset.params;
|
|
|
|
|
|
|
|
const InstrumentParams fresh;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The fixtures must really move every field the sweep asserts, or the sweep is vacuous.
|
|
|
|
|
|
|
|
CHECK(fresh.keyTrack != before.keyTrack);
|
|
|
|
|
|
|
|
CHECK(before.limiterEnabled != fresh.limiterEnabled);
|
|
|
|
|
|
|
|
CHECK(before.bakeHold != fresh.bakeHold);
|
|
|
|
|
|
|
|
CHECK(dialedOther().bakeHold != fresh.bakeHold);
|
|
|
|
|
|
|
|
CHECK(dialedOther().bakeHold != before.bakeHold);
|
|
|
|
|
|
|
|
CHECK(!sameCurve(before.velocityCurve, VelocityCurve::flat()));
|
|
|
|
|
|
|
|
CHECK(!sameCurve(before.play.ampSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
|
|
|
|
CHECK(!sameCurve(dialedOther().play.ampSpline.contour, VelocityCurve::rampDown()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- SURVIVE: mapping facts, absent from the printed audio ----------------------
|
|
|
|
|
|
|
|
CHECK(after.rootOverride.has_value());
|
|
|
|
|
|
|
|
CHECK(after.rootOverride == before.rootOverride);
|
|
|
|
|
|
|
|
CHECK(after.keyTrack == before.keyTrack);
|
|
|
|
|
|
|
|
CHECK(after.keyTrack == 0.5); // and it is the dialed value, not the default 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- The neutral, over both fixtures --------------------------------------------
|
|
|
|
|
|
|
|
g_ctx = "dialed";
|
|
|
|
|
|
|
|
checkNeutral(reset);
|
|
|
|
|
|
|
|
g_ctx = "dialedOther";
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const BakeReset other = resetAfterBake(dialedOther());
|
|
|
|
|
|
|
|
checkNeutral(other);
|
|
|
|
|
|
|
|
// The survivors travel from the OTHER fixture too, so the sweep above is agreeing
|
|
|
|
|
|
|
|
// with a reset that read its input rather than one that ignores it wholesale.
|
|
|
|
|
|
|
|
CHECK(other.params.rootOverride == before.rootOverride);
|
|
|
|
|
|
|
|
CHECK(other.params.keyTrack == before.keyTrack);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
g_ctx = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- A GATE-dialed instrument lands on Trigger too -------------------------------
|
|
|
|
|
|
|
|
// This is a reset to a chosen neutral, not the dialed value surviving.
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
InstrumentParams gated = dialed();
|
|
|
|
|
|
|
|
gated.play.playMode = PlayMode::Gate;
|
|
|
|
|
|
|
|
CHECK(resetAfterBake(gated).params.play.playMode == PlayMode::Trigger);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- The loop enable and its points come back together ---------------------------
|
|
|
|
|
|
|
|
// What the band shows after a bake is the reset override read against the newly banked
|
|
|
|
|
|
|
|
// entry, which carries NO loop intrinsic (the shell records that pairing where it builds
|
|
|
|
|
|
|
|
// the entry) — so the enable has nothing left to fall back to.
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
constexpr std::int64_t kFrames = 1000;
|
|
|
|
|
|
|
|
const LoopMarks dialedMarks =
|
|
|
|
|
|
|
|
resolveLoopMarks(StoredLoop{before.loopOverride, std::nullopt,
|
|
|
|
|
|
|
|
before.loopCrossfadeFrames, before.startPoint},
|
|
|
|
|
|
|
|
kFrames);
|
|
|
|
|
|
|
|
CHECK(dialedMarks.hasLoop); // the fixture really did dial a loop on…
|
|
|
|
|
|
|
|
CHECK(!dialedMarks.parked); // …at its own positions
|
|
|
|
|
|
|
|
CHECK(dialedMarks.crossfade == 512);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const LoopMarks neutral =
|
|
|
|
|
|
|
|
resolveLoopMarks(StoredLoop{after.loopOverride, std::nullopt,
|
|
|
|
|
|
|
|
after.loopCrossfadeFrames, after.startPoint},
|
|
|
|
|
|
|
|
kFrames);
|
|
|
|
|
|
|
|
CHECK(!neutral.hasLoop);
|
|
|
|
|
|
|
|
CHECK(neutral.parked); // re-offered on the defaults, not left coincident
|
|
|
|
|
|
|
|
CHECK(neutral.loopStart < neutral.loopEnd);
|
|
|
|
|
|
|
|
CHECK(neutral.crossfade == 0);
|
|
|
|
|
|
|
|
CHECK(neutral.start == 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- An absent root override stays absent (nothing is invented) ------------------
|
|
|
|
// --- An absent root override stays absent (nothing is invented) ------------------
|
|
|
|
{
|
|
|
|
{
|
|
|
|