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

# Kotlin

> 공식 Kotlin SDK로 타입캐스트 API에 액세스하세요.

[타입캐스트 API](https://typecast.ai)를 위한 공식 Kotlin 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요.

Kotlin 1.9+ 및 JDK 17 이상 버전과 호환됩니다. Gradle (Kotlin DSL 또는 Groovy) 및 Maven과 함께 작동합니다.

<CardGroup cols={2}>
  <Card title="Maven Central" icon="cube" href="https://central.sonatype.com/artifact/com.neosapience/typecast-kotlin">
    Typecast Kotlin SDK
  </Card>

  <Card title="소스 코드" icon="github" href="https://github.com/neosapience/typecast-sdk/tree/main/typecast-kotlin">
    Typecast Kotlin SDK 소스 코드
  </Card>
</CardGroup>

## 설치

<Tabs>
  <Tab title="Gradle (Kotlin DSL)">
    `build.gradle.kts`에 다음 의존성을 추가하세요:

    ```kotlin theme={null}
    dependencies {
        implementation("com.neosapience:typecast-kotlin:1.2.7")
    }
    ```
  </Tab>

  <Tab title="Gradle (Groovy)">
    `build.gradle`에 추가하세요:

    ```groovy theme={null}
    implementation 'com.neosapience:typecast-kotlin:1.2.7'
    ```
  </Tab>

  <Tab title="Maven">
    `pom.xml`에 다음 의존성을 추가하세요:

    ```xml theme={null}
    <dependency>
        <groupId>com.neosapience</groupId>
        <artifactId>typecast-kotlin</artifactId>
        <version>1.2.7</version>
    </dependency>
    ```
  </Tab>
</Tabs>

<Warning>
  최신 등록 버전은 Maven Central 기준 **1.2.7**입니다. **버전 1.2.7 이상**이 설치되어 있는지 확인하세요. 이전 버전이 있다면 의존성 버전을 업데이트하세요.
</Warning>

## 빠른 시작

```kotlin theme={null}
import com.neosapience.TypecastClient
import com.neosapience.models.*
import java.io.File

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

    // 텍스트를 음성으로 변환
    val request = TTSRequest.builder()
        .voiceId("tc_672c5f5ce59fac2a48faeaee")
        .text("안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.")
        .model(TTSModel.SSFM_V30)
        .build()

    val response = client.textToSpeech(request)

    // 오디오 파일 저장
    File("output.${response.format}").writeBytes(response.audioData)

    println("Audio saved! Duration: ${response.duration}s, Format: ${response.format}")

    // 정리
    client.close()
}
```

## 기능

Typecast Kotlin SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다:

* **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원
* **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 37개 언어 지원
* **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론
* **오디오 사용자 정의**: 라우드니스 LUFS(-70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어
* **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API
* **관용적 Kotlin**: data class를 사용한 Kotlin 친화적 빌더 패턴 구문
* **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터
* **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송
* **포괄적인 오류 처리**: 각 오류 유형에 대한 특정 예외 클래스

## 보이스 추천

원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다.

```kotlin theme={null}
val voices = client.recommendVoices(
    "warm female voice for a product tutorial",
    count = 3,
)

voices.forEach { voice ->
    println("${voice.voiceId} ${voice.voiceName} ${voice.score}")
}
```

추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요.

## 구성

환경 변수, `.env` 파일 또는 빌더를 통해 API 키를 설정하세요:

```kotlin theme={null}
// 환경 변수 사용
// export TYPECAST_API_KEY="your-api-key-here"
val client = TypecastClient.create()

// 또는 직접 전달
val client = TypecastClient.create("your-api-key-here")

// 또는 사용자 정의 구성을 위해 빌더 사용
val client = TypecastClient.builder()
    .apiKey("your-api-key-here")
    .baseUrl("https://custom-api.example.com")
    .build()
```

<Info>
  자체 프록시를 통해 요청하는 경우 `baseUrl`을 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다.
</Info>

```kotlin API 키 없는 프록시 theme={null}
val client = TypecastClient.builder()
    .baseUrl("https://your-proxy.example.com")
    .build()
```

### 환경 파일

프로젝트 루트에 `.env` 파일을 만드세요:

```bash theme={null}
TYPECAST_API_KEY=your-api-key-here
```

## 고급 사용법

### 감정 제어 (ssfm-v30)

ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**.

<Tabs>
  <Tab title="스마트 모드">
    AI가 문맥에서 감정을 추론하도록 합니다:

    ```kotlin theme={null}
    val request = TTSRequest.builder()
        .voiceId("tc_672c5f5ce59fac2a48faeaee")
        .text("모든 것이 잘 될 거예요.")
        .model(TTSModel.SSFM_V30)
        .prompt(SmartPrompt.builder()
            .previousText("방금 최고의 소식을 들었어요!")  // 선택적 문맥
            .nextText("축하할 수 있어서 너무 기다려져요!")     // 선택적 문맥
            .build())
        .build()

    val response = client.textToSpeech(request)
    ```
  </Tab>

  <Tab title="프리셋 모드">
    프리셋 값으로 감정을 명시적으로 설정합니다:

    ```kotlin theme={null}
    val request = TTSRequest.builder()
        .voiceId("tc_672c5f5ce59fac2a48faeaee")
        .text("이 기능들을 보여드리게 되어 정말 기대됩니다!")
        .model(TTSModel.SSFM_V30)
        .prompt(PresetPrompt.builder()
            .emotionPreset(EmotionPreset.HAPPY)  // normal, happy, sad, angry, whisper, toneup, tonedown
            .emotionIntensity(1.5)               // 범위: 0.0 ~ 2.0
            .build())
        .build()

    val response = client.textToSpeech(request)
    ```
  </Tab>
</Tabs>

### 오디오 사용자 정의

라우드니스, 피치, 템포 및 출력 형식을 제어합니다:

```kotlin theme={null}
val request = TTSRequest.builder()
    .voiceId("tc_672c5f5ce59fac2a48faeaee")
    .text("사용자 정의 오디오 출력!")
    .model(TTSModel.SSFM_V30)
    .output(Output.builder()
        .targetLufs(-14.0)                 // 범위: -70 ~ 0 (LUFS)
        .audioPitch(2)                  // 범위: -12 to +12 반음
        .audioTempo(1.2)                // 범위: 0.5x to 2.0x
        .audioFormat(AudioFormat.MP3)   // 옵션: WAV, MP3
        .build())
    .seed(42)  // 부호 없는 정수 시드 (재현 가능한 결과)
    .build()

val response = client.textToSpeech(request)

File("output.${response.format}").writeBytes(response.audioData)
println("Duration: ${response.duration}s, Format: ${response.format}")
```

### 파일로 바로 생성하기

`generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다.

```kotlin theme={null}
client.generateToFile(
    "output.mp3",
    GenerateToFileRequest(
        text = "안녕하세요, 타입캐스트입니다.",
        voiceId = "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://typecast.ai/developers/api/voices 에서 확인하세요.
    )
)
```

### 텍스트만으로 쉼 표현

한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다.

```kotlin theme={null}
val audio = client.composeSpeech()
    .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30))
    .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?")
    .generate()
