let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "모든 것이 잘 될 거예요.", model: .ssfmV30, prompt: .smart(SmartPrompt( previousText: "방금 최고의 소식을 들었어요!", // 선택적 문맥 nextText: "축하할 수 있어서 너무 기다려져요!" // 선택적 문맥 )))let response = try await client.textToSpeech(request)
프리셋 값으로 감정을 명시적으로 설정합니다:
let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "이 기능들을 보여드리게 되어 정말 기대됩니다!", model: .ssfmV30, prompt: .preset(PresetPrompt( emotionPreset: .happy, // normal, happy, sad, angry, whisper, toneup, tonedown emotionIntensity: 1.5 // 범위: 0.0 ~ 2.0 )))let response = try await client.textToSpeech(request)
import AVFoundationimport Typecastlet engine = AVAudioEngine()let playerNode = AVAudioPlayerNode()let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 32000, channels: 1, interleaved: true)!engine.attach(playerNode)engine.connect(playerNode, to: engine.mainMixerNode, format: format)try engine.start()playerNode.play()let stream = try await client.textToSpeechStream(request)var first = truefor try await chunk in stream { var pcmData = chunk if first { pcmData = chunk.dropFirst(44) // 44바이트 WAV 헤더 건너뛰기 first = false } let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(pcmData.count / 2))! buffer.frameLength = buffer.frameCapacity pcmData.withUnsafeBytes { ptr in buffer.int16ChannelData!.pointee.update(from: ptr.bindMemory(to: Int16.self).baseAddress!, count: Int(buffer.frameLength)) } playerNode.scheduleBuffer(buffer)}
WAV 스트리밍 형식: 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = 0xFFFFFFFF)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. Foundation.OutputStream과의 이름 충돌을 피하려면 Typecast.OutputStream을 사용하세요. 스트리밍 엔드포인트는 volume 및 targetLufs를 지원하지 않습니다.
textToSpeechWithTimestamps()는 POST /v1/text-to-speech/with-timestamps를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다.
granularity: .word(기본값) 또는 granularity: .char를 설정해 정렬 단위를 제어합니다.
// 문자 단위 정렬 — 일본어·중국어에 필수let result = try await client.textToSpeechWithTimestamps(TTSRequestWithTimestamps( voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: "ssfm-v30", granularity: .char))
import Typecastdo { let response = try await client.textToSpeech(request)} catch let error as TypecastError { switch error { case .unauthorized(let message): // 401: 잘못된 API 키 print("Invalid API key: \(message)") case .paymentRequired(let message): // 402: 크레딧 부족 print("Insufficient credits: \(message)") case .notFound(let message): // 404: 리소스를 찾을 수 없음 print("Voice not found: \(message)") case .validationError(let message): // 422: 유효성 검사 오류 print("Validation error: \(message)") case .rateLimitExceeded(let message): // 429: 요청 한도 초과 print("Rate limit exceeded: \(message)") case .serverError(let message): // 500: 서버 오류 print("Server error: \(message)") case .networkError(let underlyingError): // 네트워크 연결 문제 print("Network error: \(underlyingError.localizedDescription)") case .invalidResponse(let message): // 서버의 잘못된 응답 print("Invalid response: \(message)") default: print("Error: \(error.localizedDescription)") } // 사용 가능한 경우 상태 코드에 액세스 if let statusCode = error.statusCode { print("HTTP status: \(statusCode)") }}