Реализация синтеза речи (Text-to-Speech) в мобильном приложении
Text-to-Speech — одна из немногих мобильных AI-функций, где нативные API обеспечивают приемлемое качество из коробки без сторонних зависимостей. iOS AVSpeechSynthesizer и Android TextToSpeech работают on-device, поддерживают русский язык и не требуют интернета. Основная работа — правильная интеграция, управление очередью и выбор голоса.
AVSpeechSynthesizer на iOS
Базовый случай — три строки кода. Реальный продакшн сложнее.
let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: text)
utterance.voice = AVSpeechSynthesisVoice(language: "ru-RU")
utterance.rate = 0.5 // 0.0–1.0, default = 0.5
synthesizer.speak(utterance)
Голоса на iOS делятся на «compact» (встроенные, ~50 MB) и «enhanced» (качественнее, скачиваются ~300 MB). Enhanced-голоса используют нейросетевой синтез. Если устройство их не скачало — AVSpeechSynthesisVoice(identifier: "com.apple.voice.enhanced.ru-RU.Milena") вернёт nil. Нужна проверка и fallback на compact.
let enhanced = AVSpeechSynthesisVoice(identifier: "com.apple.voice.enhanced.ru-RU.Milena")
utterance.voice = enhanced ?? AVSpeechSynthesisVoice(language: "ru-RU")
Управление AVAudioSession — обязательно. TTS должен работать даже если приложение переключило сессию для записи микрофона или воспроизведения музыки. Категория .playback с mixWithOthers или .duckOthers в зависимости от требований.
Android TextToSpeech: инициализация и очередь
TextToSpeech требует асинхронной инициализации — распространённая ошибка: вызвать speak() до того, как onInit(status) вернул SUCCESS.
val tts = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
tts.language = Locale("ru", "RU")
// только теперь можно вызывать speak()
}
}
QUEUE_FLUSH — обрывает текущее высказывание и начинает новое. QUEUE_ADD — добавляет в очередь. Для последовательных уведомлений (например, озвучка шагов навигации) — QUEUE_ADD. Для ответов ассистента — QUEUE_FLUSH, чтобы не накапливалась очередь при быстром вводе.
UtteranceProgressListener — для отслеживания начала и конца высказывания:
tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
override fun onStart(utteranceId: String) { /* показать индикатор */ }
override fun onDone(utteranceId: String) { /* скрыть индикатор */ }
override fun onError(utteranceId: String) { /* обработать ошибку */ }
})
Каждому вызову speak() нужно передавать уникальный utteranceId — иначе callbacks не вызовутся корректно.
Управление скоростью и паузами
SSML (Speech Synthesis Markup Language) поддерживается iOS с версии 14.0:
let ssml = "<speak><prosody rate='slow'>Внимание</prosody>, <break time='500ms'/>следующая остановка.</speak>"
let utterance = AVSpeechUtterance(ssmlRepresentation: ssml)
На Android SSML поддержка зависит от движка (Google TTS поддерживает, Samsung TTS — частично). Для критичных случаев лучше разбить текст на несколько speak() вызовов с паузами через playSilentUtterance.
Регулировка скорости для доступности (accessibility): предоставьте пользователю контроль rate в настройках приложения. Пожилые пользователи часто предпочитают 0.35–0.4 вместо дефолтных 0.5.
Сроки
Базовая интеграция TTS с управлением очередью и обработкой голосов — 2–3 рабочих дня. Стоимость рассчитывается индивидуально.







