Commit fd1fae82 by PLN (Algolia)

feat(foundry): sampling-classics demo sources for loop-quality iteration

Add a curated list of 10 sampling classics as built-in demo sources — fuel for
demoing the Foundry, precomputing a fixed validation corpus, and iterating on
loop/chop quality (#19 autotune input).

WHY: we need a stable, pedagogically-diverse set to validate the finder across
material types instead of ad-hoc URLs. The set spans the axes that stress the
finder differently: canonical drum BREAKS (Amen, Funky Drummer, Apache — the
gold standards; if the rubric can't nail these it's wrong), a BASS-defining
groove (Chic – Good Times), VOCAL/no-drums (Loituma), ORCHESTRAL/no-drums edge
case (Mozart 40 — exercises the no-drums grid fallback), a full clean POP mix
(Rickroll), hip-hop sample-collage (Grandmaster Flash Wheels of Steel, Humpty
Dance), and a drum+vocal-stab combo (Lyn Collins – Think).

WHAT:
- demos.json — authored data (slug/title/year/source/why/expect/tags). Each
  entry flags drums-presence because the finder's grid-from-drums path depends
  on it (no-drums → bass/first-stem fallback, per the recall notes).
- engine/demos.py — loader (load / by_slug / source_of).
- foundry.py "demos" command — list, or precompute via --catch <slug> / --all
  (+ --sep to separate). This batch path is exactly what #19 autotune consumes.
- server.py — GET /api/demos; /api/fetch now accepts "ytsearch1:" queries too.
- ui/index.html — a 'sampling classics' quick-pick strip (click → fills the URL,
  tooltip shows why/expect).

HONESTY ON LINKS: only Loituma (from the repo's own test fixture) and Rickroll
(universally known id) ship as verified watch URLs (✓). The rest use
"ytsearch1:" queries (≈) that resolve to the top hit at fetch time — so we never
ship a guessed video id that silently breaks. yt-dlp accepts both forms.

VALIDATION: foundry demos lists 10 (✓/≈ marked); /api/demos serves the JSON;
UI strip renders; loader resolves source_of('amen-break').
parent 394bec04
{
"_comment": "Sampling-classics demo sources for the Foundry — fuel for demo, precompute, and loop/chop-quality iteration (#19 autotune). 'source' is whatever yt-dlp accepts: a verified watch URL where we're certain of the id, else an `ytsearch1:` query that resolves to the top hit at fetch time (so we never ship a guessed/broken video id). Each entry notes drums-presence because the finder's grid-from-drums path depends on it (no-drums sources fall back to bass/first stem — see project_foundry recall notes).",
"demos": [
{
"slug": "loituma",
"title": "Loituma — Ievan Polkka",
"year": 1995,
"source": "https://www.youtube.com/watch?v=hqthspSKZV8",
"verified": true,
"why": "The 'Leekspin' meme source; rapid Finnish a-cappella-ish vocal over accordion.",
"expect": "No drums. Voice + accordion. Tests VOCAL chop mode + no-drums grid fallback.",
"tags": ["vocal", "no-drums", "chops", "fast"]
},
{
"slug": "rickroll",
"title": "Rick Astley — Never Gonna Give You Up",
"year": 1987,
"source": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"verified": true,
"why": "The universal demo track; clean, well-produced 80s pop.",
"expect": "Full 4-stem mix (drums/bass/vocals/synth). Tests clean separation + bar-aligned loops on a famous reference.",
"tags": ["full-mix", "pop", "loops", "4-stem"]
},
{
"slug": "amen-break",
"title": "The Winstons — Amen, Brother",
"year": 1969,
"source": "ytsearch1:The Winstons Amen Brother original",
"verified": false,
"why": "THE Amen break — the most-sampled drum break in history (jungle/DnB/hip-hop).",
"expect": "Drum break ~7s in. The gold standard for drum loop/chop detection — if the finder can't nail this, the rubric is wrong.",
"tags": ["break", "drums", "loops", "chops", "ground-truth"]
},
{
"slug": "funky-drummer",
"title": "James Brown — Funky Drummer",
"year": 1970,
"source": "ytsearch1:James Brown Funky Drummer full",
"verified": false,
"why": "Clyde Stubblefield's break — among the most-sampled grooves ever.",
"expect": "Funk band; isolated drum break mid-track. Tests groove-loop detection + drums grid on a live-feel tempo.",
"tags": ["break", "drums", "funk", "loops"]
},
{
"slug": "think-break",
"title": "Lyn Collins — Think (About It)",
"year": 1972,
"source": "ytsearch1:Lyn Collins Think About It",
"verified": false,
"why": "The 'Think' break + the ubiquitous 'woo!/yeah!' vocal stabs (It Takes Two, etc.).",
"expect": "Both a drum break AND short vocal one-shots. Tests drum loops + vocal CHOP/one-shot detection in one source.",
"tags": ["break", "drums", "vocal", "chops", "one-shots"]
},
{
"slug": "grandmaster-flash-wheels",
"title": "Grandmaster Flash — The Adventures of Grandmaster Flash on the Wheels of Steel",
"year": 1981,
"source": "ytsearch1:Grandmaster Flash Adventures on the Wheels of Steel",
"verified": false,
"why": "Pioneering turntablist collage — many sources cut/scratched together.",
"expect": "Messy multi-source montage. Stress-tests separation + finder on non-stationary, collaged material.",
"tags": ["collage", "turntablism", "hard", "loops"]
},
{
"slug": "mozart-sym40",
"title": "Mozart — Symphony No. 40 in G minor, K.550 (1st mvt)",
"year": 1788,
"source": "ytsearch1:Mozart Symphony No 40 G minor first movement",
"verified": false,
"why": "A clean classical reference — the 'sampled Mozart' archetype for melodic material.",
"expect": "NO drums, NO bass-as-808; all 'other' (strings/winds). Tests separation on orchestral + phrase-length loops with no rhythmic grid.",
"tags": ["classical", "no-drums", "melodic", "loops", "edge-case"]
},
{
"slug": "humpty-dance",
"title": "Digital Underground — The Humpty Dance",
"year": 1990,
"source": "ytsearch1:Digital Underground The Humpty Dance",
"verified": false,
"why": "Hip-hop classic built FROM samples (Parliament/Sly) and itself widely sampled.",
"expect": "Strong drums + bass + vocal. Tests bass/drum loop extraction; pairs with the existing 'humpty' GT in the sample corpus.",
"tags": ["hip-hop", "drums", "bass", "vocal", "loops"]
},
{
"slug": "good-times",
"title": "Chic — Good Times",
"year": 1979,
"source": "ytsearch1:Chic Good Times 1979",
"verified": false,
"why": "Bernard Edwards' bassline — the foundation of Rapper's Delight and countless others.",
"expect": "Iconic disco bassline + tight drums. Tests BASS loop extraction quality specifically.",
"tags": ["disco", "bass", "drums", "loops", "groove"]
},
{
"slug": "apache",
"title": "Incredible Bongo Band — Apache",
"year": 1973,
"source": "ytsearch1:Incredible Bongo Band Apache",
"verified": false,
"why": "'The national anthem of hip-hop' — the Apache break (bongos + drums).",
"expect": "Percussion-rich break. Tests percussion/break loop detection + onset chop mode on busy drums.",
"tags": ["break", "drums", "percussion", "loops", "chops"]
}
]
}
"""Demo sources — a curated list of sampling classics (demos.json).
Fuel for demoing the Foundry, precomputing a fixed corpus, and iterating on
loop/chop quality (#19 autotune). The data is authored in demos.json (so it's
editable without touching code); this module just loads + resolves it.
"""
import json
from pathlib import Path
_PATH = Path(__file__).resolve().parent.parent / "demos.json"
def load() -> list[dict]:
"""All demo entries (order preserved)."""
return json.loads(_PATH.read_text())["demos"]
def by_slug(slug: str) -> dict | None:
return next((d for d in load() if d["slug"] == slug), None)
def source_of(slug: str) -> str:
"""The yt-dlp input (verified URL or `ytsearch1:` query) for a demo slug."""
d = by_slug(slug)
if not d:
raise KeyError(f"no demo {slug!r} (try: {', '.join(x['slug'] for x in load())})")
return d["source"]
...@@ -94,6 +94,31 @@ def cmd_link(a): ...@@ -94,6 +94,31 @@ def cmd_link(a):
raise SystemExit(f"⚠ {r.link} is a real directory, not a symlink — left untouched") raise SystemExit(f"⚠ {r.link} is a real directory, not a symlink — left untouched")
def cmd_demos(a):
"""List the sampling-classic demos, or precompute them (fetch [+ separate])."""
from engine import demos as D
items = D.load()
if not (a.catch or a.all):
for d in items:
v = "✓" if d.get("verified") else "≈" # ≈ = ytsearch-resolved
print(f"{v} {d['slug']:24.24} {d['title']:46.46} [{','.join(d['tags'])}]")
print(f"\n{len(items)} demos · `foundry demos --catch <slug>` or `--all` to precompute"
f" (add --sep to separate too). ✓ verified url · ≈ ytsearch top-hit")
return
ws = a.workspace or F.default_workspace()
targets = items if a.all else [D.by_slug(a.catch)]
if targets == [None]:
raise SystemExit(f"no demo {a.catch!r} — run `foundry demos` to list")
for d in targets:
print(f"\n▶ {d['slug']} — {d['title']}")
catch = F.fetch(d["source"], ws)
if a.sep:
S.separate(catch, ws, backend=a.backend, model=a.model, shifts=a.shifts,
jobs=a.jobs, device=a.device,
progress=lambda p: print(f"\r {a.backend}: {p:3d}%", end="", flush=True))
print()
def cmd_serve(a): def cmd_serve(a):
import server import server
server.run(workspace=a.workspace or F.default_workspace(), port=a.port) server.run(workspace=a.workspace or F.default_workspace(), port=a.port)
...@@ -119,6 +144,11 @@ def main(argv=None): ...@@ -119,6 +144,11 @@ def main(argv=None):
sub.add_parser("list").set_defaults(fn=cmd_list) sub.add_parser("list").set_defaults(fn=cmd_list)
sub.add_parser("backends").set_defaults(fn=cmd_backends) sub.add_parser("backends").set_defaults(fn=cmd_backends)
sp = sub.add_parser("link"); sp.add_argument("kit"); sp.set_defaults(fn=cmd_link) sp = sub.add_parser("link"); sp.add_argument("kit"); sp.set_defaults(fn=cmd_link)
sp = sub.add_parser("demos", help="list/precompute sampling-classic demo sources")
sp.add_argument("--catch", metavar="SLUG", help="fetch one demo by slug")
sp.add_argument("--all", action="store_true", help="fetch every demo")
sp.add_argument("--sep", action="store_true", help="also separate (with --backend etc.)")
add_sep_opts(sp); sp.set_defaults(fn=cmd_demos)
sp = sub.add_parser("serve"); sp.add_argument("--port", type=int, default=8765); sp.set_defaults(fn=cmd_serve) sp = sub.add_parser("serve"); sp.add_argument("--port", type=int, default=8765); sp.set_defaults(fn=cmd_serve)
a = p.parse_args(argv) a = p.parse_args(argv)
......
...@@ -107,8 +107,8 @@ class Handler(SimpleHTTPRequestHandler): ...@@ -107,8 +107,8 @@ class Handler(SimpleHTTPRequestHandler):
body = json.loads(self.rfile.read(n) or b"{}") if n else {} body = json.loads(self.rfile.read(n) or b"{}") if n else {}
if p == "/api/fetch": if p == "/api/fetch":
url = (body.get("url") or "").strip() url = (body.get("url") or "").strip()
if "://" not in url: if "://" not in url and not url.startswith("ytsearch"):
return self._json({"error": "need a url"}, 400) return self._json({"error": "need a url or ytsearch query"}, 400)
job = _job("fetch", url=url) job = _job("fetch", url=url)
threading.Thread(target=_run_fetch, args=(job, self.workspace, url), threading.Thread(target=_run_fetch, args=(job, self.workspace, url),
daemon=True).start() daemon=True).start()
...@@ -163,6 +163,9 @@ class Handler(SimpleHTTPRequestHandler): ...@@ -163,6 +163,9 @@ class Handler(SimpleHTTPRequestHandler):
out.append({"name": name, "available": ok, "hint": hint, out.append({"name": name, "available": ok, "hint": hint,
"default_model": be.default_model, "models": be.models}) "default_model": be.default_model, "models": be.models})
return self._json(out) return self._json(out)
if p == "/api/demos":
from engine import demos as D
return self._json(D.load())
if p == "/api/catches": if p == "/api/catches":
return self._json([self._catch_dict(c) for c in self._catches()]) return self._json([self._catch_dict(c) for c in self._catches()])
m = re.match(r"/api/job/([a-f0-9]+)$", p) m = re.match(r"/api/job/([a-f0-9]+)$", p)
......
...@@ -118,6 +118,14 @@ ...@@ -118,6 +118,14 @@
autocomplete="off"> autocomplete="off">
<button id="castBtn">Cast ⚓</button> <button id="castBtn">Cast ⚓</button>
</div> </div>
<div class="demos" id="demos"></div>
<style>
.demos{display:flex;flex-wrap:wrap;gap:.3rem;align-items:center;margin:.1rem 0 .5rem}
.demoLabel{opacity:.55;font-size:.82em;margin-right:.15rem}
.demo{font-size:.8em;padding:.12rem .5rem;border-radius:1rem;cursor:pointer;
border:1px solid #3a3a4a;background:transparent;color:inherit;opacity:.85}
.demo:hover{opacity:1;border-color:#c0f}
</style>
<div class="opts"> <div class="opts">
<span>engine</span> <span>engine</span>
<select id="backend"></select> <select id="backend"></select>
...@@ -366,10 +374,26 @@ async function separate(slug, btn){ ...@@ -366,10 +374,26 @@ async function separate(slug, btn){
poll(j.id, ()=>render()); poll(j.id, ()=>render());
} }
async function loadDemos(){
const el=$("#demos"); if(!el) return;
let ds; try{ ds=await api("/api/demos"); }catch(e){ return; }
if(!Array.isArray(ds)||!ds.length) return;
el.innerHTML=`<span class="demoLabel">sampling classics:</span>`+ds.map((d,i)=>{
const tip=`${d.why||''} · → ${d.expect||''}`.replace(/"/g,'&quot;');
const name=(d.title||d.slug).split(' — ')[0];
return `<button class="demo" data-i="${i}" title="${tip}">${name} ${d.verified?'✓':'≈'}</button>`;
}).join('');
el.querySelectorAll(".demo").forEach(b=>b.onclick=()=>{
const d=ds[b.dataset.i]; $("#url").value=d.source;
toast(`loaded ${d.slug} — Cast ⚓ to fetch`); $("#url").focus();
});
}
$("#castBtn").onclick=cast; $("#castBtn").onclick=cast;
$("#url").addEventListener("keydown",e=>{if(e.key==="Enter")cast();}); $("#url").addEventListener("keydown",e=>{if(e.key==="Enter")cast();});
$("#refresh").onclick=render; $("#refresh").onclick=render;
loadBackends().then(render); loadBackends().then(render);
loadDemos();
</script> </script>
</body> </body>
</html> </html>
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