gabrielpamplonapg
Real-sample drum reference bank + active-window MERT + per-stem family filter
437a99f | """validate_real_drum_reference.py — Held-out validation of drum-machine gear | |
| ID against the MERT reference bank. | |
| Held-out axes vs the reference recipes (build_real_drum_reference.py): | |
| - one-shot variants: "held" half (odd-indexed files) where pools allow | |
| - seeds: 7/8/9 (reference used 1/2) | |
| - same styles (four_floor/electro/breaks) at rng-drawn BPMs | |
| Reports per-machine top-1 model accuracy, top-1 family accuracy, and a | |
| confusion matrix. Also runs a harder single-hit test (isolated kick/snare/hat). | |
| Usage: python3 tools/validate_real_drum_reference.py | |
| """ | |
| import logging | |
| import os | |
| import sys | |
| import tempfile | |
| import numpy as np | |
| import soundfile as sf | |
| logging.basicConfig(level=logging.ERROR) | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from backend.mert_embeddings import MERTExtractor, get_reference_db | |
| from tools.real_kits import (SR, discover_kits, render_real_loop, | |
| render_solo_kit, pool_overlap, _pool, _load) | |
| from tools.fetch_real_samples import DISPLAY_NAMES | |
| LOOP_CASES = [ # (style, seed) | |
| ("four_floor", 7), ("four_floor", 8), | |
| ("electro", 7), ("breaks", 7), | |
| ] | |
| SINGLE_HIT_VOICES = ("kick", "snare", "hat_closed") | |
| def _embed(extractor, y): | |
| with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tf: | |
| path = tf.name | |
| try: | |
| sf.write(path, y, SR) | |
| return extractor.extract(path) | |
| finally: | |
| os.unlink(path) | |
| def main(): | |
| kits = discover_kits() | |
| db = get_reference_db() | |
| machines = {e["model"] for e in db.entries if e["family"] == "drum_machine"} | |
| print(f"Reference DB: {len(db.entries)} entries, {len(machines)} drum machines\n") | |
| extractor = MERTExtractor() | |
| confusion: dict = {} | |
| loop_hits = loop_total = fam_hits = 0 | |
| print("── Loop test (held-out variants, unseen seeds/BPMs) ──") | |
| for slug, kit in kits.items(): | |
| truth = DISPLAY_NAMES.get(slug, slug) | |
| overlap = " (single-variant pools: ref/held overlap)" if pool_overlap(kit) else "" | |
| row_hits = 0 | |
| for style, seed in LOOP_CASES: | |
| y = render_real_loop(kit, style=style, seed=seed, variant_half="held") | |
| matches = db.find_nearest(_embed(extractor, y), top_n=3) | |
| top_model, top_family, top_sim = matches[0] | |
| loop_total += 1 | |
| fam_hits += int(top_family == "drum_machine") | |
| ok = top_model == truth | |
| row_hits += int(ok) | |
| loop_hits += int(ok) | |
| confusion.setdefault(truth, {}).setdefault(top_model, 0) | |
| confusion[truth][top_model] += 1 | |
| if not ok: | |
| print(f" ✗ {truth} [{style} s{seed}] -> {top_model} ({top_sim:.0%})") | |
| print(f" {truth:<28} {row_hits}/{len(LOOP_CASES)}{overlap}") | |
| print(f"\nLoop top-1 model: {loop_hits}/{loop_total} = {loop_hits/loop_total:.0%}") | |
| print(f"Loop top-1 family: {fam_hits}/{loop_total} = {fam_hits/loop_total:.0%} (drum_machine)") | |
| print("\n── Confusion (rows=truth, only misses shown above) ──") | |
| for truth, row in confusion.items(): | |
| parts = ", ".join(f"{m}×{c}" for m, c in | |
| sorted(row.items(), key=lambda kv: -kv[1])) | |
| print(f" {truth}: {parts}") | |
| print("\n── Single-hit test (harder: one isolated held-out hit) ──") | |
| hit_ok = hit_fam = hit_total = 0 | |
| for slug, kit in kits.items(): | |
| truth = DISPLAY_NAMES.get(slug, slug) | |
| for voice in SINGLE_HIT_VOICES: | |
| if voice not in kit: | |
| continue | |
| pool = _pool(kit[voice], "held") | |
| y = _load(pool[0]) | |
| matches = db.find_nearest(_embed(extractor, y), top_n=3) | |
| top_model, top_family, _ = matches[0] | |
| hit_total += 1 | |
| hit_ok += int(top_model == truth) | |
| hit_fam += int(top_family == "drum_machine") | |
| print(f"Single-hit top-1 model: {hit_ok}/{hit_total} = {hit_ok/hit_total:.0%}") | |
| print(f"Single-hit top-1 family: {hit_fam}/{hit_total} = {hit_fam/hit_total:.0%}") | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |