메인 콘텐츠로 건너뛰기
타입캐스트 API를 위한 공식 Rust 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Tokio 런타임을 사용한 async/await 지원으로 구축되었습니다. Cargo 패키지 관리자와 함께 작동합니다.

Crates.io

Typecast Rust SDK

소스 코드

Typecast Rust SDK 소스 코드

설치

Cargo.toml에 다음을 추가하세요:
[dependencies]
typecast-rust = "0.1.1"
tokio = { version = "1", features = ["full"] }
또는 Cargo를 사용하여 의존성을 추가하세요:
cargo add typecast-rust tokio --features tokio/full
버전 0.1.0 이상이 설치되어 있는지 확인하세요. 업데이트가 필요하면 Cargo.toml을 확인하세요.

빠른 시작

use typecast_rust::{TypecastClient, TTSRequest, TTSModel};
use std::fs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 클라이언트 초기화 (환경변수에서 TYPECAST_API_KEY 읽기)
    let client = TypecastClient::from_env()?;

    // 텍스트를 음성으로 변환
    let request = TTSRequest::new(
        "tc_672c5f5ce59fac2a48faeaee",
        "안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.",
        TTSModel::SsfmV30,
    );

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

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

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

    Ok(())
}

기능

Typecast Rust 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
  • 빌더 패턴: 쉬운 요청 구성을 위한 메서드 체이닝 Fluent API
  • Async/Await: 효율적인 비동기 작업을 위해 Tokio 기반으로 구축
  • 스트리밍: 저지연 재생을 위한 실시간 청크 오디오 전송
  • 포괄적인 오류 처리: 패턴 매칭을 지원하는 타입화된 에러 enum

구성

환경 변수 또는 생성자를 통해 API 키를 설정하세요:
use typecast_rust::{TypecastClient, ClientConfig};
use std::time::Duration;

// 환경 변수 사용 (권장)
// export TYPECAST_API_KEY="your-api-key-here"
let client = TypecastClient::from_env()?;

// 또는 직접 전달
let client = TypecastClient::with_api_key("your-api-key-here")?;

// 또는 사용자 정의 구성과 함께
let config = ClientConfig::new("your-api-key-here")
    .base_url("https://api.typecast.ai")
    .timeout(Duration::from_secs(120));

let client = TypecastClient::new(config)?;

환경 파일

프로젝트 루트에 .env 파일을 만드세요:
TYPECAST_API_KEY=your-api-key-here
.env 파일에서 환경 변수를 로드하려면 dotenvy 크레이트를 사용하세요.

고급 사용법

감정 제어 (ssfm-v30)

ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: 프리셋스마트.
AI가 문맥에서 감정을 추론하도록 합니다:
use typecast_rust::{TTSRequest, TTSModel, SmartPrompt};

let request = TTSRequest::new(
    "tc_672c5f5ce59fac2a48faeaee",
    "모든 것이 잘 될 거예요.",
    TTSModel::SsfmV30,
)
.prompt(
    SmartPrompt::new()
        .previous_text("방금 최고의 소식을 들었어요!")  // 선택적 문맥
        .next_text("축하할 수 있어서 너무 기다려져요!")     // 선택적 문맥
);

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

오디오 사용자 정의

라우드니스, 피치, 템포 및 출력 형식을 제어합니다:
use typecast_rust::{TTSRequest, TTSModel, Output, AudioFormat};

let request = TTSRequest::new(
    "tc_672c5f5ce59fac2a48faeaee",
    "사용자 정의 오디오 출력!",
    TTSModel::SsfmV30,
)
.output(
    Output::new()
        .target_lufs(-14.0)                 // 범위: -70 ~ 0 (LUFS)
        .audio_pitch(2)                   // 범위: -12 to +12 반음
        .audio_tempo(1.2)                 // 범위: 0.5x to 2.0x
        .audio_format(AudioFormat::Mp3)   // 옵션: Wav, Mp3
)
.seed(42);  // 부호 없는 정수 시드 (재현 가능한 결과)

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

fs::write("output.mp3", &response.audio_data)?;
println!("Duration: {:.2}s, Format: {:?}", response.duration, response.format);

음성 탐색 (V2 API)

향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다:
use typecast_rust::{TypecastClient, VoicesV2Filter, TTSModel, Gender, Age};

// 모든 음성 가져오기
let voices = client.get_voices_v2(None).await?;

// 기준으로 필터링
let filter = VoicesV2Filter::new()
    .model(TTSModel::SsfmV30)
    .gender(Gender::Female)
    .age(Age::YoungAdult);

let filtered = client.get_voices_v2(Some(filter)).await?;

// 음성 정보 표시
for voice in &voices {
    println!("ID: {}, Name: {}", voice.voice_id, voice.voice_name);
    println!("Gender: {:?}, Age: {:?}", voice.gender, voice.age);

    for model in &voice.models {
        println!("Model: {:?}, Emotions: {:?}", model.version, model.emotions);
    }

    if let Some(use_cases) = &voice.use_cases {
        println!("Use cases: {}", use_cases.join(", "));
    }
}

// ID로 특정 음성 가져오기
let voice = client.get_voice_v2("tc_672c5f5ce59fac2a48faeaee").await?;
println!("Voice: {} ({:?})", voice.voice_name, voice.gender);

다국어 콘텐츠

SDK는 자동 언어 감지와 함께 37개 언어를 지원합니다:
// 언어 자동 감지 (권장)
let request = TTSRequest::new(
    "tc_672c5f5ce59fac2a48faeaee",
    "こんにちは。お元気ですか。",
    TTSModel::SsfmV30,
);

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

// 또는 명시적으로 언어 지정
let korean_request = TTSRequest::new(
    "tc_672c5f5ce59fac2a48faeaee",
    "안녕하세요. 반갑습니다.",
    TTSModel::SsfmV30,
)
.language("kor");  // ISO 639-3 언어 코드

let korean_response = client.text_to_speech(&korean_request).await?;

스트리밍

저지연 재생을 위한 실시간 오디오 청크 스트리밍:
// 원시 PCM 추출 (44바이트 WAV 헤더 건너뛰기)
let mut stream = client.text_to_speech_stream(&request).await?;
let mut first = true;

while let Some(chunk) = stream.next().await {
    let bytes = chunk?;
    let pcm = if first {
        first = false;
        &bytes[44..]  // WAV 헤더 건너뛰기
    } else {
        &bytes
    };
    // pcm은 32000 Hz 16비트 모노 원시 PCM
    // 오디오 출력으로 전달 (예: rodio, cpal)
}
WAV 스트리밍 형식: 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = 0xFFFFFFFF)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. StreamExt를 사용하려면 futures-util이 필요합니다. 스트리밍 엔드포인트는 volumetarget_lufs를 지원하지 않습니다.

지원 언어

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 오류를 처리할 수 있는 타입화된 에러 enum을 제공합니다:
use typecast_rust::{TypecastClient, TTSRequest, TTSModel, TypecastError};

let request = TTSRequest::new("voice_id", "안녕하세요", TTSModel::SsfmV30);

match client.text_to_speech(&request).await {
    Ok(response) => {
        println!("Success! Duration: {:.2}s", response.duration);
    }
    Err(TypecastError::Unauthorized { detail }) => {
        // 401: 잘못된 API 키
        eprintln!("Invalid API key: {}", detail);
    }
    Err(TypecastError::PaymentRequired { detail }) => {
        // 402: 크레딧 부족
        eprintln!("Insufficient credits: {}", detail);
    }
    Err(TypecastError::NotFound { detail }) => {
        // 404: 리소스를 찾을 수 없음
        eprintln!("Voice not found: {}", detail);
    }
    Err(TypecastError::RateLimited { detail }) => {
        // 429: 요청 한도 초과
        eprintln!("Rate limit exceeded - please try again later: {}", detail);
    }
    Err(TypecastError::ServerError { detail }) => {
        // 500: 서버 오류
        eprintln!("Server error: {}", detail);
    }
    Err(e) => {
        eprintln!("Error: {}", e);
    }
}

오류 유형

오류 변형상태 코드설명
BadRequest400잘못된 요청 매개변수
Unauthorized401잘못되거나 누락된 API 키
PaymentRequired402크레딧 부족
Forbidden403접근 거부
NotFound404리소스를 찾을 수 없음
ValidationError422유효성 검사 오류
RateLimited429요청 한도 초과
ServerError500서버 오류
HttpError-HTTP 클라이언트 오류
JsonError-JSON 직렬화 오류

헬퍼 메서드

if let Err(e) = result {
    if e.is_unauthorized() {
        println!("Please check your API key");
    } else if e.is_rate_limited() {
        println!("Please try again later");
    } else if e.is_server_error() {
        println!("Server issue, please try again later");
    }

    if let Some(code) = e.status_code() {
        println!("HTTP status: {}", code);
    }
}

API 레퍼런스

TypecastClient 메서드

메서드설명
from_env()환경 변수에서 클라이언트 생성
with_api_key(key)API 키로 클라이언트 생성
new(config)사용자 정의 구성으로 클라이언트 생성
text_to_speech(&request)텍스트를 음성 오디오로 변환
get_voices_v2(filter)선택적 필터로 사용 가능한 음성 가져오기
get_voice_v2(voice_id)ID로 특정 음성 가져오기

TTSRequest 필드

필드타입필수설명
voice_idString음성 ID (형식: tc_* 또는 uc_*)
textString합성할 텍스트 (최대 2000자)
modelTTSModelTTS 모델 (SsfmV21 또는 SsfmV30)
languageOption<String>ISO 639-3 코드 (생략 시 자동 감지)
promptOption<TTSPrompt>감정 설정 (Prompt/PresetPrompt/SmartPrompt)
outputOption<Output>오디오 출력 설정
seedOption<u32>재현성을 위한 부호 없는 정수 시드 (≥ 0)

TTSResponse 필드

필드타입설명
audio_dataVec<u8>생성된 오디오 데이터
durationf64오디오 길이 (초)
formatAudioFormat오디오 형식 (Wav 또는 Mp3)

완전한 예제

use typecast_rust::{
    TypecastClient, TTSRequest, TTSModel, PresetPrompt, 
    EmotionPreset, Output, AudioFormat, VoicesV2Filter, Gender,
};
use std::fs;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // 클라이언트 초기화
    let client = TypecastClient::from_env()?;
    
    // 음성 탐색
    let filter = VoicesV2Filter::new()
        .model(TTSModel::SsfmV30)
        .gender(Gender::Female);
    
    let voices = client.get_voices_v2(Some(filter)).await?;
    println!("Found {} female voices", voices.len());
    
    // 첫 번째 음성 사용
    if let Some(voice) = voices.first() {
        let request = TTSRequest::new(
            &voice.voice_id,
            "Typecast에 오신 것을 환영합니다! 텍스트 음성 변환 API의 데모입니다.",
            TTSModel::SsfmV30,
        )
        .language("kor")
        .prompt(
            PresetPrompt::new()
                .emotion_preset(EmotionPreset::Happy)
                .emotion_intensity(1.2)
        )
        .output(
            Output::new()
                .target_lufs(-14.0)
                .audio_format(AudioFormat::Mp3)
        );
        
        let response = client.text_to_speech(&request).await?;
        
        fs::write("welcome.mp3", &response.audio_data)?;
        println!("welcome.mp3 saved ({:.2}s)", response.duration);
    }
    
    Ok(())
}