chore: checkpoint 第一版
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-09-06 17:04:11 +08:00
parent a8cdec98d1
commit 0c2151ace9
659 changed files with 185855 additions and 554 deletions
@@ -0,0 +1,76 @@
const assert = require("node:assert/strict");
const test = require("node:test");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const source = fs.readFileSync(path.join(__dirname, "../public/pcm-playback.worklet.js"), "utf8");
function fixture({ outputRate = 48000, prebufferMs = 120 } = {}) {
const messages = [];
let Processor;
class AudioWorkletProcessor {
constructor() {
this.port = { onmessage: null, postMessage: (message) => messages.push(message) };
}
}
const context = vm.createContext({
AudioWorkletProcessor,
Float32Array,
ArrayBuffer,
Math,
sampleRate: outputRate,
registerProcessor: (name, implementation) => {
assert.equal(name, "pcm16-playback");
Processor = implementation;
},
});
vm.runInContext(source, context);
const processor = new Processor({ processorOptions: { sourceSampleRate: 16000, prebufferMs } });
const push = (values) => {
const samples = new Float32Array(values);
processor.port.onmessage({ data: { type: "push", samples: samples.buffer } });
};
const process = () => {
const output = new Float32Array(128);
assert.equal(processor.process([], [[output]]), true);
return output;
};
return { messages, processor, push, process };
}
test("waits for prebuffer then resamples continuous 16k PCM without fragment gaps", () => {
const f = fixture({ outputRate: 48000, prebufferMs: 10 });
f.push(new Array(120).fill(0.25));
assert.ok(f.process().every((sample) => sample === 0), "must remain silent below the prebuffer threshold");
f.push(new Array(400).fill(0.25));
const first = f.process();
const second = f.process();
assert.ok(first.some((sample) => sample > 0.05), "buffered playback should start with a short anti-pop fade");
assert.ok(second.every((sample) => sample > 0.12), "playback should remain continuous across source fragments");
assert.equal(f.messages.filter((message) => message.type === "underflow").length, 0);
});
test("plays 16k source at real-time duration instead of the former 1.5x speed", () => {
const f = fixture({ outputRate: 48000, prebufferMs: 1 });
f.push(new Array(1000).fill(0.25));
f.process();
assert.ok(Math.abs(f.processor.readPosition - 128 / 3) < 0.001, `consumed ${f.processor.readPosition} source samples`);
});
test("reports network starvation and re-enters prebuffering instead of scheduling tiny buffers", () => {
const f = fixture({ outputRate: 48000, prebufferMs: 0.01 });
f.push(new Array(8).fill(0.5));
f.process();
assert.equal(f.messages.filter((message) => message.type === "underflow").length, 1);
assert.ok(f.process().every((sample) => sample === 0), "starved playback should wait for a new prebuffer");
});
test("drains the final response cleanly and emits a completion signal", () => {
const f = fixture({ outputRate: 24000 });
f.push(new Array(64).fill(-0.25));
f.processor.port.onmessage({ data: { type: "end" } });
f.process();
assert.equal(f.messages.filter((message) => message.type === "drained").length, 1);
assert.equal(f.messages.filter((message) => message.type === "underflow").length, 0);
});