export interface VoiceParticipant { userId: string; streamId?: string; } export interface VoiceRoute { fromUserId: string; channelId: string; gain: number; } /** * Signaling seam for voice: who is in a channel and which media stream * descriptor they published. The media plane (WebRTC, SFU, or anything else * that moves audio bytes) stays behind this seam, host-supplied — the engine * never touches it. subscribers delivers the channel roster on every change, * starting with the current roster. */ export interface VoiceTransport { join(channelId: string, streamId?: string): Promise; leave(channelId: string): Promise; publish(channelId: string, streamId: string): Promise; subscribers(channelId: string, onChange: (participants: readonly VoiceParticipant[]) => void): () => void; } export declare function createLocalVoiceTransport(options?: { userId?: string; }): { transport: VoiceTransport; participants(channelId: string): readonly VoiceParticipant[]; }; export type PushToTalkMode = "hold" | "toggle" | "openMic"; export type PushToTalkStatus = "idle" | "keyed" | "open"; export interface PushToTalk { mode(): PushToTalkMode; setMode(mode: PushToTalkMode): void; keyDown(): void; keyUp(): void; muted(): boolean; setMuted(muted: boolean): void; status(): PushToTalkStatus; transmitting(): boolean; } export declare function createPushToTalk(config?: { mode?: PushToTalkMode; onChange?: (transmitting: boolean) => void; }): PushToTalk;