melodic-zoo-29246
02/13/2026, 5:14 AMtall-airline-16756
02/13/2026, 5:14 AMtall-airline-16756
02/13/2026, 5:15 AMsession.say() instead of sending text for synthesis.
What this gives you
• Completely skips the TTS model (no TTS tokens used).
• Plays any pre-recorded audio (.wav/.mp3) wrapped as audio frames.
• Keeps session events/transcripts working if you still pass optional transcript text.
• Lets you switch between cached audio in tests and real TTS in production.
How to do it (high-level)
1. Pre-synthesize and store your audio files (e.g., .wav) for the responses you want to reuse.
2. When testing, read the audio file and stream it to session.say() as an audio generator/stream instead of passing text.
3. Optionally include text for transcripts or fallback purposes.
Examples
Python (idea):
import wave
from livekit import rtc
def audio_generator(file_path):
with wave.open(file_path, 'rb') as wav:
frames = wav.readframes(wav.getnframes())
audio_frame = rtc.AudioFrame(
data=frames,
sample_rate=wav.getframerate(),
num_channels=wav.getnchannels(),
samples_per_channel=wav.getnframes()
)
yield audio_frame
# Use cached audio to avoid TTS
await session.say(audio=audio_generator("cached_response.wav"), text="Optional transcript text")
TypeScript (idea):
// Create a ReadableStream<AudioFrame> from a cached audio file and pass it:
await session.say('Optional transcript text', {
audio: audioStream, // ReadableStream<audioframe>
});
Testing vs production
• In test mode: call session.say(audio=...) or pass an audio stream.
• In production: call session.say(text=...) to use live TTS.
If you'd like, I can:
• Show a full example for your runtime (Node/Python) that converts a .wav/.mp3 to audio frames/stream compatible with session.say().
• Suggest a simple test toggle pattern to switch between cached audio and TTS.
Which language/runtime are you using for your Agent so I can provide a concrete snippet?
Sources: Agent speech and audio | LiveKit Documentationmelodic-zoo-29246
02/13/2026, 5:15 AMtall-airline-16756
02/13/2026, 5:15 AMsession.say() to bypass TTS during testing, plus a simple toggle to switch between test and production behavior.
Notes:
• This streams a pre-synthesized WAV file as audio frames so no TTS synthesis or tokens are used.
• Optionally pass text for transcripts/events while still using cached audio.
• Adjust the WAV reading logic if your files are stereo/mono or different sample formats.
Example (Python):
import wave
import asyncio
from typing import Iterator
# Replace with the actual LiveKit session import/path in your project
# from livekit import rtc
class DummyAudioFrame:
# Replace with livekit.rtc.AudioFrame in real code
def __init__(self, data: bytes, sample_rate: int, num_channels: int, samples_per_channel: int):
self.data = data
self.sample_rate = sample_rate
self.num_channels = num_channels
self.samples_per_channel = samples_per_channel
def wav_to_audio_frames(path: str) -> Iterator[DummyAudioFrame]:
"""
Read a .wav file and yield a single AudioFrame (or multiple frames if you chunk).
Replace DummyAudioFrame with livekit.rtc.AudioFrame in your code.
"""
with wave.open(path, "rb") as wav:
frames = wav.readframes(wav.getnframes())
sample_rate = wav.getframerate()
num_channels = wav.getnchannels()
samples_per_channel = wav.getnframes()
# Yield one frame containing the whole file. You can chunk this for long files.
yield DummyAudioFrame(
data=frames,
sample_rate=sample_rate,
num_channels=num_channels,
samples_per_channel=samples_per_channel,
)
async def say_cached_audio(session, wav_path: str, transcript_text: str = ""):
"""
Play cached audio via session.say().
`session` is your LiveKit Agent session object.
"""
audio_iter = wav_to_audio_frames(wav_path)
# In real code, pass the audio generator/stream compatible with LiveKit's API.
# Example signature: await session.say(audio=audio_iter, text=transcript_text)
await session.say(audio=audio_iter, text=transcript_text)
async def say_with_tts(session, text: str):
"""
Use normal TTS path (this uses tokens).
"""
await session.say(text=text)
# Toggle between test (cached audio) and production (TTS)
async def respond(session, text: str, cached_wav: str = None, use_cached: bool = True):
if use_cached and cached_wav:
# Play cached audio, pass text optionally for transcripts
await say_cached_audio(session, cached_wav, transcript_text=text)
else:
# Use TTS
await say_with_tts(session, text)
# Example usage (within your async agent handler):
# await respond(session, "Hello, this is a test response", cached_wav="responses/hello.wav", use_cached=True)
If you'd like, I can:
• Provide the exact import and AudioFrame usage for your LiveKit Python package (share the package name/version if different),
• Show how to chunk larger WAV files into multiple frames for streaming,
• Or give a small script that converts MP3 files to WAV and adjusts sample rates if needed.
Which of those would be most helpful?