diff options
Diffstat (limited to 'utils/rbutilqt/base/ttssapi.cpp')
-rw-r--r-- | utils/rbutilqt/base/ttssapi.cpp | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/utils/rbutilqt/base/ttssapi.cpp b/utils/rbutilqt/base/ttssapi.cpp new file mode 100644 index 0000000000..1b326b301e --- /dev/null +++ b/utils/rbutilqt/base/ttssapi.cpp | |||
@@ -0,0 +1,274 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2007 by Dominik Wenger | ||
10 | * | ||
11 | * All files in this archive are subject to the GNU General Public License. | ||
12 | * See the file COPYING in the source tree root for full license agreement. | ||
13 | * | ||
14 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
15 | * KIND, either express or implied. | ||
16 | * | ||
17 | ****************************************************************************/ | ||
18 | |||
19 | #include "ttssapi.h" | ||
20 | #include "utils.h" | ||
21 | #include "rbsettings.h" | ||
22 | #include "playerbuildinfo.h" | ||
23 | #include "Logger.h" | ||
24 | |||
25 | TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent) | ||
26 | { | ||
27 | m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang " | ||
28 | "/voice:\"%voice\" /speed:%speed \"%options\""; | ||
29 | m_TTSVoiceTemplate = "cscript //nologo \"%exe\" /language:%lang /listvoices"; | ||
30 | m_TTSType = "sapi"; | ||
31 | defaultLanguage = "english"; | ||
32 | m_started = false; | ||
33 | } | ||
34 | |||
35 | TTSBase::Capabilities TTSSapi::capabilities() | ||
36 | { | ||
37 | return None; | ||
38 | } | ||
39 | |||
40 | void TTSSapi::generateSettings() | ||
41 | { | ||
42 | // language | ||
43 | QMap<QString, QVariant> langmap = PlayerBuildInfo::instance()->value( | ||
44 | PlayerBuildInfo::LanguageList).toMap(); | ||
45 | EncTtsSetting* setting = new EncTtsSetting(this, | ||
46 | EncTtsSetting::eSTRINGLIST, tr("Language:"), | ||
47 | RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage), | ||
48 | langmap.keys()); | ||
49 | connect(setting, &EncTtsSetting::dataChanged, this, &TTSSapi::updateVoiceList); | ||
50 | insertSetting(eLANGUAGE,setting); | ||
51 | // voice | ||
52 | setting = new EncTtsSetting(this, | ||
53 | EncTtsSetting::eSTRINGLIST, tr("Voice:"), | ||
54 | RbSettings::subValue(m_TTSType, RbSettings::TtsVoice), | ||
55 | getVoiceList(RbSettings::subValue(m_TTSType, | ||
56 | RbSettings::TtsLanguage).toString()), | ||
57 | EncTtsSetting::eREFRESHBTN); | ||
58 | connect(setting, &EncTtsSetting::refresh, this, &TTSSapi::updateVoiceList); | ||
59 | insertSetting(eVOICE,setting); | ||
60 | //speed | ||
61 | int speed = RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toInt(); | ||
62 | if(speed > 10 || speed < -10) | ||
63 | speed = 0; | ||
64 | insertSetting(eSPEED, new EncTtsSetting(this, | ||
65 | EncTtsSetting::eINT, tr("Speed:"), speed, -10, 10)); | ||
66 | // options | ||
67 | insertSetting(eOPTIONS, new EncTtsSetting(this, | ||
68 | EncTtsSetting::eSTRING, tr("Options:"), | ||
69 | RbSettings::subValue(m_TTSType, RbSettings::TtsOptions))); | ||
70 | |||
71 | } | ||
72 | |||
73 | void TTSSapi::saveSettings() | ||
74 | { | ||
75 | //save settings in user config | ||
76 | RbSettings::setSubValue(m_TTSType, RbSettings::TtsLanguage, | ||
77 | getSetting(eLANGUAGE)->current().toString()); | ||
78 | RbSettings::setSubValue(m_TTSType, RbSettings::TtsVoice, | ||
79 | getSetting(eVOICE)->current().toString()); | ||
80 | RbSettings::setSubValue(m_TTSType, RbSettings::TtsSpeed, | ||
81 | getSetting(eSPEED)->current().toInt()); | ||
82 | RbSettings::setSubValue(m_TTSType, RbSettings::TtsOptions, | ||
83 | getSetting(eOPTIONS)->current().toString()); | ||
84 | |||
85 | RbSettings::sync(); | ||
86 | } | ||
87 | |||
88 | void TTSSapi::updateVoiceList() | ||
89 | { | ||
90 | LOG_INFO() << "updating voicelist"; | ||
91 | QStringList voiceList = getVoiceList(getSetting(eLANGUAGE)->current().toString()); | ||
92 | getSetting(eVOICE)->setList(voiceList); | ||
93 | if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0)); | ||
94 | else getSetting(eVOICE)->setCurrent(""); | ||
95 | } | ||
96 | |||
97 | bool TTSSapi::start(QString *errStr) | ||
98 | { | ||
99 | |||
100 | m_TTSOpts = RbSettings::subValue(m_TTSType, RbSettings::TtsOptions).toString(); | ||
101 | m_TTSLanguage =RbSettings::subValue(m_TTSType, RbSettings::TtsLanguage).toString(); | ||
102 | m_TTSVoice=RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString(); | ||
103 | m_TTSSpeed=RbSettings::subValue(m_TTSType, RbSettings::TtsSpeed).toString(); | ||
104 | |||
105 | QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); | ||
106 | QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); | ||
107 | m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; | ||
108 | |||
109 | QFileInfo tts(m_TTSexec); | ||
110 | if(!tts.exists()) | ||
111 | { | ||
112 | *errStr = tr("Could not copy the SAPI script"); | ||
113 | return false; | ||
114 | } | ||
115 | // create the voice process | ||
116 | QString execstring = m_TTSTemplate; | ||
117 | execstring.replace("%exe",m_TTSexec); | ||
118 | execstring.replace("%options",m_TTSOpts); | ||
119 | execstring.replace("%lang",m_TTSLanguage); | ||
120 | execstring.replace("%voice",m_TTSVoice); | ||
121 | execstring.replace("%speed",m_TTSSpeed); | ||
122 | |||
123 | LOG_INFO() << "Start:" << execstring; | ||
124 | voicescript = new QProcess(nullptr); | ||
125 | //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error())); | ||
126 | voicescript->start(execstring); | ||
127 | LOG_INFO() << "wait for process"; | ||
128 | if(!voicescript->waitForStarted()) | ||
129 | { | ||
130 | *errStr = tr("Could not start SAPI process"); | ||
131 | LOG_ERROR() << "starting process timed out!"; | ||
132 | return false; | ||
133 | } | ||
134 | |||
135 | if(!voicescript->waitForReadyRead(300)) | ||
136 | { | ||
137 | *errStr = voicescript->readAllStandardError(); | ||
138 | if(*errStr != "") | ||
139 | return false; | ||
140 | } | ||
141 | |||
142 | voicestream = new QTextStream(voicescript); | ||
143 | #if QT_VERSION < 0x060000 | ||
144 | voicestream->setCodec("UTF16-LE"); | ||
145 | #else | ||
146 | voicestream->setEncoding(QStringConverter::Utf16LE); | ||
147 | #endif | ||
148 | |||
149 | m_started = true; | ||
150 | return true; | ||
151 | } | ||
152 | |||
153 | QString TTSSapi::voiceVendor(void) | ||
154 | { | ||
155 | bool keeprunning = m_started; | ||
156 | QString vendor; | ||
157 | if(!m_started) { | ||
158 | QString error; | ||
159 | start(&error); | ||
160 | } | ||
161 | *voicestream << "QUERY\tVENDOR\r\n"; | ||
162 | voicestream->flush(); | ||
163 | while((vendor = voicestream->readLine()).isEmpty()) | ||
164 | QCoreApplication::processEvents(); | ||
165 | |||
166 | LOG_INFO() << "TTS vendor:" << vendor; | ||
167 | if(!keeprunning) { | ||
168 | stop(); | ||
169 | } | ||
170 | return vendor; | ||
171 | } | ||
172 | |||
173 | QStringList TTSSapi::getVoiceList(QString language) | ||
174 | { | ||
175 | QStringList result; | ||
176 | |||
177 | QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); | ||
178 | m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; | ||
179 | |||
180 | QFileInfo tts(m_TTSexec); | ||
181 | if(!tts.exists()) | ||
182 | return result; | ||
183 | |||
184 | // create the voice process | ||
185 | QString execstring = m_TTSVoiceTemplate; | ||
186 | execstring.replace("%exe",m_TTSexec); | ||
187 | execstring.replace("%lang",language); | ||
188 | |||
189 | LOG_INFO() << "Start:" << execstring; | ||
190 | voicescript = new QProcess(nullptr); | ||
191 | voicescript->start(execstring); | ||
192 | LOG_INFO() << "wait for process"; | ||
193 | if(!voicescript->waitForStarted()) { | ||
194 | LOG_INFO() << "process startup timed out!"; | ||
195 | return result; | ||
196 | } | ||
197 | voicescript->closeWriteChannel(); | ||
198 | voicescript->waitForReadyRead(); | ||
199 | |||
200 | QString dataRaw = voicescript->readAllStandardError().data(); | ||
201 | if(dataRaw.startsWith("Error")) { | ||
202 | LOG_INFO() << "Error:" << dataRaw; | ||
203 | } | ||
204 | #if QT_VERSION >= 0x050e00 | ||
205 | result = dataRaw.split(";", Qt::SkipEmptyParts); | ||
206 | #else | ||
207 | result = dataRaw.split(";", QString::SkipEmptyParts); | ||
208 | #endif | ||
209 | if(result.size() > 0) | ||
210 | { | ||
211 | result.sort(); | ||
212 | result.removeFirst(); | ||
213 | for(int i = 0; i< result.size();i++) | ||
214 | { | ||
215 | result[i] = result.at(i).simplified(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | delete voicescript; | ||
220 | QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs", | ||
221 | QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | ||
222 | | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser | ||
223 | | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | ||
224 | | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther ); | ||
225 | QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); | ||
226 | return result; | ||
227 | } | ||
228 | |||
229 | |||
230 | |||
231 | TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr) | ||
232 | { | ||
233 | (void) errStr; | ||
234 | QString query = "SPEAK\t"+wavfile+"\t"+text; | ||
235 | LOG_INFO() << "voicing" << query; | ||
236 | // append newline to query. Done now to keep debug output more readable. | ||
237 | query.append("\r\n"); | ||
238 | *voicestream << query; | ||
239 | *voicestream << "SYNC\tbla\r\n"; | ||
240 | voicestream->flush(); | ||
241 | // do NOT poll the output with readLine(), this causes sync issues! | ||
242 | voicescript->waitForReadyRead(); | ||
243 | |||
244 | if(!QFileInfo(wavfile).isFile()) { | ||
245 | LOG_ERROR() << "output file does not exist:" << wavfile; | ||
246 | return FatalError; | ||
247 | } | ||
248 | return NoError; | ||
249 | } | ||
250 | |||
251 | bool TTSSapi::stop() | ||
252 | { | ||
253 | *voicestream << "QUIT\r\n"; | ||
254 | voicestream->flush(); | ||
255 | voicescript->waitForFinished(); | ||
256 | delete voicestream; | ||
257 | delete voicescript; | ||
258 | QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs", | ||
259 | QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | ||
260 | | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser | ||
261 | | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | ||
262 | | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther ); | ||
263 | QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); | ||
264 | m_started = false; | ||
265 | return true; | ||
266 | } | ||
267 | |||
268 | bool TTSSapi::configOk() | ||
269 | { | ||
270 | if(RbSettings::subValue(m_TTSType, RbSettings::TtsVoice).toString().isEmpty()) | ||
271 | return false; | ||
272 | return true; | ||
273 | } | ||
274 | |||