import { Avatar } from './Avatar'; import { ConnectionState, AvatarError, DrivingServiceMode, ConversationState, PostProcessingConfig } from '../types'; import { FrameRateInfo } from '../performance/FrameRateMonitor'; export declare class AvatarController { private networkLayer?; private readonly playbackMode; private isStartingPlayback; private currentConversationId; private reqEnd; onConnectionState: ((state: ConnectionState) => void) | null; onConversationState: ((state: ConversationState) => void) | null; onError: ((error: AvatarError) => void) | null; private eventListeners; private readonly frameRateMonitor; /** Frame rate monitoring callback. Fires with aggregated metrics from a 2-second sliding window. */ get onFrameRateInfo(): ((info: FrameRateInfo) => void) | null; set onFrameRateInfo(value: ((info: FrameRateInfo) => void) | null); /** Whether frame rate monitoring is enabled. Default is false (zero overhead when disabled). */ get frameRateMonitorEnabled(): boolean; set frameRateMonitorEnabled(value: boolean); private renderCallback?; private characterHandle; private characterId; private postProcessingConfig; private playbackLoopId; private playbackLoopGeneration; private lastRenderedFrameIndex; private keyframesOffset; private readonly MAX_KEYFRAMES; private readonly KEYFRAMES_CLEANUP_THRESHOLD; private lastSyncLogTime; private lastOutOfBoundsState; private isFallbackMode; private frameStarvationEvents; private isFrameStarved; private playbackStuckCheckState; private readonly MAX_AUDIO_TIME_ZERO_COUNT; private readonly MAX_AUDIO_TIME_STUCK_COUNT; private readonly AUDIO_TIME_STUCK_THRESHOLD; private hostModeMetrics; private readonly audioBytesPerSecond; constructor(avatar: Avatar, options?: { playbackMode?: DrivingServiceMode; }); private handleVisibilityChange; private shouldReportPlaybackStats; private _getDeviceScoreProps; /** * Get current conversation ID * Returns the current conversation ID for the active audio session * @returns Current conversation ID, or null if no active session */ getCurrentConversationId(): string | null; /** * Initialize audio context (must be called in user gesture context) * * This method must be called before any audio operations (send, yieldAudioData, etc.) * to ensure AudioContext is created and initialized in a user gesture context. * * @example * // In user click handler * button.addEventListener('click', async () => { * await avatarView.controller.initializeAudioContext() * // Now you can safely use send() or yieldAudioData() * }) */ initializeAudioContext(): Promise; /** * Start service (SDK mode only) */ start(): Promise; /** * Send audio to server (SDK mode only) * Also cache to data layer for playback * @returns conversationId - Conversation ID for this audio session */ send(audioData: ArrayBuffer, end?: boolean): string | null; /** * Close service (SDK mode only) */ close(): void; /** * Send audio data (host mode) * Stream additional audio data after playback() * @returns conversationId - Conversation ID for this audio session */ yieldAudioData(data: Uint8Array, isLast?: boolean): string | null; /** * Send animation keyframes (host mode or SDK mode) * Stream additional animation data after playback() * * Public API: accepts binary data array (protobuf encoded Message array) * @param keyframesDataArray - Animation keyframes binary data array (each element is a protobuf encoded Message) or empty array to trigger audio-only mode * @param conversationId - Conversation ID (required). If conversationId doesn't match current conversationId, keyframes will be discarded. * Use getCurrentConversationId() to get the current conversationId. * @returns `true` if the server has sent all animation data for this conversation (end signal received), `false` otherwise. */ yieldFramesData(keyframesDataArray: (Uint8Array | ArrayBuffer)[], conversationId: string): boolean; /** * Pause playback (can be resumed later) * Pause audio playback and stop render loop, but preserve all state (keyframes, audio buffers, etc.) */ pause(): void; /** * Resume playback (from paused state) * Resume audio playback and restart render loop * Animation will continue from paused frame (because animation time base comes from audio, will auto-sync) */ resume(): Promise; /** * Interrupt current playback */ interrupt(): void; /** * Clear all data and resources */ clear(): void; /** * Get point cloud count of the current avatar * @returns Point cloud count, or null if avatar is not loaded */ getPointCount(): number | null; /** * Set post-processing configuration * These parameters will be applied in real-time to animation parameters returned by the server * @param config Post-processing configuration, or null to clear */ setPostProcessingConfig(config: PostProcessingConfig | null): void; /** * Set audio playback volume * Note: This only controls the avatar audio player volume, not the system volume * @param volume Volume value, range from 0.0 to 1.0 (0.0 = mute, 1.0 = max volume) */ setVolume(volume: number): void; /** * Get current audio playback volume * @returns Current volume value (0.0 - 1.0) */ getVolume(): number; }