when i interrupt and close input/output audio in t...
# ask-ai
a
when i interrupt and close input/output audio in the room, i hear like a trashy sound or similar like an interruption, can it affect the agent in some way?
t
Thanks for reaching out, Sebastián. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
I understand that the "trashy sound" you’re hearing when interrupting and closing input/output audio in a LiveKit room can be quite frustrating. This usually happens because the audio stream isn’t being closed properly or there’s some leftover audio in the buffer. The good news is, if handled correctly, this shouldn’t impact the agent’s experience. Here are a few tips to help minimize those interruptions: • Make sure any queued audio finishes playing before you close the audio output stream. • Follow the right sequence when closing a session—wait for any ongoing speech to finish before detaching the input/output streams. • Try toggling the audio input/output dynamically during the session instead of fully disabling or restarting the audio components. • Turn on noise and echo cancellation features to enhance the overall audio quality. If you continue to experience this issue, it might be related to hardware or network playback challenges. Please feel free to reach out if you'd like help implementing these steps or if you want to troubleshoot further—I’m here to support you every step of the way! Sources: livekit-agents/livekit/agents/voice/agent_session.py | agents/src/voice/room_io/room_io.ts | SIP troubleshooting guide | LiveKit Docs | Text and transcriptions | LiveKit Docs
a
but how could i know when the audio was finished playing on the client, i can only from the agent side if playback finished right? also, noise and echo cancellation how can it be turn on?
t
I'm happy to help you detect when audio finishes playing on the client! You can use the agent's
playback_finished
event for this. This event can be broadcast to clients via RPC (Remote Procedure Call), and on the client side, you can listen for this RPC to sync things like UI updates or finalizing transcripts. If you'd like, I can provide more detailed steps tailored to your platform—just let me know! Regarding noise and echo cancellation, here are some options to consider: • Default WebRTC Noise and Echo Cancellation: Most SDKs have this enabled by default. You can fine-tune it using
AudioCaptureOptions
in your SDK connection. • Enhanced Noise Cancellation with Krisp: For even better noise suppression, the Krisp plugin is a great choice. On the frontend, you can install and set up the
@livekit/krisp-noise-filter
package. For server-side agents, the
livekit-plugins-noise-cancellation
package will help apply noise cancellation to inbound audio. If you’d like any help walking through the implementation or have other questions, just reach out—I’m here to support you! Sources: livekit-plugins/livekit-plugins-aws/livekit/plugins/aws/experimental/realti... | How to detect when an agent has finished speaking | Bringing AI avatars to voice agents | Bringing AI avatars to voice agents | Bringing AI avatars to voice agents
a
show me in python client SDK please and see how to know when the last webrtc frame actually arrived to client
t
In the Python client SDK, a great way to detect when the last WebRTC frame arrives is by keeping an eye on the end of the
async for
loop over the
VideoStream
. When this loop finishes, it tells us that the video stream has ended—this can happen when the remote participant unpublishes the track, disconnects, or if the stream is unsubscribed. Here’s a friendly example to guide you:
Copy code
from livekit import rtc
import asyncio

async def receive_frames_until_end(stream: rtc.VideoStream):
    last_frame = None
    try:
        async for event in stream:
            last_frame = event.frame
            print(f"Received frame: {event.frame.width}x{event.frame.height}")
        
        if last_frame:
            print("Last video frame received and processed.")
        else:
            print("Stream ended without receiving any frames.")
    except Exception as e:
        print(f"Error during frame reception: {e}")
        raise
    finally:
        await stream.aclose()
To make this work smoothly, you can create the
VideoStream
whenever a video track is subscribed, like so:
Copy code
@room.on("track_subscribed")
def on_track_subscribed(track: rtc.Track, publication: rtc.TrackPublication, participant: rtc.RemoteParticipant):
    if track.kind == rtc.TrackKind.KIND_VIDEO:
        video_stream = rtc.VideoStream(track)
        asyncio.create_task(receive_frames_until_end(video_stream))
