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

# Implement text-to-speech

<Warning>
  Qwen3-TTS is a **preview** feature available for SambaStack users only. Preview features may change before general availability.
</Warning>

SambaStack provides text-to-speech (TTS) capabilities through the `qwen3-tts` model, which converts text input into streaming PCM audio. The model supports 11 languages.

## Qwen3-TTS

* **Model ID**: `qwen3-tts`
* **Provider**: Qwen (Alibaba Cloud)
* **Endpoint**: `POST /v1/audio/speech`
* **Status**: Preview

### Supported voices

<Note>
  The `voice` parameter is accepted for API compatibility but is currently ignored. Audio is always generated using the reference voice configured at deployment time.
</Note>

| Voice ID   | Description |
| :--------- | :---------- |
| `serena`   | Female      |
| `vivian`   | Female      |
| `uncle_fu` | Male        |
| `ryan`     | Male        |
| `aiden`    | Male        |
| `ono_anna` | Female      |
| `sohee`    | Female      |
| `eric`     | Male        |
| `dylan`    | Male        |

### Supported languages

| Language      | Accepted values               |
| :------------ | :---------------------------- |
| Auto (detect) | `auto`                        |
| Chinese       | `Chinese`, `zh`, `cn`, `chi`  |
| English       | `English`, `en`, `eng`        |
| French        | `French`, `fr`, `fra`, `fre`  |
| German        | `German`, `de`, `ger`         |
| Italian       | `Italian`, `it`, `ita`        |
| Japanese      | `Japanese`, `ja`, `jp`, `jpn` |
| Korean        | `Korean`, `ko`, `kor`         |
| Portuguese    | `Portuguese`, `pt`, `por`     |
| Russian       | `Russian`, `ru`, `rus`        |
| Spanish       | `Spanish`, `es`, `spa`        |

Language matching is case-insensitive. If you omit the `language` parameter, it defaults to `english`.

### Request parameters

| Parameter         | Type    | Required | Description                                                                                                                  |
| :---------------- | :------ | :------- | :--------------------------------------------------------------------------------------------------------------------------- |
| `model`           | string  | Yes      | Must be `qwen3-tts`.                                                                                                         |
| `input`           | string  | Yes      | Text to synthesize into speech.                                                                                              |
| `voice`           | string  | No       | Speaker voice ID. Accepted for API compatibility; any value is currently ignored. See [Supported voices](#supported-voices). |
| `stream`          | boolean | Yes      | Must be `true`. Non-streaming responses are not yet supported.                                                               |
| `language`        | string  | No       | Language of the input text. Default: `english`. See [Supported languages](#supported-languages).                             |
| `instructions`    | string  | No       | Instructions to influence speaking style. Maximum 512 characters.                                                            |
| `sampling_params` | object  | No       | Sampling configuration. Accepted keys: `temperature`, `top_k`, `top_p`, `seed`. Example: `{"temperature": 0.8, "seed": 42}`. |

### Example usage

<CodeGroup>
  ```python Python (SambaNova) theme={null}
  import base64
  import json
  import struct
  import wave
  import requests

  url = "https://your-sambanova-base-url/v1/audio/speech"
  headers = {
      "Authorization": "Bearer your-sambanova-api-key",
      "Content-Type": "application/json",
  }
  payload = {
      "model": "qwen3-tts",
      "input": "Hello, welcome to SambaNova.",
      "voice": "serena",
      "language": "english",
      "stream": True,
  }

  pcm_frames = []

  with requests.post(url, headers=headers, json=payload, stream=True) as resp:
      resp.raise_for_status()
      for line in resp.iter_lines():
          if not line or not line.startswith(b"data: "):
              continue
          data = line[len(b"data: "):]
          if data == b"[DONE]":
              break
          event = json.loads(data)
          if event.get("finish_reason") == "error":
              raise RuntimeError(f"Stream error: {event.get('error')}")
          audio_b64 = event.get("audio_b64", "")
          if audio_b64:
              pcm_frames.append(base64.b64decode(audio_b64))
          if event.get("finish_reason") == "stop":
              break

  # Write f32le PCM frames to a WAV file
  pcm_bytes = b"".join(pcm_frames)
  num_samples = len(pcm_bytes) // 4  # 4 bytes per f32 sample
  samples = struct.unpack(f"{num_samples}f", pcm_bytes)
  pcm_int16 = bytes(
      struct.pack("<h", max(-32768, min(32767, int(s * 32767)))) for s in samples
  )

  with wave.open("output.wav", "wb") as wf:
      wf.setnchannels(1)
      wf.setsampwidth(2)  # 16-bit
      wf.setframerate(24000)
      wf.writeframes(pcm_int16)

  print("Saved output.wav")
  ```

  ```bash cURL theme={null}
  # This command shows the raw SSE stream. Use the Python example to produce a playable audio file.
  curl -X POST https://your-sambanova-base-url/v1/audio/speech \
    -H "Authorization: Bearer your-sambanova-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "qwen3-tts",
      "input": "Hello, welcome to SambaNova.",
      "voice": "serena",
      "language": "english",
      "stream": true
    }'
  ```
</CodeGroup>

### Response format

The endpoint streams a [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) response. Each event is a JSON object on a `data:` line.

**Response headers**

| Header                | Value               | Description                     |
| :-------------------- | :------------------ | :------------------------------ |
| `Content-Type`        | `text/event-stream` | SSE stream                      |
| `X-Audio-Format`      | `f32le`             | 32-bit float, little-endian PCM |
| `X-Audio-Sample-Rate` | `24000`             | 24 kHz sample rate              |
| `X-Audio-Channels`    | `1`                 | Mono                            |

**Event fields**

| Field           | Type           | Description                                                                                 |
| :-------------- | :------------- | :------------------------------------------------------------------------------------------ |
| `audio_b64`     | string         | Base64-encoded PCM audio chunk.                                                             |
| `chunk_index`   | integer        | Sequential index of the audio chunk.                                                        |
| `finish_reason` | string \| null | `null` while streaming; `"stop"` on the final chunk; `"error"` if the stream fails mid-way. |
| `id`            | string         | Request identifier.                                                                         |
| `object`        | string         | Always `audio.speech.chunk`.                                                                |
| `created`       | integer        | Unix timestamp.                                                                             |

**Example event**

```json theme={null}
{
  "audio_b64": "<base64-encoded PCM data>",
  "chunk_index": 0,
  "finish_reason": null,
  "id": "abc123",
  "object": "audio.speech.chunk",
  "created": 1750000000
}
```

**Final event**

```json theme={null}
{
  "audio_b64": "<base64-encoded PCM data>",
  "chunk_index": 12,
  "finish_reason": "stop",
  "id": "abc123",
  "object": "audio.speech.chunk",
  "created": 1750000000
}
```

### Limitations

* **Streaming only**: `stream: false` is not supported. All responses are SSE streams.
* **Concurrency**: The model processes one request at a time. Concurrent requests queue behind the active request.
* **Maximum audio length**: 320 seconds per request. If the input text is long enough to exceed this limit, the response ends at the cap – no error is returned. For longer audio, split the text into smaller passages and send separate requests.
* **Mid-stream audio artifacts**: Longer audio clips may contain audible artifacts partway through the stream. If you encounter this, split the input into shorter passages and send separate requests.
* **No context cache**: Each request is stateless.
