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

# 빠른 시작

## 인증 시작하기
타입캐스트 API를 사용하려면 API 키로 요청을 인증해야 합니다. 다음 단계를 따르세요:

<Steps>
  <Step title="첫 번째 단계">
    [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)을 방문하여 새 API 키를 생성하세요
  </Step>
  <Step title="두 번째 단계">
    API 키를 안전하게 보관하세요 - 환경 변수로 저장하는 것을 권장합니다
  </Step>
</Steps>

## 첫 번째 요청 실행하기

<Note>
Python SDK 0.5.0은 Python 3.11~3.14를 지원합니다. **Python 3.8, 3.9, 3.10은 EOL로 인해 지원이 중단되었습니다.** 마지막 호환 SDK는 Python 3.8/3.9의 경우 **typecast-python 0.3.15**, Python 3.10의 경우 **0.4.1**입니다. 먼저 Python을 업그레이드하세요. [지원 범위와 이전 환경 안내](/ko/sdk/python)를 확인하세요.
</Note>

<Tabs>
  <Tab title="SDK">
    <Steps>
      <Step title="SDK 설치">
        <CodeGroup>
        ```bash Python
        pip install --upgrade typecast-python
        ```

        ```bash Javascript
        npm install @neosapience/typecast-js
        # pnpm add @neosapience/typecast-js
        # yarn add @neosapience/typecast-js
        ```

        ```bash C#/.NET
        dotnet add package typecast-csharp
        ```

        ```xml Java (Maven)
        <dependency>
            <groupId>com.neosapience</groupId>
            <artifactId>typecast-java</artifactId>
            <version>1.2.12</version>
        </dependency>
        ```

        ```kotlin Kotlin (Gradle)
        dependencies {
            implementation("com.neosapience:typecast-kotlin:1.2.13")
        }
        ```

        ```toml Rust (Cargo.toml)
        [dependencies]
        typecast-rust = "0.3.15"
        tokio = { version = "1", features = ["full"] }
        ```

        ```bash Go
        go get github.com/neosapience/typecast-sdk/typecast-go
        ```
        </CodeGroup>

        <Warning>
        모든 SDK는 최신 버전이 필요합니다.
        - **Python**: 이전 버전이 있다면 `pip install --upgrade typecast-python`으로 업그레이드하세요
        - **Javascript**: 이전 버전이 있다면 `npm update @neosapience/typecast-js`로 업그레이드하세요
        - **C#**: `dotnet add package typecast-csharp`로 업데이트하세요
        - **Java**: `pom.xml` 또는 `build.gradle`에서 버전을 업데이트하세요
        - **Kotlin**: `build.gradle.kts`에서 버전을 업데이트하세요
        - **Rust**: `Cargo.toml`에서 버전을 업데이트하세요
        </Warning>

        <Tip>
        음성 합성과 오디오 파일 저장만 필요하다면 SDK의 `generateToFile` 또는 `generate_to_file` 헬퍼를 사용하세요. 각 SDK 페이지에서 언어별 예시를 확인할 수 있습니다.
        </Tip>
      </Step>
      <Step title="가져오기 및 초기화">
        <CodeGroup>

        ```python Python
        from typecast import Typecast
        from typecast.models import TTSRequest, SmartPrompt

        # 클라이언트 초기화
        client = Typecast(api_key="YOUR_API_KEY")

        # 텍스트를 음성으로 변환
        response = client.text_to_speech(TTSRequest(
            text="Everything is going to be okay.",
            model="ssfm-v30",
            voice_id="tc_672c5f5ce59fac2a48faeaee",
            prompt=SmartPrompt(
                emotion_type="smart",
                previous_text="I just got the best news!",
                next_text="I can't wait to celebrate!"
            )
        ))

        # 오디오 파일 저장
        with open('typecast.wav', 'wb') as f:
            f.write(response.audio_data)
        ```

        ```javascript Javascript
        import { TypecastClient } from '@neosapience/typecast-js';
        import fs from 'fs';

        // 클라이언트 초기화
        const client = new TypecastClient({
            apiKey: 'YOUR_API_KEY'
        });

        // 텍스트를 음성으로 변환
        const audio = await client.textToSpeech({
            text: "Everything is going to be okay.",
            model: "ssfm-v30",
            voice_id: "tc_672c5f5ce59fac2a48faeaee",
            prompt: {
                emotion_type: "smart",
                previous_text: "I just got the best news!",
                next_text: "I can't wait to celebrate!"
            }
        });

        // 오디오 파일 저장
        await fs.promises.writeFile('typecast.wav', Buffer.from(audio.audioData));
        ```

        ```csharp C#
        using Typecast;
        using Typecast.Models;

        // 클라이언트 초기화
        using var client = new TypecastClient("YOUR_API_KEY");

        // 텍스트를 음성으로 변환
        var request = new TTSRequest(
            text: "Everything is going to be okay.",
            voiceId: "tc_672c5f5ce59fac2a48faeaee",
            model: TTSModel.SsfmV30
        )
        {
            Prompt = new SmartPrompt(
                previousText: "I just got the best news!",
                nextText: "I can't wait to celebrate!"
            )
        };

        var response = await client.TextToSpeechAsync(request);

        // 오디오 파일 저장
        await response.SaveToFileAsync("typecast.wav");
        ```

        ```java Java
        import com.neosapience.TypecastClient;
        import com.neosapience.models.*;

        import java.io.FileOutputStream;

        // 클라이언트 초기화
        TypecastClient client = new TypecastClient("YOUR_API_KEY");

        // 텍스트를 음성으로 변환
        TTSRequest request = TTSRequest.builder()
                .voiceId("tc_672c5f5ce59fac2a48faeaee")
                .text("Everything is going to be okay.")
                .model(TTSModel.SSFM_V30)
                .prompt(SmartPrompt.builder()
                        .previousText("I just got the best news!")
                        .nextText("I can't wait to celebrate!")
                        .build())
                .build();

        TTSResponse response = client.textToSpeech(request);

        // 오디오 파일 저장
        try (FileOutputStream fos = new FileOutputStream("typecast.wav")) {
            fos.write(response.getAudioData());
        }

        client.close();
        ```

        ```kotlin Kotlin
        import com.neosapience.TypecastClient
        import com.neosapience.models.*
        import java.io.File

        // 클라이언트 초기화
        val client = TypecastClient.create("YOUR_API_KEY")

        // 텍스트를 음성으로 변환
        val request = TTSRequest.builder()
            .voiceId("tc_672c5f5ce59fac2a48faeaee")
            .text("Everything is going to be okay.")
            .model(TTSModel.SSFM_V30)
            .prompt(SmartPrompt.builder()
                .previousText("I just got the best news!")
                .nextText("I can't wait to celebrate!")
                .build())
            .build()

        val response = client.textToSpeech(request)

        // 오디오 파일 저장
        File("typecast.wav").writeBytes(response.audioData)

        client.close()
        ```

        ```rust Rust
        use typecast_rust::{TypecastClient, TTSRequest, TTSModel, SmartPrompt};
        use std::fs;

        #[tokio::main]
        async fn main() -> Result<(), Box<dyn std::error::Error>> {
            // 클라이언트 초기화
            let client = TypecastClient::with_api_key("YOUR_API_KEY")?;

            // 텍스트를 음성으로 변환
            let request = TTSRequest::new(
                "tc_672c5f5ce59fac2a48faeaee",
                "Everything is going to be okay.",
                TTSModel::SsfmV30,
            )
            .prompt(
                SmartPrompt::new()
                    .previous_text("I just got the best news!")
                    .next_text("I can't wait to celebrate!")
            );

            let response = client.text_to_speech(&request).await?;

            // 오디오 파일 저장
            fs::write("typecast.wav", &response.audio_data)?;

            Ok(())
        }
        ```

        </CodeGroup>
      </Step>
    </Steps>
  </Tab>
  <Tab title="Direct API">
    API 키를 두 가지 방법으로 설정할 수 있습니다:
    {/* - .env 파일에 추가 */}
    - 애플리케이션 코드에서 직접 구성
    - 셸 환경 변수로 설정

    <CodeGroup>
    ```bash Shell (Linux/macOS)
    # 현재 세션에 설정
    export TYPECAST_API_KEY='YOUR_API_KEY'
    ```

    ```bash Shell (Windows)
    # 현재 세션에 설정
    set TYPECAST_API_KEY=YOUR_API_KEY
    ```

    {/* ```bash .env
    TYPECAST_API_KEY = 'YOUR_API_KEY';
    ``` */}
    </CodeGroup>

    <CodeGroup>

    ```python Python
    import requests
    import os

    api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY")

    url = "https://api.typecast.ai/v1/text-to-speech"
    headers = {"X-API-KEY": api_key, "Content-Type": "application/json"}
    payload = {
        "text": "Everything is going to be okay.",
        "model": "ssfm-v30",
        "voice_id": "tc_672c5f5ce59fac2a48faeaee",
        "prompt": {
            "emotion_type": "smart",
            "previous_text": "I just got the best news!",
            "next_text": "I can't wait to celebrate!"
        }
    }

    response = requests.post(url, headers=headers, json=payload)

    if response.status_code == 200:
        with open('typecast.wav', 'wb') as f:
            f.write(response.content)
        print("Audio file saved as typecast.wav")
    else:
        print(f"Error: {response.status_code} - {response.text}")
    ```

    ```javascript Javascript
    import fs from "fs";
    const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY';

    async function convertTextToSpeech() {
      const url = 'https://api.typecast.ai/v1/text-to-speech';
      const payload = {
        text: "Everything is going to be okay.",
        model: "ssfm-v30",
        voice_id: "tc_672c5f5ce59fac2a48faeaee",
        prompt: {
          emotion_type: "smart",
          previous_text: "I just got the best news!",
          next_text: "I can't wait to celebrate!"
        }
      };

      try {
        const response = await fetch(url, {
          method: 'POST',
          headers: {
            'X-API-KEY': apiKey,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(payload)
        });

        if (!response.ok) {
          throw new Error(`Error: ${response.status} - ${await response.text()}`);
        }

        const audioData = await response.arrayBuffer();
        fs.writeFileSync('typecast.wav', Buffer.from(audioData));
        console.log('Audio file saved as typecast.wav');
      } catch (error) {
        console.error('Error converting text to speech:', error);
      }
    }

    convertTextToSpeech();
    ```

    ```java Java
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;

    String apiKey = System.getenv("TYPECAST_API_KEY");
    if (apiKey == null) apiKey = "YOUR_API_KEY";

    String urlString = "https://api.typecast.ai/v1/text-to-speech";
    String payload = "{" +
        "\"text\": \"Everything is going to be okay.\"," +
        "\"model\": \"ssfm-v30\"," +
        "\"voice_id\": \"tc_672c5f5ce59fac2a48faeaee\"," +
        "\"prompt\": {" +
            "\"emotion_type\": \"smart\"," +
            "\"previous_text\": \"I just got the best news!\"," +
            "\"next_text\": \"I can't wait to celebrate!\"" +
        "}" +
    "}";

    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    conn.setRequestProperty("X-API-KEY", apiKey);
    conn.setRequestProperty("Content-Type", "application/json");
    conn.setDoOutput(true);

    try (OutputStream os = conn.getOutputStream()) {
        os.write(payload.getBytes("UTF-8"));
    }

    if (conn.getResponseCode() == 200) {
        try (InputStream is = conn.getInputStream();
             FileOutputStream fos = new FileOutputStream("typecast.wav")) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
        System.out.println("Audio file saved as typecast.wav");
    } else {
        System.out.println("Error: " + conn.getResponseCode());
    }
    ```

    ```csharp C#
    using System.Net.Http.Headers;
    using System.Text;
    using System.Text.Json;

    var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY";

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("X-API-KEY", apiKey);

    var payload = new
    {
        text = "Everything is going to be okay.",
        model = "ssfm-v30",
        voice_id = "tc_672c5f5ce59fac2a48faeaee",
        prompt = new
        {
            emotion_type = "smart",
            previous_text = "I just got the best news!",
            next_text = "I can't wait to celebrate!"
        }
    };

    var content = new StringContent(
        JsonSerializer.Serialize(payload),
        Encoding.UTF8,
        "application/json"
    );

    var response = await client.PostAsync("https://api.typecast.ai/v1/text-to-speech", content);

    if (response.IsSuccessStatusCode)
    {
        var audioData = await response.Content.ReadAsByteArrayAsync();
        await File.WriteAllBytesAsync("typecast.wav", audioData);
        Console.WriteLine("Audio file saved as typecast.wav");
    }
    else
    {
        Console.WriteLine($"Error: {response.StatusCode}");
    }
    ```

    ```bash cURL
    curl -X POST "https://api.typecast.ai/v1/text-to-speech" \
         -H "X-API-KEY: YOUR_API_KEY" \
         -H "Content-Type: application/json" \
         -d '{
             "model": "ssfm-v30",
             "text": "Everything is going to be okay.",
             "voice_id": "tc_672c5f5ce59fac2a48faeaee",
             "prompt": {
                 "emotion_type": "smart",
                 "previous_text": "I just got the best news!",
                 "next_text": "I can'\''t wait to celebrate!"
             }
         }' --output typecast.wav
    ```

    </CodeGroup>

    ### 오디오 출력 설정

    요청에 `output` 객체를 추가하여 오디오 출력을 커스터마이즈할 수 있습니다:

    | 파라미터 | 타입 | 범위 | 기본값 | 설명 |
    |---------|------|------|--------|------|
    | `volume` | integer | 0–200 | 100 | 상대적 볼륨 스케일링. `target_lufs`와 동시에 사용할 수 없습니다. |
    | `target_lufs` | number | -70–0 | - | LUFS 기반 절대 라우드니스 정규화. `volume`과 동시에 사용할 수 없습니다. |
    | `audio_pitch` | integer | -12–12 | 0 | 피치 조정 (반음 단위). |
    | `audio_tempo` | number | 0.5–2.0 | 1.0 | 재생 속도 배율. |
    | `audio_format` | string | wav, mp3 | wav | 출력 오디오 포맷. |

    <Tip>`target_lufs`는 여러 클립 간 일관된 라우드니스가 필요할 때, `volume`은 단순한 상대적 볼륨 조절이 필요할 때 사용하세요.</Tip>

    ```json 예시: target_lufs를 사용한 출력 설정
    {
        "text": "일관된 라우드니스 예시입니다.",
        "model": "ssfm-v30",
        "voice_id": "tc_672c5f5ce59fac2a48faeaee",
        "output": {
            "target_lufs": -14.0,
            "audio_format": "mp3"
        }
    }
    ```
  </Tab>
