How do you use Anecho speaker isolation as a Pipecat audio filter?
Write a small Pipecat BaseAudioFilter that re-chunks the transport's inbound audio into 240-sample hops at 16 kHz and runs the Anecho SDK on each hop. It is code you write, not a published plugin; the full filter and its numbers are below.
You use Anecho speaker isolation in Pipecat by writing a small BaseAudioFilter that re-chunks the transport's inbound audio into 240-sample hops at 16 kHz and runs the Anecho SDK on each hop. We do not ship a Pipecat package. Pipecat's audio-filter slot already carries Krisp VIVA, ai-coustics, RNNoise, Koala and noisereduce (docs.pipecat.ai, retrieved 2026-10-15); ours is the code below, which you own and adapt.
Where it sits
A Pipecat transport accepts audio_in_filter, which sees raw inbound bytes before VAD and STT. That is the right place for a model that must run on a continuous stream. The SDK contract: Processor.process() takes a float32 block and returns the same number of samples 15 ms later; the first 240 samples are silence (the algorithmic delay). The UsageReporter sends durations, never audio.
The filter
Pipecat hands the filter arbitrary-sized int16 byte chunks at the transport's rate. The model wants 240-sample hops at 16 kHz and the filter must return as many bytes as it received, so it buffers on both sides.
import os
import numpy as np
from pipecat.audio.filters.base_audio_filter import BaseAudioFilter
from pipecat.frames.frames import FilterControlFrame
from anecho import Model, Processor, UsageReporter
RATE, HOP = 16_000, 240 # model rate; 240 samples = 15 ms
class AnechoFilter(BaseAudioFilter):
"""Speaker isolation on the transport's inbound audio. Our code, not a package."""
def __init__(self, model_path: str):
self._model = Model.from_file(model_path)
self._reporter = UsageReporter(os.environ["ANECHO_API_KEY"])
self._proc = None
self._pending = np.zeros(0, dtype=np.float32) # input not yet a full hop
self._out = np.zeros(HOP, dtype=np.float32) # one-hop prefill keeps output ahead of input
async def start(self, sample_rate: int):
if sample_rate != RATE: # resample at the transport, not here
raise ValueError(f"AnechoFilter needs 16 kHz input, got {sample_rate}")
self._proc = Processor(self._model, self._reporter.ensure_license())
self._reporter.attach(self._proc)
self._reporter.start()
async def stop(self):
self._proc = None
async def process_frame(self, frame: FilterControlFrame):
pass # no runtime controls
async def filter(self, audio: bytes) -> bytes:
x = np.frombuffer(audio, dtype=np.int16).astype(np.float32) / 32768.0
self._pending = np.concatenate([self._pending, x])
while len(self._pending) >= HOP:
block, self._pending = self._pending[:HOP], self._pending[HOP:]
self._out = np.concatenate([self._out, self._proc.process(block)])
y, self._out = self._out[: len(x)], self._out[len(x):] # same byte count out as in
return (np.clip(y, -1.0, 1.0) * 32767).astype(np.int16).tobytes()
transport = SmallWebRTCTransport(
webrtc_connection=conn,
params=TransportParams(
audio_in_enabled=True,
audio_in_filter=AnechoFilter("anecho.ai_focus_model_16khz_v8_2.anecho"),
),
)
This is a sketch against the BaseAudioFilter interface (start, stop, process_frame, filter). We have not benchmarked a live Pipecat pipeline, so check the interface against your Pipecat version and test on your own audio. Because output is buffered to the input length, the filter adds the model's 15 ms plus up to one hop of buffering.
Notes
- The filter requires 16 kHz input and raises otherwise, so set the transport's
audio_in_sample_rateor resample before it. Upsampling 8 kHz telephony cannot restore bandwidth, and in our run the isolation model made telephony conditions slightly worse; see 8 kHz is where voice AI breaks. - Create one filter per call, since the processor holds state.
- Pipecat also has turn-detection models trained for noise; isolation and turn detection fix different failure modes (why voice agents fail in noisy calls).
What we measured
| Item | Value | Source and date |
|---|---|---|
| Algorithmic delay | 15 ms | Null Test, 2026-08-14 |
| Real-time factor, one CPU thread | 0.067 | Null Test, 2026-08-14 (Apple M1 Max) |
| Worst block time, p99 | 3.96 ms | Null Test, 2026-08-14 |
| Two voices at 0 dB, WER raw vs ours | 84.74% vs 74.21% | Null Test, 2026-08-14 |
| Pooled over 19 conditions, WER raw vs ours | 14.32% vs 16.59% | Null Test, 2026-08-14 |
The pooled row is a loss, which is why this is a filter you opt into, per deployment, after measuring: how to evaluate speaker isolation.
FAQ
Is there an Anecho package for Pipecat?
No. There is no published package. The filter above is code you write against the Pipecat BaseAudioFilter interface.
Which Pipecat extension point do I use?
The transport's audio_in_filter, which sees inbound audio before VAD and STT.
Why does the filter buffer?
The model works on 240-sample hops at 16 kHz while Pipecat delivers arbitrary-sized chunks at the transport rate, and the filter must return the same number of bytes it receives.