Character Components
EstuaryCharacter
MonoBehaviour -- Represents a single AI character. Attach one per character in your scene.
Namespace: Estuary
Inspector Fields
| Field | Type | Default | Description |
|---|---|---|---|
CharacterId | string | "" | The character's ID from the Estuary Dashboard |
PlayerId | string | "" | Player identifier. If blank, defaults to player_<first 8 chars of device ID> |
AutoConnect | bool | true | Connect automatically on Start() |
AutoReconnect | bool | true | Reconnect automatically if the connection drops unexpectedly |
AudioSource | EstuaryAudioSource | — | Optional audio source for TTS playback |
Microphone | EstuaryMicrophone | — | Optional microphone component for voice input |
AutoStartVoiceSession | bool | false | Start a voice session after connecting |
StripActionsFromText | bool | true | Remove <action> tags from CurrentPartialResponse (Inspector-only; not exposed as a runtime property) |
Properties
| Property | Type | Access | Description |
|---|---|---|---|
CharacterId | string | get/set | Character ID |
PlayerId | string | get/set | Player ID |
AutoConnect | bool | get/set | Whether to connect on Start() |
AutoStartVoiceSession | bool | get/set | Whether to start a voice session after connecting |
IsConnected | bool | get | true when this character's session is active |
CurrentSession | SessionInfo | get | Session info after connecting (null before) |
IsVoiceSessionActive | bool | get | true when a voice session is running |
CurrentPartialResponse | string | get | Accumulated streaming response text |
CurrentMessageId | string | get | The message ID currently being processed |
Methods
| Method | Returns | Description |
|---|---|---|
Connect() | void | Make this character active and start the connection |
Disconnect() | void | Disconnect from the server |
SendText(string message) | void | Fire-and-forget wrapper for SendTextAsync(string) |
SendText(string message, bool textOnly) | void | Fire-and-forget wrapper for SendTextAsync(string, bool) |
SendTextAsync(string message) | Task | Send a text message. Suppresses TTS automatically when no voice session is active. |
SendTextAsync(string message, bool textOnly) | Task | Send a text message; textOnly=true suppresses TTS for this turn |
SayLine(string text, bool textOnly = false) | void | Fire-and-forget wrapper for SayLineAsync |
SayLineAsync(string text, bool textOnly = false) | Task | Script the character to say a specific prewritten line via TTS |
StartVoiceSession() | void | Fire-and-forget wrapper for StartVoiceSessionAsync |
StartVoiceSessionAsync() | Task | Start a voice session (enables backend STT and starts the microphone) |
EndVoiceSession() | void | Fire-and-forget wrapper for EndVoiceSessionAsync |
EndVoiceSessionAsync() | Task | End the current voice session |
StreamAudioAsync(string audioBase64) | Task | Stream base64-encoded PCM audio (used internally by EstuaryMicrophone in WebSocket mode) |
Interrupt() | void | Stop local audio playback and clear partial response state |
C# Events
| Event | Signature | Description |
|---|---|---|
OnConnected | Action<SessionInfo> | Connection established |
OnDisconnected | Action | Connection lost |
OnBotResponse | Action<BotResponse> | Streaming text chunk received |
OnVoiceReceived | Action<BotVoice> | TTS audio chunk received |
OnTranscript | Action<SttResponse> | Speech-to-text result |
OnInterrupt | Action<InterruptData> | Server-confirmed interrupt received |
OnError | Action<string> | Error message |
OnConnectionStateChanged | Action<ConnectionState> | Connection state changed |
OnActionReceived | Action<AgentAction> | Action tag parsed from a bot response |
UnityEvents (Inspector)
These events are serialized and can be wired in the Inspector without code:
| Event | Parameter | Description |
|---|---|---|
onConnected | SessionInfo | Connection established |
onDisconnected | — | Connection lost |
onBotResponse | BotResponse | Bot response chunk received |
onVoiceReceived | BotVoice | TTS audio chunk received |
onTranscript | SttResponse | Transcript received |
onInterrupt | — | Interrupt received |
onError | string | Error message |
onActionReceived | AgentAction | Action 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
| Field | Type | Default | Description |
|---|---|---|---|
AudioSource | AudioSource | auto | Unity AudioSource used for playback |
Volume | float | 1.0 | Playback volume (0–1) |
AutoInterruptOnUserSpeech | bool | true | Stop playback when the referenced microphone detects user speech |
MicrophoneRef | EstuaryMicrophone | — | Microphone used for auto-interrupt detection |
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
| Property | Type | Access | Description |
|---|---|---|---|
IsPlaying | bool | get | true while audio is playing |
BufferedSampleCount | int | get | Samples currently buffered for playback |
CurrentMessageId | string | get | Message ID of the audio currently playing |
Volume | float | get/set | Playback volume (also propagated to LiveKit's bot audio in LiveKit mode) |
Methods
| Method | Returns | Description |
|---|---|---|
EnqueueAudio(BotVoice voice) | void | Decode and write a bot voice chunk into the ring buffer |
EnqueueAudio(byte[] audioBytes, int sampleRate, string messageId = null) | void | Write raw PCM16 bytes into the ring buffer |
StopPlayback() | void | Stop playback and clear the buffer |
InterruptMessage(string messageId) | void | Stop playback only if the given messageId is currently playing |
ClearStreamingBuffer() | void | Clear pending audio without firing interrupt events |
SetMicrophoneReference(EstuaryMicrophone microphone) | void | Update the microphone used for auto-interrupt detection |
SetLiveKitManager(ILiveKitVoiceManager manager) | void | Switch to LiveKit playback mode (audio routed via LiveKit) |
Configure(VoiceMode voiceMode, ILiveKitVoiceManager liveKitManager = null) | void | Configure the source for WebSocket or LiveKit mode |
Events
| Event | Signature | Description |
|---|---|---|
OnPlaybackStarted | Action | Audio playback began |
OnPlaybackComplete | Action | Audio playback finished (buffer drained) |
OnPlaybackInterrupted | Action | Playback 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:
- Decodes the base64 audio into PCM16 bytes (already done on
BotVoice) - Converts PCM16 bytes to float samples
- Resamples if the server sample rate differs from Unity's output rate
- Writes to a 30-second circular ring buffer
- Feeds samples to a streaming
AudioClipvia 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:
- Playback stops and the buffer is cleared
OnPlaybackInterruptedfires- A
client_interruptevent is sent to the server with the currentmessage_id - The server stops generating audio for that message