</Tabs>

<Info>요청에 사용할 수 있는 Voice ID를 찾아보려면 API 레퍼런스의 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices)를 참조하세요.</Info>

## 모든 보이스 목록 조회하기
타입캐스트를 효과적으로 사용하려면 Voice ID에 액세스해야 합니다. 최신 `/v3/voices` 엔드포인트는 다국어 이름, 지원 모델, 감정, 미리듣기 메타데이터가 포함된 보이스 목록을 제공합니다.

모델, 성별, 연령대 및 사용 사례 등의 선택적 쿼리 파라미터를 사용하여 보이스를 필터링할 수 있습니다.

<Info>[보이스](https://studio.typecast.ai/developers/api/voices) 페이지에서 API 호출 없이 API에서 사용할 수 있는 보이스 목록과 샘플 음성을 미리 보고 들어볼 수 있습니다. 먼저 보이스를 비교한 뒤, 사용할 Voice ID를 API 요청에 넣어주세요.</Info>

<Tabs>
  <Tab title="SDK">
    <CodeGroup>
    ```python Python
    from typecast import Typecast
    from typecast.models import VoicesV2Filter, TTSModel

    # 클라이언트 초기화
    client = Typecast(api_key="YOUR_API_KEY")

    # 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
    voices = client.voices_v3(VoicesV2Filter(model=TTSModel.SSFM_V30))

    print(f"Found {len(voices)} voices:")
    for voice in voices:
        for model in voice.models:
            print(f"ID: {voice.voice_id}, Name: {voice.voice_name.kor}, Model: {model.version.value}, Emotions: {', '.join(model.emotions)}")
    ```

    ```javascript Javascript
    import { TypecastClient } from '@neosapience/typecast-js';

    // 클라이언트 초기화
    const client = new TypecastClient({
        apiKey: 'YOUR_API_KEY'
    });

    // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
    const voices = await client.getVoicesV3({model: 'ssfm-v30'});

    console.log(`Found ${voices.length} voices:`);
    voices.forEach(voice => {
      voice.models.forEach(model => {
        console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.kor}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`);
      });
    });
    ```

    ```csharp C#
    using Typecast;
    using Typecast.Models;

    // 클라이언트 초기화
    using var client = new TypecastClient("YOUR_API_KEY");

    // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
    var filter = new VoicesV2Filter { Model = TTSModel.SsfmV30 };
    var voices = await client.GetVoicesV3Async(filter);

    Console.WriteLine($"Found {voices.Count} voices:");
    foreach (var voice in voices)
    {
        foreach (var model in voice.Models)
        {
            Console.WriteLine($"ID: {voice.VoiceId}, Name: {voice.VoiceName.Kor}, Model: {model.Version}, Emotions: {string.Join(", ", model.Emotions)}");
        }
    }
    ```

    ```java Java
    import com.neosapience.TypecastClient;
    import com.neosapience.models.*;

    // 클라이언트 초기화
    TypecastClient client = new TypecastClient("YOUR_API_KEY");

    // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
    VoicesV2Filter filter = VoicesV2Filter.builder()
            .model(TTSModel.SSFM_V30)
            .build();

    List<VoiceV3Response> voices = client.getVoicesV3(filter);

    System.out.println("Found " + voices.size() + " voices:");
    for (VoiceV3Response voice : voices) {
        for (ModelInfo model : voice.getModels()) {
            System.out.println("ID: " + voice.getVoiceId() + ", Name: " + voice.getVoiceName().kor +
                    ", Model: " + model.getVersion() + ", Emotions: " + String.join(", ", model.getEmotions()));
        }
    }

    client.close();
    ```

    </CodeGroup>
  </Tab>
  <Tab title="Direct API">
    <CodeGroup>
    ```python Python
    import requests
    import os

    api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY")

    url = "https://api.typecast.ai/v3/voices"
    headers = {"X-API-KEY": api_key}
    params = {"model": "ssfm-v30"}  # 선택 사항: model, gender, age, use_cases

    response = requests.get(url, headers=headers, params=params)

    if response.status_code == 200:
        voices = response.json()
        print(f"Found {len(voices)} voices:")
        for voice in voices:
            for model in voice['models']:
                print(f"ID: {voice['voice_id']}, Name: {voice['voice_name']['kor']}, Model: {model['version']}, Emotions: {', '.join(model['emotions'])}")
    else:
        print(f"Error: {response.status_code} - {response.text}")
    ```

    ```javascript Javascript
    const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY';

    async function getVoices() {
      const url = 'https://api.typecast.ai/v3/voices';
      const params = new URLSearchParams({model: 'ssfm-v30'});  // 선택 사항: model, gender, age, use_cases

      try {
        const response = await fetch(`${url}?${params}`, {
          method: 'GET',
          headers: {'X-API-KEY': apiKey}
        });

        if (!response.ok) {
          throw new Error(`Error: ${response.status} - ${await response.text()}`);
        }

        const voices = await response.json();
        console.log(`Found ${voices.length} voices:`);
        voices.forEach(voice => {
          voice.models.forEach(model => {
            console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.kor}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`);
          });
        });
      } catch (error) {
        console.error('Error fetching voices:', error);
      }
    }

    getVoices();
    ```

    ```java Java
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;

    String apiKey = System.getenv("TYPECAST_API_KEY");
    if (apiKey == null) apiKey = "YOUR_API_KEY";

    String urlString = "https://api.typecast.ai/v3/voices?model=ssfm-v30";

    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setRequestProperty("X-API-KEY", apiKey);

    if (conn.getResponseCode() == 200) {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            System.out.println(response.toString());
        }
    } else {
        System.out.println("Error: " + conn.getResponseCode());
    }
    ```

    ```csharp C#
    var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY";

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("X-API-KEY", apiKey);

    var response = await client.GetAsync("https://api.typecast.ai/v3/voices?model=ssfm-v30");

    if (response.IsSuccessStatusCode)
    {
        var json = await response.Content.ReadAsStringAsync();
        Console.WriteLine(json);
    }
    else
    {
        Console.WriteLine($"Error: {response.StatusCode}");
    }
    ```

    ```bash cURL
    curl -X GET "https://api.typecast.ai/v3/voices?model=ssfm-v30" \
         -H "X-API-KEY: YOUR_API_KEY"
    ```
    </CodeGroup>
  </Tab>
</Tabs>

응답은 각각 다음을 포함하는 음성 객체의 JSON 배열입니다:

```json
{
  "voice_id": "tc_672c5f5ce59fac2a48faeaee",
  "voice_name": {"eng": "Dylan", "kor": "딜런"},
  "models": [
    {
      "version": "ssfm-v30",
      "emotions": ["normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown"]
    }
  ],
  "gender": "male",
  "age": "young_adult",
  "use_cases": ["Conversational", "TikTok/Reels/Shorts", "Audiobook/Storytelling"],
  "voice_type": "original",
  "preview_url": "https://..."
}
```

<Info>
텍스트 음성 변환 요청을 할 때 유효한 Voice ID가 필요합니다. ssfm-v30을 사용하면 모든 7가지 감정 프리셋을 모든 보이스에서 사용할 수 있습니다.
</Info>


## 실시간 오디오 스트리밍

저지연 애플리케이션의 경우, 스트리밍 엔드포인트를 사용하여 전체 합성을 기다리지 않고 오디오 청크가 도착하는 즉시 재생할 수 있습니다.

**WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다.

<Tabs>
  <Tab title="SDK">
    <CodeGroup>

    ```python Python
    # pip install typecast-python sounddevice
    import sounddevice as sd
    from typecast import Typecast
    from typecast.models import TTSRequestStream, OutputStream

    client = Typecast(api_key="YOUR_API_KEY")

    request = TTSRequestStream(
        text="이 텍스트를 실시간으로 오디오로 스트리밍합니다.",
        model="ssfm-v30",
        voice_id="tc_672c5f5ce59fac2a48faeaee",
        output=OutputStream(audio_format="wav", target_lufs=-14.0)
    )

    with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player:
        buf, first = bytearray(), True
        for chunk in client.text_to_speech_stream(request):
            if first:
                chunk = chunk[44:]  # 44바이트 WAV 헤더 건너뛰기
                first = False
            buf.extend(chunk)
            n = len(buf) - (len(buf) % 2)  # int16 정렬
            if n:
                player.write(bytes(buf[:n]))
                del buf[:n]
    ```

    ```javascript Javascript
    // Node 18+. 스트림을 ffplay로 파이핑하여 실시간 재생.
    // 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)
    import { spawn } from "node:child_process";
    import { TypecastClient } from '@neosapience/typecast-js';

    const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });

    const ffplay = spawn(
        "ffplay",
        ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"],
        { stdio: ["pipe", "ignore", "ignore"] },
    );

    const stream = await client.textToSpeechStream({
        text: "이 텍스트를 실시간으로 오디오로 스트리밍합니다.",
        model: "ssfm-v30",
        voice_id: "tc_672c5f5ce59fac2a48faeaee",
        output: { audio_format: "wav", target_lufs: -14.0 }
    });

    const reader = stream.getReader();
    while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        ffplay.stdin.write(value);
    }
    ffplay.stdin.end();
    await new Promise((resolve) => ffplay.on("close", resolve));
    ```

    ```java Java
    import com.neosapience.TypecastClient;
    import com.neosapience.models.*;
    import javax.sound.sampled.*;
    import java.io.*;

    TypecastClient client = new TypecastClient("YOUR_API_KEY");

    TTSRequestStream request = TTSRequestStream.builder()
            .voiceId("tc_672c5f5ce59fac2a48faeaee")
            .text("이 텍스트를 실시간으로 오디오로 스트리밍합니다.")
            .model(TTSModel.SSFM_V30)
            .output(com.neosapience.models.OutputStream.builder()
                    .audioFormat(AudioFormat.WAV)
                    .targetLufs(-14.0).build())
            .build();

    // 32000 Hz, 16비트, 모노, signed, 리틀엔디안
    javax.sound.sampled.AudioFormat format =
        new javax.sound.sampled.AudioFormat(32000, 16, 1, true, false);
    SourceDataLine line = javax.sound.sampled.AudioSystem.getSourceDataLine(format);
    line.open(format, 8192);
    line.start();

    try (InputStream stream = client.textToSpeechStream(request)) {
        byte[] buf = new byte[4096];
        boolean first = true;
        int bytesRead;
        while ((bytesRead = stream.read(buf)) != -1) {
            int offset = 0;
            if (first) { offset = 44; bytesRead -= 44; first = false; }
            line.write(buf, offset, bytesRead);
        }
    }
    line.drain();
    line.close();
    client.close();
    ```

    </CodeGroup>

    <Info>더 많은 언어(Go, Rust, Swift, C#, Kotlin, C)의 실시간 재생 예시는 각 [SDK 문서](/ko/sdk/python)를 참조하세요.</Info>
  </Tab>
  <Tab title="Direct API">
    <CodeGroup>

    ```python Python
    # pip install requests sounddevice
    import requests
    import sounddevice as sd
    import os

    api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY")

    response = requests.post(
        "https://api.typecast.ai/v1/text-to-speech/stream",
        headers={"X-API-KEY": api_key, "Content-Type": "application/json"},
        json={
            "text": "이 텍스트를 실시간으로 오디오로 스트리밍합니다.",
            "model": "ssfm-v30",
            "voice_id": "tc_672c5f5ce59fac2a48faeaee",
            "output": {"audio_format": "wav", "target_lufs": -14.0},
        },
        stream=True
    )
    response.raise_for_status()

    with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player:
        buf, first = bytearray(), True
        for chunk in response.iter_content(chunk_size=4096):
            if not chunk:
                continue
            if first:
                chunk = chunk[44:]  # WAV 헤더 건너뛰기
                first = False
            buf.extend(chunk)
            n = len(buf) - (len(buf) % 2)
            if n:
                player.write(bytes(buf[:n]))
                del buf[:n]
    ```

    ```bash cURL + ffplay
    # ffplay로 직접 파이핑하여 즉시 재생
    curl -s -X POST "https://api.typecast.ai/v1/text-to-speech/stream" \
         -H "X-API-KEY: YOUR_API_KEY" \
         -H "Content-Type: application/json" \
         -d '{
             "model": "ssfm-v30",
             "text": "이 텍스트를 실시간으로 오디오로 스트리밍합니다.",
             "voice_id": "tc_672c5f5ce59fac2a48faeaee",
             "output": {"audio_format": "wav", "target_lufs": -14.0}
         }' | ffplay -autoexit -nodisp -loglevel error -i pipe:0
    ```

    </CodeGroup>

    | 파라미터 | 타입 | 범위 | 기본값 | 설명 |
    |---------|------|------|--------|------|
    | `audio_pitch` | integer | -12–12 | 0 | 세미톤 단위의 피치 조절 |
    | `audio_tempo` | number | 0.5–2.0 | 1.0 | 속도 배율 |
    | `audio_format` | string | wav, mp3 | wav | 출력 오디오 형식 |
    | `target_lufs` | number | -70–0 | - | LUFS 기반 절대 라우드니스 정규화 |

    <Tip>`target_lufs`로 스트리밍 오디오의 라우드니스를 클립 간 일관되게 맞출 수 있습니다. 스트리밍 모드에서는 `volume`을 지원하지 않습니다.</Tip>
  </Tab>
</Tabs>

## 타임스탬프 TTS로 자막 생성하기

`POST /v1/text-to-speech/with-timestamps`를 사용하면 오디오와 함께 단어 단위 정렬 데이터를 받아 자막, 가라오케, 립싱크 애플리케이션을 만들 수 있습니다.

<CodeGroup>
```python Python
from typecast import Typecast
from typecast.models import TTSRequestWithTimestamps

client = Typecast(api_key="YOUR_API_KEY")
response = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps(
    text="Hello. How are you?",
    model="ssfm-v30",
    voice_id="tc_60e5426de8b95f1d3000d7b5",
))

# 단어별 타임스탬프 출력
for word in response.words:
    print(f"[{word.start_time:.3f}s – {word.end_time:.3f}s] {word.text}")

# SRT 자막 내보내기
srt = response.to_srt()
with open("output.srt", "w") as f:
    f.write(srt)
```

```typescript JavaScript
import Typecast from "@neosapience/typecast-js";

const client = new Typecast({ apiKey: "YOUR_API_KEY" });
const response = await client.textToSpeechWithTimestamps({
  voiceId: "tc_60e5426de8b95f1d3000d7b5",
  text: "Hello. How are you?",
  model: "ssfm-v30",
});

// 단어별 타임스탬프 출력
for (const word of response.words) {
  console.log(`[${word.startTime.toFixed(3)}s – ${word.endTime.toFixed(3)}s] ${word.text}`);
}

// VTT 자막 내보내기
const vtt = response.toVtt();
```

```bash cURL
curl -X POST https://api.typecast.ai/v1/text-to-speech/with-timestamps \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "voice_id": "tc_60e5426de8b95f1d3000d7b5",
    "text": "Hello. How are you?",
    "model": "ssfm-v30",
    "granularity": "word"
  }'
```
</CodeGroup>

<Info>
각 언어별 타임스탬프 TTS 사용법은 [SDK 문서](/ko/sdk)를 참조하세요. 일본어(jpn), 중국어(zho)는 `granularity: "char"` (문자 단위) 를 사용해야 합니다.
</Info>

## 다음 단계
축하합니다! 첫 번째 AI 음성을 만들었습니다. 더 자세히 알아보려면 다음 리소스를 참조하세요:

<CardGroup cols={2}>
  <Card title="API 레퍼런스" icon="code" href="/ko/api-reference/text-to-speech/text-to-speech">
    타입캐스트 API 사용 방법 알아보기
  </Card>
  <Card title="모델" icon="microchip" href="/ko/models">
    ssfm-v30 및 ssfm-v21 모델에 대해 알아보기
  </Card>
  <Card title="변경 로그" icon="clock-rotate-left" href="/ko/changelog">
    최신 API 변경 사항 및 업데이트 확인
  </Card>
</CardGroup>
