Metadata-Version: 2.4
Name: arctan-vi
Version: 0.7.1
Summary: Arctan Voice Isolation for voice agents
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: cryptography<49,>=48.0.1
Requires-Dist: numpy<3,>=2
Requires-Dist: sentry-sdk>=2.64.0
Provides-Extra: livekit
Requires-Dist: livekit<2,>=1.0.25; extra == "livekit"
Provides-Extra: pipecat
Requires-Dist: pipecat-ai<2,>=1.3; python_version >= "3.11" and extra == "pipecat"

# Arctan Voice Isolation

Arctan Voice Isolation is a Python SDK for real-time voice isolation in voice
agents and audio pipelines.

The SDK supports:

- standard Numpy audio processing
- LiveKit audio input processing
- Pipecat audio input filtering

An Arctan SDK key is required before audio can be processed.

Model and runtime artifacts are downloaded only after authorization and are
verified against authorization-provided SHA-256 digests. The encrypted Eigen
bundle is decrypted into caller-zeroable process memory and loaded through the
runtime's in-memory ABI; SDK `0.7.1` does not materialize plaintext model files,
anonymous file descriptors, or named pipes.

## Installation

Install the base SDK:

```bash
pip install arctan-vi
```

Install with framework adapters:

```bash
pip install "arctan-vi[livekit]"
pip install "arctan-vi[pipecat]"
```

The base SDK and LiveKit adapter support Python 3.10+. The Pipecat adapter
requires Python 3.11+.

Set your SDK key in the runtime environment:

```bash
export ARCTAN_SDK_KEY="arc_sk_live_..."
```

You can also pass `license_key="arc_sk_live_..."` directly to the processor or
adapter constructors. An explicit `license_key` takes precedence over
`ARCTAN_SDK_KEY`.

## Standard Python

For multiple streams, load one shared `Model` and create one ordered processor
per stream. `ProcessorAsync` runs blocking native inference off the event loop;
calls on one processor remain sequential while different processors can run in
parallel.

```python
import asyncio
import numpy as np
import arctan

model = arctan.Model()
config = arctan.ProcessorConfig.optimal(model, num_channels=1)
call_a = arctan.ProcessorAsync(model=model, config=config)
call_b = arctan.ProcessorAsync(
    model=model,
    config=arctan.ProcessorConfig.optimal(model),
)

async def process_calls():
    audio = np.zeros((config.num_channels, config.num_frames), dtype=np.float32)
    enhanced_a, enhanced_b = await asyncio.gather(
        call_a.process_async(audio),
        call_b.process_async(audio),
    )
    return enhanced_a, enhanced_b

try:
    enhanced_a, enhanced_b = asyncio.run(process_calls())
finally:
    call_a.close()
    call_b.close()
    model.close()
```

Create exactly one processor per audio stream and do not submit frames from the
same stream out of order. The number of processors is the number of active
streams; it is not a universal host-capacity claim.

The existing single-stream convenience remains supported. It creates and owns
a private model internally:

```python
processor = arctan.Processor(
    config=arctan.ProcessorConfig(sample_rate=24000),
)
```

`Processor.process()` accepts `float32` audio shaped as `channels x frames`.
Samples must be finite and in `[-1.0, 1.0]`. The output is a new `float32`
array with the same shape.

Mono and stereo input are supported. Stereo input is downmixed for voice
isolation and returned as dual-mono stereo. The sample rate must remain stable
for the lifetime of a processor.

If `num_frames` is omitted, initialization fills it with the native frame size
for the selected sample rate. If you set `num_frames`, it must contain a whole
number of native processing blocks.

## LiveKit

Install the LiveKit extra:

```bash
pip install "arctan-vi[livekit]"
```

Create an Arctan noise-cancellation processor and pass it to LiveKit audio input
options:

```python
from arctan.livekit import arctan_enhancer
from livekit.agents import room_io

await session.start(
    agent=Assistant(),
    room=ctx.room,
    room_options=room_io.RoomOptions(
        audio_input=room_io.AudioInputOptions(
            noise_cancellation=arctan_enhancer.noise_canceller(),
        ),
    ),
)
```

You can pass SDK options directly to Arctan:

```python
noise_cancellation = arctan_enhancer.noise_canceller(
    license_key="arc_sk_live_...",
)
```

LiveKit mono and stereo frames are supported. Stereo frames are downmixed for
voice isolation and returned as dual-mono stereo. The sample rate must remain
stable, and each input frame must contain a whole number of native processing
blocks.

## Pipecat

Install the Pipecat extra:

```bash
pip install "arctan-vi[pipecat]"
```

Create an `ArctanAudioFilter` and pass it as the input audio filter for your
transport:

```python
from arctan.pipecat import arctan_enhancer
from pipecat.transports.base_transport import TransportParams

audio_filter = arctan_enhancer.ArctanAudioFilter()

transport_params = TransportParams(
    audio_in_enabled=True,
    audio_in_filter=audio_filter,
)
```

`ArctanAudioFilter` implements Pipecat's `BaseAudioFilter`. Pipecat calls
`start(sample_rate)`, `filter(audio)`, and `stop()` through the transport
lifecycle.

The Pipecat adapter accepts mono PCM16 input. Arbitrary byte chunks are buffered
until a complete native processing block is available, so `filter()` may return
`b""`. `FilterEnableFrame` can bypass or re-enable processing; toggling clears
any incomplete buffered block.
