Синтез речи: голоса и очередь
Выбрать голос без ошибки на пустом списке и управлять запросами речи.
Синтез речи использует доступные системные голоса. Их список может быть пустым, а сохранённый ID — исчезнуть после изменения системы. Никогда не обращайтесь к voices[0] без проверки длины.
Подготовка
Включите audio/general/text_to_speech в проекте для инициализации при старте. Отдельно храните пользовательскую настройку озвучивания: проектный флаг не заменяет согласованное управление тем, что и когда произносится. DisplayServer.HasFeature(DisplayServer.Feature.TextToSpeech) проверяет возможность backend, но не наличие нужного языка.
Готовый обработчик с отказом без голоса
Добавьте скрипт к Entity и включайте SpeechEnabled по настройке пользователя. TryAnnounce() вызывайте для значимого изменения интерфейса, а не каждый кадр.
using System;
using Reckfell;
public partial class SpeechNarrator : Entity
{
[Export] public bool SpeechEnabled { get; set; }
[Export] public string Language { get; set; } = "en";
public string PreferredVoice { get; set; } = "";
public static string? ChooseVoice(string[] voices, string preferred)
{
if (Array.IndexOf(voices, preferred) >= 0) return preferred;
return voices.Length > 0 ? voices[0] : null;
}
public bool TryAnnounce(string text)
{
if (!SpeechEnabled || string.IsNullOrWhiteSpace(text) ||
!DisplayServer.HasFeature(DisplayServer.Feature.TextToSpeech)) return false;
string? voice = ChooseVoice(DisplayServer.TtsGetVoicesForLanguage(Language), PreferredVoice);
if (voice is null) return false;
DisplayServer.TtsSpeak(text, voice, volume: 50, pitch: 1, rate: 1, utteranceId: 0L, interrupt: false);
return true; // Queued request, not speech-completion acknowledgement.
}
}ChooseVoice() предпочитает сохранённый ID, затем первый доступный голос выбранного языка; при пустом списке возвращает null. В интерфейсе покажите фактически выбранный голос и разрешите его сменить. После переподключения системного сервиса или изменения голосов обновите список.
Language передаётся в TtsGetVoicesForLanguage(). Нативная фильтрация проверяет начало языковой строки, поэтому не считайте любой код локали сайта гарантированно подходящим идентификатором голоса. Отдельно храните локаль текста, выбранный голос и пользовательские параметры речи.
Очередь и завершение
TtsSpeak() отправляет запрос и сразу возвращает управление. true от примера означает отправку, а не завершение воспроизведения и не правильное произношение. Длительность не выводится из длины строки.
По умолчанию пример добавляет сообщение без прерывания. interrupt=true прерывает текущую речь ради новой; TtsStop() останавливает общую очередь. Если несколько экранов говорят одновременно, управляйте очередью через один сервис. Не вызывайте глобальную остановку при закрытии каждого случайного объекта.
Для отслеживания начала/окончания используйте callbacks TtsSetUtteranceCallback() с utteranceId, а не фиксированный таймер. Отмена и ошибка — отдельные исходы. Эти callbacks и реальное воспроизведение данным примером не проверялись.
Полезное поведение интерфейса
Передавайте осмысленный текст без разметки и технических ID. Не повторяйте всё описание при каждом движении указателя. Дайте пользователю выбрать голос, скорость, громкость и отключение озвучивания. Не удаляйте визуальный текст, когда включена речь.
Сам TtsSpeak() не добавляет подписи, порядок фокуса и семантику элементов интерфейса для средств доступности. Эти свойства настраиваются отдельно; наличие TTS не является подтверждением доступности всего приложения.
Если не говорит
Проверьте пользовательский флаг, непустой текст, поддержку backend, список голосов нужного языка и фактический выбранный ID. Если список пуст, оставьте текстовый интерфейс рабочим и предложите настройку голоса. Не публикуйте универсальную команду установки пакетов для всех систем: доступность зависит от целевой ОС и её конфигурации.
Проверены отключение озвучивания, пустой текст, пустой список, сохранённый/исчезнувший ID, отказ headless backend и перечисление голосов Windows без произнесения. Громкость, произношение, завершение/отмена фраз, средства доступности ОС и экспорт не проверялись.