```

### 다중 화자 합성

한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 각 구간을 WAV로 생성하고 앞뒤 무음 PCM 샘플을 trim한 뒤 합성합니다. MP3가 필요하면 먼저 WAV를 생성한 다음 앱 또는 서버 파이프라인에서 변환하세요.

```kotlin theme={null}
val client = TypecastClient.create("YOUR_API_KEY")

val audio = client.composeSpeech()
    .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30))
    .say("Hello there")
    .pause(5.0)
    .say("Nice to meet you", ComposerSettings(voiceId = "tc_60e5426de8b95f1d3000d7b5", output = Output(audioPitch = 2)))
    .say("Today")
    .pause(2.0)
    .say("How does the weather feel?")
    .generate()

File("conversation.wav").writeBytes(audio.audioData)
```

### 음성 탐색 (V2 API)

향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다:

```kotlin theme={null}
// 모든 음성 가져오기
val voices = client.getVoicesV2()

// 기준으로 필터링
val filter = VoicesV2Filter.builder()
    .model(TTSModel.SSFM_V30)
    .gender(GenderEnum.FEMALE)
    .age(AgeEnum.YOUNG_ADULT)
    .build()

val filtered = client.getVoicesV2(filter)

// 음성 정보 표시
voices.forEach { voice ->
    println("ID: ${voice.voiceId}, Name: ${voice.voiceName}")
    println("Gender: ${voice.gender}, Age: ${voice.age}")

    voice.models.forEach { model ->
        println("Model: ${model.version}, Emotions: ${model.emotions}")
    }

    voice.useCases?.let { useCases ->
        println("Use cases: ${useCases.joinToString(", ")}")
    }
}
```

### 다국어 콘텐츠

SDK는 자동 언어 감지와 함께 37개 언어를 지원합니다:

```kotlin theme={null}
// 자동 언어 감지 (권장)
val request = TTSRequest.builder()
    .voiceId("tc_672c5f5ce59fac2a48faeaee")
    .text("こんにちは。お元気ですか。")
    .model(TTSModel.SSFM_V30)
    .build()

