























@@ -1,48 +1,59 @@
11import { Readable } from "node:stream";
22import { describe, expect, it, vi } from "vitest";
3-import { decodeOpusStream, decodeOpusStreamChunks, resolveOpusDecoderPreference } from "./audio.js";
3+import {
4+createDiscordOpusEncodeStream,
5+decodeOpusStream,
6+decodeOpusStreamChunks,
7+} from "./audio.js";
485-describe("discord voice opus decoder selection", () => {
6-it("defaults to the safe WASM opus decoder", async () => {
9+async function collectBuffers(stream: Readable): Promise<Buffer[]> {
10+const chunks: Buffer[] = [];
11+for await (const chunk of stream) {
12+chunks.push(Buffer.from(chunk as Buffer));
13+}
14+return chunks;
15+}
16+17+describe("discord voice opus codec", () => {
18+it("defaults to libopus-wasm for receive decoding", async () => {
719const verbose: string[] = [];
820const warnings: string[] = [];
9-const previousPreference = process.env.OPENCLAW_DISCORD_OPUS_DECODER;
10-delete process.env.OPENCLAW_DISCORD_OPUS_DECODER;
112112-try {
13-const decoded = await decodeOpusStream(Readable.from([]), {
14-onVerbose: (message) => verbose.push(message),
15-onWarn: (message) => warnings.push(message),
16-});
22+const decoded = await decodeOpusStream(Readable.from([]), {
23+onVerbose: (message) => verbose.push(message),
24+onWarn: (message) => warnings.push(message),
25+});
172618-expect(decoded.length).toBe(0);
19-expect(verbose).toContain("opus decoder: opus-decoder");
20-expect(warnings).toEqual([]);
21-} finally {
22-if (previousPreference === undefined) {
23-delete process.env.OPENCLAW_DISCORD_OPUS_DECODER;
24-} else {
25-process.env.OPENCLAW_DISCORD_OPUS_DECODER = previousPreference;
26-}
27-}
27+expect(decoded.length).toBe(0);
28+expect(verbose).toContain("opus decoder: libopus-wasm");
29+expect(warnings).toEqual([]);
2830});
293130-it("requires an explicit preference for native opus", () => {
31-const previousPreference = process.env.OPENCLAW_DISCORD_OPUS_DECODER;
32-delete process.env.OPENCLAW_DISCORD_OPUS_DECODER;
32+it("encodes raw Discord PCM into Opus packets for realtime playback", async () => {
33+const encoder = createDiscordOpusEncodeStream();
34+const packetsPromise = collectBuffers(encoder);
35+36+encoder.end(Buffer.alloc(960 * 2 * 2));
37+const packets = await packetsPromise;
38+39+expect(packets).toHaveLength(1);
40+expect(packets[0]?.length).toBeGreaterThan(0);
41+42+const decoded = await decodeOpusStream(Readable.from(packets), {
43+onVerbose: vi.fn(),
44+onWarn: vi.fn(),
45+});
46+expect(decoded.length).toBe(960 * 2 * 2);
47+});
48+49+it("pads final partial PCM frames before encoding", async () => {
50+const encoder = createDiscordOpusEncodeStream();
51+const packetsPromise = collectBuffers(encoder);
52+53+encoder.end(Buffer.alloc((960 * 2 * 2) / 2));
54+const packets = await packetsPromise;
335534-try {
35-expect(resolveOpusDecoderPreference()).toBe("wasm");
36-expect(resolveOpusDecoderPreference("opusscript")).toBe("opusscript");
37-expect(resolveOpusDecoderPreference("native")).toBe("native");
38-expect(resolveOpusDecoderPreference("@discordjs/opus")).toBe("native");
39-} finally {
40-if (previousPreference === undefined) {
41-delete process.env.OPENCLAW_DISCORD_OPUS_DECODER;
42-} else {
43-process.env.OPENCLAW_DISCORD_OPUS_DECODER = previousPreference;
44-}
45-}
56+expect(packets).toHaveLength(1);
4657});
47584859it("surfaces chunk decode stream failures to callers", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。