Skip to main content

Character Components

EstuaryCharacter

MonoBehaviour -- Represents a single AI character. Attach one per character in your scene.

Namespace: Estuary

Inspector Fields

FieldTypeDefaultDescription
CharacterIdstring""The character's ID from the Estuary Dashboard
PlayerIdstring""Player identifier. If blank, defaults to player_<first 8 chars of device ID>
AutoConnectbooltrueConnect automatically on Start()
AutoReconnectbooltrueReconnect automatically if the connection drops unexpectedly
AudioSourceEstuaryAudioSourceOptional audio source for TTS playback
MicrophoneEstuaryMicrophoneOptional microphone component for voice input
AutoStartVoiceSessionboolfalseStart a voice session after connecting
StripActionsFromTextbooltrueRemove <action> tags from CurrentPartialResponse (Inspector-only; not exposed as a runtime property)

Properties

PropertyTypeAccessDescription
CharacterIdstringget/setCharacter ID
PlayerIdstringget/setPlayer ID
AutoConnectboolget/setWhether to connect on Start()
AutoStartVoiceSessionboolget/setWhether to start a voice session after connecting
IsConnectedboolgettrue when this character's session is active
CurrentSessionSessionInfogetSession info after connecting (null before)
IsVoiceSessionActiveboolgettrue when a voice session is running
CurrentPartialResponsestringgetAccumulated streaming response text
CurrentMessageIdstringgetThe message ID currently being processed

Methods

MethodReturnsDescription
Connect()voidMake this character active and start the connection
Disconnect()voidDisconnect from the server
SendText(string message)voidFire-and-forget wrapper for SendTextAsync(string)
SendText(string message, bool textOnly)voidFire-and-forget wrapper for SendTextAsync(string, bool)
SendTextAsync(string message)TaskSend a text message. Suppresses TTS automatically when no voice session is active.
SendTextAsync(string message, bool textOnly)TaskSend a text message; textOnly=true suppresses TTS for this turn
SayLine(string text, bool textOnly = false)voidFire-and-forget wrapper for SayLineAsync
SayLineAsync(string text, bool textOnly = false)TaskScript the character to say a specific prewritten line via TTS
StartVoiceSession()voidFire-and-forget wrapper for StartVoiceSessionAsync
StartVoiceSessionAsync()TaskStart a voice session (enables backend STT and starts the microphone)
EndVoiceSession()voidFire-and-forget wrapper for EndVoiceSessionAsync
EndVoiceSessionAsync()TaskEnd the current voice session
StreamAudioAsync(string audioBase64)TaskStream base64-encoded PCM audio (used internally by EstuaryMicrophone in WebSocket mode)
Interrupt()voidStop local audio playback and clear partial response state

C# Events

EventSignatureDescription
OnConnectedAction<SessionInfo>Connection established
OnDisconnectedActionConnection lost
OnBotResponseAction<BotResponse>Streaming text chunk received
OnVoiceReceivedAction<BotVoice>TTS audio chunk received
OnTranscriptAction<SttResponse>Speech-to-text result
OnInterruptAction<InterruptData>Server-confirmed interrupt received
OnErrorAction<string>Error message
OnConnectionStateChangedAction<ConnectionState>Connection state changed
OnActionReceivedAction<AgentAction>Action tag parsed from a bot response

UnityEvents (Inspector)

These events are serialized and can be wired in the Inspector without code:

EventParameterDescription
onConnectedSessionInfoConnection established
onDisconnectedConnection lost
onBotResponseBotResponseBot response chunk received
onVoiceReceivedBotVoiceTTS audio chunk received
onTranscriptSttResponseTranscript received
onInterruptInterrupt received
onErrorstringError message
onActionReceivedAgentActionAction parsed from response

Default Player ID

When PlayerId is left blank, the SDK generates a default during Awake:

player_<first 8 characters of SystemInfo.deviceUniqueIdentifier>

This provides basic persistence across app sessions on the same device without requiring any setup.


EstuaryAudioSource

MonoBehaviour -- Plays streaming TTS audio from bot responses. Uses a fixed 30-second ring buffer for smooth, gap-free playback in WebSocket mode. In LiveKit mode, audio is handled by LiveKit's AudioStream and this component primarily tracks message IDs for interrupts and provides a unified Volume and interrupt API.

Namespace: Estuary

[RequireComponent(typeof(AudioSource))] -- a Unity AudioSource is added automatically.

Inspector Fields

FieldTypeDefaultDescription
AudioSourceAudioSourceautoUnity AudioSource used for playback
Volumefloat1.0Playback volume (0–1)
AutoInterruptOnUserSpeechbooltrueStop playback when the referenced microphone detects user speech
MicrophoneRefEstuaryMicrophoneMicrophone used for auto-interrupt detection
note

The ring buffer length is a fixed 30-second constant -- it is not Inspector-configurable. The streaming sample rate matches Unity's AudioSettings.outputSampleRate; incoming audio at a different rate is resampled automatically.

Properties

PropertyTypeAccessDescription
IsPlayingboolgettrue while audio is playing
BufferedSampleCountintgetSamples currently buffered for playback
CurrentMessageIdstringgetMessage ID of the audio currently playing
Volumefloatget/setPlayback volume (also propagated to LiveKit's bot audio in LiveKit mode)

Methods

MethodReturnsDescription
EnqueueAudio(BotVoice voice)voidDecode and write a bot voice chunk into the ring buffer
EnqueueAudio(byte[] audioBytes, int sampleRate, string messageId = null)voidWrite raw PCM16 bytes into the ring buffer
StopPlayback()voidStop playback and clear the buffer
InterruptMessage(string messageId)voidStop playback only if the given messageId is currently playing
ClearStreamingBuffer()voidClear pending audio without firing interrupt events
SetMicrophoneReference(EstuaryMicrophone microphone)voidUpdate the microphone used for auto-interrupt detection
SetLiveKitManager(ILiveKitVoiceManager manager)voidSwitch to LiveKit playback mode (audio routed via LiveKit)
Configure(VoiceMode voiceMode, ILiveKitVoiceManager liveKitManager = null)voidConfigure the source for WebSocket or LiveKit mode

Events

EventSignatureDescription
OnPlaybackStartedActionAudio playback began
OnPlaybackCompleteActionAudio playback finished (buffer drained)
OnPlaybackInterruptedActionPlayback was stopped (interrupt or StopPlayback)

How It Works

WebSocket mode: Bot audio arrives as base64-encoded PCM16 chunks in bot_voice events. EstuaryManager routes them to the active character, which calls EnqueueAudio(BotVoice). The component:

  1. Decodes the base64 audio into PCM16 bytes (already done on BotVoice)
  2. Converts PCM16 bytes to float samples
  3. Resamples if the server sample rate differs from Unity's output rate
  4. Writes to a 30-second circular ring buffer
  5. Feeds samples to a streaming AudioClip via a PCM reader callback

The ring buffer ensures smooth playback even when network jitter causes irregular chunk delivery, and applies a short fade-out on underrun to avoid clicks.

LiveKit mode: Audio playback is handled by LiveKit's AudioStream, which creates its own AudioSource at runtime. EstuaryAudioSource still tracks the current message_id for interrupt handling and propagates Volume to the LiveKit voice manager.

Auto-Interrupt

When AutoInterruptOnUserSpeech is enabled and the referenced EstuaryMicrophone detects speech while the bot is talking:

  1. Playback stops and the buffer is cleared
  2. OnPlaybackInterrupted fires
  3. A client_interrupt event is sent to the server with the current message_id
  4. The server stops generating audio for that message