val response = client.textToSpeech(request)

// 또는 언어를 명시적으로 지정
val koreanRequest = TTSRequest.builder()
    .voiceId("tc_672c5f5ce59fac2a48faeaee")
    .text("안녕하세요. 반갑습니다.")
    .model(TTSModel.SSFM_V30)
    .language(LanguageCode.KOR)  // ISO 639-3 언어 코드
    .build()

val koreanResponse = client.textToSpeech(koreanRequest)

File("output.${response.format}").writeBytes(response.audioData)
```

### 스트리밍

저지연 재생을 위한 실시간 오디오 청크 스트리밍:

```kotlin theme={null}
import javax.sound.sampled.*

// 오디오 재생 설정: 32000 Hz, 16비트, 모노, 리틀엔디안
val format = AudioFormat(32000f, 16, 1, true, false)
val line = AudioSystem.getSourceDataLine(format).apply {
    open(format, 8192)
    start()
}

val stream = client.textToSpeechStream(request)
val buf = ByteArray(4096)
var first = true

while (true) {
    val bytesRead = stream.read(buf)
    if (bytesRead == -1) break
    var offset = 0
    var len = bytesRead
    if (first) {
        offset = 44           // 44바이트 WAV 헤더 건너뛰기
        len -= 44
        first = false
    }
    line.write(buf, offset, len)
}
line.drain()
line.close()
stream.close()
client.close()
```

<Note>
  **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다.
</Note>

## 타임스탬프 TTS

`textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다.

### 기본 사용법

```kotlin theme={null}
import com.neosapience.TypecastClient
import com.neosapience.models.TTSRequestWithTimestamps
import java.nio.file.Files
import java.nio.file.Paths

val client = TypecastClient("YOUR_API_KEY")

val result = client.textToSpeechWithTimestamps(
    TTSRequestWithTimestamps.builder()
        .voiceId("tc_60e5426de8b95f1d3000d7b5")
        .text("Hello. How are you?")
        .model("ssfm-v30")
        .build()
)

Files.write(Paths.get("output.wav"), result.audioBytes())
println("재생 시간: ${"%.3f".format(result.audioDuration)}초")

result.words.forEach { word ->
    println("  [${"%.3f".format(word.startTime)}s – ${"%.3f".format(word.endTime)}s] ${word.text}")
}
```

### 정밀도(Granularity) 설정

`granularity("word")`(기본값) 또는 `granularity("char")`을 설정해 정렬 단위를 제어합니다.

