fix(q-w0): six audit fix-nows — linked-lag stereo SOLA, playable-span prime bound, declick dead-state, provenance cursor hardening, rate-derived gain ramp + fade ceiling
This commit is contained in:
@@ -2474,6 +2474,68 @@ static void testPreserveTriggerTailGapFree() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Q-W0 T1-03: the Preserve prime is bounded by the PLAYABLE span. A Trigger zone whose
|
||||
// play length is shorter than the OLA window must never carry source PAST the user's
|
||||
// chosen stop into the ring — pre-fix, the prime pulled a full window bounded only by
|
||||
// frameCount, and an up-shifted tap PLAYED the cut content (transposed) before the voice
|
||||
// freed. The sample poisons everything past playEnd with amplitude 8: if any of it
|
||||
// reaches the output, the peak bound fails. ---
|
||||
static void testPreservePrimeStopsAtTriggerPlayEnd() {
|
||||
const std::size_t frames = 8000;
|
||||
const std::size_t w = 2048; // OLA window >> playable span
|
||||
const std::size_t playLen = 500; // playEnd = round(8000 * 0.0625) = 500
|
||||
SampleData s;
|
||||
s.frames.resize(frames);
|
||||
const double f0 = 1.0 / 50.0; // 10 cycles inside the playable span
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
s.frames[i] = i < playLen
|
||||
? static_cast<float>(std::sin(2.0 * kPi * f0 * static_cast<double>(i)))
|
||||
: 8.0f; // POISON: cut content past the play end
|
||||
}
|
||||
s.rootNote = 60;
|
||||
s.play.playMode = PlayMode::Trigger;
|
||||
s.play.pitchEngine = PitchEngine::Preserve;
|
||||
s.play.trigger.lengthFraction = 0.0625; // exactly 500 / 8000
|
||||
Keymap km = Keymap::singleSampleChromatic(std::move(s));
|
||||
VoiceEngine eng(1, km, /*preserveCap=*/0, static_cast<std::int64_t>(w));
|
||||
eng.noteOn(72, 127); // +1 octave: the tap outruns the read head into
|
||||
// the deepest primed history the ring holds
|
||||
std::vector<AudioSample> out;
|
||||
eng.render(out, playLen + 64); // through the voice's own end (readPos >= playEnd)
|
||||
double peak = 0.0;
|
||||
for (const AudioSample v : out) {
|
||||
const double a = std::fabs(static_cast<double>(v));
|
||||
if (a > peak) peak = a;
|
||||
}
|
||||
CHECK(peak < 1.5); // the 8.0 poison never sounds: nothing past playEnd entered the ring
|
||||
CHECK(peak > 0.4); // ...and the real span genuinely played (the bound is not vacuous)
|
||||
}
|
||||
|
||||
// --- Q-W0 T1-03 (companion): a whole sample SHORTER than the window (Gate, no loop) must not
|
||||
// get zero padding declared as valid ring history — pre-fix, the prime zero-filled the
|
||||
// window remainder with filled_ = window, so splices/tap travel landed in silence:
|
||||
// hundreds-of-frames dead runs inside a sub-window one-shot (the pre-GA2 burst/gap
|
||||
// artifact re-entering for short material). Post-fix the prime stops at the sample end
|
||||
// and freezes the tail immediately, so the ring recycles ONLY real content. ---
|
||||
static void testPreserveSubWindowSampleNoZeroPadInRing() {
|
||||
const std::size_t frames = 1200; // sample < one window
|
||||
const std::size_t w = 2048;
|
||||
const double f0 = 1.0 / 96.0; // period 96: zero crossings dwell ~2 frames
|
||||
SampleData s = tailSine(frames, f0, 60);
|
||||
s.play.pitchEngine = PitchEngine::Preserve;
|
||||
s.play.adsr = flatAdsr();
|
||||
Keymap km = Keymap::singleSampleChromatic(std::move(s));
|
||||
VoiceEngine eng(1, km, /*preserveCap=*/0, static_cast<std::int64_t>(w));
|
||||
eng.noteOn(72, 127); // +1 octave up-shift (tap sweeps the whole ring)
|
||||
std::vector<AudioSample> out;
|
||||
eng.render(out, frames); // voice runs to its natural end (no loop)
|
||||
// Pre-fix: the tap crossed the declared-valid zero pad repeatedly — quiet runs of 150+
|
||||
// frames. Post-fix every relocation stays inside the real filled span; only sine zero
|
||||
// crossings dip below the threshold.
|
||||
CHECK(worstQuietRun(out, 0, frames, 0.05) < 30);
|
||||
CHECK(blockPeak(out, 0, frames) > 0.5); // and it genuinely played at full level
|
||||
}
|
||||
|
||||
int main() {
|
||||
testChromaticSingleRoot();
|
||||
testZonedRangesBoundaries();
|
||||
@@ -2584,6 +2646,11 @@ int main() {
|
||||
testPreserveTailReleaseContinuous();
|
||||
testPreserveTriggerTailGapFree();
|
||||
|
||||
// Q-W0 T1-03 — the prime is bounded by the playable span (Trigger playEnd / sample end),
|
||||
// with an immediate tail freeze on sub-window spans.
|
||||
testPreservePrimeStopsAtTriggerPlayEnd();
|
||||
testPreserveSubWindowSampleNoZeroPadInRing();
|
||||
|
||||
if (g_fail == 0) {
|
||||
std::printf("all sampler_core tests passed\n");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user