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.
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-amyEnglish Female High ~60 MB piper-en-us-dannyEnglish Male High ~60 MB piper-en-gb-alanEnglish Male High ~60 MB piper-de-thorstenGerman Male High ~60 MB piper-es-mlsSpanish Mixed High ~60 MB piper-fr-mlsFrench Mixed High ~60 MB
System Voices
Built-in iOS/macOS voices via AVSpeechSynthesizer:
Voice ID Platform com.apple.ttsbundle.siri_female_en-US_compactiOS/macOS com.apple.ttsbundle.siri_male_en-US_compactiOS/macOS com.apple.ttsbundle.Samantha-compactiOS/macOS com.apple.voice.enhanced.en-US.SamanthamacOS
Loading Voices
Load a Neural Voice
try await RunAnywhere. loadTTSVoice ( "piper-en-us-amy" )
List Available Voices
let voices = await RunAnywhere. availableTTSVoices
for voice in voices {
print (voice)
}
Check Current Voice
if let currentVoice = await RunAnywhere.currentTTSVoiceId {
print ( "Current voice: \( currentVoice ) " )
}
Unload Voice
try await RunAnywhere. unloadTTSVoice ()
Voice Selection
By Language
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
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
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
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:
// 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:
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
Download and load voices at app startup or in onboarding to avoid delays.
Save user’s preferred voice to UserDefaults and restore on app launch.
Always have a fallback to system voices if neural voices fail to load.
synthesize() Basic synthesis →
Streaming TTS Stream long text →