```kotlin theme={null}
// 문자 단위 정렬 — 일본어·중국어에 필수
val result = client.textToSpeechWithTimestamps(
    TTSRequestWithTimestamps.builder()
        .voiceId("tc_60e5426de8b95f1d3000d7b5")
        .text("Hello. How are you?")
        .model("ssfm-v30")
        .granularity("char")
        .build()
)
```

### 자막 내보내기

```kotlin theme={null}
val srt = result.toSrt()
Files.writeString(Paths.get("output.srt"), srt)

val vtt = result.toVtt()
Files.writeString(Paths.get("output.vtt"), vtt)
```

<Note>
  **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `granularity("char")`를 사용해 문자 단위 정렬 데이터를 얻으세요.
</Note>

## 지원 언어

SDK는 자동 언어 감지와 함께 37개 언어를 지원합니다:

| 코드    | 언어    | 코드    | 언어     | 코드    | 언어     |
| ----- | ----- | ----- | ------ | ----- | ------ |
| `ENG` | 영어    | `JPN` | 일본어    | `UKR` | 우크라이나어 |
| `KOR` | 한국어   | `ELL` | 그리스어   | `IND` | 인도네시아어 |
| `SPA` | 스페인어  | `TAM` | 타밀어    | `DAN` | 덴마크어   |
| `DEU` | 독일어   | `TGL` | 타갈로그어  | `SWE` | 스웨덴어   |
| `FRA` | 프랑스어  | `FIN` | 핀란드어   | `MSA` | 말레이어   |
| `ITA` | 이탈리아어 | `ZHO` | 중국어    | `CES` | 체코어    |
| `POL` | 폴란드어  | `SLK` | 슬로바키아어 | `POR` | 포르투갈어  |
| `NLD` | 네덜란드어 | `ARA` | 아랍어    | `BUL` | 불가리아어  |
| `RUS` | 러시아어  | `HRV` | 크로아티아어 | `RON` | 루마니아어  |
| `BEN` | 벵골어   | `HIN` | 힌디어    | `HUN` | 헝가리어   |
| `NAN` | 민난어   | `NOR` | 노르웨이어  | `PAN` | 펀자브어   |
| `THA` | 태국어   | `TUR` | 터키어    | `VIE` | 베트남어   |
| `YUE` | 광둥어   |       |        |       |        |

<Info>
  지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다.
</Info>

## 오류 처리

SDK는 API 오류 처리를 위한 특정 예외 클래스를 제공합니다:

```kotlin theme={null}
import com.neosapience.TypecastClient
import com.neosapience.exceptions.*

try {
    val response = client.textToSpeech(request)
} catch (e: UnauthorizedException) {
    // 401: 잘못된 API 키
    println("Invalid API key: ${e.message}")
} catch (e: PaymentRequiredException) {
    // 402: 크레딧 부족
    println("Insufficient credits: ${e.message}")
} catch (e: ForbiddenException) {
    // 403: 접근 거부
    println("Access denied: ${e.message}")
} catch (e: NotFoundException) {
    // 404: 리소스를 찾을 수 없음
    println("Voice not found: ${e.message}")
} catch (e: UnprocessableEntityException) {
    // 422: 유효성 검사 오류
    println("Validation error: ${e.message}")
} catch (e: RateLimitException) {
    // 429: 요청 한도 초과
    println("Rate limit exceeded - please try again later")
} catch (e: InternalServerException) {
    // 500: 서버 오류
    println("Server error: ${e.message}")
} catch (e: TypecastException) {
    // 일반 오류
    println("API error (${e.statusCode}): ${e.message}")
}
```

### 예외 계층 구조

| 예외                             | 상태 코드 | 설명              |
| ------------------------------ | ----- | --------------- |
| `BadRequestException`          | 400   | 잘못된 요청 매개변수     |
| `UnauthorizedException`        | 401   | 잘못되거나 누락된 API 키 |
| `PaymentRequiredException`     | 402   | 크레딧 부족          |
| `ForbiddenException`           | 403   | 접근 거부           |
| `NotFoundException`            | 404   | 리소스를 찾을 수 없음    |
| `UnprocessableEntityException` | 422   | 유효성 검사 오류       |
| `RateLimitException`           | 429   | 요청 한도 초과        |
| `InternalServerException`      | 500   | 서버 오류           |
| `TypecastException`            | \*    | 기본 예외 클래스       |

## IntelliJ IDEA 설정

<Steps>
  <Step title="새 프로젝트 생성">
    1. IntelliJ IDEA 열기
    2. `File` → `New` → `Project...` 이동
    3. "Kotlin" 및 "Gradle (Kotlin)" 선택
    4. JDK를 17 이상으로 설정
  </Step>

  <Step title="의존성 추가">
    `build.gradle.kts`에 추가:

    ```kotlin theme={null}
    dependencies {
        implementation("com.neosapience:typecast-kotlin:1.2.7")
    }
    ```
  </Step>

  <Step title="프로젝트 동기화">
    Gradle 동기화 버튼 클릭 또는 `build.gradle.kts` 우클릭 → `Reload Gradle Project`
  </Step>
</Steps>

## Android 설정

<Steps>
  <Step title="의존성 추가">
    앱의 `build.gradle.kts`에 추가:

    ```kotlin theme={null}
    dependencies {
        implementation("com.neosapience:typecast-kotlin:1.2.7")
    }
    ```
  </Step>

  <Step title="인터넷 권한 추가">
    `AndroidManifest.xml`에 추가:

    ```xml theme={null}
    <uses-permission android:name="android.permission.INTERNET" />
    ```
  </Step>

  <Step title="백그라운드 스레드에서 사용">
    코루틴 또는 백그라운드 스레드에서 API 호출:

    ```kotlin theme={null}
    lifecycleScope.launch(Dispatchers.IO) {
        val client = TypecastClient.create("YOUR_API_KEY")
        val response = client.textToSpeech(request)
        // 응답 처리
    }
    ```
  </Step>
</Steps>

## API 레퍼런스

### TypecastClient 메서드

| 메서드                                           | 설명                    |
| --------------------------------------------- | --------------------- |
| `textToSpeech(TTSRequest)`                    | 텍스트를 음성 오디오로 변환       |
| `generateToFile(path, GenerateToFileRequest)` | 음성을 생성하고 로컬 파일로 바로 저장 |
| `getVoicesV2()`                               | 모든 사용 가능한 음성 가져오기     |
| `getVoicesV2(VoicesV2Filter)`                 | 필터링된 음성 가져오기          |
| `getVoiceV2(voiceId: String)`                 | ID로 특정 음성 가져오기        |
| `close()`                                     | 리소스 해제                |

### TTSRequest 필드

| 필드         | 타입                                        | 필수 | 설명                                |
| ---------- | ----------------------------------------- | -- | --------------------------------- |
| `voiceId`  | `String`                                  | ✓  | 음성 ID (형식: `tc_*` 또는 `uc_*`)      |
| `text`     | `String`                                  | ✓  | 합성할 텍스트 (최대 2000자)                |
| `model`    | `TTSModel`                                | ✓  | TTS 모델 (`SSFM_V21` 또는 `SSFM_V30`) |
| `language` | `LanguageCode`                            |    | ISO 639-3 코드 (생략 시 자동 감지)         |
| `prompt`   | `Prompt` / `PresetPrompt` / `SmartPrompt` |    | 감정 설정                             |
| `output`   | `Output`                                  |    | 오디오 출력 설정                         |
| `seed`     | `UInt32`                                  |    | 재현성을 위한 부호 없는 정수 시드 (≥ 0)         |

### TTSResponse 필드

| 필드          | 타입          | 설명                      |
| ----------- | ----------- | ----------------------- |
| `audioData` | `ByteArray` | 생성된 오디오 데이터             |
| `duration`  | `Double`    | 오디오 길이(초)               |
| `format`    | `String`    | 오디오 형식 (`wav` 또는 `mp3`) |
