Commit 34e01673 by PLN (Algolia)

fix(foundry): the manifest described audio that was never written

kitgate re-grades the shipped files and compares against what the finder recorded, on
the principle that two code paths reaching the same verdict is worth more than one path
asserting it. It immediately caught my own bug: staging applied the kit gain at write
time, AFTER grading, so the recorded grade described the pre-staging clip. Several
sub-scores have a level term, so the two differ — 0.875 recorded against 0.925 on disk —
and the gate correctly called that a defect.

Staged clips are now re-graded before the manifest is written, so the number stored is
the number you get by grading the file. Selection still runs on the pre-staging grades,
which is fine and worth stating: the gain is uniform across a kit, so it cannot reorder
its members.

This is the second time the cross-check has paid for itself in a day — it also caught the
stale manifest after restage. A checker that only ever agrees with the thing it checks is
not earning anything.

Also: the report now lists cuts whose CLAP instrument tag contradicts the kit they landed
in. Nothing is re-filed on that basis. For a stem whose filename token is unrecognised
there is no producer label to fall back on, so it lands wherever the coarse measurement
pointed — MAREA's `marea` stem went into the drums kit while CLAP hears a spoken voice.
CLAP is the one signal here that can separate a sung line from a keyboard, so it is used
to say what to audition first, not to decide.

24 tests.
parent 8cf1e841
...@@ -343,7 +343,18 @@ def export_track(result: dict, *, prefix: str, samples_root: Optional[Path] = No ...@@ -343,7 +343,18 @@ def export_track(result: dict, *, prefix: str, samples_root: Optional[Path] = No
role_tok = _tidal_token(s["name"].role_token) or "loop" role_tok = _tidal_token(s["name"].role_token) or "loop"
suffix = f"{cand.bars}b" if cand.bars > 0 else "chop" suffix = f"{cand.bars}b" if cand.bars > 0 else "chop"
dest = kit_dir / f"{i:02d}_{role_tok}_{suffix}.wav" dest = kit_dir / f"{i:02d}_{role_tok}_{suffix}.wav"
sf.write(str(dest), clip * head, ANALYSIS_SR, subtype="PCM_24") clip = clip * head
if head != 1.0:
# re-grade the STAGED clip: several sub-scores have a level term, so the
# grade computed before staging describes audio that is not what shipped.
# Selection above ran on the pre-staging grades, which is fine — the gain
# is uniform across the kit, so it cannot reorder its members — but the
# number recorded in the manifest has to be the one you get by grading
# the file on disk, or `kitgate`'s cross-check is comparing two different
# signals and calling the difference a defect.
g = G.grade_array(clip.T, ANALYSIS_SR, path=str(dest),
role=R.FAMILIES.get(s["family"], {}).get("stem"))
sf.write(str(dest), clip, ANALYSIS_SR, subtype="PCM_24")
written.append(dest) written.append(dest)
out.append(CutLoop( out.append(CutLoop(
kit=kit, name=dest.stem, path=str(dest), track=result["dir"].name, kit=kit, name=dest.stem, path=str(dest), track=result["dir"].name,
...@@ -502,6 +513,47 @@ def track_summary(r: dict) -> dict: ...@@ -502,6 +513,47 @@ def track_summary(r: dict) -> dict:
} }
# CLAP phrases → the family they imply. Keyword matching, not an exhaustive map: the
# point is only to notice a loud disagreement, never to reassign anything.
_CLAP_FAMILY_HINTS = (
("drums", ("drum", "hi-hat", "hihat", "percussion", "snare", "kick", "clap",
"cymbal", "tom", "break")),
("bass", ("bass", "sub-bass", "808")),
("vox", ("voice", "singing", "vocal", "choir", "chant", "rap", "speech")),
)
def clap_family(tags: dict) -> Optional[str]:
"""The family CLAP's top instrument tag implies, or None if it says nothing useful."""
inst = (tags or {}).get("instrument") or []
if not inst:
return None
phrase = str(inst[0][0]).lower()
for fam, keys in _CLAP_FAMILY_HINTS:
if any(k in phrase for k in keys):
return fam
return "tonal"
def clap_disagreements(cuts: list[CutLoop]) -> list[tuple]:
"""Cuts whose CLAP tag contradicts the kit they landed in. Advisory, never applied.
The cheap probe cannot separate a sung line from a rhodes chord, and for a stem whose
filename token we do not recognise there is no producer label to fall back on either —
so those land wherever the coarse measurement pointed. CLAP is the one thing here that
can tell the difference, and it runs after the cut, so the honest use of it is to
surface the cases worth a second listen rather than to silently re-file them
(`feedback_sample_identity`: CLAP proposes, PLN's ear confirms).
"""
out = []
for c in cuts:
cf = clap_family(c.tags)
if cf and cf != c.family and not (cf == "tonal" and c.family in ("fx", "vox")):
inst = c.tags["instrument"][0]
out.append((c.kit, c.name, c.stem_role, c.family, cf, inst[0], inst[1]))
return out
def report_md(results: list[dict], cuts: list[CutLoop]) -> str: def report_md(results: list[dict], cuts: list[CutLoop]) -> str:
from collections import Counter from collections import Counter
lines = ["# Fred stem-pack cut — report", ""] lines = ["# Fred stem-pack cut — report", ""]
......
...@@ -247,3 +247,19 @@ def test_headroom_preserves_relative_levels_within_a_kit(): ...@@ -247,3 +247,19 @@ def test_headroom_preserves_relative_levels_within_a_kit():
assert max(after) == pytest.approx(-6.0, abs=0.05) assert max(after) == pytest.approx(-6.0, abs=0.05)
# the spacing between the loops is untouched, which is the whole point # the spacing between the loops is untouched, which is the whole point
assert [a - after[0] for a in after] == pytest.approx([p - peaks[0] for p in peaks]) assert [a - after[0] for a in after] == pytest.approx([p - peaks[0] for p in peaks])
def test_a_staged_kit_records_the_grade_of_what_it_actually_wrote(tmp_path, monkeypatch):
"""kitgate compares the manifest against a fresh grade of the file. If staging moves
the audio after grading, that comparison is between two different signals and the
difference gets reported as a defect — which is how this bug was found."""
import engine.stempack as SP
from engine import grade as G
y = _click_track()
y = y / (np.max(np.abs(y)) + 1e-9) # full scale ⇒ staging will engage
clip = np.stack([y, y], axis=1)
head = SP.kit_headroom_gain([0.0])
assert head < 1.0
before = G.grade_array(clip.T, SR).grade
after = G.grade_array((clip * head).T, SR).grade
assert abs(before - after) > 0.02 # the grade really does move
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment