diff options
Diffstat (limited to 'utils/rbutilqt/base/talkgenerator.cpp')
-rw-r--r-- | utils/rbutilqt/base/talkgenerator.cpp | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/utils/rbutilqt/base/talkgenerator.cpp b/utils/rbutilqt/base/talkgenerator.cpp new file mode 100644 index 0000000000..9139ceb274 --- /dev/null +++ b/utils/rbutilqt/base/talkgenerator.cpp | |||
@@ -0,0 +1,337 @@ | |||
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 "talkgenerator.h" | ||
20 | #include "rbsettings.h" | ||
21 | #include "playerbuildinfo.h" | ||
22 | #include "wavtrim.h" | ||
23 | #include "Logger.h" | ||
24 | |||
25 | TalkGenerator::TalkGenerator(QObject* parent): QObject(parent) | ||
26 | { | ||
27 | |||
28 | } | ||
29 | |||
30 | //! \brief Creates Talkfiles. | ||
31 | //! | ||
32 | TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth) | ||
33 | { | ||
34 | m_abort = false; | ||
35 | QString errStr; | ||
36 | bool warnings = false; | ||
37 | |||
38 | //tts | ||
39 | emit logItem(tr("Starting TTS Engine"), LOGINFO); | ||
40 | m_tts = TTSBase::getTTS(this, RbSettings::value(RbSettings::Tts).toString()); | ||
41 | if(!m_tts) | ||
42 | { | ||
43 | LOG_ERROR() << "getting the TTS object failed!"; | ||
44 | emit logItem(tr("Init of TTS engine failed"), LOGERROR); | ||
45 | emit done(true); | ||
46 | return eERROR; | ||
47 | } | ||
48 | if(!m_tts->start(&errStr)) | ||
49 | { | ||
50 | emit logItem(errStr.trimmed(),LOGERROR); | ||
51 | emit logItem(tr("Init of TTS engine failed"), LOGERROR); | ||
52 | emit done(true); | ||
53 | return eERROR; | ||
54 | } | ||
55 | QCoreApplication::processEvents(); | ||
56 | |||
57 | // Encoder | ||
58 | emit logItem(tr("Starting Encoder Engine"),LOGINFO); | ||
59 | m_enc = EncoderBase::getEncoder(this, PlayerBuildInfo::instance()->value( | ||
60 | PlayerBuildInfo::Encoder).toString()); | ||
61 | if(!m_enc->start()) | ||
62 | { | ||
63 | emit logItem(tr("Init of Encoder engine failed"),LOGERROR); | ||
64 | emit done(true); | ||
65 | m_tts->stop(); | ||
66 | return eERROR; | ||
67 | } | ||
68 | QCoreApplication::processEvents(); | ||
69 | |||
70 | emit logProgress(0,0); | ||
71 | |||
72 | // Voice entries | ||
73 | emit logItem(tr("Voicing entries..."),LOGINFO); | ||
74 | Status voiceStatus= voiceList(list,wavtrimth); | ||
75 | if(voiceStatus == eERROR) | ||
76 | { | ||
77 | m_tts->stop(); | ||
78 | m_enc->stop(); | ||
79 | emit done(true); | ||
80 | return eERROR; | ||
81 | } | ||
82 | else if( voiceStatus == eWARNING) | ||
83 | warnings = true; | ||
84 | |||
85 | QCoreApplication::processEvents(); | ||
86 | |||
87 | // Encoding Entries | ||
88 | emit logItem(tr("Encoding files..."),LOGINFO); | ||
89 | Status encoderStatus = encodeList(list); | ||
90 | if( encoderStatus == eERROR) | ||
91 | { | ||
92 | m_tts->stop(); | ||
93 | m_enc->stop(); | ||
94 | emit done(true); | ||
95 | return eERROR; | ||
96 | } | ||
97 | else if( voiceStatus == eWARNING) | ||
98 | warnings = true; | ||
99 | |||
100 | QCoreApplication::processEvents(); | ||
101 | |||
102 | m_tts->stop(); | ||
103 | m_enc->stop(); | ||
104 | emit logProgress(1,1); | ||
105 | |||
106 | if(warnings) | ||
107 | return eWARNING; | ||
108 | return eOK; | ||
109 | } | ||
110 | |||
111 | //! \brief Voices a List of string | ||
112 | //! | ||
113 | TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth) | ||
114 | { | ||
115 | int progressMax = list->size(); | ||
116 | int m_progress = 0; | ||
117 | emit logProgress(m_progress,progressMax); | ||
118 | |||
119 | QStringList errors; | ||
120 | QStringList duplicates; | ||
121 | |||
122 | bool warnings = false; | ||
123 | for(int i=0; i < list->size(); i++) | ||
124 | { | ||
125 | if(m_abort) | ||
126 | { | ||
127 | emit logItem(tr("Voicing aborted"), LOGERROR); | ||
128 | return eERROR; | ||
129 | } | ||
130 | |||
131 | // skip duplicated wav entrys | ||
132 | if(!duplicates.contains(list->at(i).wavfilename)) | ||
133 | duplicates.append(list->at(i).wavfilename); | ||
134 | else | ||
135 | { | ||
136 | LOG_INFO() << "duplicate skipped"; | ||
137 | (*list)[i].voiced = true; | ||
138 | emit logProgress(++m_progress,progressMax); | ||
139 | continue; | ||
140 | } | ||
141 | |||
142 | // skip already voiced entrys | ||
143 | if(list->at(i).voiced == true) | ||
144 | { | ||
145 | emit logProgress(++m_progress,progressMax); | ||
146 | continue; | ||
147 | } | ||
148 | // skip entry whith empty text | ||
149 | if(list->at(i).toSpeak == "") | ||
150 | { | ||
151 | emit logProgress(++m_progress,progressMax); | ||
152 | continue; | ||
153 | } | ||
154 | |||
155 | // voice entry | ||
156 | QString error; | ||
157 | LOG_INFO() << "voicing: " << list->at(i).toSpeak | ||
158 | << "to" << list->at(i).wavfilename; | ||
159 | TTSStatus status = m_tts->voice(list->at(i).toSpeak, | ||
160 | list->at(i).wavfilename, &error); | ||
161 | if(status == Warning) | ||
162 | { | ||
163 | warnings = true; | ||
164 | emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error), | ||
165 | LOGWARNING); | ||
166 | } | ||
167 | else if (status == FatalError) | ||
168 | { | ||
169 | emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error), | ||
170 | LOGERROR); | ||
171 | return eERROR; | ||
172 | } | ||
173 | else | ||
174 | (*list)[i].voiced = true; | ||
175 | |||
176 | // wavtrim if needed | ||
177 | if(wavtrimth != -1) | ||
178 | { | ||
179 | char buffer[255]; | ||
180 | if(wavtrim(list->at(i).wavfilename.toLocal8Bit().data(), | ||
181 | wavtrimth, buffer, 255)) | ||
182 | { | ||
183 | LOG_ERROR() << "wavtrim returned error on" | ||
184 | << list->at(i).wavfilename; | ||
185 | return eERROR; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | emit logProgress(++m_progress,progressMax); | ||
190 | QCoreApplication::processEvents(); | ||
191 | } | ||
192 | if(warnings) | ||
193 | return eWARNING; | ||
194 | else | ||
195 | return eOK; | ||
196 | } | ||
197 | |||
198 | |||
199 | //! \brief Encodes a List of strings | ||
200 | //! | ||
201 | TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list) | ||
202 | { | ||
203 | QStringList duplicates; | ||
204 | |||
205 | int progressMax = list->size(); | ||
206 | int m_progress = 0; | ||
207 | emit logProgress(m_progress,progressMax); | ||
208 | |||
209 | for(int i=0; i < list->size(); i++) | ||
210 | { | ||
211 | if(m_abort) | ||
212 | { | ||
213 | emit logItem(tr("Encoding aborted"), LOGERROR); | ||
214 | return eERROR; | ||
215 | } | ||
216 | |||
217 | //skip non-voiced entrys | ||
218 | if(list->at(i).voiced == false) | ||
219 | { | ||
220 | LOG_WARNING() << "non voiced entry detected:" | ||
221 | << list->at(i).toSpeak; | ||
222 | emit logProgress(++m_progress,progressMax); | ||
223 | continue; | ||
224 | } | ||
225 | //skip duplicates | ||
226 | if(!duplicates.contains(list->at(i).talkfilename)) | ||
227 | duplicates.append(list->at(i).talkfilename); | ||
228 | else | ||
229 | { | ||
230 | LOG_INFO() << "duplicate skipped"; | ||
231 | (*list)[i].encoded = true; | ||
232 | emit logProgress(++m_progress,progressMax); | ||
233 | continue; | ||
234 | } | ||
235 | |||
236 | //encode entry | ||
237 | LOG_INFO() << "encoding " << list->at(i).wavfilename | ||
238 | << "to" << list->at(i).talkfilename; | ||
239 | if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename)) | ||
240 | { | ||
241 | emit logItem(tr("Encoding of %1 failed").arg( | ||
242 | QFileInfo(list->at(i).wavfilename).baseName()), LOGERROR); | ||
243 | return eERROR; | ||
244 | } | ||
245 | (*list)[i].encoded = true; | ||
246 | emit logProgress(++m_progress,progressMax); | ||
247 | QCoreApplication::processEvents(); | ||
248 | } | ||
249 | return eOK; | ||
250 | } | ||
251 | |||
252 | //! \brief slot, which is connected to the abort of the Logger. | ||
253 | //Sets a flag, so Creating Talkfiles ends at the next possible position | ||
254 | //! | ||
255 | void TalkGenerator::abort() | ||
256 | { | ||
257 | m_abort = true; | ||
258 | } | ||
259 | |||
260 | QString TalkGenerator::correctString(QString s) | ||
261 | { | ||
262 | QString corrected = s; | ||
263 | int i = 0; | ||
264 | int max = m_corrections.size(); | ||
265 | while(i < max) { | ||
266 | corrected = corrected.replace(QRegExp(m_corrections.at(i).search, | ||
267 | m_corrections.at(i).modifier.contains("i") | ||
268 | ? Qt::CaseInsensitive : Qt::CaseSensitive), | ||
269 | m_corrections.at(i).replace); | ||
270 | i++; | ||
271 | } | ||
272 | |||
273 | if(corrected != s) | ||
274 | LOG_INFO() << "corrected string" << s << "to" << corrected; | ||
275 | |||
276 | return corrected; | ||
277 | m_abort = true; | ||
278 | } | ||
279 | |||
280 | void TalkGenerator::setLang(QString name) | ||
281 | { | ||
282 | m_lang = name; | ||
283 | |||
284 | // re-initialize corrections list | ||
285 | m_corrections.clear(); | ||
286 | QFile correctionsFile(":/builtin/voice-corrections.txt"); | ||
287 | correctionsFile.open(QIODevice::ReadOnly); | ||
288 | |||
289 | QString engine = RbSettings::value(RbSettings::Tts).toString(); | ||
290 | TTSBase* tts = TTSBase::getTTS(this,RbSettings::value(RbSettings::Tts).toString()); | ||
291 | if(!tts) | ||
292 | { | ||
293 | LOG_ERROR() << "getting the TTS object failed!"; | ||
294 | return; | ||
295 | } | ||
296 | QString vendor = tts->voiceVendor(); | ||
297 | delete tts; | ||
298 | |||
299 | if(m_lang.isEmpty()) | ||
300 | m_lang = "english"; | ||
301 | LOG_INFO() << "building string corrections list for" | ||
302 | << m_lang << engine << vendor; | ||
303 | QTextStream stream(&correctionsFile); | ||
304 | while(!stream.atEnd()) { | ||
305 | QString line = stream.readLine(); | ||
306 | if(line.startsWith(" ") || line.length() < 10) | ||
307 | continue; | ||
308 | // separator is first character | ||
309 | QString separator = line.at(0); | ||
310 | line.remove(0, 1); | ||
311 | QStringList items = line.split(separator); | ||
312 | // we need to have at least 6 separate entries. | ||
313 | if(items.size() < 6) | ||
314 | continue; | ||
315 | |||
316 | QRegExp re_lang(items.at(0)); | ||
317 | QRegExp re_engine(items.at(1)); | ||
318 | QRegExp re_vendor(items.at(2)); | ||
319 | if(!re_lang.exactMatch(m_lang)) { | ||
320 | continue; | ||
321 | } | ||
322 | if(!re_vendor.exactMatch(vendor)) { | ||
323 | continue; | ||
324 | } | ||
325 | if(!re_engine.exactMatch(engine)) { | ||
326 | continue; | ||
327 | } | ||
328 | struct CorrectionItems co; | ||
329 | co.search = items.at(3); | ||
330 | co.replace = items.at(4); | ||
331 | // Qt uses backslash for back references, Perl uses dollar sign. | ||
332 | co.replace.replace(QRegExp("\\$(\\d+)"), "\\\\1"); | ||
333 | co.modifier = items.at(5); | ||
334 | m_corrections.append(co); | ||
335 | } | ||
336 | correctionsFile.close(); | ||
337 | } | ||