The official Rust library for the Typecast API. Convert text to lifelike speech using AI-powered voices.
Built with async/await support using Tokio runtime. Works with Cargo package manager.
Installation
Add the following to your Cargo.toml:
[dependencies]
typecast-rust = "0.3.10"
tokio = { version = "1", features = ["full"] }
Or use Cargo to add the dependency:
cargo add typecast-rust tokio --features tokio/full
Quick Start
use typecast_rust::{TypecastClient, TTSRequest, TTSModel};
use std::fs;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize client (reads TYPECAST_API_KEY from environment)
let client = TypecastClient::from_env()?;
// Convert text to speech
let request = TTSRequest::new(
"tc_672c5f5ce59fac2a48faeaee",
"Hello there! I'm your friendly text-to-speech agent.",
TTSModel::SsfmV30,
);
let response = client.text_to_speech(&request).await?;
// Save audio file
fs::write("output.wav", &response.audio_data)?;
println!(
"Audio saved! Duration: {:.2}s, Format: {:?}",
response.duration, response.format
);
Ok(())
}
Features
The Typecast Rust SDK provides powerful features for text-to-speech conversion:
- Multiple Voice Models: Support for
ssfm-v30(latest) andssfm-v21AI voice models - Multi-language Support: 37 languages including English, Korean, Spanish, Japanese, Chinese, and more
- Emotion Control: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference
- Audio Customization: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3)
- Voice Discovery: V2 Voices API with filtering by model, gender, age, and use cases
- Instant Voice Cloning: Upload a WAV/MP3 sample and create a custom voice ID
- Builder Pattern: Fluent API with method chaining for easy request construction
- Async/Await: Built on Tokio for efficient asynchronous operations
- Timestamp TTS: Word- and character-level alignment data for subtitles, karaoke, and lip-sync
- Comprehensive Error Handling: Typed error enum with pattern matching support
- Streaming: Real-time chunked audio delivery for low-latency playback
Voice Recommendations
Use recommend_voices when you know the desired style but not the exact voice_id.
let voices = client
.recommend_voices("warm female voice for a product tutorial", Some(3))
.await?;
for voice in voices {
println!("{} {} {}", voice.voice_id, voice.voice_name, voice.score);
}
Recommendation results contain only voice_id, voice_name, and score. Use get_voice_v2 or get_voices_v2 when you need detailed metadata such as supported models, emotions, gender, age, or use cases.
Configuration
Set your API key via environment variable or constructor:
use typecast_rust::{TypecastClient, ClientConfig};
use std::time::Duration;
// Using environment variable (recommended)
// export TYPECAST_API_KEY="your-api-key-here"
let client = TypecastClient::from_env()?;
// Or pass directly
let client = TypecastClient::with_api_key("your-api-key-here")?;
// Or with custom configuration
let config = ClientConfig::new("your-api-key-here")
.base_url("https://api.typecast.ai")
.timeout(Duration::from_secs(120));
let client = TypecastClient::new(config)?;
let config = ClientConfig::new("")
.base_url("https://your-proxy.example.com");
let client = TypecastClient::new(config)?;
Environment File
Create a .env file in your project root:
TYPECAST_API_KEY=your-api-key-here
Advanced Usage
Emotion Control (ssfm-v30)
ssfm-v30 offers two emotion control modes: Preset and Smart.
Let the AI infer emotion from context:
use typecast_rust::{TTSRequest, TTSModel, SmartPrompt};
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!") // Optional context
.next_text("I can't wait to celebrate!") // Optional context
);
let response = client.text_to_speech(&request).await?;
Audio Customization
Control loudness, pitch, tempo, and output format:
use typecast_rust::{TTSRequest, TTSModel, Output, AudioFormat};
let request = TTSRequest::new(
"tc_672c5f5ce59fac2a48faeaee",
"Customized audio output!",
TTSModel::SsfmV30,
)
.output(
Output::new()
.target_lufs(-14.0) // Range: -70 to 0 (LUFS)
.audio_pitch(2) // Range: -12 to +12 semitones
.audio_tempo(1.2) // Range: 0.5x to 2.0x
.audio_format(AudioFormat::Mp3) // Options: Wav, Mp3
)
.seed(42); // Unsigned seed for reproducible results
let response = client.text_to_speech(&request).await?;
fs::write("output.mp3", &response.audio_data)?;
println!("Duration: {:.2}s, Format: {:?}", response.duration, response.format);
Generate audio to a file
Use generate_to_file when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to ssfm-v30, and .mp3 / .wav extensions infer the output format when no output format is set. Browse available voice IDs on the Voices page.
client.generate_to_file(
"output.mp3",
GenerateToFileRequest::new("tc_672c5f5ce59fac2a48faeaee", "Hello from Typecast."), // Find voice IDs at https://studio.typecast.ai/developers/api/voices
).await?;
Text pauses
Use text pause markup when you only need silent gaps inside one composed text segment. Put <|5s|>, <|1s|>, <|0.3s|>, or <|0.34413s|> directly in the text. The value is interpreted as seconds and must end with s. This keeps the pause expression visible in plain text without adding separate pause calls.
let audio = client
.compose_speech()
.defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TtsModel::SsfmV30))
.say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?")
.generate()
.await?;
Multi-speaker composition
Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer generates each segment as WAV, trims leading/trailing silent PCM samples, and concatenates the result. If you need MP3, generate WAV first and convert it in your app or server pipeline.
use typecast_rust::{ComposerSettings, Output, TTSModel, TypecastClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TypecastClient::new("YOUR_API_KEY")?;
let audio = client
.compose_speech()
.defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TTSModel::SSFM_V30))
.say("Hello there")
.pause(5.0)
.say_with(
"Nice to meet you",
ComposerSettings::new().voice_id("tc_60e5426de8b95f1d3000d7b5").output(Output { audio_pitch: Some(2), ..Default::default() }),
)
.say("Today")
.pause(2)
.say("How does the weather feel?")
.generate()
.await?;
std::fs::write("conversation.wav", audio.audio_data)?;
Ok(())
}
Voice Discovery (V2 API)
List and filter available voices with enhanced metadata:
use typecast_rust::{TypecastClient, VoicesV2Filter, TTSModel, Gender, Age};
// Get all voices
let voices = client.get_voices_v2(None).await?;
// Filter by criteria
let filter = VoicesV2Filter::new()
.model(TTSModel::SsfmV30)
.gender(Gender::Female)
.age(Age::YoungAdult);
let filtered = client.get_voices_v2(Some(filter)).await?;
// Display voice info
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(", "));
}
}
// Get a specific voice by ID
let voice = client.get_voice_v2("tc_672c5f5ce59fac2a48faeaee").await?;
println!("Voice: {} ({:?})", voice.voice_name, voice.gender);
Multilingual Content
The SDK supports 37 languages with automatic language detection:
// Auto-detect language (recommended)
let request = TTSRequest::new(
"tc_672c5f5ce59fac2a48faeaee",
"こんにちは。お元気ですか。",
TTSModel::SsfmV30,
);
let response = client.text_to_speech(&request).await?;
// Or specify language explicitly
let korean_request = TTSRequest::new(
"tc_672c5f5ce59fac2a48faeaee",
"안녕하세요. 반갑습니다.",
TTSModel::SsfmV30,
)
.language("kor"); // ISO 639-3 language code
let korean_response = client.text_to_speech(&korean_request).await?;
Streaming
Stream audio chunks in real-time for low-latency playback:
// Stream and extract raw PCM (skip 44-byte WAV header)
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..] // Skip WAV header
} else {
&bytes
};
// pcm is raw 16-bit mono PCM at 32000 Hz
// Feed to your audio output (e.g. rodio, cpal)
}
Timestamp TTS
text_to_speech_with_timestamps() wraps POST /v1/text-to-speech/with-timestamps and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications.
Basic Usage
use typecast_rust::{TypecastClient, TTSRequestWithTimestamps, TTSModel};
use std::fs;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TypecastClient::from_env()?;
let request = TTSRequestWithTimestamps::new(
"tc_60e5426de8b95f1d3000d7b5",
"Hello. How are you?",
TTSModel::SsfmV30,
);
let result = client.text_to_speech_with_timestamps(&request).await?;
fs::write("output.wav", result.audio_bytes())?;
println!("Duration: {:.3}s", result.audio_duration);
for word in &result.words {
println!(" [{:.3}s – {:.3}s] {}", word.start_time, word.end_time, word.text);
}
Ok(())
}
Granularity
Chain .granularity(Granularity::Word) (default) or .granularity(Granularity::Char) to control the alignment unit.
use typecast_rust::Granularity;
let request = TTSRequestWithTimestamps::new(
"tc_60e5426de8b95f1d3000d7b5",
"Hello. How are you?",
TTSModel::SsfmV30,
)
.granularity(Granularity::Char); // required for Japanese / Chinese
Subtitle Export
fs::write("output.srt", result.to_srt())?;
fs::write("output.vtt", result.to_vtt())?;
Instant Voice Cloning
Clone a custom voice from a short audio sample, then pass the returned uc_ voice ID directly to TTS.
use typecast_rust::{TypecastClient, TTSModel, TTSRequest};
let client = TypecastClient::from_env()?;
let audio = std::fs::read("sample.wav")?;
let voice = client
.clone_voice(audio, "sample.wav", "My Voice", "ssfm-v30")
.await?;
let request = TTSRequest::new(
&voice.voice_id,
"Hello from my cloned voice!",
TTSModel::SsfmV30,
);
let response = client.text_to_speech(&request).await?;
std::fs::write("output.wav", &response.audio_data)?;
client.delete_voice(&voice.voice_id).await?;
Supported Languages
The SDK supports 37 languages with automatic language detection:
| Code | Language | Code | Language | Code | Language |
|---|---|---|---|---|---|
eng | English | jpn | Japanese | ukr | Ukrainian |
kor | Korean | ell | Greek | ind | Indonesian |
spa | Spanish | tam | Tamil | dan | Danish |
deu | German | tgl | Tagalog | swe | Swedish |
fra | French | fin | Finnish | msa | Malay |
ita | Italian | zho | Chinese | ces | Czech |
pol | Polish | slk | Slovak | por | Portuguese |
nld | Dutch | ara | Arabic | bul | Bulgarian |
rus | Russian | hrv | Croatian | ron | Romanian |
ben | Bengali | hin | Hindi | hun | Hungarian |
nan | Hokkien | nor | Norwegian | pan | Punjabi |
tha | Thai | tur | Turkish | vie | Vietnamese |
yue | Cantonese |
Error Handling
The SDK provides a typed error enum for handling API errors with pattern matching:
use typecast_rust::{TypecastClient, TTSRequest, TTSModel, TypecastError};
let request = TTSRequest::new("voice_id", "Hello", TTSModel::SsfmV30);
match client.text_to_speech(&request).await {
Ok(response) => {
println!("Success! Duration: {:.2}s", response.duration);
}
Err(TypecastError::Unauthorized { detail }) => {
// 401: Invalid API key
eprintln!("Invalid API key: {}", detail);
}
Err(TypecastError::PaymentRequired { detail }) => {
// 402: Insufficient credits
eprintln!("Insufficient credits: {}", detail);
}
Err(TypecastError::NotFound { detail }) => {
// 404: Resource not found
eprintln!("Voice not found: {}", detail);
}
Err(TypecastError::RateLimited { detail }) => {
// 429: Rate limit exceeded
eprintln!("Rate limit exceeded - please retry later: {}", detail);
}
Err(TypecastError::ServerError { detail }) => {
// 500: Server error
eprintln!("Server error: {}", detail);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
Error Types
| Error Variant | Status Code | Description |
|---|---|---|
BadRequest | 400 | Invalid request parameters |
Unauthorized | 401 | Invalid or missing API key |
PaymentRequired | 402 | Insufficient credits |
Forbidden | 403 | Access denied |
NotFound | 404 | Resource not found |
ValidationError | 422 | Validation error |
RateLimited | 429 | Rate limit exceeded |
ServerError | 500 | Server error |
HttpError | - | HTTP client error |
JsonError | - | JSON serialization error |
Helper Methods
if let Err(e) = result {
if e.is_unauthorized() {
println!("Check your API key");
} else if e.is_rate_limited() {
println!("Wait and retry");
} else if e.is_server_error() {
println!("Server issue, try again later");
}
if let Some(code) = e.status_code() {
println!("HTTP status: {}", code);
}
}
API Reference
TypecastClient Methods
| Method | Description |
|---|---|
from_env() | Create client from environment variables |
with_api_key(key) | Create client with API key |
new(config) | Create client with custom configuration |
text_to_speech(&request) | Convert text to speech audio |
generate_to_file(path, request) | Generate speech and save it directly to a local file |
clone_voice(audio, filename, name, model) | Create a custom voice via instant cloning |
delete_voice(voice_id) | Delete a custom cloned voice |
get_voices_v2(filter) | Get available voices with optional filter |
get_voice_v2(voice_id) | Get a specific voice by ID |
TTSRequest Fields
| Field | Type | Required | Description |
|---|---|---|---|
voice_id | String | ✓ | Voice ID (format: tc_* or uc_*) |
text | String | ✓ | Text to synthesize (max 2000 chars) |
model | TTSModel | ✓ | TTS model (SsfmV21 or SsfmV30) |
language | Option<String> | ISO 639-3 code (auto-detected if omitted) | |
prompt | Option<TTSPrompt> | Emotion settings (Prompt/PresetPrompt/SmartPrompt) | |
output | Option<Output> | Audio output settings | |
seed | Option<u32> | Unsigned integer seed for reproducibility (≥ 0) |
TTSResponse Fields
| Field | Type | Description |
|---|---|---|
audio_data | Vec<u8> | Generated audio data |
duration | f64 | Audio duration in seconds |
format | AudioFormat | Audio format (Wav or Mp3) |
Complete Example
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>> {
// Initialize client
let client = TypecastClient::from_env()?;
// Discover voices
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());
// Use first voice
if let Some(voice) = voices.first() {
let request = TTSRequest::new(
&voice.voice_id,
"Welcome to Typecast! This is a demonstration of our text-to-speech API.",
TTSModel::SsfmV30,
)
.language("eng")
.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!("Saved welcome.mp3 ({:.2}s)", response.duration);
}
Ok(())
}