fancy-planet-81885
12/19/2025, 2:26 PMtall-airline-16756
12/19/2025, 2:26 PMtall-airline-16756
12/19/2025, 2:27 PMfrom livekit.plugins import noise_cancellation
from livekit.agents.voice import room_io
await session.start(
room=ctx.room,
agent=Assistant(),
room_options=room_io.RoomOptions(
audio_input=room_io.AudioInputOptions(
noise_cancellation=noise_cancellation.BVC(), # or NC()
),
),
)
• After processing each chunk/segment, measure a metric you care about (residual noise energy, failed STT attempts, voice activity detection patterns). There isn’t a single built-in boolean like is_too_noisy, so infer it from these signals.
• Avoid enabling noise cancellation both in the frontend and agent simultaneously (can cause artifacts).
Quick question to narrow this for you
• Do you want the agent to act in real-time (during the call) or post-call (alerts/analytics)?
• Which SDK/language will your agent use (Node, Python, JS, mobile)?
Tell me which option you prefer and your SDK, and I’ll provide a concise implementation example for real-time detection and triggering.
Sources: Enhanced noise cancellation | LiveKit docs | Agents Framework | livekit/livekit_composite | DeepWikifancy-planet-81885
12/19/2025, 2:35 PMtall-airline-16756
12/19/2025, 2:36 PMfrom collections import deque
import numpy as np
THRESH = 0.02 # tune per your audio level units
CONSECUTIVE = 3
recent = deque(maxlen=CONSECUTIVE)
def rms(samples):
return float(np.sqrt(np.mean(np.square(samples))))
async def on_audio_chunk(filtered_pcm):
# filtered_pcm: numpy array of float32/int16 samples after BVC
level = rms(filtered_pcm)
recent.append(level)
if len(recent) == CONSECUTIVE and all(l > THRESH for l in recent):
await play_tts_prompt("Please move to a quieter place or mute when not speaking")
recent.clear()
else:
# continue STT -> LLM pipeline
await send_to_stt(filtered_pcm)
Tuning & reliability tips
• Calibrate THRESH using sample calls in your environment.
• Use a short debounce window (2–5 chunks) to avoid false positives.
• Combine RMS with VAD or STT confidence for fewer spurious prompts.
• Rate-limit prompts (e.g., once per minute per participant) to avoid annoyance.
If you want, I can:
• Provide a concrete code snippet that hooks into LiveKit’s Python agent SDK callbacks you’re using.
• Suggest precise thresholds and VAD parameters if you share sample audio levels or your buffer/frame size. Which would you prefer?fancy-planet-81885
12/19/2025, 2:37 PMtall-airline-16756
12/19/2025, 2:38 PMon_track_subscribed.
• Reads raw frames from an AudioStream after BVC runs.
• Computes RMS per frame, debounces sustained high-noise, and calls session.say() to play a verbal prompt.
• Includes simple rate-limiting to avoid repeated prompts.
Code snippet
import asyncio
import time
import numpy as np
from collections import deque
from livekit import rtc
from livekit.agents import AgentSession, stt
# Tuning params
NOISE_THRESHOLD = 0.02 # tune this with your audio (RMS normalized to [-1,1])
SUSTAIN_SECONDS = 1.0 # noise must persist this long to trigger
FRAME_DURATION = 0.02 # assumed frame length in seconds (20ms typical)
RATE_LIMIT_SECONDS = 60 # minimum seconds between prompts per participant
async def analyze_audio_for_noise(session: AgentSession, audio_stream: rtc.AudioStream, participant_sid: str):
frames_needed = int(SUSTAIN_SECONDS / FRAME_DURATION)
noisy_count = 0
last_prompt_time = 0
async for event in audio_stream:
frame = event.frame
# Convert int16 PCM -> float32 normalized to [-1,1]
samples = np.frombuffer(frame.data, dtype=np.int16).astype(np.float32) / 32768.0
rms = float(np.sqrt(np.mean(np.square(samples))))
if rms > NOISE_THRESHOLD:
noisy_count += 1
else:
noisy_count = max(0, noisy_count - 1)
if noisy_count >= frames_needed:
now = time.time()
if now - last_prompt_time >= RATE_LIMIT_SECONDS:
last_prompt_time = now
noisy_count = 0
# Play TTS prompt into the room (agent voice)
await session.say("I'm having trouble hearing you due to background noise. Could you please move to a quieter place or mute when not speaking?")
else:
noisy_count = 0 # reset to avoid immediate retrigger
@server.rtc_session()
async def entrypoint(ctx: rtc.JobContext):
# Create your AgentSession with BVC configured in room_options as you already have
session = AgentSession(
ctx=ctx,
stt=stt.DeepgramSTT(), # or your STT
# tts=..., other options
)
await session.start()
@ctx.room.on("track_subscribed")
def on_track_subscribed(track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant):
if track.kind == rtc.TrackKind.KIND_AUDIO:
# Wrap the inbound track so we can read raw frames after processing (BVC is applied inbound)
audio_stream = rtc.AudioStream(track)
# Start background task to monitor noise for this participant
asyncio.create_task(analyze_audio_for_noise(session, audio_stream, participant.sid))
Notes and tuning
• Place this alongside your existing BVC room_options configuration — BVC will have been applied to the inbound audio the agent receives.
• Tune NOISE_THRESHOLD, SUSTAIN_SECONDS, and RATE_LIMIT_SECONDS with sample calls to avoid false positives.
• For fewer spurious prompts, combine RMS with STT confidence or VAD: require both high RMS and low STT confidence before calling session.say().
• If you want a snippet that uses your exact Agent class or STT/TTS setup, paste those bits and I’ll adapt this to match.
Would you like the same snippet adapted to use STT confidence in the trigger condition?
Sources: Pipeline nodes and hooks | LiveKit docs | Enhanced noise cancellation | LiveKit docs | Processing raw media tracks | LiveKit docs | Playing Audio | LiveKit docs