> ## 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.

# 빠른 시작

> 타입캐스트를 시작하고 첫 번째 AI 음성을 만들어 보세요.

## 인증 시작하기

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

<Steps>
  <Step title="첫 번째 단계">
    [타입캐스트 API 콘솔](https://typecast.ai/developers)을 방문하여 새 API 키를 생성하세요
  </Step>

  <Step title="두 번째 단계">
    API 키를 안전하게 보관하세요 - 환경 변수로 저장하는 것을 권장합니다
  </Step>
</Steps>

## 첫 번째 요청 실행하기

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

          ```bash Javascript theme={null}
          npm install @neosapience/typecast-js
          # pnpm add @neosapience/typecast-js
          # yarn add @neosapience/typecast-js
          ```

          ```bash C#/.NET theme={null}
          dotnet add package typecast-csharp
          ```

          ```xml Java (Maven) theme={null}
          <dependency>
              <groupId>com.neosapience</groupId>
              <artifactId>typecast-java</artifactId>
              <version>1.2.3</version>
          </dependency>
          ```

          ```kotlin Kotlin (Gradle) theme={null}
          dependencies {
              implementation("com.neosapience:typecast-kotlin:1.2.3")
          }
          ```

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

          ```bash Go theme={null}
          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 theme={null}
          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 theme={null}
          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# theme={null}
          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 theme={null}
          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 theme={null}
          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 theme={null}
          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 키를 두 가지 방법으로 설정할 수 있습니다:

    * 애플리케이션 코드에서 직접 구성
    * 셸 환경 변수로 설정

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

      ```bash Shell (Windows) theme={null}
      # 현재 세션에 설정
      set TYPECAST_API_KEY=YOUR_API_KEY
      ```
    </CodeGroup>

    <CodeGroup>
      ```python Python theme={null}
      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 theme={null}
      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 theme={null}
      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# theme={null}
      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 theme={null}
      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를 사용한 출력 설정 theme={null}
    {
        "text": "일관된 라우드니스 예시입니다.",
        "model": "ssfm-v30",
        "voice_id": "tc_672c5f5ce59fac2a48faeaee",
        "output": {
            "target_lufs": -14.0,
            "audio_format": "mp3"
        }
    }
    ```
  </Tab>
</Tabs>

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

## 모든 보이스 목록 조회하기

타입캐스트를 효과적으로 사용하려면 Voice ID에 액세스해야 합니다. `/v2/voices` 엔드포인트는 고유 식별자, 이름, 지원 모델 및 감정이 포함된 사용 가능한 보이스의 전체 목록을 제공합니다.

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

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

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

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

      # 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
      voices = client.voices_v2(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}, Model: {model.version.value}, Emotions: {', '.join(model.emotions)}")
      ```

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

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

      // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
      const voices = await client.getVoicesV2({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}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`);
        });
      });
      ```

      ```csharp C# theme={null}
      using Typecast;
      using Typecast.Models;

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

      // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링)
      var filter = new VoicesV2Filter { Model = TTSModel.SsfmV30 };
      var voices = await client.GetVoicesV2Async(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}, Model: {model.Version}, Emotions: {string.Join(", ", model.Emotions)}");
          }
      }
      ```

      ```java Java theme={null}
      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<VoiceV2Response> voices = client.getVoicesV2(filter);

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

      client.close();
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Direct API">
    <CodeGroup>
      ```python Python theme={null}
      import requests
      import os

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

      url = "https://api.typecast.ai/v2/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']}, Model: {model['version']}, Emotions: {', '.join(model['emotions'])}")
      else:
          print(f"Error: {response.status_code} - {response.text}")
      ```

      ```javascript Javascript theme={null}
      const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY';

      async function getVoices() {
        const url = 'https://api.typecast.ai/v2/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}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`);
            });
          });
        } catch (error) {
          console.error('Error fetching voices:', error);
        }
      }

      getVoices();
      ```

      ```java Java theme={null}
      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/v2/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# theme={null}
      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/v2/voices?model=ssfm-v30");

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

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

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

```json theme={null}
{
  "voice_id": "tc_672c5f5ce59fac2a48faeaee",
  "voice_name": "Dylan",
  "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"]
}
```

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

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

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

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

<Tabs>
  <Tab title="SDK">
    <CodeGroup>
      ```python Python theme={null}
      # 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 theme={null}
      // 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 theme={null}
      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 theme={null}
      # 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 theme={null}
      # 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 theme={null}
  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 theme={null}
  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 theme={null}
  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>
