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

# Text-to-Speech (Realtime)

> Real-time text-to-speech with streaming audio via WebSocket.

<Info>
  **Currently in beta.** You're on the priority waitlist and among the first to get access.
</Info>

## Overview

Stream audio in real-time with the lowest latency. Perfect for interactive assistants and live applications.

| Use case                                      | Recommended endpoint                        |
| --------------------------------------------- | ------------------------------------------- |
| Lowest-latency interactive / live apps        | **This endpoint**                           |
| Full audio in one response (downloads, batch) | [TTS REST](/vachana/TTS/tts-inference)      |
| Chunked playback as audio is generated        | [TTS Streaming (SSE)](/vachana/TTS/tts-sse) |

<Note>
  Passing numbers, IDs, dates, or currency as raw strings causes mispronunciations. See the [Input Formatting Guide](/vachana/TTS/tts-input-formating) for correct formatting of phone numbers, account numbers, PINs, Aadhaar, vehicle registration numbers, GSTIN, currency, and more.
</Note>

***

## Available Voices

To see the available voices, [click here](/api/TTS/available-voices).

***

## Endpoint

```text theme={null}
WSS wss://api.vachana.ai/api/v1/tts
```

***

## Connection Headers

All configuration is passed as WebSocket upgrade headers at connection time. Headers cannot be changed mid-session — reconnect with new headers to change settings.

| Header         | Required | Description                     | Example             |
| -------------- | -------- | ------------------------------- | ------------------- |
| `Content-Type` | ✅        | Must be `application/json`      | `application/json`  |
| `X-API-Key-ID` | ✅        | Your API key for authentication | `<your-api-key-id>` |

***

## Connection Flow

A WebSocket session follows a strict sequence:

1. **Client connects** — opens a WebSocket to `/api/v1/tts` with all required headers.
2. **Client sends text** — sends a JSON message with the text to synthesize and `audio_config`.
3. **Server streams audio** — returns audio as binary chunks according to the requested `audio_config`.
4. **Server completes** — synthesis finishes; the client may close the connection or send another request.
5. **Either side closes** — client or server may close the connection at any time.

<Note>
  Send one synthesis request at a time and consume audio chunks as they arrive for the lowest end-to-end latency.
</Note>

***

## Request Parameters

Send a JSON text frame with the following structure:

```json theme={null}
{
  "text": "नमस्ते, आप कैसे हैं?",
  "voice": "Pranav",
  "model": "Timbre v2.0 / Timbre v2.5",
  "audio_config": {
    "sample_rate": 48000,
    "num_channels": 1,
    "sample_width": 2,
    "encoding": "linear_pcm",
    "container": "wav"
  }
}
```

<ParamField body="text" type="string" required>
  The text to synthesize into speech. Pass numbers, dates, and currency as spoken words to avoid mispronunciations — see [Input Formatting Guide](/vachana/TTS/tts-input-formating).
</ParamField>

<ParamField body="model" type="string" required>
  TTS model to use. Currently supported: `Timbre v2.0 / Timbre v2.5`
</ParamField>

<ParamField body="voice" type="string">
  ID of a pre-defined voice. See [Available Voices](/api/TTS/available-voices)
</ParamField>

<ParamField body="sample_rate" type="integer" default="48000">
  Sample rate in Hz. Supported: `8000`, `16000`, `22050`, `24000`, `44100`, `48000`.
</ParamField>

<ParamField body="num_channels" type="integer" required>
  Number of audio channels (e.g., `1` for mono, `2` for stereo)
</ParamField>

<ParamField body="sample_width" type="integer" required>
  Sample width in bytes (e.g., `2` for 16-bit audio)
</ParamField>

<ParamField body="encoding" type="string" default="linear_pcm">
  Audio encoding. Options: `linear_pcm`, `pcm_s16le`, `pcm_mulaw`, `pcm_alaw`, `oggopus`. For telephony, prefer `container=mulaw` or `container=alaw` over this field — both produce the same output. Use `oggopus` (or `container=ogg`) for a playable OGG Opus file.
