타입캐스트 API를 위한 공식 Kotlin 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요.
Kotlin 1.9+ 및 JDK 17 이상 버전과 호환됩니다. Gradle (Kotlin DSL 또는 Groovy) 및 Maven과 함께 작동합니다.
설치
build.gradle.kts에 다음 의존성을 추가하세요:
dependencies {
implementation("com.neosapience:typecast-kotlin:1.2.9")
}
빠른 시작
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-v21AI 음성 모델 지원 - 다국어 지원: 영어, 한국어, 스페인어, 일본어, 중국어 등 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를 사용합니다.
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 키를 설정하세요:
// 환경 변수 사용
// 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()
val client = TypecastClient.builder()
.baseUrl("https://your-proxy.example.com")
.build()
환경 파일
프로젝트 루트에 .env 파일을 만드세요:
TYPECAST_API_KEY=your-api-key-here
고급 사용법
감정 제어 (ssfm-v30)
ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: 프리셋 및 스마트.
AI가 문맥에서 감정을 추론하도록 합니다:
val request = TTSRequest.builder()
.voiceId("tc_672c5f5ce59fac2a48faeaee")
.text("모든 것이 잘 될 거예요.")
.model(TTSModel.SSFM_V30)
.prompt(SmartPrompt.builder()
.previousText("방금 최고의 소식을 들었어요!") // 선택적 문맥
.nextText("축하할 수 있어서 너무 기다려져요!") // 선택적 문맥
.build())
.build()
val response = client.textToSpeech(request)
오디오 사용자 정의
라우드니스, 피치, 템포 및 출력 형식을 제어합니다:
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 확장자로 출력 형식을 결정합니다.
client.generateToFile(
"output.mp3",
GenerateToFileRequest(
text = "안녕하세요, 타입캐스트입니다.",
voiceId = "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요.
)
)
텍스트만으로 쉼 표현
한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. <|5s|>, <|1s|>, <|0.3s|>, <|0.34413s|>처럼 쓰며 값은 초 단위이고 반드시 s로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다.
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를 생성한 다음 앱 또는 서버 파이프라인에서 변환하세요.
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)
향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다:
// 모든 음성 가져오기
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개 언어를 지원합니다:
// 자동 언어 감지 (권장)
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)
스트리밍
저지연 재생을 위한 실시간 오디오 청크 스트리밍:
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()
타임스탬프 TTS
textToSpeechWithTimestamps()는 POST /v1/text-to-speech/with-timestamps를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다.
기본 사용법
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")을 설정해 정렬 단위를 제어합니다.
// 문자 단위 정렬 - 일본어·중국어에 필수
val result = client.textToSpeechWithTimestamps(
TTSRequestWithTimestamps.builder()
.voiceId("tc_60e5426de8b95f1d3000d7b5")
.text("Hello. How are you?")
.model("ssfm-v30")
.granularity("char")
.build()
)
자막 내보내기
val srt = result.toSrt()
Files.writeString(Paths.get("output.srt"), srt)
val vtt = result.toVtt()
Files.writeString(Paths.get("output.vtt"), vtt)
지원 언어
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 | 광둥어 |
오류 처리
SDK는 API 오류 처리를 위한 특정 예외 클래스를 제공합니다:
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 설정
새 프로젝트 생성 - IntelliJ IDEA 열기
File→New→Project...이동- "Kotlin" 및 "Gradle (Kotlin)" 선택
- JDK를 17 이상으로 설정
의존성 추가 build.gradle.kts에 추가:dependencies { implementation("com.neosapience:typecast-kotlin:1.2.9") }프로젝트 동기화 Gradle 동기화 버튼 클릭 또는
build.gradle.kts우클릭 →Reload Gradle Project
Android 설정
의존성 추가 앱의
build.gradle.kts에 추가:dependencies { implementation("com.neosapience:typecast-kotlin:1.2.9") }인터넷 권한 추가 AndroidManifest.xml에 추가:<uses-permission android:name="android.permission.INTERNET" />백그라운드 스레드에서 사용 코루틴 또는 백그라운드 스레드에서 API 호출:
lifecycleScope.launch(Dispatchers.IO) { val client = TypecastClient.create("YOUR_API_KEY") val response = client.textToSpeech(request) // 응답 처리 }
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) |