> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ai-coustics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> Process a WAV with Python, then adapt the SDK to synchronous or async audio streams.

**ai-coustics** Python bindings accept NumPy audio arrays. The tutorial below uses `aic-sdk` 3.3.0, which wraps native SDK 0.25.0. Use the [Python API reference](/reference/sdk/api/python/index) for classes, functions, properties and errors. The released [type declarations](https://github.com/ai-coustics/aic-sdk-py/blob/3.3.0/aic_sdk.pyi) and [examples](https://github.com/ai-coustics/aic-sdk-py/tree/3.3.0/examples) provide source context.

## Installation

Use Python 3.11 for this example on a [supported platform](/reference/sdk/compatibility-matrix). The SDK package declares Python 3.10 or later. This example pins `aic-sdk` 3.3.0 and NumPy 2.2.6. Create a project directory and a virtual environment:

<CodeGroup>
  ```bash macOS / Linux theme={null}
  mkdir aic-python-quickstart
  cd aic-python-quickstart
  python3 -m venv .venv
  source .venv/bin/activate
  python -m pip install "aic-sdk==3.3.0" "numpy==2.2.6"
  ```

  ```powershell Windows PowerShell theme={null}
  mkdir aic-python-quickstart
  cd aic-python-quickstart
  py -m venv .venv
  .venv\Scripts\Activate.ps1
  python -m pip install "aic-sdk==3.3.0" "numpy==2.2.6"
  ```
</CodeGroup>

## Quickstart

### Prepare the key and input

Generate an SDK key on the [developer platform](https://developers.ai-coustics.com). Set it in the terminal where you will run the example, replacing `YOUR_SDK_KEY` with your key. Keep it out of source control and shared terminal logs.

<CodeGroup>
  ```bash macOS / Linux theme={null}
  export AIC_SDK_LICENSE="YOUR_SDK_KEY"
  ```

  ```powershell Windows PowerShell theme={null}
  $env:AIC_SDK_LICENSE = "YOUR_SDK_KEY"
  ```
</CodeGroup>

Download the [noisy speech fixture](/assets/audio/noisy-speech.wav) and save it as `input.wav` in `aic-python-quickstart`. It contains 56,080 mono PCM16 samples at 16 kHz (3.505 seconds). The [fixture guide](/guides/test-audio) includes attribution, checksums and the aligned clean reference.

For your own recording, export a short sentence with background noise as **mono, 16 kHz, signed 16-bit PCM WAV**. Renaming a file does not convert it.

The example downloads `quail-vf-2.2-l-16khz` into `./models`. Quail Voice Focus isolates the primary speaker for speech-to-text (STT) input. The first run needs network access for the model download and SDK key authorization. See [authentication](/models/get-started/authenticate-apps) for deployment options.

### Save and run

Save this complete program as `quickstart.py` in the same directory:

```python quickstart.py theme={null}
import os
from pathlib import Path
import sys
import wave

import aic_sdk as aic
import numpy as np


def main():
    license_key = os.environ.get("AIC_SDK_LICENSE")
    if not license_key or license_key == "YOUR_SDK_KEY":
        raise ValueError("Set AIC_SDK_LICENSE to your SDK key, then retry.")

    with wave.open("input.wav", "rb") as source:
        if (source.getnchannels(), source.getframerate(), source.getsampwidth()) != (1, 16000, 2):
            raise ValueError("Export input.wav as mono, 16 kHz, 16-bit PCM WAV, then retry.")
        frames = source.getnframes()
        raw = source.readframes(frames)
        if len(raw) != frames * 2:
            raise ValueError("input.wav is truncated. Export the complete recording, then retry.")
    audio = np.frombuffer(raw, dtype="<i2").astype(np.float32) / 32768.0
    if audio.size == 0:
        raise ValueError("input.wav is empty. Record speech, then retry.")

    model_path = aic.Model.download("quail-vf-2.2-l-16khz", Path("models"))
    model = aic.Model.from_file(model_path)
    config = aic.ProcessorConfig.optimal(model, sample_rate=16000)
    processor = aic.Processor(model, license_key)
    try:
        processor.initialize(config)
        delay = processor.get_context().get_audio_delay()
        # Pad the last block and flush the delayed tail with silence.
        padded_size = ((audio.size + delay + config.block_size - 1) // config.block_size) * config.block_size
        padded = np.zeros(padded_size, dtype=np.float32)
        padded[:audio.size] = audio
        output = np.empty_like(padded)
        for start in range(0, padded_size, config.block_size):
            end = start + config.block_size
            # Python returns a new array; the input block is unchanged.
            output[start:end] = processor.process(padded[start:end])
        enhanced = output[delay:delay + audio.size]
        if not np.isfinite(enhanced).all():
            raise ValueError("Processing returned non-finite samples; no output was written.")
        pcm = (np.clip(enhanced, -1.0, 32767 / 32768) * 32768).astype("<i2")
        with wave.open("enhanced.wav", "wb") as destination:
            destination.setnchannels(1)
            destination.setsampwidth(2)
            destination.setframerate(16000)
            destination.writeframes(pcm.tobytes())
        print(f"SDK {aic.get_sdk_version()}, model {model.get_id()}")
        print(f"Processed {audio.size} samples at 16000 Hz; wrote enhanced.wav")
    finally:
        processor.terminate_session()


if __name__ == "__main__":
    try:
        main()
    except Exception as error:
        message = getattr(error, "message", str(error))
        print(f"{type(error).__name__}: {message}", file=sys.stderr)
        sys.exit(1)
```

Run it from `aic-python-quickstart`, with the virtual environment active:

```bash theme={null}
python quickstart.py
```

### Check the result

The program prints the native SDK version, resolved model ID and processed sample count. It writes `enhanced.wav` beside `input.wav` at 16 kHz with the same sample count, compensating for [processing delay](/reference/concepts/latency).

With the supplied fixture, the processed sample count is `56080`. Verify the file headers:

```bash theme={null}
python -c "import wave; a=wave.open('input.wav'); b=wave.open('enhanced.wav'); assert a.getparams()[:4] == b.getparams()[:4]; print('PASS: matching channels, bit depth, sample rate and frame count'); a.close(); b.close()"
```

Listen to both recordings and compare them with the same STT settings to evaluate quality.

### Recover from an error

| Problem | Cause and retry |
| - | - |
| **Package installation fails** | Check `python --version` and the [compatibility matrix](/reference/sdk/compatibility-matrix). Activate the virtual environment and rerun the pinned installation. |
| **Missing key or `LicenseFormatInvalidError`** | Set `AIC_SDK_LICENSE` in this terminal to the complete SDK key from the developer platform, then rerun `python quickstart.py`. |
| **`LicenseExpiredError` or `ProcessingNotAllowedError`** | Check key validity, account entitlement and required connectivity in [authentication](/models/get-started/authenticate-apps). Correct the cause and restart the program with a new processor. |
| **Missing or invalid `input.wav`** | Save a nonempty mono, 16 kHz, signed 16-bit PCM WAV in the current directory, then rerun the program. |
| **Model download or load fails** | Check connectivity, write access to `./models` and the exact model ID. For manual model files, check the [model format version](/reference/sdk/compatibility-matrix). Retry the download after fixing the cause. |
| **`AudioConfigMismatchError`** | Keep every call at `config.block_size` samples. The example pads the final block; do not pass a short final slice with fixed block sizes. |

### Adapt this example

Use one processor per independent stream and retain it between consecutive blocks. Reset its context when seeking or starting unrelated audio. Downmix stereo input or give each channel its own processor. See [audio format](/reference/concepts/audio-format), [streams and state](/reference/concepts/streams-and-state) and the [Python binding guide](/reference/sdk/language-bindings/python#integration-behavior) for async processing and lifecycle details.

This example reads the full file into memory. For live audio or long files, process bounded blocks and keep file I/O, model downloads and session termination outside the audio callback. For human listening, evaluate [Rook Multi Speaker](/models/perceptual-speech-enhancement/rook-multi-speaker).

## Integration behavior

| Concern | Python 3.3.0 behavior |
| - | - |
| **Input and output** | `Processor.process(audio)` accepts a one-dimensional mono `numpy.float32` array and returns a new array. It leaves the input unchanged. Use normalized samples, nominally -1.0 to 1.0. |
| **Initialization** | Pass a `ProcessorConfig` to the constructor or call `initialize(config)` before processing. `ProcessorConfig.optimal(model, sample_rate=...)` selects the block size for your input rate. |
| **Block size** | Calls use exactly `config.block_size` samples by default. With `variable_block_size=True`, shorter blocks are allowed, with additional buffering delay. Larger blocks are rejected. |
| **State** | Retain one processor per stream. A loaded model can be shared by independent processors. Use `processor.get_context().reset()` on a discontinuity or before unrelated audio. Reset preserves the configured audio settings. |
| **Parameters and delay** | Obtain a context with `get_context()`. It exposes `set_parameter`, `get_parameter` and `get_audio_delay`. Delay is measured in samples at the configured input rate. |
| **Async processing** | `ProcessorAsync` exposes `initialize_async`, `process_async` and `terminate_session_async`. Await each operation before the next block on that stream. It returns a new array. |
| **Worker configuration** | `AIC_NUM_THREADS` controls the Python SDK's async processing thread pool. Set it before starting the process. Measure throughput and latency for your workload before changing it. |
| **Cleanup** | Stop submitting audio, then call `terminate_session()` in `finally`, or await `terminate_session_async()` for async processing. This requests termination; processing may be disabled after the call returns. Native memory is released when Python objects are destroyed. |
| **Model files** | `Model.from_file` memory-maps the model. Keep the model file unchanged while the model or its processors are alive. `Model.download` can reuse a fresh cached manifest and a valid cached model file. It accesses the network when the cache needs revalidation or a download. See [model downloading](/reference/sdk/api/python/models-and-config#model-download) for cache and retry behavior. |

Construction, model loading and `ProcessorAsync.get_context()` are synchronous. Create these objects before the audio loop; using the async processor does not make every operation nonblocking. SDK session termination can block and belongs outside a real-time callback.

For voice activity detection (VAD), use a separate `Vad` or `VadAsync` with a dedicated VAD model. Pass the original input to VAD alongside enhancement; see [ai-coustics VAD](/models/voice-activity-detection/vad) and the [VAD example](https://github.com/ai-coustics/aic-sdk-py/blob/3.3.0/examples/vad.py). To read speech decisions from the enhancement processor itself, without a second model, use [`get_energy_vad_context()`](/reference/sdk/api/python/enhancement#processor-get_energy_vad_context). `FileAnalyzer` provides Python file-analysis helpers for Tyto; this is a different task from enhancement.

## Handle SDK errors

Catch specific SDK exception classes where your application can recover. Version 3.3.0 exposes classes such as `LicenseFormatInvalidError`, `ModelVersionUnsupportedError`, `AudioConfigMismatchError` and `ProcessingNotAllowedError`; their diagnostic text is available as `.message`. Do not rely on generic `ValueError` or `RuntimeError` alone to catch SDK errors.

Keep bounded error handling around each stream. A retry does not fix an unsupported model format or an incorrect block size: correct the configuration first. For credential failures, follow [authentication](/models/get-started/authenticate-apps). For audio interruptions and processor reuse, follow [streams and state](/reference/concepts/streams-and-state).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.