</ParamField>

<ParamField body="container" type="string" default="wav">
  Output container. Options: `wav`, `raw`, `mp3`, `ogg`, `mulaw`, `alaw`. Use `ogg` for OGG Opus. Use `mulaw` or `alaw` for G.711 telephony (forces 8000 Hz regardless of `sample_rate`).
</ParamField>

<ParamField body="bitrate" type="string" default="128k">
  MP3 bitrate. Only used when `container` is `mp3`. Supported: `32k`, `64k`, `96k`, `128k`, `192k`.
</ParamField>

## Audio Format Reference

| `container` | `encoding`   | Output                 | `sample_rate`      | `bitrate`    | Content-Type               |
| ----------- | ------------ | ---------------------- | ------------------ | ------------ | -------------------------- |
| `wav`       | `linear_pcm` | WAV file (with header) | 8000–48000 Hz      | —            | `audio/wav`                |
| `raw`       | `linear_pcm` | Raw 16-bit PCM         | 8000–48000 Hz      | —            | `application/octet-stream` |
| `mp3`       | —            | MP3 file               | 8000–48000 Hz      | `32k`–`192k` | `audio/mpeg`               |
| `ogg`       | —            | OGG Opus file          | 8000–48000 Hz      | —            | `audio/ogg`                |
| `mulaw`     | —            | Raw G.711 µ-law        | **forced 8000 Hz** | —            | `audio/basic`              |
| `alaw`      | —            | Raw G.711 A-law        | **forced 8000 Hz** | —            | `audio/alaw`               |

**Encoding aliases** — produce identical output to the `container` rows above:

| `container`    | `encoding`  | Equivalent to     |
| -------------- | ----------- | ----------------- |
| `raw`          | `pcm_mulaw` | `container=mulaw` |
| `raw`          | `pcm_alaw`  | `container=alaw`  |
| `raw` or `ogg` | `oggopus`   | `container=ogg`   |

`bitrate` only applies when `container=mp3`. `container=mulaw`/`alaw` override `sample_rate` to 8000 Hz.

<Note>
  When `container=ogg` or `encoding=oggopus` is requested, all audio is encoded into a single OGG Opus file and delivered as one chunk after synthesis completes.
</Note>

***

## Server Messages

The server streams audio data in real-time. Each binary chunk contains audio encoded in the format specified by `audio_config`.

### Audio chunk

Binary frames containing synthesized audio. Append chunks in order to rebuild the full audio stream.

| Property   | Description                                        |
| ---------- | -------------------------------------------------- |
| Frame type | Binary WebSocket frame                             |
| Contents   | PCM audio bytes matching `audio_config`            |
| Ordering   | Chunks arrive sequentially as synthesis progresses |

### `completed`

Emitted when synthesis for the current request has finished.

```json theme={null}
{
  "event": "completed",
  "status": "success"
}
```

| Field    | Type     | Description                                          |
| -------- | -------- | ---------------------------------------------------- |
| `event`  | `string` | Always `"completed"`.                                |
| `status` | `string` | Outcome of the synthesis request (e.g. `"success"`). |

### `error`

Sent when the server encounters a recoverable or fatal error. The connection may remain open after a recoverable error.

```json theme={null}
{
  "event": "error",
  "message": "Invalid text or audio configuration."
}
```

| Field     | Type     | Description                              |
| --------- | -------- | ---------------------------------------- |
| `event`   | `string` | Always `"error"`.                        |
| `message` | `string` | Human-readable description of the error. |

***

## Code Example

