50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
/**
|
||
* Test-time access to the recordings in tests/audio_files: decode with
|
||
* ffmpeg-static (trusted devDependency) and the WER text normalization used
|
||
* by tests/live-real-model.test.ts.
|
||
*/
|
||
import { join } from "node:path";
|
||
import ffmpegPath from "ffmpeg-static";
|
||
|
||
export const AUDIO_DIR = join(import.meta.dir, "audio_files");
|
||
|
||
/** Decode `tests/audio_files/<name>` to mono float32 PCM at `rate` (16 kHz by default). */
|
||
export async function decodeClip(name: string, rate = 16_000): Promise<Float32Array> {
|
||
const path = join(AUDIO_DIR, name);
|
||
const proc = Bun.spawn(
|
||
[
|
||
ffmpegPath as unknown as string,
|
||
...["-hide_banner", "-loglevel", "error"],
|
||
...["-i", path],
|
||
...["-ac", "1", "-ar", String(rate), "-f", "f32le", "-"],
|
||
],
|
||
{ stdout: "pipe", stderr: "pipe" },
|
||
);
|
||
const [bytes, errors, exitCode] = await Promise.all([
|
||
new Response(proc.stdout).arrayBuffer(),
|
||
new Response(proc.stderr).text(),
|
||
proc.exited,
|
||
]);
|
||
if (exitCode !== 0) throw new Error(`ffmpeg failed on ${path}: ${errors}`);
|
||
return new Float32Array(bytes);
|
||
}
|
||
|
||
/** Reference transcript of `tests/audio_files/<base>.txt`. */
|
||
export function readReference(base: string): Promise<string> {
|
||
return Bun.file(join(AUDIO_DIR, `${base}.txt`)).text();
|
||
}
|
||
|
||
/**
|
||
* WER normalization: casing, punctuation, and apostrophes are not
|
||
* transcription accuracy; "Århus"/"Aarhus" unify (same as the real-model test).
|
||
*/
|
||
export function normalize(text: string): string {
|
||
return text
|
||
.toLowerCase()
|
||
.replaceAll("århus", "aarhus")
|
||
.replace(/['’]/g, "")
|
||
.replace(/[.,!?;:"“”‘‚«»()\-–—…]/g, " ")
|
||
.replace(/\s+/g, " ")
|
||
.trim();
|
||
}
|