If you’d like me to walk you through this further or provide more examples, just let me know—I’m here to help! Sources: complex-agents/vision/agent.py | An open source stack for real-time multimodal AI | How to detect when an agent has finished speaking | Vision Agent Quickstart | LiveKit Docs | Receiving and publishing tracks | LiveKit Docs
a
no but this audio only
t
In the Python client SDK, a great way to know when the last WebRTC audio frame arrives is by keeping an eye on when the
async for
loop over the
AudioStream
finishes. When this loop ends, it means the audio stream has concluded—this could happen if the remote participant unpublishes the track, disconnects, or if the stream is unsubscribed. Here’s a simple example to illustrate this:
Copy code
async def receive_audio_frames(stream: rtc.AudioStream, participant: rtc.RemoteParticipant):
    frames_received = 0
    print(f"Audio receive task started for participant: {participant.identity}")

    try:
        # Process each audio frame as it arrives
        async for frame_event in stream:
            frames_received += 1
            # Optional: Process the frame (e.g., analyze, playback, transcribe)
            print(f"Received audio frame: {frame_event.frame}")

        # This line runs right after the last frame is received and the stream ends
        print(f"Last audio frame received for: {participant.identity}. Total frames: {frames_received}")
    
    finally:
        # Make sure to clean up the stream properly
        await stream.aclose()
        print(f"Audio stream closed for participant: {participant.identity}")
To put this into action, you’d want to create the
AudioStream
when an audio track gets subscribed:
Copy code
@room.on("track_subscribed")
def on_track_subscribed(track: rtc.Track, publication: rtc.TrackPublication, participant: rtc.RemoteParticipant):
    if track.kind == rtc.TrackKind.KIND_AUDIO:
        audio_stream = rtc.AudioStream(track)
        asyncio.create_task(receive_audio_frames(audio_stream, participant))
If anything here isn’t clear or if you’d like me to walk you through more examples, just let me know—I’m happy to help! Sources: terminal-client/client.py | How to detect when an agent has finished speaking | stream_audio.py | livekit-rtc/livekit/rtc/audio_stream.py | Receiving and publishing tracks | LiveKit Docs
a
no but i don't mean that the user has left, is for example detect the last frame of a specific sent (to match it with the playback finished), assume the output audio is silenced after thi
t
To help you detect the last WebRTC audio frame of a specific segment, the approach really depends on your particular context. Let me walk you through the different scenarios: • For TTS-generated audio: You can use the
is_final
flag in the
SynthesizedAudio
object. This handy flag marks the last frame of a TTS segment. Here's a quick example:
Copy code
async for event in tts_stream:
    if event.is_final:
        print(f"Last frame of segment {event.segment_id} detected")
        final_frame = event.frame
For playback completion: You can listen for the
playback_finished
event on
AudioOutput
to know exactly when the audio segment finishes playing. Here's how you might do it:
Copy code
@session.output.audio.on("playback_finished")
def _on_playback_finished(ev: PlaybackFinishedEvent):
    print("TTS segment playback completed.")
For real-time user audio: Since WebRTC audio frames don’t have a built-in "final frame" flag, you might consider defining segment boundaries using tools like VAD (Voice Activity Detection), STT (Speech-to-Text), or manual signaling. If you don’t mind sharing, could you please clarify whether you’re working with TTS-generated audio, real-time user audio, or perhaps another specific use case? That way, I can help tailor the solution even better to your needs. Sources: livekit-agents/livekit/agents/voice/avatar/_datastream_io.py | Capturing the First Few Seconds of a Call for Processing | How to detect when an agent has finished speaking
a
realtime audio with server VAD, no STT
t
To help you detect the last WebRTC audio frame of a segment using server-side Voice Activity Detection (VAD), a great way is to listen for the
END_OF_SPEECH
event that the VAD system emits. This event happens when a period of silence goes beyond the set threshold, indicating the end of a speech segment. The last audio frame of that segment is simply the final frame in the
frames
list of the event. Here’s a friendly example in Python to guide you:
Copy code
from livekit.plugins import silero
from livekit.agents.vad import VADEventType

async def process_audio_with_vad(audio_stream):
    vad = silero.VAD.load(
        min_speech_duration=0.2,  # Minimum speech duration to trigger start
        min_silence_duration=0.6, # Silence duration to trigger end
        sample_rate=16000
    )
    vad_stream = vad.stream()

    async def on_vad_event(vad_event):
        if vad_event.type == VADEventType.END_OF_SPEECH:
            if vad_event.frames:
                last_frame = vad_event.frames[-1]
                print(f"Last frame of segment: timestamp={last_frame.timestamp}, samples={last_frame.samples_per_channel}")

    async for audio_event in audio_stream:
        vad_stream.push_frame(audio_event.frame)

    await vad_stream.aclose()
