2009-10-13 19:54:27 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 by Dominik Wenger
|
|
|
|
*
|
|
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "ttssapi.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "rbsettings.h"
|
2010-01-24 21:12:11 +00:00
|
|
|
#include "systeminfo.h"
|
2009-10-13 19:54:27 +00:00
|
|
|
|
|
|
|
TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent)
|
|
|
|
{
|
2010-03-03 22:26:00 +00:00
|
|
|
m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang /voice:\"%voice\""
|
|
|
|
" /speed:%speed \"%options\"";
|
2011-09-26 21:28:05 +00:00
|
|
|
defaultLanguage = "english";
|
|
|
|
m_sapi4 = false;
|
|
|
|
m_started = false;
|
2009-10-13 19:54:27 +00:00
|
|
|
}
|
|
|
|
|
2010-06-04 21:22:25 +00:00
|
|
|
TTSBase::Capabilities TTSSapi::capabilities()
|
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2009-10-13 19:54:27 +00:00
|
|
|
void TTSSapi::generateSettings()
|
|
|
|
{
|
|
|
|
// language
|
2011-02-04 23:28:34 +00:00
|
|
|
QMap<QString, QString> languages = SystemInfo::languages();
|
2009-10-13 19:54:27 +00:00
|
|
|
EncTtsSetting* setting =new EncTtsSetting(this,EncTtsSetting::eSTRINGLIST,
|
2010-03-03 22:26:00 +00:00
|
|
|
tr("Language:"),RbSettings::subValue("sapi",RbSettings::TtsLanguage),
|
2011-02-04 23:28:34 +00:00
|
|
|
languages.values());
|
2009-10-13 19:54:27 +00:00
|
|
|
connect(setting,SIGNAL(dataChanged()),this,SLOT(updateVoiceList()));
|
|
|
|
insertSetting(eLANGUAGE,setting);
|
|
|
|
// voice
|
|
|
|
setting = new EncTtsSetting(this,EncTtsSetting::eSTRINGLIST,
|
2010-03-03 22:26:00 +00:00
|
|
|
tr("Voice:"),RbSettings::subValue("sapi",RbSettings::TtsVoice),
|
|
|
|
getVoiceList(RbSettings::subValue("sapi",RbSettings::TtsLanguage).toString()),
|
|
|
|
EncTtsSetting::eREFRESHBTN);
|
2009-10-13 19:54:27 +00:00
|
|
|
connect(setting,SIGNAL(refresh()),this,SLOT(updateVoiceList()));
|
|
|
|
insertSetting(eVOICE,setting);
|
|
|
|
//speed
|
2011-04-22 18:58:01 +00:00
|
|
|
int speed = RbSettings::subValue("sapi", RbSettings::TtsSpeed).toInt();
|
|
|
|
if(speed > 10 || speed < -10)
|
|
|
|
speed = 0;
|
|
|
|
insertSetting(eSPEED, new EncTtsSetting(this, EncTtsSetting::eINT,
|
|
|
|
tr("Speed:"), speed, -10, 10));
|
2009-10-13 19:54:27 +00:00
|
|
|
// options
|
|
|
|
insertSetting(eOPTIONS,new EncTtsSetting(this,EncTtsSetting::eSTRING,
|
|
|
|
tr("Options:"),RbSettings::subValue("sapi",RbSettings::TtsOptions)));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void TTSSapi::saveSettings()
|
|
|
|
{
|
|
|
|
//save settings in user config
|
2010-03-03 22:26:00 +00:00
|
|
|
RbSettings::setSubValue("sapi",RbSettings::TtsLanguage,
|
|
|
|
getSetting(eLANGUAGE)->current().toString());
|
|
|
|
RbSettings::setSubValue("sapi",RbSettings::TtsVoice,
|
|
|
|
getSetting(eVOICE)->current().toString());
|
|
|
|
RbSettings::setSubValue("sapi",RbSettings::TtsSpeed,
|
|
|
|
getSetting(eSPEED)->current().toInt());
|
|
|
|
RbSettings::setSubValue("sapi",RbSettings::TtsOptions,
|
|
|
|
getSetting(eOPTIONS)->current().toString());
|
2009-10-13 19:54:27 +00:00
|
|
|
|
|
|
|
RbSettings::sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TTSSapi::updateVoiceList()
|
|
|
|
{
|
|
|
|
qDebug() << "update voiceList";
|
|
|
|
QStringList voiceList = getVoiceList(getSetting(eLANGUAGE)->current().toString());
|
|
|
|
getSetting(eVOICE)->setList(voiceList);
|
|
|
|
if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
|
|
|
|
else getSetting(eVOICE)->setCurrent("");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TTSSapi::start(QString *errStr)
|
|
|
|
{
|
|
|
|
|
|
|
|
m_TTSOpts = RbSettings::subValue("sapi",RbSettings::TtsOptions).toString();
|
|
|
|
m_TTSLanguage =RbSettings::subValue("sapi",RbSettings::TtsLanguage).toString();
|
|
|
|
m_TTSVoice=RbSettings::subValue("sapi",RbSettings::TtsVoice).toString();
|
|
|
|
m_TTSSpeed=RbSettings::subValue("sapi",RbSettings::TtsSpeed).toString();
|
|
|
|
m_sapi4 = RbSettings::subValue("sapi",RbSettings::TtsUseSapi4).toBool();
|
|
|
|
|
|
|
|
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
|
|
|
|
QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
|
|
|
|
m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
|
|
|
|
|
|
|
|
QFileInfo tts(m_TTSexec);
|
|
|
|
if(!tts.exists())
|
|
|
|
{
|
2012-01-11 19:18:26 +00:00
|
|
|
*errStr = tr("Could not copy the SAPI script");
|
2009-10-13 19:54:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// create the voice process
|
|
|
|
QString execstring = m_TTSTemplate;
|
|
|
|
execstring.replace("%exe",m_TTSexec);
|
|
|
|
execstring.replace("%options",m_TTSOpts);
|
|
|
|
execstring.replace("%lang",m_TTSLanguage);
|
|
|
|
execstring.replace("%voice",m_TTSVoice);
|
|
|
|
execstring.replace("%speed",m_TTSSpeed);
|
|
|
|
|
|
|
|
if(m_sapi4)
|
|
|
|
execstring.append(" /sapi4 ");
|
|
|
|
|
2012-01-11 19:18:26 +00:00
|
|
|
qDebug() << "[TTSSapi] Start:" << execstring;
|
2009-10-13 19:54:27 +00:00
|
|
|
voicescript = new QProcess(NULL);
|
|
|
|
//connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error()));
|
|
|
|
voicescript->start(execstring);
|
2012-01-11 19:18:26 +00:00
|
|
|
qDebug() << "[TTSSapi] wait for process";
|
2009-10-13 19:54:27 +00:00
|
|
|
if(!voicescript->waitForStarted())
|
|
|
|
{
|
2012-01-11 19:18:26 +00:00
|
|
|
*errStr = tr("Could not start SAPI process");
|
|
|
|
qDebug() << "[TTSSapi] starting process timed out!";
|
2009-10-13 19:54:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!voicescript->waitForReadyRead(300))
|
|
|
|
{
|
|
|
|
*errStr = voicescript->readAllStandardError();
|
|
|
|
if(*errStr != "")
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
voicestream = new QTextStream(voicescript);
|
|
|
|
voicestream->setCodec("UTF16-LE");
|
|
|
|
|
2011-09-26 21:28:05 +00:00
|
|
|
m_started = true;
|
2009-10-13 19:54:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-09-26 21:28:05 +00:00
|
|
|
QString TTSSapi::voiceVendor(void)
|
|
|
|
{
|
|
|
|
bool keeprunning = m_started;
|
|
|
|
QString vendor;
|
|
|
|
if(!m_started) {
|
|
|
|
QString error;
|
|
|
|
start(&error);
|
|
|
|
}
|
|
|
|
*voicestream << "QUERY\tVENDOR\r\n";
|
|
|
|
voicestream->flush();
|
|
|
|
while((vendor = voicestream->readLine()).isEmpty())
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
qDebug() << "[TTSSAPI] TTS vendor:" << vendor;
|
|
|
|
if(!keeprunning) {
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
return vendor;
|
|
|
|
}
|
2009-10-13 19:54:27 +00:00
|
|
|
|
|
|
|
QStringList TTSSapi::getVoiceList(QString language)
|
|
|
|
{
|
|
|
|
QStringList result;
|
|
|
|
|
|
|
|
QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
|
|
|
|
m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
|
|
|
|
|
|
|
|
QFileInfo tts(m_TTSexec);
|
|
|
|
if(!tts.exists())
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// create the voice process
|
|
|
|
QString execstring = "cscript //nologo \"%exe\" /language:%lang /listvoices";
|
|
|
|
execstring.replace("%exe",m_TTSexec);
|
|
|
|
execstring.replace("%lang",language);
|
|
|
|
|
|
|
|
if(RbSettings::value(RbSettings::TtsUseSapi4).toBool())
|
|
|
|
execstring.append(" /sapi4 ");
|
|
|
|
|
2012-01-11 19:18:26 +00:00
|
|
|
qDebug() << "[TTSSapi] Start:" << execstring;
|
2009-10-13 19:54:27 +00:00
|
|
|
voicescript = new QProcess(NULL);
|
|
|
|
voicescript->start(execstring);
|
2012-01-11 19:18:26 +00:00
|
|
|
qDebug() << "[TTSSapi] wait for process";
|
|
|
|
if(!voicescript->waitForStarted()) {
|
|
|
|
qDebug() << "[TTSSapi] process startup timed out!";
|
2009-10-13 19:54:27 +00:00
|
|
|
return result;
|
2012-01-11 19:18:26 +00:00
|
|
|
}
|
2009-10-13 19:54:27 +00:00
|
|
|
voicescript->closeWriteChannel();
|
|
|
|
voicescript->waitForReadyRead();
|
|
|
|
|
|
|
|
QString dataRaw = voicescript->readAllStandardError().data();
|
|
|
|
result = dataRaw.split(",",QString::SkipEmptyParts);
|
|
|
|
if(result.size() > 0)
|
|
|
|
{
|
|
|
|
result.sort();
|
|
|
|
result.removeFirst();
|
|
|
|
for(int i = 0; i< result.size();i++)
|
|
|
|
{
|
|
|
|
result[i] = result.at(i).simplified();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete voicescript;
|
2010-03-03 22:26:00 +00:00
|
|
|
QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
|
|
|
|
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
|
|
|
|
| QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
|
|
|
|
| QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
|
|
|
|
| QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
|
2009-10-13 19:54:27 +00:00
|
|
|
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
|
|
|
|
{
|
|
|
|
(void) errStr;
|
2010-09-26 11:43:34 +00:00
|
|
|
QString query = "SPEAK\t"+wavfile+"\t"+text;
|
|
|
|
qDebug() << "[TTSSapi] voicing" << query;
|
|
|
|
// append newline to query. Done now to keep debug output more readable.
|
|
|
|
query.append("\r\n");
|
2009-10-13 19:54:27 +00:00
|
|
|
*voicestream << query;
|
|
|
|
*voicestream << "SYNC\tbla\r\n";
|
|
|
|
voicestream->flush();
|
2010-06-04 21:22:25 +00:00
|
|
|
char temp[20];
|
|
|
|
|
|
|
|
//we use this, because waitForReadyRead doesnt work from a different thread
|
|
|
|
while( voicescript->readLine(temp,20) == 0)
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
2012-01-17 19:18:47 +00:00
|
|
|
if(!QFileInfo(wavfile).isFile()) {
|
2012-01-17 21:11:32 +00:00
|
|
|
qDebug() << "[TTSSapi] output file does not exist:" << wavfile;
|
2012-01-17 19:18:47 +00:00
|
|
|
return FatalError;
|
|
|
|
}
|
2009-10-13 19:54:27 +00:00
|
|
|
return NoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TTSSapi::stop()
|
|
|
|
{
|
|
|
|
*voicestream << "QUIT\r\n";
|
|
|
|
voicestream->flush();
|
|
|
|
voicescript->waitForFinished();
|
|
|
|
delete voicestream;
|
|
|
|
delete voicescript;
|
2010-03-03 22:26:00 +00:00
|
|
|
QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
|
|
|
|
QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
|
|
|
|
| QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
|
|
|
|
| QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
|
|
|
|
| QFile::ReadOther | QFile::WriteOther | QFile::ExeOther );
|
2009-10-13 19:54:27 +00:00
|
|
|
QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
|
2011-09-26 21:28:05 +00:00
|
|
|
m_started = false;
|
2009-10-13 19:54:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TTSSapi::configOk()
|
|
|
|
{
|
|
|
|
if(RbSettings::subValue("sapi",RbSettings::TtsVoice).toString().isEmpty())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2010-03-03 22:26:00 +00:00
|
|
|
|