fix: S12 R2 — marker-drag sets selectedZone_, ADSR rate-resolve, gateAdsr doc
Marker drag now sets selectedZone_ so single-capture controls stay reachable. Lifted/default ADSR rescales nominal 44100-Hz frame counts by sampleRate/44100 via adsrNeedsRateResolve; v4-authored zones skip rescale. gateAdsr documented vestigial.
This commit is contained in:
@@ -1194,6 +1194,84 @@ static void testV3BlobLiftsAdsrToNominalDefaults() {
|
||||
CHECK(a.decayFrames == kTier0NominalDecayFrames); // 0
|
||||
CHECK(a.sustainLevel == kTier0NominalSustainLevel); // 1.0
|
||||
CHECK(a.releaseFrames == kTier0NominalReleaseFrames); // 2646 (0.060 s at 44100)
|
||||
// The lifted zone must be flagged for rate-resolve so buildZonedKeymap rescales at the live rate.
|
||||
CHECK(back.zones[0].adsrNeedsRateResolve == true);
|
||||
}
|
||||
|
||||
// A v4 blob (explicit A/D/S/R tail) must NOT set adsrNeedsRateResolve — those values
|
||||
// were authored at the DAW's rate and must not be rescaled again at keymap build time.
|
||||
static void testV4BlobClearsAdsrNeedsRateResolve() {
|
||||
PerformanceMap m;
|
||||
PerformanceZone z = zone("pad", 0, 127);
|
||||
z.play.adsr.attackFrames = 441;
|
||||
z.play.adsr.releaseFrames = 8820;
|
||||
m.zones.push_back(z);
|
||||
// A round-trip through serialize/deserialize writes a v4 payload (current version).
|
||||
const PerformanceMap back = deserializePerformance(serializePerformance(m));
|
||||
CHECK(back.zones.size() == 1);
|
||||
if (back.zones.size() != 1) return;
|
||||
// v4 tail was explicitly read — adsrNeedsRateResolve must be false.
|
||||
CHECK(back.zones[0].adsrNeedsRateResolve == false);
|
||||
}
|
||||
|
||||
// buildTier0Keymap at 48k must produce ADSR frame counts equal to tier0Adsr(48000):
|
||||
// attack = round(kTier0NominalAttackFrames * 48000 / 44100) = round(143.67) = 144,
|
||||
// release = round(kTier0NominalReleaseFrames * 48000 / 44100) = round(2880.0) = 2880.
|
||||
// This is the "pre-fix, the Gate voice used tier0Adsr(sampleRate_)" invariant restored
|
||||
// for the single-capture fast path at any DAW rate.
|
||||
static void testBuildTier0KeymapRescalesAdsrAt48k() {
|
||||
const Keymap km = buildTier0Keymap({0.5f}, 48000, 60, SampleLoop{});
|
||||
CHECK(km.samples.size() == 1);
|
||||
if (km.samples.empty()) return;
|
||||
const AdsrParams& a = km.samples[0].play.adsr;
|
||||
// Rescaled from 44100-nominal at 48000 Hz:
|
||||
CHECK(a.attackFrames == 144); // round(132 * 48000.0 / 44100.0)
|
||||
CHECK(a.decayFrames == 0); // 0 * factor = 0 (no change)
|
||||
CHECK(a.sustainLevel == 1.0); // level, not frames (no rescale)
|
||||
CHECK(a.releaseFrames == 2880); // round(2646 * 48000.0 / 44100.0)
|
||||
}
|
||||
|
||||
// buildZonedKeymap at 48k with adsrNeedsRateResolve=true must rescale ADSR to match
|
||||
// tier0Adsr(48000), mirroring what Gate voices saw before the per-zone-ADSR fix.
|
||||
static void testBuildZonedKeymapRescalesNominalAdsrAt48k() {
|
||||
// Build a zone with nominal 44100-Hz ADSR and the needs-resolve flag (the default).
|
||||
ResolvedZone z;
|
||||
z.lowNote = 0; z.highNote = 127; z.rootNote = 60;
|
||||
z.adsrNeedsRateResolve = true; // 44100-nominal values, rescale needed
|
||||
z.play.adsr.attackFrames = kTier0NominalAttackFrames; // 132
|
||||
z.play.adsr.decayFrames = kTier0NominalDecayFrames; // 0
|
||||
z.play.adsr.sustainLevel = kTier0NominalSustainLevel; // 1.0
|
||||
z.play.adsr.releaseFrames = kTier0NominalReleaseFrames; // 2646
|
||||
const DecodedZonePcm pcm{{0.5f}, 48000}; // 48k WAV
|
||||
const Keymap km = buildZonedKeymap({z}, {pcm});
|
||||
CHECK(km.samples.size() == 1);
|
||||
if (km.samples.empty()) return;
|
||||
const AdsrParams& a = km.samples[0].play.adsr;
|
||||
CHECK(a.attackFrames == 144); // round(132 * 48000.0 / 44100.0)
|
||||
CHECK(a.decayFrames == 0); // 0 * factor = 0
|
||||
CHECK(a.sustainLevel == 1.0); // level, not rescaled
|
||||
CHECK(a.releaseFrames == 2880); // round(2646 * 48000.0 / 44100.0)
|
||||
}
|
||||
|
||||
// buildZonedKeymap must NOT rescale a zone whose adsrNeedsRateResolve is false —
|
||||
// those frame counts are explicitly user-authored at the DAW's rate.
|
||||
static void testBuildZonedKeymapDoesNotRescaleV4Adsr() {
|
||||
ResolvedZone z;
|
||||
z.lowNote = 0; z.highNote = 127; z.rootNote = 60;
|
||||
z.adsrNeedsRateResolve = false; // v4 blob or user-edited — do not rescale
|
||||
z.play.adsr.attackFrames = 441; // 0.01 s at 44100 Hz (non-nominal)
|
||||
z.play.adsr.decayFrames = 4410;
|
||||
z.play.adsr.sustainLevel = 0.7;
|
||||
z.play.adsr.releaseFrames = 8820;
|
||||
const DecodedZonePcm pcm{{0.5f}, 48000}; // 48k WAV — rescale would change values
|
||||
const Keymap km = buildZonedKeymap({z}, {pcm});
|
||||
CHECK(km.samples.size() == 1);
|
||||
if (km.samples.empty()) return;
|
||||
const AdsrParams& a = km.samples[0].play.adsr;
|
||||
CHECK(a.attackFrames == 441); // unchanged (not rescaled)
|
||||
CHECK(a.decayFrames == 4410);
|
||||
CHECK(a.sustainLevel == 0.7);
|
||||
CHECK(a.releaseFrames == 8820);
|
||||
}
|
||||
|
||||
int main() {
|
||||
@@ -1249,6 +1327,10 @@ int main() {
|
||||
testPlayParamsThroughComponentEnvelope();
|
||||
testFullAdsrV4RoundTrip();
|
||||
testV3BlobLiftsAdsrToNominalDefaults();
|
||||
testV4BlobClearsAdsrNeedsRateResolve();
|
||||
testBuildTier0KeymapRescalesAdsrAt48k();
|
||||
testBuildZonedKeymapRescalesNominalAdsrAt48k();
|
||||
testBuildZonedKeymapDoesNotRescaleV4Adsr();
|
||||
testComponentStateRoundTrip();
|
||||
testComponentStateLoopStartRoundTrip();
|
||||
testComponentStateSelectionOnlyNoZones();
|
||||
|
||||
Reference in New Issue
Block a user