feat: migra cliente web para áudio contínuo com VAD no backend

This commit is contained in:
alex alves
2026-11-03 18:34:45 -03:00
parent bdd2f6f67d
commit 7cbe86ed39
14 changed files with 834 additions and 1574 deletions

View File

@@ -0,0 +1,48 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";
import vm from "node:vm";
function processor() {
let Processor;
class Base {
constructor() {
this.port = { sent: [], postMessage(data) { this.sent.push(data); } };
}
}
vm.runInNewContext(readFileSync(new URL("../../src/voice_assistant/audio_gateway/web/pcm-worklet.js", import.meta.url), "utf8"), {
AudioWorkletProcessor: Base, sampleRate: 16000,
registerProcessor(name, constructor) { assert.equal(name, "pcm-input"); Processor = constructor; },
});
return new Processor();
}
test("negotiated capture emits complete little-endian PCM16 frames", () => {
const input = processor();
input.port.onmessage({ data: { type: "start", chunkSamples: 2 } });
input.process([[new Float32Array([-1, 0, 1])]]);
const chunk = input.port.sent[0];
assert.equal(chunk.type, "pcm");
assert.equal(chunk.buffer.byteLength, 4);
const samples = new DataView(chunk.buffer);
assert.equal(samples.getInt16(0, true), -32768);
assert.equal(samples.getInt16(2, true), 0);
});
test("capture continues across process callbacks until stop", () => {
const input = processor();
input.port.onmessage({ data: { type: "start", chunkSamples: 2 } });
input.process([[new Float32Array([0, 0, 0])]]);
input.process([[new Float32Array([0, 0])]]);
assert.equal(input.port.sent.filter((event) => event.type === "pcm").length, 2);
input.port.onmessage({ data: { type: "stop" } });
input.process([[new Float32Array([0, 0, 0])]]);
assert.equal(input.port.sent.length, 2);
});
test("invalid capture window reports an error", () => {
const input = processor();
input.port.onmessage({ data: { type: "start", chunkSamples: 0 } });
assert.equal(input.port.sent[0].type, "error");
assert.equal(input.capturing, false);
});