The official PHP library for the Typecast API. Convert text to lifelike speech using AI-powered voices.
Built with Guzzle 7 for reliable HTTP communication. Requires PHP 8.1+ and Composer.
Installation
Install via Composer:
composer require neosapience/typecast-php
Quick Start
<?php
use Neosapience\Typecast\TypecastClient;
use Neosapience\Typecast\Models\TTSRequest;
// Initialize client
$client = new TypecastClient(apiKey: 'YOUR_API_KEY');
// Convert text to speech
$response = $client->textToSpeech(new TTSRequest(
voiceId: 'tc_672c5f5ce59fac2a48faeaee',
text: "Hello there! I'm your friendly text-to-speech agent.",
model: 'ssfm-v30',
));
// Save audio file
file_put_contents('output.wav', $response->audioData);
echo "Duration: {$response->duration}s, Format: {$response->format}\n";
Features
- 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
- Timestamp TTS: Word- and character-level alignment data for subtitles, karaoke, and lip-sync
- Streaming: Real-time chunked audio delivery for low-latency playback via callback
- Guzzle 7: Industry-standard HTTP client with automatic retries and connection pooling
- Type Safety: Typed properties and named arguments (PHP 8.1+)
Voice Recommendations
Use recommendVoices when you know the desired style but not the exact voice_id.
$voices = $client->recommendVoices(
'warm female voice for a product tutorial',
count: 3,
);
foreach ($voices as $voice) {
echo "{$voice->voiceId} {$voice->voiceName} {$voice->score}\n";
}
Recommendation results contain only voiceId, voiceName, and score. Use getVoiceV2 or getVoicesV2 when you need detailed metadata such as supported models, emotions, gender, age, or use cases.
Configuration
Set your API key via environment variable or pass directly:
export TYPECAST_API_KEY="your-api-key-here"
$client = new TypecastClient(
baseUrl: 'https://your-proxy.example.com',
);
Advanced Usage
Emotion Control (ssfm-v30)
ssfm-v30 offers two emotion control modes: Preset and Smart.
Let the AI infer emotion from context:
use Neosapience\Typecast\Models\{TTSRequest, SmartPrompt};
$response = $client->textToSpeech(new TTSRequest(
voiceId: 'tc_672c5f5ce59fac2a48faeaee',
text: 'Everything is going to be okay.',
model: 'ssfm-v30',
prompt: new SmartPrompt(
previousText: 'I just got the best news!',
nextText: "I can't wait to celebrate!",
),
));
Audio Customization
Control loudness, pitch, tempo, and output format:
use Neosapience\Typecast\Models\{TTSRequest, Output};
$response = $client->textToSpeech(new TTSRequest(
voiceId: 'tc_672c5f5ce59fac2a48faeaee',
text: 'Customized audio output!',
model: 'ssfm-v30',
output: new Output(
targetLufs: -14.0,
audioPitch: 2,
audioTempo: 1.2,
audioFormat: 'mp3',
),
seed: 42,
));
file_put_contents('output.mp3', $response->audioData);
Generate audio to a file
Use generateToFile 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->generateToFile(
'output.mp3',
'Hello from Typecast.',
'tc_672c5f5ce59fac2a48faeaee' // Find voice IDs at https://studio.typecast.ai/developers/api/voices
);
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.
$audio = $client->composeSpeech()
->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30'))
->say('Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?')
->generate();
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 Neosapience\Typecast\ComposerSettings;
use Neosapience\Typecast\Models\Output;
$audio = $client->composeSpeech()
->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30'))
->say('Hello there')
->pause(5.0)
->say('Nice to meet you', new ComposerSettings(
voiceId: 'tc_60e5426de8b95f1d3000d7b5',
output: new Output(audioPitch: 2)
))
->say('Today')
->pause(2.0)
->say('How does the weather feel?')
->generate();
file_put_contents('conversation.wav', $audio->audioData);
Voice Discovery (V2 API)
List and filter available voices with enhanced metadata:
use Neosapience\Typecast\Models\VoicesV2Filter;
// Get all voices
$voices = $client->getVoicesV2();
// Filter by criteria
$filtered = $client->getVoicesV2(new VoicesV2Filter(
model: 'ssfm-v30',
gender: 'female',
age: 'young_adult',
));
foreach ($voices as $voice) {
echo "ID: {$voice->voiceId}, Name: {$voice->voiceName}\n";
echo "Gender: {$voice->gender}, Age: {$voice->age}\n";
}
// Get a specific voice by ID
$voice = $client->getVoiceV2('tc_672c5f5ce59fac2a48faeaee');
Streaming
Stream audio chunks in real-time for low-latency playback via callback:
use Neosapience\Typecast\Models\TTSRequestStream;
$first = true;
$client->textToSpeechStream(
new TTSRequestStream(
voiceId: 'tc_672c5f5ce59fac2a48faeaee',
text: 'Stream this text as audio in real time.',
model: 'ssfm-v30',
),
function (string $chunk) use (&$first): void {
if ($first) {
$chunk = substr($chunk, 44); // Skip 44-byte WAV header
$first = false;
}
// $chunk is raw 16-bit mono PCM at 32000 Hz
// Feed to your audio output or pipe to ffplay
},
);
Timestamp TTS
textToSpeechWithTimestamps() 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 Neosapience\Typecast\TypecastClient;
use Neosapience\Typecast\Models\TTSRequestWithTimestamps;
$client = new TypecastClient(apiKey: 'YOUR_API_KEY');
$result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps(
voiceId: 'tc_60e5426de8b95f1d3000d7b5',
text: 'Hello. How are you?',
model: 'ssfm-v30',
));
file_put_contents('output.wav', $result->audioData);
echo "Duration: {$result->audioDuration}s\n";
foreach ($result->words as $word) {
echo " [{$word->startTime}s – {$word->endTime}s] {$word->text}\n";
}
Granularity
Pass granularity: 'word' (default) or granularity: 'char' to control the alignment unit.
$result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps(
voiceId: 'tc_60e5426de8b95f1d3000d7b5',
text: 'Hello. How are you?',
model: 'ssfm-v30',
granularity: 'char', // required for Japanese / Chinese
));
Subtitle Export
file_put_contents('output.srt', $result->toSrt());
file_put_contents('output.vtt', $result->toVtt());
Instant Voice Cloning
Clone a custom voice from a short audio sample, then pass the returned uc_ voice ID directly to TTS.
<?php
use Neosapience\Typecast\TypecastClient;
use Neosapience\Typecast\Models\TTSRequest;
$client = new TypecastClient(apiKey: 'YOUR_API_KEY');
$voice = $client->cloneVoice(
audio: file_get_contents('sample.wav'),
filename: 'sample.wav',
name: 'My Voice',
model: 'ssfm-v30',
);
$response = $client->textToSpeech(new TTSRequest(
voiceId: $voice->voiceId,
text: 'Hello from my cloned voice!',
model: 'ssfm-v30',
));
file_put_contents('output.wav', $response->audioData);
$client->deleteVoice($voice->voiceId);
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 throws specific exceptions for each HTTP error:
use Neosapience\Typecast\Exceptions\{
TypecastException,
UnauthorizedException,
PaymentRequiredException,
RateLimitException,
};
try {
$response = $client->textToSpeech($request);
} catch (UnauthorizedException $e) {
echo "Invalid API key: {$e->getMessage()}\n";
} catch (PaymentRequiredException $e) {
echo "Insufficient credits\n";
} catch (RateLimitException $e) {
echo "Rate limit exceeded - please retry later\n";
} catch (TypecastException $e) {
echo "Error: {$e->getMessage()}\n";
}
| Exception | Status Code | Description |
|---|---|---|
BadRequestException | 400 | Invalid request parameters |
UnauthorizedException | 401 | Invalid or missing API key |
PaymentRequiredException | 402 | Insufficient credits |
NotFoundException | 404 | Resource not found |
UnprocessableEntityException | 422 | Validation error |
RateLimitException | 429 | Rate limit exceeded |
InternalServerException | 500 | Server error |
API Reference
TypecastClient Methods
| Method | Description |
|---|---|
textToSpeech(TTSRequest) | Convert text to speech audio |
generateToFile(path, text, voiceId) | Generate speech and save it directly to a local file |
textToSpeechStream(TTSRequestStream, callable) | Stream audio chunks via callback |
cloneVoice($audio, filename, name, model) | Create a custom voice via instant cloning |
deleteVoice(string $voiceId) | Delete a custom cloned voice |
getMySubscription() | Get subscription info |
getVoices(?string $model) | Get available voices (V1) |
getVoicesV2(?VoicesV2Filter) | Get voices with metadata (V2) |
getVoiceV2(string $voiceId) | Get a specific voice |