Делюсь таким простеньким скриптом. Он позволяет отправлять смешанные предложения, содержащие английский и не-английский язык.
Это клиент для Festival TTS server. Преимущество festival - работает локально даже на чайниках железках уровня Intel Celeron x32.
Festival - это многоязычная система синтеза речи, разработанная CSTR (Centre for Speech Technology Research). Она предлагает основу для построения систем синтеза речи, а также включает примеры различных модулей.
Для максимального быстродействия работы с двумя языками на локальной машине надо запустить два инстанса Festival (можно юзать nohup или создать демона):
festival --server '(set! server_port 1314)'
festival --server '(set! server_port 1315)' --language russian
Создать файл festival-tts-client.php с содержимым:
<?php
// by ScorpioT1000 © 2023
class FestivalTTSClient {
private $socketEn = null;
private $socketNonEn = null;
private $mt = 0;
public function __construct(
string $ipPortEnglish = 'localhost:1314',
string $ipPortNonEnglish = null
) {
$this->mt = microtime(true);
$this->socketEn = socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP);
if(!$this->socketEn) {
throw new \Exception("Cannot create a socket en");
}
if(!socket_connect(
$this->socketEn,
explode(':',$ipPortEnglish)[0],
explode(':',$ipPortEnglish)[1])
) {
throw new \Exception("Could not connect to the english server");
}
if($ipPortNonEnglish) {
$this->socketNonEn = socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP);
if(!$this->socketNonEn) {
throw new \Exception("Cannot create a socket non en");
}
if(!socket_connect(
$this->socketNonEn,
explode(':',$ipPortNonEnglish)[0],
explode(':',$ipPortNonEnglish)[1])
) {
throw new \Exception("Could not connect to non english server");
}
}
}
public function __destruct()
{
if($this->socketEn) {
socket_close($this->socketEn);
}
if($this->socketNonEn) {
socket_close($this->socketNonEn);
}
}
public function say(string $text)
{
if(!$this->socketEn) {
throw new \Exception("No active connection");
}
if(!$this->socketNonEn) {
$this->sendText($text, $this->socketEn);
} else {
$expressions = $this->splitByLanguage($text);
foreach($expressions as $expr) {
$this->sendText($expr, $this->isEnglish($expr) ? $this->socketEn : $this->socketNonEn);
}
}
}
private function splitByLanguage(string $text): array
{
$words = preg_split('/[\s]+/', $text);
$expressions = [];
$expr = '';
$lang = null;
foreach($words as $word) {
if(trim($word)) {
$thisLang = $this->isEnglish($word);
if($lang === $thisLang) {
$expr .= ' '.$word;
} else {
$expressions[] = $expr;
$expr = $word;
$lang = $thisLang;
}
}
}
if($expr) {
$expressions[] = $expr;
}
return $expressions;
}
private function isEnglish(string $text): bool
{
return !ctype_digit(trim($text)) && !preg_match('/[^\x20-\x7e]/', $text);
}
private function sendText(string $text, $socket)
{
if(!trim($text)) { return; }
$this->send('(SayText "'.addslashes($text).'")', $socket);
}
private function send(string $message, $socket)
{
echo '['.number_format(microtime(true)-$this->mt, 4, '.', '').'] Request: '.$message.PHP_EOL;
if(socket_write($socket, $message) === false) {
throw new \Exception('Could not write to socket');
}
for($i = 0; $i < 3; ++$i) {
$resp = socket_read($socket, 1024);
echo '['.number_format(microtime(true)-$this->mt, 4, '.', '').'] Response: '.($resp === false ? '(false returned)' : $resp).PHP_EOL;
if(strpos($resp, 'OK') !== false || strpos($resp, 'ER') !== false || $resp === false) {
break;
}
}
}
}
$options = getopt('', ['text:', 'server-eng:', 'server-non-eng::']);
if(empty($options['server-eng'])) {
echo 'Privide --server-eng=IP:PORT as an argument'.PHP_EOL;
exit(1);
}
$client = new FestivalTTSClient($options['server-eng'], ($options['server-non-eng'] ?? null) ?: null);
$text = $options['text'] ?? '';
if($text) {
$client->say($text);
} else {
echo 'Privide --text as an argument'.PHP_EOL;
exit(1);
}
Затем выполнить данный скрипт на php 7+ (apt-get install php будет достаточно):
php festival-tts-client.php --server-eng="localhost:1314" --server-non-eng="localhost:1315" --text="Moskau! Раз два три! Moskau! Посмотри!"
Результат: