feat(voice): Implement real-time audio streaming with binary protocol support for companion client

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-08 21:29:50 -07:00
parent 5676f1fee3
commit abe3df7387
2 changed files with 8 additions and 6 deletions

View file

@ -52,7 +52,7 @@ export function decodeDownstreamFrame(buffer: ArrayBuffer): AudioDownstreamFrame
}
export function isAudioFrame(data: ArrayBuffer): boolean {
if (data.byteLength < 1) return false;
if (data.byteLength < 1) {return false;}
const view = new DataView(data);
return view.getUint8(0) === DOWNSTREAM_FRAME_TYPE;
}

View file

@ -1,7 +1,9 @@
import { io, Socket } from 'socket.io-client';
import type { VoiceEvent, AudioUpstreamFrame, AudioDownstreamFrame } from '../types/events';
import { io, type Socket } from 'socket.io-client';
import { encodeUpstreamFrame, decodeDownstreamFrame, isAudioFrame } from './binary-protocol';
import type { VoiceEvent, AudioUpstreamFrame, AudioDownstreamFrame } from '../types/events';
export interface VoiceClientConfig {
/** companion-api base URL, e.g. http://localhost:3850 */
baseUrl: string;
@ -43,7 +45,7 @@ export class VoiceClient {
}
connect(): void {
if (this.state === 'connecting' || this.state === 'connected') return;
if (this.state === 'connecting' || this.state === 'connected') {return;}
this.state = 'connecting';
this.openSocket();
}
@ -66,7 +68,7 @@ export class VoiceClient {
* Silently drops if not connected.
*/
sendAudio(frame: AudioUpstreamFrame): void {
if (this.state !== 'connected' || !this.socket) return;
if (this.state !== 'connected' || !this.socket) {return;}
const encoded = encodeUpstreamFrame(frame);
this.socket.emit('message', encoded);
}
@ -123,7 +125,7 @@ export class VoiceClient {
}
private handleBinary(buffer: ArrayBuffer): void {
if (!isAudioFrame(buffer)) return;
if (!isAudioFrame(buffer)) {return;}
try {
const frame = decodeDownstreamFrame(buffer);