Preserve's period detection: probes are placed by position, and a sustain loop is the span analysed
This commit is contained in:
@@ -926,6 +926,45 @@ static void testBuildSampleDataDetectsThirtyHertzSourcePeriod() {
|
||||
CHECK(std::fabs(sd.sourcePeriodFrames - 1470.0) < 2.0); // 44100 / 30 Hz
|
||||
}
|
||||
|
||||
// The span half of the same wire: buildSampleData must hand detection the LOOP region when the
|
||||
// capture carries one, not the whole decoded PCM. Asserted through the real build for the same
|
||||
// reason as the test above — period_detect's own coverage cannot see which span the loader picks.
|
||||
static void testBuildSampleDataDetectsOverTheSustainLoopNotTheWholeSource() {
|
||||
const int rate = 44100;
|
||||
const std::size_t frames = 120000;
|
||||
const std::int64_t loopStart = 60000;
|
||||
const double kPi = 3.14159265358979323846;
|
||||
const double loopPeriod = static_cast<double>(rate) / 30.0; // 1470 frames
|
||||
|
||||
// Head at 147 Hz, looped tail at 30 Hz: analysed whole, the probes split two-and-two and
|
||||
// detection correctly refuses. Analysed over the loop, the 30 Hz sustain is unambiguous.
|
||||
std::vector<AudioSample> pcm(frames);
|
||||
double phase = 0.0;
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
phase += 2.0 * kPi / (i < static_cast<std::size_t>(loopStart) ? 300.0 : loopPeriod);
|
||||
pcm[i] = static_cast<float>(std::sin(phase));
|
||||
}
|
||||
|
||||
InstrumentParams noLoop;
|
||||
const SampleData bare = buildSampleData(resolveCapture(ref("b/a.wav", 60), noLoop),
|
||||
DecodedPcm{pcm, rate, {}});
|
||||
CHECK(bare.sourcePeriodFrames == 0.0); // no loop -> whole source -> no ONE period
|
||||
|
||||
InstrumentParams looped;
|
||||
looped.loopOverride = SampleLoop{true, loopStart, static_cast<std::int64_t>(frames)};
|
||||
const SampleData sd = buildSampleData(resolveCapture(ref("b/a.wav", 60), looped),
|
||||
DecodedPcm{pcm, rate, {}});
|
||||
CHECK(std::fabs(sd.sourcePeriodFrames - loopPeriod) < 2.0);
|
||||
|
||||
// A loop too short to host the full search band falls back to the whole source rather than
|
||||
// to none — here that whole source has no one period, so the answer is the bare one above.
|
||||
InstrumentParams shortLoop;
|
||||
shortLoop.loopOverride = SampleLoop{true, 118000, static_cast<std::int64_t>(frames)};
|
||||
const SampleData shortSd = buildSampleData(resolveCapture(ref("b/a.wav", 60), shortLoop),
|
||||
DecodedPcm{pcm, rate, {}});
|
||||
CHECK(shortSd.sourcePeriodFrames == bare.sourcePeriodFrames);
|
||||
}
|
||||
|
||||
static void testBuildSampleDataCarriesTheVelocityCurve() {
|
||||
InstrumentParams p;
|
||||
p.velocityCurve = VelocityCurve::linear();
|
||||
@@ -994,6 +1033,7 @@ int main() {
|
||||
testBuildSampleDataEmptyPcmIsUnplayable();
|
||||
testBuildSampleDataCarriesTheVelocityCurve();
|
||||
testBuildSampleDataDetectsThirtyHertzSourcePeriod();
|
||||
testBuildSampleDataDetectsOverTheSustainLoopNotTheWholeSource();
|
||||
|
||||
if (g_fail == 0) std::printf("sample_map: all tests passed\n");
|
||||
return g_fail != 0;
|
||||
|
||||
Reference in New Issue
Block a user