opencode/packages/voice/test/audio-session.test.ts
2026-07-28 21:00:35 -04:00

106 lines
2.9 KiB
TypeScript

import { expect, test } from "bun:test"
import type { AudioDevice } from "../src/audio-device"
import { createAudioSession, type AudioEvent } from "../src/audio-session"
test("reports muted notification playback as inaudible", async () => {
const device = scriptedDevice()
const audio = await createAudioSession({
duplex: false,
speakers: false,
inputActivity: "server",
debug: false,
device,
})
expect(audio.toggleSpeaker()).toBe(true)
audio.play(Buffer.alloc(4_800))
expect(await audio.finishPlayback()).toBe("inaudible")
expect(device.writes).toEqual([])
})
test("settles active playback when interrupted", async () => {
const device = scriptedDevice()
const audio = await createAudioSession({
duplex: false,
speakers: false,
inputActivity: "server",
debug: false,
device,
})
audio.play(Buffer.alloc(4_800))
const outcome = audio.finishPlayback()
audio.flushPlayback()
expect(await outcome).toBe("interrupted")
expect(device.flushes).toBe(1)
})
test("emits captured audio and local speech observations", async () => {
const device = scriptedDevice()
const events: AudioEvent[] = []
const audio = await createAudioSession({
duplex: true,
speakers: false,
inputActivity: "local",
debug: false,
device,
})
await audio.start((event) => events.push(event))
const input = Buffer.alloc(4_800)
for (let offset = 0; offset < input.length; offset += 2) input.writeInt16LE(12_000, offset)
device.capture(input)
expect(events).toContainEqual({ type: "input", audio: input, level: expect.any(Number) })
expect(events).toContainEqual({ type: "user.started" })
audio.close()
})
test("preserves quiet duplex microphone audio during assistant playback", async () => {
const device = scriptedDevice()
const events: AudioEvent[] = []
const audio = await createAudioSession({
duplex: false,
speakers: true,
inputActivity: "server",
debug: false,
device,
})
await audio.start((event) => events.push(event))
audio.play(Buffer.alloc(24_000))
const input = Buffer.alloc(4_800)
for (let offset = 0; offset < input.length; offset += 2) input.writeInt16LE(800, offset)
device.capture(input)
expect(audio.fullDuplex).toBe(true)
expect(events).toContainEqual({ type: "input", audio: input, level: expect.any(Number) })
audio.close()
})
function scriptedDevice() {
let onInput: ((audio: Buffer) => void) | undefined
const writes: Buffer[] = []
const device = {
fullDuplex: true,
mode: "scripted",
async start(listener) {
onInput = listener
},
write(audio) {
writes.push(audio)
},
flush() {
device.flushes += 1
},
close() {},
writes,
flushes: 0,
capture(audio: Buffer) {
onInput?.(audio)
},
} satisfies AudioDevice & {
readonly writes: Buffer[]
flushes: number
capture(audio: Buffer): void
}
return device
}