Files
STSheets/tests/pipeline/transcribe.test.ts
T
2026-10-30 03:22:14 +01:00

42 lines
1.8 KiB
TypeScript

import { test, expect, beforeAll } from "bun:test";
import { join } from "node:path";
import { buildSong, loadSongDir } from "../../src/bench/dataset.ts";
import { createTranscriber } from "../../src/pipeline/transcribe.ts";
import { matchNotes } from "../../src/eval/notes.ts";
import type { Audio, TimedNote } from "../../src/core/types.ts";
const MODEL = join(import.meta.dir, "../../models/basic-pitch/nmp.onnx");
const SLOW = 300_000;
let transcribe: Awaited<ReturnType<typeof createTranscriber>>;
beforeAll(async () => {
transcribe = await createTranscriber(new Uint8Array(await Bun.file(MODEL).arrayBuffer()));
}, SLOW);
test("oracle guitar stem of t1a/1 scores F1 > 0.5", async () => {
const dir = await buildSong("t1a", 1);
const d = await loadSongDir(dir);
const track = d.song.tracks.find((t) => t.role === "guitar")!;
const ref = d.truth.tracks.find((t) => t.name === track.name)!.notes;
const est = await transcribe(d.stems.get(track.name)!);
const m = matchNotes(ref, est);
console.log(`guitar F1=${m.f1} P=${m.precision} R=${m.recall} n_ref=${ref.length} n_est=${est.length}`);
expect(m.f1!).toBeGreaterThan(0.5);
}, SLOW);
test("monophonic mode yields non-overlapping notes on a vocals stem", async () => {
const dir = await buildSong("t1c", 1);
const d = await loadSongDir(dir);
const track = d.song.tracks.find((t) => t.role === "vocals")!;
const est: TimedNote[] = await transcribe(d.stems.get(track.name)!, { monophonic: true });
expect(est.length).toBeGreaterThan(0);
const s = [...est].sort((a, b) => a.onset - b.onset);
for (let i = 1; i < s.length; i++) expect(s[i]!.onset).toBeGreaterThanOrEqual(s[i - 1]!.offset - 1e-9);
}, SLOW);
test("silence returns no notes", async () => {
const a: Audio = { sampleRate: 44100, channels: [new Float32Array(88200)] };
expect(await transcribe(a)).toEqual([]);
});