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

# Voices

> Available TTS voices and voice management

The RunAnywhere SDK supports multiple voice sources: neural ONNX voices (Piper) and system voices via AVSpeechSynthesizer.

## Available Voice Types

### Neural Voices (ONNX/Piper)

High-quality neural voices that run on-device:

| Voice ID            | Language | Gender | Quality | Size    |
| ------------------- | -------- | ------ | ------- | ------- |
| `piper-en-us-amy`   | English  | Female | High    | \~60 MB |
| `piper-en-us-danny` | English  | Male   | High    | \~60 MB |
| `piper-en-gb-alan`  | English  | Male   | High    | \~60 MB |
| `piper-de-thorsten` | German   | Male   | High    | \~60 MB |
| `piper-es-mls`      | Spanish  | Mixed  | High    | \~60 MB |
| `piper-fr-mls`      | French   | Mixed  | High    | \~60 MB |

### System Voices

Built-in iOS/macOS voices via AVSpeechSynthesizer:

| Voice ID                                        | Platform  |
| ----------------------------------------------- | --------- |
| `com.apple.ttsbundle.siri_female_en-US_compact` | iOS/macOS |
| `com.apple.ttsbundle.siri_male_en-US_compact`   | iOS/macOS |
| `com.apple.ttsbundle.Samantha-compact`          | iOS/macOS |
| `com.apple.voice.enhanced.en-US.Samantha`       | macOS     |

## Loading Voices

### Load a Neural Voice

```swift theme={null}
try await RunAnywhere.loadTTSVoice("piper-en-us-amy")
```

### List Available Voices

```swift theme={null}
let voices = await RunAnywhere.availableTTSVoices
for voice in voices {
    print(voice)
}
```

### Check Current Voice

```swift theme={null}
if let currentVoice = await RunAnywhere.currentTTSVoiceId {
    print("Current voice: \(currentVoice)")
}
```

### Unload Voice

```swift theme={null}
try await RunAnywhere.unloadTTSVoice()
```

## Voice Selection

### By Language

```swift theme={null}
func loadVoiceForLanguage(_ language: String) async throws {
    let voiceMap: [String: String] = [
        "en": "piper-en-us-amy",
        "de": "piper-de-thorsten",
        "es": "piper-es-mls",
        "fr": "piper-fr-mls"
    ]

    if let voiceId = voiceMap[language] {
        try await RunAnywhere.loadTTSVoice(voiceId)
    } else {
        // Fall back to system voice
        try await RunAnywhere.loadTTSVoice("com.apple.ttsbundle.siri_female_en-US_compact")
    }
}
```

### Voice Picker UI

```swift theme={null}
struct VoicePickerView: View {
    @State private var selectedVoice = "piper-en-us-amy"
    @State private var availableVoices: [String] = []
    @State private var isLoading = false

    let voiceNames: [String: String] = [
        "piper-en-us-amy": "Amy (English, Female)",
        "piper-en-us-danny": "Danny (English, Male)",
        "piper-en-gb-alan": "Alan (British, Male)",
        "piper-de-thorsten": "Thorsten (German, Male)",
        "piper-es-mls": "Spanish Neural",
        "piper-fr-mls": "French Neural"
    ]

    var body: some View {
        VStack(spacing: 20) {
            Text("Select Voice")
                .font(.headline)

            Picker("Voice", selection: $selectedVoice) {
                ForEach(availableVoices, id: \.self) { voice in
                    Text(voiceNames[voice] ?? voice)
                        .tag(voice)
                }
            }
            .pickerStyle(.wheel)

            Button(action: loadSelectedVoice) {
                HStack {
                    if isLoading {
                        ProgressView()
                            .scaleEffect(0.8)
                    }
                    Text(isLoading ? "Loading..." : "Apply")
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
            }
            .disabled(isLoading)

            Button("Preview") {
                Task {
                    try? await RunAnywhere.speak("Hello! This is a preview of the selected voice.")
                }
            }
            .disabled(isLoading)
        }
        .padding()
        .task {
            availableVoices = await RunAnywhere.availableTTSVoices
        }
    }

    func loadSelectedVoice() {
        isLoading = true

        Task {
            do {
                try await RunAnywhere.loadTTSVoice(selectedVoice)
            } catch {
                print("Failed to load voice: \(error)")
            }

            await MainActor.run {
                isLoading = false
            }
        }
    }
}
```

