Close Γ-W1-T7 re-review: pitch-sync cadence math, floor-model regression check, evidence-count fix, one-home comments
New cadence-collapse-band test at P=1470 shows PSOLA eliminates the corner rather than regressing it (18.52% -> 0.00%).
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// The one gated case that runs the WHOLE load->voice wire through REAL detection, closing the
|
||||
// gap between two halves proven separately: testBuildSampleDataDetectsThirtyHertzSourcePeriod
|
||||
// (test_sample_map.cpp, detectPeriod -> SampleData) never renders, and
|
||||
// testSourcePeriodChangesTheRenderedStream (test_sampler_core.cpp, SampleData -> Voice -> audio)
|
||||
// sets sourcePeriodFrames by hand rather than detecting it from PCM. Deliberately its own
|
||||
// target: sample_map_tests and sampler_core_tests each keep their one-lib-only structural proof
|
||||
// (map doesn't link the voice engine, the engine doesn't link period_detect), so bridging the
|
||||
// two lives here instead of extending either.
|
||||
|
||||
#include "../src/core/instrument/map/sample_map.h"
|
||||
#include "../src/core/instrument/engine/voice_engine.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace reasampler;
|
||||
using namespace reasampler::instrument::engine;
|
||||
using namespace reasampler::instrument::map;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
constexpr double kPi = 3.14159265358979323846;
|
||||
|
||||
// An ADSR that stays fully open (level 1) forever while held — isolates the render from
|
||||
// envelope shaping (matches test_sampler_core.cpp's flatAdsr; not shared, both files stand
|
||||
// alone).
|
||||
static AdsrParams flatAdsr() {
|
||||
AdsrParams a;
|
||||
a.attackFrames = 0;
|
||||
a.decayFrames = 0;
|
||||
a.sustainLevel = 1.0;
|
||||
a.releaseFrames = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
// FNV-1a over the raw float bits (matches test_sampler_core.cpp's hashStream).
|
||||
static std::uint64_t hashStream(const std::vector<AudioSample>& v) {
|
||||
std::uint64_t h = 1469598103934665603ull;
|
||||
for (const AudioSample s : v) {
|
||||
std::uint32_t bits = 0;
|
||||
std::memcpy(&bits, &s, sizeof(bits));
|
||||
for (int b = 0; b < 4; ++b) {
|
||||
h ^= static_cast<std::uint64_t>((bits >> (8 * b)) & 0xffu);
|
||||
h *= 1099511628211ull;
|
||||
}
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static void renderVoice(const SampleData& s, int note, std::int64_t window,
|
||||
std::size_t outFrames, std::vector<AudioSample>& out) {
|
||||
Voice v;
|
||||
v.presizePreserveShifters(window);
|
||||
v.start(note, 127, s, /*declickTakeover=*/false, /*rate=*/1.0);
|
||||
out.resize(outFrames);
|
||||
for (std::size_t i = 0; i < outFrames; ++i) out[i] = v.renderFrame();
|
||||
}
|
||||
|
||||
// 30 Hz @ 44.1k through the REAL wire: buildSampleData (sample_map.cpp:333-334) calls
|
||||
// detectPeriod itself, so this proves the detected period actually reaches and moves the
|
||||
// Preserve render — not just that a hand-set sourcePeriodFrames does (that is the sampler_core
|
||||
// half; this is the missing map->engine seam).
|
||||
static void testDetectedPeriodReachesAndMovesThePreserveRender() {
|
||||
const std::int64_t w = 2205; // the product window at 44.1k
|
||||
const int rate = 44100;
|
||||
const std::size_t frames = 30000;
|
||||
std::vector<AudioSample> pcm(frames);
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
pcm[i] = static_cast<float>(
|
||||
std::sin(2.0 * kPi * 30.0 * static_cast<double>(i) / rate));
|
||||
}
|
||||
|
||||
SelectedSample ref;
|
||||
ref.relativePath = "b/a.wav";
|
||||
ref.rootNote = 60;
|
||||
SampleData on = buildSampleData(resolveCapture(ref, InstrumentParams{}),
|
||||
DecodedPcm{pcm, rate, {}});
|
||||
CHECK(std::fabs(on.sourcePeriodFrames - 1470.0) < 2.0); // 44100 / 30 Hz, real detection
|
||||
|
||||
on.play.adsr = flatAdsr();
|
||||
on.play.pitchEngine = PitchEngine::Preserve;
|
||||
SampleData off = on;
|
||||
off.sourcePeriodFrames = 0.0; // the fixed-window fallback the pre-wire render used
|
||||
|
||||
std::vector<AudioSample> outOn, outOff;
|
||||
renderVoice(on, /*note=*/67, w, 6000, outOn); // +7 st: real splices
|
||||
renderVoice(off, 67, w, 6000, outOff);
|
||||
for (AudioSample v : outOn) CHECK(std::isfinite(v));
|
||||
CHECK(hashStream(outOn) != hashStream(outOff)); // the detected period actually moved the render
|
||||
}
|
||||
|
||||
int main() {
|
||||
testDetectedPeriodReachesAndMovesThePreserveRender();
|
||||
|
||||
if (g_fail == 0) {
|
||||
std::printf("all period_render_integration tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
std::printf("%d period_render_integration check(s) failed\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user