pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog

RNNoise filter

Suppress background noise in the user's audio with RNNoise, compiled to WebAssembly so it runs on Bun, Node, Deno and Cloudflare Workers.

Overview

RNNoiseFilter removes background noise from the user's audio before it reaches VAD and speech-to-text, using RNNoise, a small recurrent neural network. It is a port of Pipecat's RNNoiseFilter. Upstream calls the native pyrnnoise library; pipedog runs RNNoise 0.2 as WebAssembly, shipped inside the package, so there is nothing to install and it works on Cloudflare Workers too.

Installation

bun add @pipedog/audio-rnnoise-filter
import { RNNoiseFilter } from "@pipedog/audio-rnnoise-filter";

Runtimes

RuntimeWorksWhat it needs
Bun✅ yesNothing to install: the WebAssembly module ships in the package.
Node.js✅ yesNothing to install: the WebAssembly module ships in the package.
Deno✅ yesNothing to install: the WebAssembly module ships in the package.
Cloudflare Workers✅ yesNothing to install: the WebAssembly module ships in the package. The bundler must import .wasm files as modules (wrangler's default rule).

Generated from pipecat.runtimes and pipecat.compat in packages/audio-rnnoise-filter/package.json: ✅ means the compat smoke test passes on that runtime.

Usage

Set the filter as the transport's audioInFilter. It starts and stops with the transport.

import { RNNoiseFilter } from "@pipedog/audio-rnnoise-filter";
import { TransportParams } from "@pipedog/core/transports/base_transport";

const params = new TransportParams({
  audioInEnabled: true,
  audioInFilter: new RNNoiseFilter(),
});

RNNoise works on 480-sample frames (10 ms) at 48 kHz. The filter buffers incoming audio into frames and, when the transport is not at 48 kHz, resamples in and out with the soxr-style stream resampler (@pipedog/resampler-wasm). A call returns the frames that completed, so the first calls can be empty, and the output lags the input by less than one frame.

Send a FilterEnableFrame to switch the filter on and off while the bot runs:

import { FilterEnableFrame } from "@pipedog/core/frames/frames";

await worker.queueFrame(new FilterEnableFrame({ enable: false }));

Configuration

new RNNoiseFilter(options?):

OptionTypeDefaultDescription
resamplerQuality"VHQ" | "HQ" | "MQ" | "LQ" | "QQ""QQ"Resampler preset when the transport is not at 48 kHz. "QQ" has the lowest latency.
rnnoise({ sampleRate }) => RNNoiseLikethe bundled WASM buildCreate your own denoiser, for example a fake in tests.

Lower-level pieces are exported too: initRnnoise(source?) loads the module from bytes, a WebAssembly.Module, a URL or a Response (on Workers only a module), RNNoise mirrors pyrnnoise's denoise_chunk, and RnnoiseEngine.createState() gives the raw 480-sample processFrame.

Cloudflare Workers

workerd refuses to compile WebAssembly from bytes, so on Workers the package's "workerd" export condition imports rnnoise.wasm as a pre-compiled module. Wrangler's default rule for .wasm files does this; nothing else is needed. The module is 1.4 MB.

Differences from upstream

  • WebAssembly instead of pyrnnoise. The model is RNNoise 0.2, so the denoising is the same kind of output, but it is not bit-identical. The module is the one built by @jitsi/rnnoise-wasm (Apache-2.0).
  • The rnnoise option and a protected _createResampler() hook replace the module-level patching upstream's tests do.

See the package README for the licenses of what it bundles.

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page