If anything feels unclear or if you’d like me to walk you through more examples, please don’t hesitate to ask. I’m here to help! Sources: Working with plugins | LiveKit Docs | livekit-agents/livekit/agents/voice/audio_recognition.py | basics/echo_transcriber_agent.py | plugins/silero/src/vad.ts | Speech-to-text (STT) integrations | LiveKit Docs
a
no but is the realtime model VAD
t
I'm glad you're exploring how to detect the last WebRTC audio frame of a segment using real-time VAD in LiveKit! You can do this by listening for the
END_OF_SPEECH
event that the VAD system emits. This event happens when the silence goes beyond the set threshold, indicating the end of a speech segment. The very last audio frame of that segment will be the final frame in the
frames
array of the event. Here's a friendly example in Python to help you get started:
Copy code
from livekit.plugins import silero
from livekit.agents.vad import VADEventType

# Initialize VAD with custom parameters
vad_detector = silero.VAD.load(
    min_speech_duration=0.2,      # Minimum speech duration to start a segment
    min_silence_duration=0.6,     # Silence duration to end a segment
    prefix_padding_duration=0.3   # Add padding to the start of the segment
)
vad_stream = vad_detector.stream()

async def process_vad_events():
    async for event in vad_stream:
        if event.type == VADEventType.END_OF_SPEECH:
            # The last frame of the segment is the final element in the frames array
            if event.frames:
                last_frame = event.frames[-1]
                print(f"Last frame timestamp: {last_frame.timestamp}")
                print(f"Last frame duration: {last_frame.duration}ms")
            else:
                print("No audio frames in the speech segment")

            # Process the entire speech segment if needed
            total_duration = sum(frame.duration for frame in event.frames)
            print(f"Total speech segment duration: {total_duration}ms")
Please don’t hesitate to reach out if you’d like me to walk you through this further or if you want more examples. I’m here to help! Sources: agents/src/voice/audio_recognition.ts | livekit-agents/livekit/agents/tts/tts.py | How does end-of-utterance detection work in conversations?
a
how to know if the echo cancellation and noise reduction is correctly applied?
t
To help you verify that echo cancellation and noise reduction are working properly in LiveKit, here are some friendly ways you can check: • Programmatic Verification: You can use SDK tests to take a closer look at the Audio Processing Module (APM) settings. For example, in the Python SDK, checking the
AudioProcessingModule
lets you see if
echo_cancellation
and
noise_suppression
are turned on. • Signal Inspection: If you’re using SDKs like Android, you can confirm that features like echo cancellation and noise suppression appear in the
featuresList
of the
UpdateAudioTrack
signal sent to the server. • Log-Based Confirmation: Another helpful way is to use example apps or tools that log the status of these audio processing features. For instance, the Rust SDK provides logs showing whether echo cancellation and noise suppression are enabled. • Processed Audio Output: You might also want to capture and listen to the processed audio output to see if the noise and echo have been effectively reduced, either manually or with automated tools. Please feel free to reach out if you'd like me to walk you through any of these methods or provide specific examples—I’m here to help! Sources: Tests/LiveKitTests/AudioProcessingTests.swift | livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.pyi | livekit-android-test/src/test/java/io/livekit/android/room/participant/Loca... | examples/local_audio/README.md
a
audio features for APM in the python sdk client is enabled? I only have the preconnect buffer for now
t
Thank you for reaching out! I wanted to let you know that the Python SDK client for LiveKit doesn’t currently have built-in support for audio processing features like echo cancellation and noise suppression. These handy features are usually handled at the WebRTC level or through platform-specific SDKs such as Web, Android, or iOS. If you’re using the Python SDK and need these capabilities, a good approach might be to preprocess the audio with an external library or tool before sending it to LiveKit. I’m here to help if you’d like some guidance on alternative methods or if you have any other questions—just let me know!
a
I’m setting the APM and doing all the processing and in the agent have BVC but I don’t know if this is correctly working
t
To help you verify if Background Voice Cancellation (BVC) is working properly in your LiveKit agent setup, here are some friendly steps you can try: • Test with Controlled Audio Input: Try using audio samples that have known background noise or overlapping voices. Play these samples through the system and see if the background noise or extra voices are being effectively reduced in the output. • Inspect Logs: Take a look at the agent logs for any messages related to the BVC module. These logs can give you clues about whether the BVC processing is active and working as it should. • Analyze Processed Audio: Capture the audio after it’s been processed and compare it to the original input. You can use audio analysis tools to check how much the background noise or interference has been minimized. • Configuration Check: Double-check that BVC is set up correctly in your agent. For instance, make sure the BVC model is loaded and applied to the audio stream. If you’d like, I’m here to help you further with testing or configuring BVC—just let me know!