Skip to content
Source
Sign inSign up
Open History to see the last commit

audio.test.ts

92 lines3.5 KB

1import { expect, it } from "vite-plus/test";
2import { createSpeechAudio } from "./audio.ts";
3​
4const frame = 512;
5const make = (overrides = {}) =>
6 createSpeechAudio({
7 frameSamples: frame,
8 filterSilence: true,
9 nativeStreaming: false,
10 longForm: false,
11 maxAudioMs: 0,
12 ...overrides,
13 });
14const frames = (count: number) =>
15 Float32Array.from({ length: count * frame }, (_, i) => (Math.floor(i / frame) + 1) / 1000);
16const seconds = (n: number) => Math.floor((n * 16000) / frame);
17​
18it("retains pre-roll, onset and offline/streaming tails without duplicating edges", () => {
19 for (const nativeStreaming of [false, true]) {
20 const audio = make({ nativeStreaming });
21 const pcm = frames(100);
22 const chunks: Float32Array[] = [];
23 for (let i = 0; i < 100; i++) {
24 audio.append(pcm.subarray(i * frame, (i + 1) * frame));
25 audio.score(i, [i >= 20 && i < 24 ? 0.9 : 0]);
26 chunks.push(...audio.take());
27 }
28 chunks.push(...audio.take(true));
29 const retained = chunks.flatMap((chunk) => [...chunk]);
30 expect(retained).toEqual([
31 ...pcm.subarray(6 * frame, (24 + (nativeStreaming ? 52 : 15)) * frame),
32 ]);
33 }
34});
35​
36it("handles silence, short speech, a partial last frame and disabled filtering", () => {
37 const silence = make();
38 silence.append(new Float32Array(frame * 4 + 7));
39 silence.score(0, [0, 0, 0, 0, 0]);
40 expect(silence.take(true)).toEqual([]);
41 const short = make();
42 short.append(new Float32Array([0.2, 0.1]));
43 short.score(0, [0.8]);
44 expect([...short.take(true)[0]!]).toEqual([...new Float32Array([0.2, 0.1])]);
45 const raw = make({ filterSilence: false });
46 const pcm = frames(50).subarray(0, 50 * frame - 3);
47 raw.append(pcm);
48 raw.score(0, new Array(50).fill(0));
49 expect(raw.take(true)[0]).toEqual(pcm);
50});
51​
52it("plans at pauses while recording and preserves ordered audio and final tails", () => {
53 const audio = make({ filterSilence: false });
54 const pcm = frames(seconds(100));
55 const chunks: Float32Array[] = [];
56 for (let start = 0; start < pcm.length; start += frame * 7) {
57 const piece = pcm.subarray(start, start + frame * 7);
58 audio.append(piece);
59 audio.score(
60 start / frame,
61 Array.from({ length: piece.length / frame }, (_, i) => {
62 const t = ((start / frame + i) * frame) / 16000;
63 return (t >= 28 && t < 29) || (t >= 55 && t < 56) ? 0 : 1;
64 }),
65 );
66 chunks.push(...audio.take());
67 }
68 expect(chunks.length).toBeGreaterThan(0);
69 expect(chunks[0]!.length / 16000).toBeCloseTo(28.5, 1);
70 chunks.push(...audio.take(true));
71 expect(Float32Array.from(chunks.flatMap((chunk) => [...chunk]))).toEqual(pcm);
72 expect(chunks.every((chunk) => chunk.length <= 40 * 16000)).toBe(true);
73 expect(chunks.at(-1)!.length).toBeGreaterThanOrEqual(5 * 16000);
74});
75​
76it("respects smaller engine limits, hard cuts, and native/internal-long-form eligibility", () => {
77 for (const config of [{}, { maxAudioMs: 4000 }, { nativeStreaming: true }, { longForm: true }]) {
78 const audio = make({ filterSilence: false, ...config });
79 const pcm = frames(seconds(84));
80 audio.append(pcm);
81 audio.score(0, new Array(pcm.length / frame).fill(1));
82 const live = audio.take();
83 const chunks = [...live, ...audio.take(true)];
84 expect(chunks.reduce((n, chunk) => n + chunk.length, 0)).toBe(pcm.length);
85 if ("nativeStreaming" in config || "longForm" in config) expect(chunks).toHaveLength(1);
86 else
87 expect(
88 chunks.every((chunk) => chunk.length <= ("maxAudioMs" in config ? 32000 : 640000)),
89 ).toBe(true);
90 if ("longForm" in config) expect(live).toEqual([]);
91 }
92});