## Voice Comparison

### Neural vs System Voices

| Feature       | Neural (Piper)    | System (AVSpeech)   |
| ------------- | ----------------- | ------------------- |
| Quality       | Higher            | Good                |
| Latency       | Slightly higher   | Lower               |
| Offline       | Yes               | Yes                 |
| Size          | \~60 MB each      | Built-in            |
| Customization | Rate, pitch       | Rate, pitch, volume |
| Languages     | Limited selection | Many languages      |

### Quality Tiers

```swift theme={null}
enum VoiceQuality {
    case high      // Neural voices
    case standard  // Enhanced system voices
    case compact   // Compact system voices
}

func recommendVoice(for quality: VoiceQuality, language: String) -> String {
    switch quality {
    case .high:
        return "piper-\(language)-*"  // Neural voice
    case .standard:
        return "com.apple.voice.enhanced.\(language)-*"
    case .compact:
        return "com.apple.ttsbundle.*-compact"
    }
}
```

## Custom Voice Configuration

### Per-Voice Settings

```swift theme={null}
struct VoiceSettings {
    let voiceId: String
    var rate: Float = 1.0
    var pitch: Float = 1.0
    var volume: Float = 1.0
}

class VoiceManager {
    private var settings: [String: VoiceSettings] = [
        "piper-en-us-amy": VoiceSettings(voiceId: "piper-en-us-amy", rate: 0.95, pitch: 1.0),
        "piper-en-us-danny": VoiceSettings(voiceId: "piper-en-us-danny", rate: 1.0, pitch: 0.95)
    ]

    func speak(_ text: String) async throws {
        let currentVoice = await RunAnywhere.currentTTSVoiceId ?? "piper-en-us-amy"
        let voiceSettings = settings[currentVoice] ?? VoiceSettings(voiceId: currentVoice)

        try await RunAnywhere.speak(
            text,
            options: TTSOptions(
                rate: voiceSettings.rate,
                pitch: voiceSettings.pitch,
                volume: voiceSettings.volume
            )
        )
    }
}
```

## Downloading Voices

Neural voices need to be downloaded before first use:

```swift theme={null}
// Check if voice is downloaded
let models = try await RunAnywhere.availableModels()
let voiceModel = models.first { $0.id == "piper-en-us-amy" }

if let model = voiceModel, !model.isDownloaded {
    // Download the voice
    let task = try await Download.shared.downloadModel(model)

    for await progress in task.progress {
        print("Downloading: \(Int(progress.overallProgress * 100))%")
    }
}

// Now load the voice
try await RunAnywhere.loadTTSVoice("piper-en-us-amy")
```

## Multi-Voice Support

Switch between voices for different speakers:

```swift theme={null}
class MultiVoiceNarrator {
    let voiceAssignments: [String: String] = [
        "narrator": "piper-en-us-amy",
        "character1": "piper-en-us-danny",
        "character2": "piper-en-gb-alan"
    ]

    func speak(as role: String, text: String) async throws {
        guard let voiceId = voiceAssignments[role] else { return }

        let currentVoice = await RunAnywhere.currentTTSVoiceId

        // Load voice if different
        if currentVoice != voiceId {
            try await RunAnywhere.loadTTSVoice(voiceId)
        }

        try await RunAnywhere.speak(text)
    }
}

// Usage
let narrator = MultiVoiceNarrator()
try await narrator.speak(as: "narrator", text: "Once upon a time...")
try await narrator.speak(as: "character1", text: "Hello, friend!")
try await narrator.speak(as: "character2", text: "Greetings!")
```

## Best Practices

<AccordionGroup>
  <Accordion title="Preload voices" icon="download">
    Download and load voices at app startup or in onboarding to avoid delays.
  </Accordion>

  <Accordion title="Cache voice selection" icon="floppy-disk">
    Save user's preferred voice to UserDefaults and restore on app launch.
  </Accordion>

  <Accordion title="Fallback strategy" icon="shield-check">
    Always have a fallback to system voices if neural voices fail to load.
  </Accordion>
</AccordionGroup>

<CardGroup cols={2}>
  <Card title="synthesize()" icon="volume-high" href="/swift/tts/synthesize">
    Basic synthesis →
  </Card>

  <Card title="Streaming TTS" icon="water" href="/swift/tts/stream">
    Stream long text →
  </Card>
</CardGroup>
