- Return an unchanged copy for a non-finite/zero grid (NaN downbeat used to
delete every guitar note).
- Skip an added note when a same-pitch note (bar, neighbouring bars or already
added) lies within tol; avoids near-duplicates across bar lines.
- bench run/real: --no-repeat-vote passes { repeatVote: false } to the pipeline.
- Tests: notes before a late downbeat, bpm 0 / NaN / Infinity, trailing partial
bar, half-tempo grid, neighbour-bar duplicate, pipeline-level on/off.
Eval unchanged: synthetic t1a 0.885 t1b 0.840 t1c 0.752 t1d 0.737, lnoe 0.507.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
64 lines
2.8 KiB
TypeScript
64 lines
2.8 KiB
TypeScript
import { runBench } from "./runner.ts";
|
|
import { runReal } from "./real.ts";
|
|
import { buildSong } from "./dataset.ts";
|
|
import type { TierId } from "../gen/tiers.ts";
|
|
import { TIERS } from "../gen/tiers.ts";
|
|
|
|
function parseSeeds(s: string): number[] {
|
|
const out: number[] = [];
|
|
for (const part of s.split(",")) {
|
|
const m = /^(\d+)-(\d+)$/.exec(part);
|
|
if (m) for (let i = Number(m[1]); i <= Number(m[2]); i++) out.push(i);
|
|
else if (/^\d+$/.test(part)) out.push(Number(part));
|
|
else throw new Error(`bad seeds: ${s}`);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function arg(args: string[], name: string, def: string): string {
|
|
const i = args.indexOf(`--${name}`);
|
|
if (i < 0) return def;
|
|
const v = args[i + 1];
|
|
if (v === undefined || v.startsWith("--")) throw new Error(`missing value for --${name}`);
|
|
return v;
|
|
}
|
|
|
|
async function main() {
|
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
switch (cmd) {
|
|
case "gen": {
|
|
const tiers = arg(rest, "tiers", "t1a,t1b,t1c,t1d").split(",") as TierId[];
|
|
for (const t of tiers) if (!Object.hasOwn(TIERS, t)) throw new Error(`unknown tier: ${t}`);
|
|
const seeds = parseSeeds(arg(rest, "seeds", "1-5"));
|
|
const root = arg(rest, "root", "data");
|
|
for (const t of tiers) for (const s of seeds) {
|
|
const t0 = performance.now();
|
|
const dir = await buildSong(t, s, root);
|
|
console.log(`${dir} (${((performance.now() - t0) / 1000).toFixed(1)}s)`);
|
|
}
|
|
break;
|
|
}
|
|
case "run": {
|
|
const tiers = arg(rest, "tiers", "t1a,t1b,t1c,t1d").split(",") as TierId[];
|
|
for (const t of tiers) if (!Object.hasOwn(TIERS, t)) throw new Error(`unknown tier: ${t}`);
|
|
const seeds = parseSeeds(arg(rest, "seeds", "1-5"));
|
|
const modes = arg(rest, "modes", "full,oracle-stems,oracle-tempo,oracle-notes").split(",");
|
|
const runId = arg(rest, "run-id", new Date().toISOString().replace(/[:.]/g, "-"));
|
|
await runBench({ tiers, seeds, modes, runId, root: arg(rest, "root", "data"), out: arg(rest, "out", "runs"), repeatVote: !rest.includes("--no-repeat-vote") });
|
|
console.log(`results/${runId}.md`);
|
|
break;
|
|
}
|
|
case "real": {
|
|
const runId = arg(rest, "run-id", new Date().toISOString().replace(/[:.]/g, "-"));
|
|
await runReal({ runId, auto: rest.includes("--auto"), root: arg(rest, "root", "reference"), out: arg(rest, "out", "runs"), repeatVote: !rest.includes("--no-repeat-vote") });
|
|
console.log(`results/real-${runId}.md`);
|
|
break;
|
|
}
|
|
default:
|
|
console.error("usage: bun run bench gen [--tiers t1a,t1b] [--seeds 1-5] [--root data]\n bun run bench run [--tiers ..] [--seeds ..] [--modes full,oracle-stems,oracle-tempo,oracle-notes] [--run-id id] [--root data] [--out runs] [--no-repeat-vote]\n bun run bench real [--auto] [--run-id id] [--root reference] [--out runs] [--no-repeat-vote]");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
await main();
|