How to setup noise cancellation for self hosted li...
# ask-ai
c
How to setup noise cancellation for self hosted livekit from livekit.agents.audio import noise_cancellation noise_canceller = noise_cancellation.BVCTelephony() this is not working for self hostel livekit. What else can I use in open sourced with livekit for noise cancellation
t
Thanks for reaching out, Developer. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I understand you’re trying to enable noise cancellation on a self-hosted LiveKit instance and
noise_cancellation.BVCTelephony()
is failing. I’ll explain why that happens and give practical, open-source alternatives you can use with a self-hosted setup. Why
BVCTelephony()
isn’t working for self-hosted • The
BVCTelephony()
and
BVC()
models are part of LiveKit’s Enhanced Noise Cancellation, which is a server-side inference feature available only in LiveKit Cloud. That functionality is not supported on self-hosted LiveKit, which is why your call to
noise_cancellation.BVCTelephony()
fails. What you can do instead (self-hosted friendly options) • Client-side noise suppression (recommended) ◦ Use WebRTC’s built-in noise suppression on the client (browser or mobile) so audio is cleaned before it’s published to the server. This uses the browser/device audio pipeline (e.g., enabling noise suppression in getUserMedia constraints or using platform audio APIs). ◦ Advantage: Low-latency and offloads processing from your server. • Agent-side (preprocessing) open-source denoisers ◦ Run noise suppression inside your agent process (before sending audio onward). You can integrate open-source denoisers such as RNNoise or other Python wrappers that implement denoising. ◦ Advantage: Centralized control on your infrastructure; works for recorded or streaming audio paths you manage in the agent. • Voice Activity Detection (VAD) + filtering ◦ Use Silero VAD (supported in agent workflows) to drop non-speech segments and reduce background noise impact on downstream processing (STT, recording). ◦ Combine VAD with a denoiser for better quality. Suggested next steps (practical) 1. Decide where you want processing: ◦ If you can change the client app: implement WebRTC noise suppression or process MediaStream audio with an RNNoise-based processor. ◦ If you prefer server/agent processing: remove
BVCTelephony()
and add a preprocessing step in your agent that runs RNNoise (or another open-source denoiser) and/or Silero VAD. 1. If you want a concrete code example, tell me: ◦ Which client SDK (browser JS, iOS, Android) or which agent runtime you’re using (Python version, agent framework). ◦ Whether you prefer client-side or agent-side processing. 1. I can then provide: ◦ A short example of enabling WebRTC noise suppression in the browser or ◦ A Python example showing how to run RNNoise or Silero VAD in your agent pipeline. If you share your environment (client SDK and agent language/runtime) I’ll give a targeted code snippet and recommended libraries to integrate. Sources: Voice AI quickstart | LiveKit Documentation | Client SDKs | livekit/livekit_composite | DeepWiki | Agent session | LiveKit Documentation | Choosing the right noise cancellation for your agent | LiveKit
c
A Python example showing how to run RNNoise
t
Thanks — I can provide a concrete Python example for RNNoise, but first a few quick questions so I give you the right code and install steps. Please tell me: 1. Do you need real-time (streaming/low-latency) denoising or offline (file-based) processing? 2. Where does the audio originate in your agent pipeline? (LiveKit PCM frames, microphone input, uploaded files, etc.) 3. Which Python version and environment are you using? (e.g., Python 3.10 in Docker on x86_64) 4. Are you OK installing a pip package that wraps RNNoise (e.g.,
rnnoise-python
), or must we build from source / use a specific wrapper? 5. What audio format and sample rate are you working with? (e.g., mono PCM16 at 16 kHz or 48 kHz) 6. Do you want the denoiser to run inside the LiveKit agent process or as a separate preprocessing service? 7. Any CPU/latency constraints or target deployment (Docker, ARM, low-power devices)? If you prefer, answer just 1–3 and 5 and I’ll provide two examples: • A short offline (file) example showing installation and processing a WAV file with
rnnoise-python
. • A simple streaming example that reads PCM frames, runs RNNoise per-frame, and writes output (suitable to adapt into an agent). Once you reply, I’ll send a ready-to-run Python snippet, required install steps, and notes about sample rates and buffering for low-latency usage.
c
import logging import os from datetime import datetime, timezone import time from pathlib import Path import json from dotenv import load_dotenv from livekit import ( rtc, api, ) from livekit.agents import ( Agent, AgentServer, AgentSession, JobContext, JobProcess, RunContext, ChatContext, cli, function_tool, room_io, metrics ) from livekit.plugins import ( openai, elevenlabs, silero, ) from livekit.plugins.turn_detector.multilingual import MultilingualModel from stt import build_stt, build_cloud_stt, build_local_stt from llm import build_llm, build_cloud_llm, build_local_llm from tts import build_tts from brightchamps_rag import query_info, query_info_stub from brightchamps_indexing import get_index from brightchampsagent_prompt import SYSTEM_PROMPT, execution_mode from mongo_push import push_to_mongo logger = logging.getLogger("agent") # basic formatter logging.basicConfig( level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s", ) # make the noisy bits verbose for name in ( "livekit", "livekit.agents", "httpx", "openai", "elevenlabs", ): logging.getLogger(name).setLevel(logging.DEBUG) class Assistant(Agent): def __init__(self, system_prompt=SYSTEM_PROMPT) -> None: super().__init__( instructions=system_prompt, tools=[query_info if execution_mode == "local" else query_info_stub], ) async def on_enter(self): await self.session.generate_reply() server = AgentServer() def prewarm(proc: JobProcess): # load silero vad once per worker process proc.userdata["vad"] = silero.VAD.load() # warm up the retrieval index (loads embeddings, connects to qdrant) if execution_mode == "local": get_index() server.setup_fnc = prewarm async def write_transcript( ctx: JobContext, session: AgentSession, ): # This code runs when the agent task is shutting down current_date = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S") reports_dir = Path("reports") reports_dir.mkdir(exist_ok=True) filename = reports_dir / f"transcript_{ctx.room.name}_{current_date}.json" try: # We use session.history.to_dict() to get the full chat log with open(filename, 'w', encoding='utf-8') as f: json.dump(session.history.to_dict(), f, indent=2, ensure_ascii=False) logger.info(f" Transcript saved to {filename}") return filename except Exception as e: logger.error(f"Failed to save transcript: {e}") return None async def push_session_to_mongo( end_time: float, start_time: float, ctx: JobContext, session: AgentSession, ): transcript_json = session.history.to_dict() duration = int(end_time - start_time) call_datetime = datetime.fromtimestamp(start_time, timezone.utc).strftime('%Y-%m-%d %H:%M:%S') recording_url = f"{os.getenv('EOS_ENDPOINT')}/{os.getenv('EOS_BUCKET_NAME')}/livekit/{ctx.room.name}.ogg" push_to_mongo(duration, call_datetime, transcript_json, recording_url) async def log_usage(usage_collector: metrics.UsageCollector): summary = usage_collector.get_summary() logger.info(f"Usage: {summary}") @breezy-insurance-66772.rtc_session() async def entry( ctx: JobContext ): start_time = time.time() # add room info to all log entries ctx.log_context_fields = { "room": ctx.room.name, } if execution_mode == "local": stt_engine = build_local_stt() # llm_engine = build_local_llm() else: stt_engine = build_cloud_stt() # llm_engine = build_cloud_llm() # small hardcoded workaround to use cloud llm always, as we are evaluating gpt 4o mini llm_engine = build_cloud_llm() tts_engine = build_tts() usage_collector = metrics.UsageCollector() session = AgentSession( stt=stt_engine, llm=llm_engine, tts=tts_engine, turn_detection=MultilingualModel(), vad=ctx.proc.userdata["vad"], preemptive_generation=True, ) # ctx.add_shutdown_callback(lambda: write_transcript(ctx=ctx, session=session)) ctx.add_shutdown_callback(lambda: log_usage(usage_collector)) ctx.add_shutdown_callback(lambda: push_session_to_mongo(end_time=time.time(), start_time=start_time, ctx=ctx, session=session)) egress_req = api.RoomCompositeEgressRequest( room_name=ctx.room.name, audio_only=True, file_outputs=[ api.EncodedFileOutput( file_type=api.EncodedFileType.OGG, filepath=f"livekit/{ctx.room.name}.ogg", s3=api.S3Upload( bucket=os.getenv("EOS_BUCKET_NAME"), access_key=os.getenv("EOS_ACCESS_KEY"), secret=os.getenv("EOS_SECRET_KEY"), endpoint=os.getenv("EOS_ENDPOINT"), ), ) ], ) lkapi = api.LiveKitAPI() await lkapi.egress.start_room_composite_egress(egress_req) # await lkapi.aclose() # await ctx.wait_for_participant() try: # Start egress BEFORE the session to ensure it's ready to capture audio # try: # await lkapi.egress.start_room_composite_egress(egress_req) # logger.info("Started room composite egress for audio recording to S3.") # except Exception as e: # logger.error(f"Failed to start egress: {e}", exc_info=True) # continue; session can still run even if egress fails # Now start the session await session.start( agent=Assistant(system_prompt=SYSTEM_PROMPT), room=ctx.room, room_options=room_io.RoomOptions( audio_input=room_io.AudioInputOptions(), ), ) except Exception as e: logger.error(f"Failed to start session: {e}", exc_info=True) raise finally: try: await lkapi.aclose() logger.info("Closed lkapi.") except Exception as e: logger.debug(f"Failed to close lkapi: {e}", exc_info=True) if name == "__main__": cli.run_app(server) i want rnnoise to be integrated with my this agent.py file.
t
Thanks — I can help add RNNoise into your agent, but I need a few details so I give a correct, ready-to-run example you can drop into your agent.py. Please answer these (short answers are fine): 1. Real-time vs offline: Do you need low-latency streaming denoising while the agent runs, or can you denoise recorded files after the fact? 2. Where to run RNNoise: Do you want the denoiser inside the Agent process (pre-STT) or as a separate preprocessing service the agent sends audio to? 3. Audio format your agent uses: What sample rate, channels and PCM format are your audio frames? (e.g., mono PCM16 at 16 kHz, mono PCM16 at 48 kHz) 4. Runtime & deployment: Python version and deployment target (e.g., Python 3.10 on Docker x86_64)? 5. Installation constraints: Can you install native libraries / compile C code (most RNNoise bindings require this), or must we use only pure-Python packages? 6. Preferred binding: Are you okay using a pip binding such as
rnnoise-python
(which typically needs building native code), or do you want instructions to build RNNoise from source and call it with ctypes? 7. Where to hook it in your code: Do you want denoising applied just before STT receives frames (pre-STT in your AgentSession), or earlier when LiveKit hands you participant audio frames? If unsure, say “uncertain.” If you prefer, answer 1–4 and 6 only and I’ll provide: • A streaming (real-time) Python example showing how to wrap incoming PCM frames with RNNoise and feed the cleaned frames into the STT path, plus install/build steps. • An offline WAV example that shows file-based denoising. Once you reply I’ll produce the appropriate code snippet and installation steps tailored to your environment. Sources: Agent session | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentation
c
Real-time denoiser inside the Agent process (pre-STT) mono PCM16 at 16 kHz Docker x86_64 must we use only pure-Python packages uncertain. But do what is best.
t
Thanks — great details. You want real-time denoising inside the Agent process (pre-STT), mono PCM16 @ 16 kHz, running in Docker x86_64, and you prefer pure-Python if possible. Based on that, the easiest, supported approach is to run a denoiser inside the agent’s STT pipeline. The LiveKit agent ecosystem provides an
NC
noise-cancellation filter that uses RNNoise internally and is suitable for real-time, pre-STT denoising in the agent process. Below I’ll show a compact, ready-to-drop-in example you can adapt in your agent.py. It: • Instantiates the RNNoise-based
NC
filter inside the agent. • Applies the filter to incoming audio frames before calling the agent’s default STT node. • Assumes mono PCM16 audio at 16 kHz. Installation • Add the noise-cancellation plugin into your Docker image: ◦ pip install livekit livekit-plugins-noise-cancellation • Build your Docker image for x86_64 as you normally would. Example integration (insert into your agent file) 1. Replace or extend your Agent subclass to override the stt_node and apply denoising:
Copy code
from typing import AsyncIterable, Optional
from livekit import rtc
from livekit.agents import Agent, ModelSettings, stt

class NoisyAgent(Agent):
    def __init__(self, system_prompt: str = None) -> None:
        super().__init__(instructions=system_prompt)

    async def stt_node(
        self,
        audio: AsyncIterable[rtc.AudioFrame],
        model_settings: ModelSettings
    ) -> Optional[AsyncIterable[stt.SpeechEvent]]:
        # Import the NC (RNNoise-based) denoiser from the plugin
        from livekit.plugins import noise_cancellation

        # Instantiate denoiser (NC uses RNNoise under the hood)
        denoiser = <http://noise_cancellation.NC|noise_cancellation.NC>()

        # The filter method returns an async iterable of AudioFrame-like objects.
        # num_channels=1 because you indicated mono PCM16 at 16 kHz.
        denoised_audio = denoiser.filter(audio, num_channels=1)

        # Forward the denoised audio to the default STT node for transcription
        async for event in Agent.default.stt_node(self, denoised_audio, model_settings):
            yield event
1. Hook this Agent in your existing flow • In your entry/session code where you create an AgentSession, use this agent class:
Copy code
session = AgentSession(
    stt=stt_engine,
    llm=llm_engine,
    tts=tts_engine,
    turn_detection=MultilingualModel(),
    vad=ctx.proc.userdata["vad"],
    preemptive_generation=True,
)
await session.start(
    agent=NoisyAgent(system_prompt=SYSTEM_PROMPT),
    room=ctx.room,
    room_options=room_io.RoomOptions(
        audio_input=room_io.AudioInputOptions(),
    ),
)
Notes and best practices • Sample rate: Your audio is 16 kHz PCM16 mono — the
NC
filter is suitable for this sample rate. Confirm the frames coming from LiveKit are indeed mono PCM16 @ 16 kHz to avoid resampling issues. • Latency: RNNoise is designed for low-latency denoising and works well for streaming/real-time scenarios. Keep buffer sizes small to minimize added latency. • Resource use: Running denoising inside the agent process consumes CPU; test under expected concurrency to size your host appropriately. • Fallback: If you see issues or need different noise characteristics, you can combine VAD (Silero VAD is already loaded in your prewarm) with denoising to avoid processing silence and reduce CPU. If you want, I can: • Provide a small Dockerfile snippet showing installation and how to build the container. • Add a concrete example of how to read/inspect the incoming AudioFrame sample rate/format to validate frames are 16 kHz PCM16. • Show how to integrate Silero VAD to only run the denoiser when speech is active (saves CPU). Which follow-up would help most: Dockerfile + build steps, or validating audio frame format in your current agent? Sources: Choosing the right noise cancellation for your agent | LiveKit