fix S11 review: extract upsertPickedOverride, restore map_ on drag-cancel, clamp start to frames-1, pin startFrame>loopEnd wrap test
This commit is contained in:
@@ -592,6 +592,43 @@ static void testStartFrameWithLoop() {
|
||||
for (std::size_t i = 60; i < out.size(); ++i) CHECK(approx(out[i], 0.5, 1e-4));
|
||||
}
|
||||
|
||||
static void testStartAfterLoopEndWrapsIntoLoop() {
|
||||
// Regression (S11 reviewer finding): if startFrame > loop.end (but still < frameCount),
|
||||
// the voice's initial read head is past the loop end. The wrap-while in renderFrame must
|
||||
// pull it back into [loopStart, loopEnd) on the very first frame, so the note sounds from
|
||||
// somewhere inside the loop rather than running off the sample end silently.
|
||||
//
|
||||
// Setup: 100-frame sample; loop is [20, 40); startFrame = 60 (past loop.end = 40).
|
||||
// loop body is a constant 0.5 so every frame inside it reads 0.5.
|
||||
// After wrap: readPos starts inside [20, 40), first output frame == 0.5.
|
||||
// Voice must stay active (loop sustains it) and emit the loop value, NOT go silent.
|
||||
SampleData s;
|
||||
s.frames.resize(100, 0.0f);
|
||||
for (int i = 20; i < 40; ++i) s.frames[i] = 0.5f; // loop body
|
||||
s.rootNote = 60;
|
||||
s.startFrame = 60; // > loop.end (40), < frameCount (100)
|
||||
s.loop.hasLoop = true;
|
||||
s.loop.start = 20;
|
||||
s.loop.end = 40;
|
||||
|
||||
Keymap km = Keymap::singleSampleChromatic(std::move(s));
|
||||
VoiceEngine eng(1, km, flatAdsr());
|
||||
eng.noteOn(60, 127); // unity ratio, full velocity
|
||||
|
||||
std::vector<AudioSample> out;
|
||||
eng.render(out, 50);
|
||||
|
||||
// Voice must still be active — the usable loop keeps it alive indefinitely.
|
||||
CHECK(eng.activeVoiceCount() == 1);
|
||||
// Every frame after the first wrap must read 0.5 (the loop body). We skip the very
|
||||
// first frame because the fractional-position wrap lands somewhere in [20,40) and the
|
||||
// exact offset depends on how many loop lengths fit into 60; what matters is that the
|
||||
// voice is alive and emitting the loop value, not 0.0 (pre-loop region).
|
||||
for (std::size_t i = 5; i < out.size(); ++i) {
|
||||
CHECK(approx(out[i], 0.5, 1e-4));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// velocity -> volume.
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -656,6 +693,7 @@ int main() {
|
||||
testStartFrameZeroIsUnchanged();
|
||||
testStartFrameOutOfRangeClampsToZero();
|
||||
testStartFrameWithLoop();
|
||||
testStartAfterLoopEndWrapsIntoLoop();
|
||||
testVelocityToVolume();
|
||||
testPolyphonyMixesAdditively();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user