РУКОВОДСТВО

Озвучка текста на Ruby: скрипты и приложения на Rails

Своего синтеза речи в Ruby нет. Скрипты часто вызывают say на Mac или espeak в Linux, а они звучат роботизированно, а приложения на Rails обычно отправляют каждый текст в облачный API с оплатой за символы.

Бесплатный движок SuperClonica работает как HTTP-сервер рядом с вашим приложением, с естественными голосами на 31 языке, и для обращения к нему хватает стандартной библиотеки Ruby. В этом руководстве — простой скрипт, связка Rails с Active Job и Active Storage и привычки, которые делают это безопасным в продакшене.

Как Ruby может говорить

Генерируйте звук один раз на каждую версию текста и отдавайте файл, а не синтезируйте на каждый запрос.

  • Системные голоса: say или espeak через system(); настраивать нечего, но звучат роботизированно и на каждой машине по-разному.
  • Облачные API: естественные голоса, но ключ, счёт за символы и ваш текст на чужих серверах.
  • SuperClonica: локальный сервер с естественными голосами, голосами персонажей и своими голосами; Ruby отправляет JSON и получает WAV.
  • Он говорит на 31 языке; китайский пока не поддерживается.

Послушайте голоса бесплатно

Прежде чем писать код, откройте страницу любого голоса, впишите фразу от 30 до 100 символов, выберите язык и нажмите «Озвучить». Через несколько секунд вы услышите голос, который может быть у вашего приложения, без регистрации.

Запустите сервер синтеза

С видеокартой NVIDIA и образом cuda быстрее, но обычного процессора достаточно.

  • Запустите сервер на той же машине или на закрытой: docker run -d -p 8000:8000 -v superclonica-models:/models superclonica/superclonica:cpu.
  • При первом запуске скачивается модель Supertonic 3, около 385 МБ; GET /health сообщит, когда сервер готов.
  • POST /synthesize принимает JSON с полями text, voice, lang, seed и normalize и отвечает 16-битным моно-WAV.
  • По умолчанию запрос принимает до 5000 символов (SUPERCLONICA_MAX_TEXT); длинные тексты делите по абзацам.

Запрос из Ruby

Гемы не нужны: require "net/http"; require "json"; uri = URI("http://localhost:8000/synthesize"); res = Net::HTTP.post(uri, { text: "Привет из Ruby!", voice: "F1", lang: "ru" }.to_json, "Content-Type" => "application/json"); File.binwrite("hello.wav", res.body) if res.is_a?(Net::HTTPSuccess). Для голоса из каталога передайте вместо стандартного имени разобранный файл: voice: JSON.parse(File.read("voice.json")). Faraday или HTTParty работают так же, если они уже есть в приложении.

Rails: фоновые задачи и Active Storage

В Rails озвучивайте текст в Active Job, а не внутри веб-запроса, и прикрепляйте результат к записи: post.audio.attach(io: StringIO.new(res.body), filename: "post-#{post.id}.wav", content_type: "audio/wav"). Перегенерируйте только при изменении текста. Не открывайте порт 8000 в интернет: задайте SUPERCLONICA_API_KEY на сервере и передавайте его в заголовке Authorization: Bearer или держите сервер в закрытой сети. Сообщайте слушателям, что звук сгенерирован.