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

# 음성 스트리밍

> 오디오가 합성되는 즉시 청크 단위로 수신합니다. 단일 클립이 길어 생성이 끝나기 전에 재생을 시작하고 싶을 때 유용합니다.

<Note>
  이 문서는 영어 원문을 기반으로 자동 번역되었습니다. 표현이 어색하거나 모호한 부분이 있을 수 있으니, 정확한 내용은 [영어 원문](/en/docs/text-to-speech/stream-speech)을 함께 확인해 주세요.
</Note>

`stream_speech`는 오디오를 청크 단위로 반환하므로, 전체 클립이 완성되기 **전에** 재생이나 전달을 시작할 수 있습니다. 경로는 `/v1/text-to-speech/{voice_id}/stream`입니다.

<Note>
  스트리밍은 현재 \*\*`sona_speech_1`\*\*에서만 지원됩니다.
</Note>

## 언제 스트리밍을 사용하는가

스트리밍은 **단일** TTS 클립이 충분히 길어, 전체가 완성될 때까지 기다리는 시간이 체감될 정도일 때 가장 유용합니다. 예를 들어 여러 문장으로 이루어진 단락을 한 번의 호출로 합성하는 경우입니다.

각 발화가 짧은 문장인 **인터랙티브 에이전트나 챗봇**의 경우, 보통 빠른 논스트리밍 모델을 사용하는 편이 전체 지연시간 면에서 더 유리합니다.

* **`sona_speech_2_flash`** — 속도와 품질의 균형을 잡은 모델입니다.
* **`supertonic_api_3`** — 추론 속도가 가장 빠르면서도 음성 안정성이 높은 모델입니다. 첫 오디오 도달 시간(time-to-first-audio)이 최우선일 때 사용해 주십시오.

자세한 논의는 [지연시간 최적화](/ko/docs/production/latency-optimization)를 참고해 주십시오. [LLM 응답을 TTS로 스트리밍하기](/ko/docs/examples/llm-streaming-tts)에서 소개하는 문장 단위 패턴은 `stream_speech`를 전혀 사용하지 않으며, 문장마다 빠른 논스트리밍 모델을 호출하는 방식에 의존합니다.

## 기본 스트리밍

<Tabs>
  <Tab title="Python (sync)">
    ```python theme={"dark"}
    from supertone import Supertone

    VOICE_ID = "20160a4c5ba38967330c84"  # replace with your voice ID

    with Supertone(api_key=API_KEY) as client:
        response = client.text_to_speech.stream_speech(
            voice_id=VOICE_ID,
            text="This response is streamed chunk by chunk.",
            language="en",
            model="sona_speech_1",
            output_format="wav",
        )

        with open("streamed.wav", "wb") as f:
            for chunk in response.result.iter_bytes():
                f.write(chunk)
    ```
  </Tab>

  <Tab title="Python (async)">
    ```python theme={"dark"}
    import asyncio
    from supertone import Supertone

    VOICE_ID = "20160a4c5ba38967330c84"  # replace with your voice ID

    async def main():
        async with Supertone(api_key=API_KEY) as client:
            response = await client.text_to_speech.stream_speech_async(
                voice_id=VOICE_ID,
                text="This response is streamed chunk by chunk.",
                language="en",
                model="sona_speech_1",
            )

            with open("streamed.wav", "wb") as f:
                async for chunk in response.result.aiter_bytes():
                    f.write(chunk)

    asyncio.run(main())
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"dark"}
    import { Supertone } from "@supertone/supertone";
    import * as fs from "node:fs";

    const VOICE_ID = "20160a4c5ba38967330c84"; // replace with your voice ID

    const client = new Supertone({ apiKey: API_KEY });

    const response = await client.textToSpeech.streamSpeech({
      voiceId: VOICE_ID,
      apiConvertTextToSpeechUsingCharacterRequest: {
        text: "This response is streamed chunk by chunk.",
        language: "en",
        model: "sona_speech_1",
        outputFormat: "wav",
      },
    });

    if (response.result && typeof response.result === "object" && "getReader" in response.result) {
      const reader = (response.result as ReadableStream<Uint8Array>).getReader();
      const chunks: Uint8Array[] = [];
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        if (value) chunks.push(value);
      }
      fs.writeFileSync("streamed.wav", Buffer.concat(chunks));
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"dark"}
    curl -X POST "https://supertoneapi.com/v1/text-to-speech/20160a4c5ba38967330c84/stream" \
      -H "x-sup-api-key: $SUPERTONE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "text": "This response is streamed chunk by chunk.",
        "language": "en",
        "model": "sona_speech_1"
      }' \
      --output streamed.wav
    ```

    `curl`은 들어오는 청크를 도착하는 대로 곧바로 파일에 기록합니다.
  </Tab>
</Tabs>

## 요청 필드

[Create speech](/ko/docs/text-to-speech/create-speech)와 동일하며, `model`은 `sona_speech_1`로 고정됩니다(현재 스트리밍을 지원하는 유일한 모델입니다). 경로는 `/v1/text-to-speech/{voice_id}/stream`입니다.

## 응답

기본적으로 응답 본문은 `output_format`에 일치하는 `Content-Type`을 가진 **바이너리 오디오 스트림**입니다.

* `audio/wav` — WAV 파일의 청크입니다(첫 번째 청크에 WAV 헤더가 포함됩니다).
* `audio/mpeg` — MP3 파일의 청크입니다.

`include_phonemes=true`인 경우, 응답은 **NDJSON**으로 전환됩니다. 한 줄에 하나의 JSON 객체가 들어 있으며, 각각 base64 오디오 청크와 대응하는 음소 데이터를 포함합니다.

## 긴 입력 스트리밍

SDK는 스트리밍 시에도 300자를 초과하는 텍스트를 자동으로 분할합니다. 내부적으로 텍스트를 분할하고 순차적인 스트리밍 요청을 보낸 뒤, 청크를 호출자의 이터레이터로 전달하므로 읽기 루프 코드는 그대로 유지할 수 있습니다.

자세한 내용은 [Long text](/ko/docs/text-to-speech/long-text)를 참고해 주십시오.

## 팁

* **플레이어 버퍼링.** 대부분의 플레이어는 재생을 시작하기 전에 초기 버퍼가 필요합니다. 첫 청크가 도착하자마자 재생하는 것보다, 1\~2초 분량의 오디오를 버퍼링한 뒤 재생하는 편이 더 매끄럽게 느껴집니다.
* **WAV 대 MP3.** WAV 청크는 크기가 크지만 연결이 쉽고, MP3 스트림은 더 작아 느린 네트워크에서 전달하기에 유리합니다.
* **오류 처리.** 스트림 오류는 읽기 도중에 발생할 수 있습니다. 반복 처리를 평소의 오류 핸들러로 감싸고, 특히 일시적인 `429`나 `5xx` 응답에 대비한 재시도를 준비해 주십시오. [재시도 및 백오프](/ko/docs/production/retries-and-backoff)를 참고해 주십시오.

## 관련 문서

<CardGroup cols={2}>
  <Card title="LLM 스트리밍 TTS" icon="robot" href="/ko/docs/examples/llm-streaming-tts">
    보이스 에이전트를 위한 문장 단위 패턴입니다.
  </Card>

  <Card title="지연시간 최적화" icon="gauge-high" href="/ko/docs/production/latency-optimization">
    낮은 지연시간을 위해 적합한 모델과 패턴을 선택하세요.
  </Card>
</CardGroup>
