---
log: 027
title: "The label and the lens"
date: 2026-08-21
task: "Foundry #20 — batch-explore many sources"
tags: [tooling, sampling, foundry, analysis]
shareable: true
---

## Cap (what & why)

Fred again.. dropped his stems. 36 GB of them — 9 tracks split by the producer himself
into 6–13 named stems each (`MAREA MIX10 123BPM KIT STEM.wav`), plus 13 acapella /
instrumental pairs. Cutting that into TidalCycles kits by hand in Audacity is exactly
the drudgery the Foundry's loop finder was built to kill. So: point the finder at
somebody else's stems and find out whether it actually works.

It mostly did. What it got wrong is the interesting part.

## Manœuvre (how)

The finder existed and was good — beat grid, Foote novelty, verify-rerank on the true
post-snap seam, downbeat rotation, an export path that guarantees bar multiples. What
did not exist was a way to feed it a folder somebody else had already separated:
`find_takes` keys stems by demucs' four names, a dict that collides on KEYS1+KEYS2 and
an export filter that would have silently dropped every stem in the pack.

So `engine/stempack.py` (the batch driver), `engine/roles.py` (the pack's ~40 role
tokens → a family, then verified by measurement), and `engine/kitcheck.py` (the audit
of what `grade` structurally cannot see). One kit per track × role family.

Then: run it, look at what came out, fix what was wrong, run it again.

## Prise (findings / artifacts)

- `tools/foundry/engine/{stempack,roles,kitcheck}.py` + `kitgate.py` + `restage.py`,
  26 new tests, suite 86 → 112.
- MAREA declares its tempo in its filenames, so it is free ground truth. Final grid:
  **123.0 bpm against a declared 123 — 0.0 % off.**
- **96 kHz was breaking the finder.** At native rate it returned 123/123/124/**119**/
  **128.1**/**128.5** bpm with a 0.661 top score; resampled to 44.1 kHz first, 123.0–123.1
  on every candidate at 0.859, in a third of the time.
- Every loop was being cut at its own local tempo — ME's grid read 129.2 while both its
  shipped loops were cut at 150.5. Bar length now comes from one robust track tempo
  (the mode of the local-BPM histogram) with a confidence, and a track with no usable
  pulse ships chops only.
- Chops beat loops mechanically every time (0.993–0.999 vs 0.841–0.881), so the vocal
  kit came out as three stabs and no loops. Modes now rank in separate pools.
- The probe was looking in the wrong place with the wrong lens, and threw away good
  material both ways — see below.

### Then four more, found by measuring the output instead of reading it

- **The periodicity check was measuring loop length.** Autocorrelation at lag *k* has
  only *N−k* overlapping frames; normalised by `ac[0]` it scored a *perfectly periodic*
  click track 0.418 / 0.623 / 0.754 at 2 / 4 / 8 bars. Then the floor had to go per role:
  medians drums 0.754, fx 0.549, tonal 0.214, bass 0.173, **vox 0.087**. One threshold
  condemned 12 of 20 vocal loops for the crime of being vocal.
- **Dead bars grade S.** A 4-bar loop whose last two bars are a fade is silent-to-silent
  — a perfect seam — at an exact bar length. Ten of the first 161 cuts were that. Now
  rejected at export, not reported afterwards.
- **Kits had no headroom.** MAREA's ALL DRUMS and KIT both peak at **+0.2 dBFS**, and
  `amp = gain^4`, so `# gain 1.2` is +7.6 dB and the orbit clips the moment you touch it.
  Kits now land at −6 dBFS as *one gain per kit* (per-file would flatten a sparse hat
  against a full drum bus) and never boosting.
- **1 loop in 6 opened on a weak beat.** The exact "timed ok but cut BCDA/DABC" complaint
  from July. The roll point is zero-crossing snapped by up to ~10 ms against a 20 ms
  attack window — enough to flip two near-tied slots. Rotation now checks its own result
  and repeats: best slot 29 → 46 of 60, worse half 10 → **1**.

And two bugs the *checks* caught in my own work, which is the argument for having them:
`kitgate` re-grades the shipped files instead of trusting the manifest, and immediately
found that staging applied its gain after grading, so the recorded number described audio
that was never written. Separately, a report section I added never rendered — a
`str.replace` whose anchor did not match, and the one patch in that file where I had not
asserted on the anchor.

## Sel (the shareable learning)

**Two ways to be wrong about a stem, and they pull in opposite directions.**

The house rule here is *never infer a sound's role from its name — validate by
analysis*. It is a good rule, learned the hard way. But applied without thinking it
becomes *the measurement always wins*, and that is a different claim, and it is false.

Measurement said MAREA's ALL DRUMS stem was **bass** — lf_frac 0.86, centroid 216 Hz,
identical to a real bass stem on those features, because a modern kick carries most of
a drum mix's energy. It said the sparse CLAP stem was **tonal**. It said the rhythmic
KEYS2 was **drums** at 5.1 onsets/s. Trust it blindly and three stems go into the wrong
kit, silently, with a confident measurement behind each one.

The resolution is not to pick a side but to notice they are decisive about *different
things*. Presence is measured — a stem under −40 dBFS peak has nothing in it, full
stop. Bass is measured — "all energy under 150 Hz, nothing above 2 kHz" is unambiguous.
Everything else keeps the producer's label, because the cheap features genuinely cannot
separate a rhythmic rhodes from a drum bus. Disagreements get reported, not resolved.
The rule was never "don't trust the name"; it was "don't *infer* from the name" — and a
human's statement about their own stem is evidence, not inference.

**And the lens has to fit the thing.** Usability was gated on overall RMS. ANGIE's
HIHATS stem reads −48.8 dBFS RMS and got binned as near-silent — but it peaks at −14.7
and is a perfectly good hi-hat stem. RMS on sparse percussion measures how much silence
it contains. Worse, the probe sampled evenly-spaced windows to save time and landed in
the gaps: it read that stem at −60.9 / 1.4 % active where the full file says −48.8 /
9.7 %, and missed PAD STUFF's material entirely, 0.0 % against 6.9 %. Look in the wrong
place with the wrong instrument and you will get a number, and it will be wrong twice.

**Bonus, same shape:** onset *count* is not a percussiveness measure. librosa's
peak-picker finds peaks in a flat envelope — a steady 50 Hz sine yields 84 onsets in
six seconds. HPSS was the obvious fix and is worse: it hears a modern kick as harmonic
and calls a drum bus 9 % percussive.

**And check the instrument before you accuse the rig.** The first measurement of "do
loops start on their downbeat" said 17 %, which looked like a broken feature. It scored
16 slots with no full-band term while the rotator scores `bars×4` slots *with* one — the
disagreement was mine. Re-measured with the rotator's own scorer, factored out so the two
cannot drift apart, the real number was 48 % best-slot with a median miss of 0.032: mostly
near-ties, but a genuine 17 % starting on a weak beat. The bug was real *and* the first
number was wrong, which is the annoying case.

## Hameçon (hook)

"The machine told me the drum stem was a bass. It was right about every number and
wrong about the sound." — three ways to measure a stem and be confidently mistaken,
and the one rule that sorts them out.

## Sillage (what it unlocks)

Foundry #20 lands: any folder of already-separated stems is now a batch away from being
a playable kit. Next: the 13 acapellas (chops mode, already 44.1 k), and the same driver
pointed at the 27 demucs catches sitting in `~/Downloads/separated`. `kitcheck`'s dead-bar
and periodicity tests are general — they belong in the tierlist pass over the whole
Samples corpus, where "grades S, useless" is presumably not rare.
