Skip to main content
Two things determine how synthesis sounds: which TTS model is loaded, and the voice field on RATTSOptions. Neural voices are models you register and download. System voices come from AVSpeechSynthesizer and need no download.

Voice sources

Apple’s AVSpeechSynthesizer synthesizes straight to the speaker and gives no way to capture samples for arbitrary text. Use speak() with system voices; use a Piper or MLX model when you need synthesize() to hand back audio.

Loading a neural voice

Voices are models, so they go through the same register, download, load sequence as anything else.
The vits-piper-* archives published under the RunAnywhere sherpa-onnx release tag are the set the iOS example app ships with. Any Piper VITS archive with the same layout works the same way.

Selecting a system voice

Nothing to load. Set voice to an AVSpeechSynthesisVoice identifier or a language code, then call speak.
Resolution order for voice on the system path: an exact voice identifier, then a language code, then languageCode as a fallback. Leaving voice empty, or setting it to "system" or "system-tts", picks the default voice for languageCode. Enumerate what the device offers with AVSpeechSynthesisVoice.speechVoices().

Enumerating engine voices

The loaded engine can report the voices it exposes. This lives on the bridge layer rather than the RunAnywhere facade.
RATTSVoiceInfo fields: id, displayName, languageCode, gender, description_p, isNeural, isSystem, sampleRate, supportedStyles.

Checking and unloading

Voice picker

Per-voice settings

Rate, pitch, and volume live on RATTSOptions, so they apply per call rather than per voice. Keep your own table if you want each voice to sound consistent.

Switching voices mid-session

Loading a different model under .speechSynthesis replaces the active one.
Swapping models costs a load. For back-and-forth dialogue, batch the lines per speaker.

Best practices

Download and load during onboarding so the first speak() is not waiting on a model read.
Save the chosen model ID and reload it at launch.
System voices need no download and are always present. Fall back to speak() with a system voice identifier when a neural voice fails to load.

synthesize()

Basic synthesis

Streaming TTS

Stream long text