<CodeGroup>
  ```javascript JavaScript theme={null}
  const ws = new WebSocket("wss://api.vachana.ai/api/v1/tts", {
    headers: {
      "Content-Type": "application/json",
      "X-API-Key-ID": "<your-api-key>",
    },
  });

  ws.on("open", () => {
    const request = {
      text: "नमस्ते, आप कैसे हैं?",
      voice: "Pranav",
      model: "Timbre v2.0 / Timbre v2.5",
      audio_config: {
        sample_rate: 48000,
        num_channels: 1,
        sample_width: 2,
        encoding: "linear_pcm",
        container: "wav",
      },
    };

    ws.send(JSON.stringify(request));
  });

  ws.on("message", (data) => {
    // Handle audio chunks
    console.log("Received audio chunk:", data);
  });

  ws.on("error", (error) => {
    console.error("WebSocket error:", error);
  });

  ws.on("close", () => {
    console.log("WebSocket connection closed");
  });
  ```

  ```python Python theme={null}
  import websocket
  import json

  def on_message(ws, message):
      # Handle audio chunks
      print(f"Received audio chunk: {len(message)} bytes")

  def on_error(ws, error):
      print(f"Error: {error}")

  def on_close(ws, close_status_code, close_msg):
      print("WebSocket connection closed")

  def on_open(ws):
      request = {
          "text": "नमस्ते, आप कैसे हैं?",
          "voice": "Pranav",
          "model": "Timbre v2.0 / Timbre v2.5",
          "audio_config": {
              "sample_rate": 48000,
              "num_channels": 1,
              "sample_width": 2,
              "encoding": "linear_pcm",
              "container": "wav",
          },
      }
      ws.send(json.dumps(request))

  ws = websocket.WebSocketApp(
      "wss://api.vachana.ai/api/v1/tts",
      header={
          "Content-Type": "application/json",
          "X-API-Key-ID": "<your-api-key>",
      },
      on_open=on_open,
      on_message=on_message,
      on_error=on_error,
      on_close=on_close,
  )

  ws.run_forever()
  ```
</CodeGroup>

***

## Python SDK

The SDK's realtime client manages the WebSocket lifecycle, audio streaming, and async iteration so you can focus on your application logic.

### Installation

```bash theme={null}
pip install gnani-vachana
```

Requires **Python 3.10+**.

### Authentication

<CodeGroup>
  ```python Constructor argument theme={null}
  from gnani.tts import GnaniTTSRealtimeClient

  client = GnaniTTSRealtimeClient(api_key="your-api-key")
  ```

  ```bash Environment variable theme={null}
  export GNANI_API_KEY="your-api-key"
  ```

  ```python Environment variable (usage) theme={null}
  from gnani.tts import GnaniTTSRealtimeClient

  client = GnaniTTSRealtimeClient()
  ```
</CodeGroup>

### Stream Audio Chunks in Real-Time

Use the async context manager to open the connection and iterate over audio chunks as they arrive.

```python theme={null}
import asyncio
from gnani.tts import GnaniTTSRealtimeClient

async def main():
    async with GnaniTTSRealtimeClient(api_key="your-api-key") as client:
        with open("output.wav", "wb") as f:
            async for chunk in client.synthesize(
                "नमस्ते, आप कैसे हैं?",
                voice="Kaveri",
            ):
                f.write(chunk)

asyncio.run(main())
```

### Collect All Audio at Once

If you don't need to process chunks as they arrive, use `synthesize_and_collect` to get the full audio as a single bytes object.

```python theme={null}
import asyncio
from gnani.tts import GnaniTTSRealtimeClient

async def main():
    async with GnaniTTSRealtimeClient(api_key="your-api-key") as client:
        audio = await client.synthesize_and_collect(
            "Realtime TTS response",
            voice="Pranav",
        )
        with open("output.wav", "wb") as f:
            f.write(audio)

asyncio.run(main())
```

***

## Supported Languages

The Gnani Timbre v2.0 API supports 2 languages.

| Language | Native Script       | Example                    |
| -------- | ------------------- | -------------------------- |
| English  | Latin               | "I am going to the market" |
| Hindi    | Devanagari (हिन्दी) | "मैं बाज़ार जा रहा हूँ"    |
