Add the synthetic meeting fixture and long-form measurement script

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-29 22:28:48 +01:00
1 parent f55ea6ecf8
commit 8e0ecdaf85
3 files changed
+253

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
import { describe, expect, test } from "bun:test";
import { ALIEN_EXCERPTS, buildMeetingFixture } from "./meetingFixture";
describe("buildMeetingFixture (SYNTHETIC meeting stand-in)", () => {
test("joins en_aiproblem and the en_alien excerpts with pauses, deterministically", async () => {
const first = await buildMeetingFixture();
const second = await buildMeetingFixture();
const seconds = first.audio.length / 16_000;
const excerptSeconds = ALIEN_EXCERPTS.reduce((n, e) => n + e.end - e.start, 0);
// lead-in 2 + aiproblem ≈ 14.4 + excerpts + pauses 3+8+2+5
expect(seconds).toBeCloseTo(2 + 14.4 + excerptSeconds + 18, 0);
expect(first.audio).toEqual(second.audio);
expect(first.reference).toBe(second.reference);
expect(first.reference).toStartWith("The problem I find with AI");
expect(first.reference).toContain("Voices of the Void");
expect(first.reference).toEndWith("Maybe no.");
// Pauses are room tone, not digital silence, and never louder than −50 dBFS.
const leadIn = first.audio.subarray(0, 2 * 16_000);
expect(leadIn.some((v) => v !== 0)).toBe(true);
expect(Math.max(...leadIn.map(Math.abs))).toBeLessThan(0.003);
}, 60_000); // decodes the 15-min en_alien twice
});
+64
View File
@@ -0,0 +1,64 @@
/**
* SYNTHETIC meeting stand-in (spec 001 "Measurement fallback", coordination
* plan 001-0): no real meeting recording exists, so this joins the repo's
* own English clips with meeting-like pauses. Built at test time from
* tests/audio_files — nothing new is checked in. Every number measured on it
* is labelled synthetic.
*
* Layout (≈ 2 min 45 s): 2 s lead-in, en_aiproblem (whole), then three
* en_alien excerpts, each clip followed by a pause of 3, 8, 2 and 5 s. Pauses and lead-in carry
* deterministic room noise at about −60 dBFS (a real recording is never
* digital silence). Excerpts are cut on reference-line boundaries; their
* times were read once from a timestamped Large v3 Turbo transcript of
* en_alien (2026-10-29) and land in the pause before the line, so each
* excerpt's reference is exactly its lines of en_alien.txt.
*/
import { decodeClip, readReference } from "./clips";
const RATE = 16_000;
const LEAD_IN_SECONDS = 2;
const PAUSES_SECONDS = [3, 8, 2, 5];
const NOISE_AMPLITUDE = 0.001; // ≈ −60 dBFS
/** en_alien excerpts: [start s, end s) and the 1-based reference lines they contain. */
export const ALIEN_EXCERPTS: { start: number; end: number; firstLine: number; lastLine: number }[] = [
{ start: 0, end: 36.8, firstLine: 1, lastLine: 9 },
{ start: 36.8, end: 99.0, firstLine: 10, lastLine: 28 },
{ start: 165.4, end: 196.1, firstLine: 45, lastLine: 58 },
];
function roomNoise(seconds: number, seed: number): Float32Array {
let state = seed;
return new Float32Array(Math.round(seconds * RATE)).map(() => {
state = (state * 1_103_515_245 + 12_345) % 2 ** 31;
return NOISE_AMPLITUDE * (state / 2 ** 30 - 1);
});
}
export async function buildMeetingFixture(): Promise<{ audio: Float32Array; reference: string }> {
const aiproblem = await decodeClip("en_aiproblem.m4a");
const alien = await decodeClip("en_alien.opus");
const alienLines = (await readReference("en_alien")).split("\n");
const speech: Float32Array[] = [aiproblem];
const references: string[] = [(await readReference("en_aiproblem")).trim()];
for (const excerpt of ALIEN_EXCERPTS) {
speech.push(alien.subarray(Math.round(excerpt.start * RATE), Math.round(excerpt.end * RATE)));
references.push(alienLines.slice(excerpt.firstLine - 1, excerpt.lastLine).join(" ").trim());
}
const parts: Float32Array[] = [roomNoise(LEAD_IN_SECONDS, 1)];
// A pause after every clip — the last one is the meeting's trailing room tone.
speech.forEach((clip, i) => {
parts.push(clip);
parts.push(roomNoise(PAUSES_SECONDS[i % PAUSES_SECONDS.length]!, i + 2));
});
const audio = new Float32Array(parts.reduce((n, p) => n + p.length, 0));
let at = 0;
for (const part of parts) {
audio.set(part, at);
at += part.length;
}
return { audio, reference: references.join(" ") };
}