Из коробки winTTS не кэширует голосовые сообщения, чем, как я думаю, выбивается из мейнстрима Мажордомо. Предлагается решение, устраняющее эту несправедливость.
ps. Устарело, всё внедрено Сергеем в штатный функционал
Небольшая правка нескольких файлов позволяет создавать пользователям winTTS кэш голосовых сообщений, позволяющий так же как и обладателям Яндекса, Гугла и RHVoice, например, слать аудиофайлы на терминалы и т.п.
При установке на Вин-систему для работы с winTTS устанавливаются два файла c:\_majordomo\htdocs\rc\sapi.js и c:\_majordomo\htdocs\rc\sapi_cached.js.
Первый из которых "тупо" проговаривает все фразы, а второй - более сложный -- кэширует новый файл в '/cms/cached/voice/sapi_md5($message).mp3 и повторяющиеся фразы проговаривает уже из кэша.
Обидно, что в windows_tts.class.php, отвечающем за winTTS-говорилку, жёстко прописана ссылка только на файл sapi.js:
safe_exec('cscript ' . DOC_ROOT . '/rc/sapi.js ' . $message, 1, $level);
Я сначала просто переименовывал sapi_cached.js в sapi.js и winTTS начинала кэшировать файлы.
Спустя какое-то время стали приходить всякие дурацкие мысли насчет того, как улучшить то, что и так работает:
скрипт на js сам считал md5($message), мучительно и на трех страницах эмулируя пхпшную функцию. Заставил считать это в windows_tts.class.php и передавать в скрипт параметром.
Вот измененный файл sapi.js:
if (WScript.Arguments.length == 0) {
WScript.Quit();
}
var fnMD5 = '';
if (WScript.Arguments.Named.Exists("md5"))
fnMD5 = WScript.Arguments.Named("md5");
var text = new Array();
for (var i=0; i<WScript.Arguments.Unnamed.Count; i++)
text.push(WScript.Arguments.Unnamed(i));
var fso = new ActiveXObject("Scripting.FileSystemObject");
var scriptPath = fso.GetFile(WScript.ScriptFullName).ParentFolder;
strWavFileName = scriptPath+'/../cms/cached/voice/sapi_'+fnMD5+'.wav';
strMp3FileName = scriptPath+'/../cms/cached/voice/sapi_'+fnMD5+'.mp3';
var wShell = WScript.CreateObject("Wscript.Shell");
if (!fso.FileExists(strMp3FileName)) {//no cached file
var sv = WScript.CreateObject("SAPI.SpVoice");
var oFilestream = WScript.CreateObject("SAPI.SpFileStream");
sv.WaitUntilDone(-1); // Don't be loud
//save to wav
oFilestream.Open(strWavFileName, 3, false);
sv.AudioOutputStream = oFilestream;
sv.speak(text.join(' '),0);
oFilestream.close();
//convert wav to mp3
var strCommand = scriptPath+"/lame.exe -V0 "+strWavFileName+" "+strMp3FileName;
wShell.run(strCommand, 0, true); //WaitUntilDone
//delete wav
if(fso.FileExists(strWavFileName)){
fso.DeleteFile(strWavFileName,true);
}
}
//play mp3
var strCommand = scriptPath+"/madplay.exe \"" + strMp3FileName+"\"";
wShell.run(strCommand, 0, true);
А вот изменения в windows_tts.class.php:
function processSubscription($event, &$details)
{
$this->getConfig();
if ($event == 'SAY' && !$this->config['DISABLED'] && (!$details['ignoreVoice']))
{
$level = $details['level'];
$message = $details['message'];
$mmd5 = md5($message); //добавили сюда расчет md5 сообщения
if ($level >= (int)getGlobal('minMsgLevel') && IsWindowsOS())
{
//safe_exec('cscript ' . DOC_ROOT . '/rc/sapi.js ' . $message, 1, $level); //закомментим то, что было
safe_exec('cscript ' . DOC_ROOT . '/rc/sapi.js /md5:' .$mmd5.' ' . $message, 1, $level); //добавим вызов нашего скрипта с новым параметром md5
$details['ignoreVoice'] = 1;
}
}
}
У меня это работает уже месяц. Полёт нормальный.
Если народу понравится, буду оформлять пулл реквест.
ps. Устарело, всё внедрено Сергеем в штатный функционал
Домодедово, Россия
На форуме: xor