Ψ-W2-T2: collapse a capture whose channels are bit-identical to one lossless mono channel, index value measured off the landed file

This commit is contained in:
2026-08-01 21:46:55 -04:00
parent 09d64c9f46
commit 4fa3c1dd15
12 changed files with 312 additions and 11 deletions
+18
View File
@@ -573,8 +573,26 @@ static void testSeamFieldsAdditiveInvariant() {
CHECK(idx.query("id-z")->rootNote == 60); // move did not disturb seam fields
}
// A collapsed capture is a 1-channel entry, and the JSON is the only thing carrying
// that count across a project reload — the instrument's mono/stereo default reads it.
static void testMonoChannelCountRoundTrip() {
BankModel idx;
Sample s = fullSample("mono");
s.channelCount = 1;
CHECK(idx.add(s) == AddResult::Added);
const std::string json = idx.serialize();
CHECK(json.find("\"channelCount\":1") != std::string::npos);
auto back = BankModel::deserialize(json);
CHECK(back.has_value());
CHECK(back && back->query("id-mono") &&
back->query("id-mono")->channelCount == 1);
}
int main() {
testFullFieldRoundTrip();
testMonoChannelCountRoundTrip();
testSerializeGoldenLiteral();
testDedupByHash();
testTierFilterAndMove();
+163 -1
View File
@@ -11,10 +11,14 @@
// buildFloat32Wav golden header + parse round-trip; hashBytes/hashWavContent
// determinism, metadata-skip, fallback, and domain separation; a golden hash
// literal pinning exact hex output for a fixed input (guards persisted
// contentHash values against a silent feed-sequence drift).
// contentHash values against a silent feed-sequence drift); and the lossless mono
// collapse (bit-identical N-channel fold, the one-sample-differs and signed-zero
// declines, already-mono, zero/single-frame, an odd padded leading chunk, and the
// content-hash consequence).
#include "../src/core/capture/wav_codec.h"
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstring>
@@ -593,6 +597,155 @@ static void testGoldenHashLiterals() {
CHECK(hashBytes(wav.data(), wav.size()) == "68d8a193c958fd44");
}
// --- Lossless mono collapse --------------------------------------------------
// Every channel carries frame f's value; the collapse must keep those values verbatim
// in one channel and leave frame count / rate / bit depth alone.
static void testCollapseBitIdenticalStereo() {
auto wav = buildFloatWav(2, 48000, 6,
[](std::size_t f, std::uint16_t) {
return 0.25f * static_cast<float>(f) - 0.5f;
});
const MonoCollapse c = collapseToMono(wav);
CHECK(c.collapsed);
const WavLayout L = parseWavLayout(c.bytes);
CHECK(L.valid); // valid implies float32: the parser rejects anything else
CHECK(L.channelCount == 1);
CHECK(L.sampleRate == 48000);
CHECK(L.frameCount() == 6);
const auto pcm = extractFloatFrames(c.bytes, L, 0, 6);
CHECK(pcm.size() == 6);
for (std::size_t f = 0; f < 6 && f < pcm.size(); ++f)
CHECK(pcm[f] == 0.25f * static_cast<float>(f) - 0.5f);
}
static void testCollapseDeclinesOnOneDifferingSample() {
// Identical everywhere except frame 4's right channel, by the smallest step the
// format can express near 1.0.
auto wav = buildFloatWav(2, 48000, 8,
[](std::size_t f, std::uint16_t ch) {
float v = 1.0f + static_cast<float>(f);
if (f == 4 && ch == 1) v = nextafterf(v, 2.0f);
return v;
});
CHECK(!collapseToMono(wav).collapsed);
CHECK(collapseToMono(wav).bytes.empty());
}
// An already-mono file must come back untouched — a second capture pass over a
// collapsed file must not rebuild (and so must not re-hash) it.
static void testCollapseDeclinesOnAlreadyMono() {
auto wav = buildFloatWav(1, 44100, 4,
[](std::size_t f, std::uint16_t) {
return static_cast<float>(f);
});
CHECK(!collapseToMono(wav).collapsed);
}
// N-channel generalization: all-identical collapses to ONE channel, never a partial
// fold (4 -> 2). Unreachable from today's capture paths, which always render 2.
static void testCollapseFourChannels() {
auto same = buildFloatWav(4, 48000, 5,
[](std::size_t f, std::uint16_t) {
return -0.125f * static_cast<float>(f);
});
const MonoCollapse c = collapseToMono(same);
CHECK(c.collapsed);
const WavLayout L = parseWavLayout(c.bytes);
CHECK(L.valid && L.channelCount == 1 && L.frameCount() == 5);
auto oneDiffers = buildFloatWav(4, 48000, 5,
[](std::size_t f, std::uint16_t ch) {
float v = -0.125f * static_cast<float>(f);
if (f == 2 && ch == 3) v += 0.5f;
return v;
});
CHECK(!collapseToMono(oneDiffers).collapsed);
}
static void testCollapseZeroAndSingleFrame() {
// No frame of evidence that the channels agree -> decline rather than rebuild.
auto empty = buildFloatWav(2, 48000, 0,
[](std::size_t, std::uint16_t) { return 0.0f; });
CHECK(parseWavLayout(empty).valid && parseWavLayout(empty).frameCount() == 0);
CHECK(!collapseToMono(empty).collapsed);
auto one = buildFloatWav(2, 48000, 1,
[](std::size_t, std::uint16_t) { return 0.75f; });
const MonoCollapse c = collapseToMono(one);
CHECK(c.collapsed);
const WavLayout L = parseWavLayout(c.bytes);
CHECK(L.valid && L.channelCount == 1 && L.frameCount() == 1);
const auto pcm = extractFloatFrames(c.bytes, L, 0, 1);
CHECK(pcm.size() == 1 && pcm[0] == 0.75f);
}
// The predicate is over BIT PATTERNS: -0.0f == +0.0f compares equal as floats but is
// a different value on disk, so folding it would not be lossless.
static void testCollapseSignedZeroIsNotIdentical() {
auto wav = buildFloatWav(2, 48000, 3,
[](std::size_t, std::uint16_t ch) {
return ch == 0 ? 0.0f : -0.0f;
});
CHECK(!collapseToMono(wav).collapsed);
}
// A leading odd-sized chunk exercises the walk's RIFF pad byte; the rebuilt file is
// canonical, so that chunk does not survive.
static void testCollapseThroughOddPaddedLeadingChunk() {
std::vector<std::uint8_t> chunks;
putTag(chunks, "LIST");
putU32(chunks, 5); // odd body -> one pad byte
for (int i = 0; i < 5; ++i) chunks.push_back(0x41);
chunks.push_back(0); // the pad
putTag(chunks, "fmt ");
putU32(chunks, 16);
putU16(chunks, 3);
putU16(chunks, 2);
putU32(chunks, 48000);
putU32(chunks, 48000u * 2u * 4u);
putU16(chunks, 8);
putU16(chunks, 32);
putTag(chunks, "data");
putU32(chunks, 3u * 2u * 4u);
for (std::size_t f = 0; f < 3; ++f)
for (int ch = 0; ch < 2; ++ch) putFloat(chunks, 0.5f * static_cast<float>(f));
std::vector<std::uint8_t> wav;
putTag(wav, "RIFF");
putU32(wav, static_cast<std::uint32_t>(4 + chunks.size()));
putTag(wav, "WAVE");
wav.insert(wav.end(), chunks.begin(), chunks.end());
const MonoCollapse c = collapseToMono(wav);
CHECK(c.collapsed);
const WavLayout L = parseWavLayout(c.bytes);
CHECK(L.valid && L.channelCount == 1 && L.frameCount() == 3);
// Canonical rebuild: byte-for-byte what buildFloat32Wav produces for the same PCM.
CHECK(c.bytes == buildFloat32Wav(1, 48000, 3, {0.0, 0.5, 1.0}));
}
static void testCollapseDeclinesOnUnparseableBytes() {
std::vector<std::uint8_t> junk = {'N','O','P','E', 0,0,0,0, 'W','A','V','E'};
CHECK(!collapseToMono(junk).collapsed);
CHECK(!collapseToMono(std::vector<std::uint8_t>{}).collapsed);
}
// Stated consequence, pinned: the collapse rewrites both the `fmt ` body and the
// `data` payload, so a collapsed capture no longer shares content identity with the
// stereo file it came from and will not dedup against one already in the bank.
static void testCollapseChangesContentHash() {
auto wav = buildFloatWav(2, 48000, 4,
[](std::size_t f, std::uint16_t) {
return static_cast<float>(f);
});
const MonoCollapse c = collapseToMono(wav);
CHECK(c.collapsed);
CHECK(hashWavContent(c.bytes) != hashWavContent(wav));
}
int main() {
testParseCanonicalStereo();
testParseMonoAndLeadingChunk();
@@ -618,6 +771,15 @@ int main() {
testHashWavContentDomainSeparationFromWholeFile();
testHashMatchesBuildOutput();
testGoldenHashLiterals();
testCollapseBitIdenticalStereo();
testCollapseDeclinesOnOneDifferingSample();
testCollapseDeclinesOnAlreadyMono();
testCollapseFourChannels();
testCollapseZeroAndSingleFrame();
testCollapseSignedZeroIsNotIdentical();
testCollapseThroughOddPaddedLeadingChunk();
testCollapseDeclinesOnUnparseableBytes();
testCollapseChangesContentHash();
if (g_fail == 0) std::printf("wav_codec: all tests passed\n");
else std::printf("wav_codec: %d CHECK(s) FAILED\n", g_fail);