> ## Documentation Index
> Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP

The official PHP library for the [Typecast API](https://studio.typecast.ai/developers/api). Convert text to lifelike speech using AI-powered voices.

Built with Guzzle 7 for reliable HTTP communication. Requires PHP 8.1+ and Composer.

<CardGroup cols={2}>
<Card title="Packagist" icon="cube" href="https://packagist.org/packages/neosapience/typecast-php">
Typecast PHP SDK
</Card>

<Card title="Source Code" icon="github" href="https://github.com/neosapience/typecast-sdk/tree/main/typecast-php">
Typecast PHP SDK Source Code
</Card>
</CardGroup>

## Installation

Install via Composer:

```bash
composer require neosapience/typecast-php:0.1.14
```

<Warning>
Recommended PHP SDK release: **v0.1.14** on Packagist, pinned in the command above. Requires **PHP 8.1 or higher** and Composer. Check your version with `php -v`.
</Warning>

## Quick Start

```php
<?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) and `ssfm-v21` AI voice models
- **Multi-language Support**: 35+ 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`.

```php
$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:

<CodeGroup>

```bash Environment Variable
export TYPECAST_API_KEY="your-api-key-here"
```

```php From Environment
use Neosapience\Typecast\TypecastClient;

$client = new TypecastClient(
    apiKey: getenv('TYPECAST_API_KEY'),
);
```

```php Direct Configuration
use Neosapience\Typecast\TypecastClient;

$client = new TypecastClient(
    apiKey: 'your-api-key-here',
);
```

</CodeGroup>

<Info>
When requests go through your own proxy, set `baseUrl` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key.
</Info>

```php Proxy without API key
$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**.

<Tabs>
  <Tab title="Smart Mode">
    Let the AI infer emotion from context:

    ```php
    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!",
        ),
    ));
    ```
  </Tab>

  <Tab title="Preset Mode">
    Explicitly set emotion with preset values:

    ```php
    use Neosapience\Typecast\Models\{TTSRequest, PresetPrompt};

    $response = $client->textToSpeech(new TTSRequest(
        voiceId: 'tc_672c5f5ce59fac2a48faeaee',
        text: 'I am so excited to show you these features!',
        model: 'ssfm-v30',
        prompt: new PresetPrompt(
            emotionPreset: 'happy',
            emotionIntensity: 1.5,
        ),
    ));
    ```
  </Tab>
</Tabs>

### Audio Customization

Control loudness, pitch, tempo, and output format:

```php
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',
    ),
));

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](https://studio.typecast.ai/developers/api/voices) page.

```php
$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.

```php
$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, or prompt. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved.

```php
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:

```php
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:

```php
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
    },
);
```

<Note>
**WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable.
</Note>

## 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

```php
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.

```php
$result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps(
    voiceId:     'tc_60e5426de8b95f1d3000d7b5',
    text:        'Hello. How are you?',
    model:       'ssfm-v30',
    granularity: 'char',  // required for Japanese / Chinese
));
```

### Subtitle Export

```php
file_put_contents('output.srt', $result->toSrt());
file_put_contents('output.vtt', $result->toVtt());
```

<Note>
**Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `granularity: 'char'` for these languages to get character-level alignment.
</Note>

## Instant Voice Cloning

Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS.

```php
<?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);
```

<Warning>
Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**.
</Warning>

## Supported Languages

The SDK supports 35+ 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 | | | | |

<Info>
If not specified, the language will be automatically detected from the input text.
</Info>

## Error Handling

The SDK throws specific exceptions for each HTTP error:

```php
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 |

## Control silence duration

Requires **0.1.14 or later**.

`remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal.

Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved.

Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content.

Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type.

```php
$output = new \Neosapience\Typecast\Models\Output(removeSilenceMs: 300);
$streamOutput = new \Neosapience\Typecast\Models\OutputStream(removeSilenceMs: 300);
```
