coqui-ai TTS: Mahalliy o’rnating va Telegramga ovozli xabarlarni yuboring

2001-yilda men tasodifan «Nutqni aniqlash tizimlari» nomli qiziq diskka duch keldim. Diskda Windows uchun turli xil STT va TTS dasturlari mavjud edi va o’sha paytda bunday dasturiy ta’minot, ovoz sintezi, yumshoq qilib aytganda, yomon eshitilishiga qaramay, mo»jizalar mo»jizasi kabi tuyulardi.

Neyron tarmoqlarining paydo bo’lishi bilan ovoz sintezi shu darajaga yetdiki, ba’zan uni jonli karnaydan ajratish qiyin. Biroq, yaxshi ovozli dvigatellar asosan bulutga asoslangan va ko’pincha bepul foydalanishda cheklovlarga ega. Lekin agar biz ovozni mahalliy ravishda sintez qilishimiz kerak bo’lsa-chi? Keling, o’zimizning TTS serverimizni yarataylik, ro’yxatdan o’tish yoki SMS talab qilinmaydi!

Internetda qidirganimdan so’ng, men coqui-ai deb nomlangan mos ochiq kodli TTS loyihasiga duch keldim va uni o’rnatishga harakat qilib ko’rishga qaror qildim.

Dasturiy ta’minot qo’llanmasida uni Docker orqali o’rnatish tasvirlangan, ammo noma’lum sabablarga ko’ra bu variant men uchun ishlamadi. Shunday ekan, keling, boshqa yondashuvni sinab ko’raylik:
O’rnatish uchun bizga kerak: Debian 12, python3 va pip.

 pip install TTS 

Paket muvaffaqiyatli o’rnatilgandan so’ng, siz tts —list_models buyrug’ini kiritish orqali joriy ovozli modellar ro’yxatini ko’rishingiz mumkin.

Ma’lum bo’lishicha, ro’yxatda rus tili uchun maxsus ovoz modeli yo’q. Biroq, rus tilini qo’llab-quvvatlaydigan ko’p tilli tts_models/multilingual/multi-dataset/xtts_v2 modeli mavjud.

Endi modelni qaysi ovozlar (karnaylar) qo’llab-quvvatlashini ko’rib chiqaylik:

 tts --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --list_speaker_idxs 

Biz har qanday ovozni tanlaymiz va nutq yaratishga harakat qilamiz:

 tts --text "Привет, дружок!" --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --out_path /tmp/speech.wav --speaker_idx "Damien Black" --language_idx "ru" 

Ushbu qatorda biz matn, model nomi, natijani saqlash yo’li, ma’ruzachi nomi va tilni ko’rsatdik. Ushbu qator bajarilganda, TTS avtomatik ravishda belgilangan modelni yuklab oladi va ishga tushiradi. Shuni yodda tutingki, modelni ishga tushirish biroz vaqt talab etadi, shuning uchun TTS bilan keyingi ishlash uchun biz uni API bilan server sifatida ishga tushiramiz.

Va mana men biroz hayratda qoldim. Ko’rsatmalarda buni qanday qilib to’g’ri bajarish kerakligi to’g’ridan-to’g’ri tushuntirilmagan. ChatGPT bilan bir oz sinov va xatolikdan so’ng, men serverni quyidagicha ishga tushirish mumkinligini aniqladim:

 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" 

Yo’llarni aniq ko’rsatmasdan, bu narsa umuman ishlamaydi. Jarayonni orqa fonga o’tkazish uchun oxiriga && qo’shamiz. Agar hamma narsa yaxshi bo’lsa, natija quyidagicha bo’ladi:

  * 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 

Endi server 5002 portida tinglamoqda va unga so’rovlar yuborilishi mumkin. Va keyin meni ikkinchi kutilmagan voqea kutib turdi: API bilan qanday ishlash haqida hech qayerda aniq tushuntirish yo’q.

ChatGPT menga POST usullarini qo’llashda davom etdi, ammo ma’lum bo’lishicha, GET ishlaydigan usul ekan. Keling, sinov so’rovini yarataylik:

http://YOUR_SERVER_IP:5002/api/tts?text=Sun’iy ovoz rus tilida shunday eshitiladi. Xo’sh, sizga bu qanday yoqadi? Rostdan ham yomon emasmi?&language_id=ru&speaker_id=Dionisio%20Schuyler

Biz haqiqiy neyron tarmog’i bilan ishlayotganimiz sababli, matnni o’zgartirmasak ham, har bir yangi qo’ng’iroq nutqni turlicha yaratadi.

Biroq, ba’zida quyidagi misolda bo’lgani kabi, g’alati artefaktlar sintezga kirib boradi:

Matn o’zgarmadi, lekin natijada qayerdandir «pan» (yoki «pam»?) paydo bo’ldi. Xulosa qilib aytganda, qandaydir nosozlik…

Endi buni amalda qo’llashga harakat qilaylik! Mening g’oyam Telegram botiga chatga ovozli xabarlar yuborishni o’rgatish edi. Bu vazifa uchun men quyidagi kod namunasini (har doimgidek) PHP tilida yozdim.

Skript muvaffaqiyatli ishlashi uchun serverga ffmpeg o’rnatishingiz kerak bo’ladi, chunki bizning audio fayllarimiz Telegram uchun OGG formatiga qayta kodlanishi kerak bo’ladi.

 <pre class="wp-block-syntaxhighlighter-code"><?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);
    }
}</pre> 

Keyinchalik, matn manbai ChatGPT bo’ldi, ammo bu biroz boshqacha hikoya… :)

Natija:
Endi mening o’z TTS serverim bor. Nutq yaratish jarayoni ancha uzoq bo’lishiga qaramay, bu variant menga mos keladi, chunki men nutqni tezkor ravishda yaratishni maqsad qilmagan edim. Biroq, agar sizda shunday talab bo’lsa, sintezni hisoblashlarni protsessordan CUDA qo’llab-quvvatlashiga ega yaxshi grafik kartaga yuklash orqali tezlashtirish mumkin. Mening serverimda hali bunday server yo’qligi sababli, siz dvigatel tezligini o’zingiz sinab ko’rishingiz kerak bo’ladi. ;)