В далёком 2001 году в мои руки попал любопытный диск “Системы распознавания речи”. Диск был заполнен различными STT и TTS программками под Windows и на те временя, такой софт казался каким-то чудом из чудес, не смотря на то, что синтез голоса звучал, мягко говоря, паршивенько.
С приходом эпохи нейросетей, синтез голоса поднялся на такой уровень, что порой его не сразу удаётся отличить от живого диктора. Однако, хорошие голосовые движки живут в основном в облаках и чаще всего, имеют ограничения на бесплатное использование. Но что-же делать, когда нам нужно синтезировать голос локально? Поднимем собственный сервер TTS, без регистрации и смс!
Порывшись в сети я наткнулся на подходящий открытый проект coqui-ai TTS и решил попробовать его установить.
В инструкции к софту имеется описание установки через Docker, но не понятным мне причинам, этот вариант у меня не заработал. Что-ж, пойдём другим путём:
Для установки нам понадобится: Debian 12, python3 и pip.
pip install TTS
После того, как пакет успешно установился, можно посмотреть список актуальных голосовых моделей, путём ввода команды tts –list_models
Как оказалось, отдельной голосовой модели для русского языка в списке нет. Однако, есть мультиязыковая модель tts_models/multilingual/multi-dataset/xtts_v2 внутри которой, в том числе, есть поддержка русского языка.
Теперь посмотрим, какие голоса (спикеры) поддерживаем модель:
tts --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --list_speaker_idxs
Выбираем любой из голосов, и пробуем сгенерировать речь:
tts --text "Привет, дружок!" --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --out_path /tmp/speech.wav --speaker_idx "Damien Black" --language_idx "ru"
В данной строке мы указали текст, имя модели, путь для сохранения результата, имя спикера и язык. В ходе выполнения этой строки, TTS сама скачает и запустит указанную модель. Следует учесть, что на запуск модели уходит некоторое время, по этому, для дальнейшей работы с TTS мы запустим её в качестве сервера с API.
И вот тут меня ждал некоторый сюрприз. В инструкции нет прямого указания на то, как это сделать правильно. Методом тыка в ChatGPT с пробами и ошибками, выяснилось, что сервер можно запустить так:
tts-server --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --config_path "/root/.local/share/tts/tts_models--multilingual--multi-dataset--xtts_v2/config.json" --model_path "/root/.local/share/tts/tts_models--multilingual--multi-dataset--xtts_v2"
Без явного указания путей эта штука ни в какую не работала. Добавим в конец &&, чтобы процесс ушёл в бэкграунд. Если всё ок, в выводе будет видно следующее:
* Serving Flask app 'TTS.server.server'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (::)
* Running on http://[::1]:5002
* Running on http://[::1]:5002
Теперь сервер слушает на порту 5002 и ему можно отправлять запросы. И тут меня ждал сюрприз №2: нигде толком не написано, как, собственно, работать с API?
ChatGPT упёрто подсовывал мне варианты с методом POST, но как оказалось, рабочим вариантом оказался GET. Формируем тестовый запрос:
Поскольку, мы имеет дело с самой настоящей нейронкой, каждый новый вызов будет генерировать речь по разному, не смотря на то, что мы не меняем текст.
Однако, иногда в синтез вкрадываются непонятные артифакты, как на следующем примере:
Текст не менялся, но в результате откуда-то взялся “пан” (или “пам”?) В общем, какой-то глюк…
Ну а теперь, попробуем сделать из этого что-то применимое на практике! Моя задумка была в том, чтобы научить телеграм бота отправлять голосовые сообщения в чатик. Для этой задачи был написан следующий пример кода, (как всегда) на php
Для успешной работы скрипта потребуется установить на сервер ffmpeg, поскольку для telegram наши аудио файлы придётся перекодировать в формат OGG.
<?php
// Функция для генерации речи через TTS-сервер
function generateTTS($text, $language, $speaker, $ttsUrl, $outputFile) {
// Подготовка данных для запроса
$params = http_build_query([
'text' => $text,
'language_id' => $language,
'speaker_id' => $speaker,
]);
// Выполнение GET-запроса к TTS-серверу
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ttsUrl . "/api/tts?" . $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
// Сохранение аудиофайла (например, в WAV)
file_put_contents($outputFile, $response);
return true;
} else {
echo "Ошибка при генерации речи: HTTP $httpCode\n";
return false;
}
}
// Функция для конвертации аудио в формат OGG
function convertToOgg($inputFile, $outputFile) {
$command = "ffmpeg -y -i " . escapeshellarg($inputFile) . " -c:a libopus " . escapeshellarg($outputFile);
exec($command, $output, $returnCode);
if ($returnCode === 0) {
echo "Аудиофайл успешно конвертирован в OGG: $outputFile\n";
return true;
} else {
echo "Ошибка при конвертации аудио: " . implode("\n", $output) . "\n";
return false;
}
}
// Функция для отправки голосового сообщения в Telegram
function sendVoiceToTelegram($chatId, $voiceFile, $botToken) {
// Подготовка данных для запроса
$url = "https://api.telegram.org/bot$botToken/sendVoice";
$postFields = [
'chat_id' => $chatId,
'voice' => new CURLFile($voiceFile)
];
// Выполнение POST-запроса к Telegram API
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
echo "Сообщение успешно отправлено в Telegram.\n";
} else {
echo "Ошибка при отправке сообщения в Telegram: HTTP $httpCode\n";
echo "Ответ: $response\n";
}
}
// Конфигурация
$ttsUrl = "http://IP_TTS_СЕРВЕРА:5002"; // URL TTS-сервера
$inputFile = "/tmp/speech.wav"; // Временный файл для оригинального аудио
$outputFile = "/tmp/speech.ogg"; // Файл для конвертации в OGG
$text = "Хочешь, я расскажу тебе сказку, дружок?"; // Текст для генерации
$language = "ru"; // Язык синтеза
$speaker = "Dionisio Schuyler"; // Говорун
$chatId = "........."; // ID чата в Telegram
$botToken = "......................."; // Токен вашего бота Telegram
// Генерация речи
if (generateTTS($text, $language, $speaker, $ttsUrl, $inputFile)) {
// Конвертация в формат OGG
if (convertToOgg($inputFile, $outputFile)) {
// Отправка в Telegram
sendVoiceToTelegram($chatId, $outputFile, $botToken);
}
}
Позже, источником текста стал ChatGPT, но это уже немного другая история… :)
Итог:
Теперь у меня есть собственный TTS сервер. Не смотря на довольно долгий процесс генерации речи, такой вариант меня устраивает, ведь в мои задачи не входило генерировать речь на лету. Однако, если у вас имеется такое требование, синтез можно ускорить, переложив вычисления с процессора на приличную видеокарту с поддержкой CUDA. Поскольку у меня в сервере таковой пока не имеется, испытать движок на скорость вам придётся самостоятельно. ;)