1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2007 by Dominik Wenger
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.
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
17 ****************************************************************************/
20 #include "voicefile.h"
22 #include "rockboxinfo.h"
23 #include "rbsettings.h"
24 #include "systeminfo.h"
27 VoiceFileCreator::VoiceFileCreator(QObject
* parent
) :QObject(parent
)
29 m_wavtrimThreshold
=500;
32 void VoiceFileCreator::abort()
38 bool VoiceFileCreator::createVoiceFile()
42 emit
logItem(tr("Starting Voicefile generation"),LOGINFO
);
44 // test if tempdir exists
45 if(!QDir(QDir::tempPath()+"/rbvoice/").exists())
47 QDir(QDir::tempPath()).mkdir("rbvoice");
49 m_path
= QDir::tempPath() + "/rbvoice/";
51 // read rockbox-info.txt
52 RockboxInfo
info(m_mountpoint
);
55 emit
logItem(tr("could not find rockbox-info.txt"),LOGERROR
);
59 QString target
= info
.target();
60 QString features
= info
.features();
61 m_targetid
= info
.targetID().toInt();
62 m_versionstring
= info
.version();
63 m_voiceformat
= info
.voicefmt();
64 QString version
= m_versionstring
.left(m_versionstring
.indexOf("-")).remove("r");
66 // check if voicefile is present on target
67 QString fn
= m_mountpoint
+ "/.rockbox/langs/voicestrings.zip";
68 qDebug() << "[VoiceFile] searching for zipped voicestrings at" << fn
;
69 if(QFileInfo(fn
).isFile()) {
70 // search for binary voice strings file in archive
73 QStringList contents
= z
.files();
75 for(index
= 0; index
< contents
.size(); ++index
) {
76 // strip any path, we don't know the structure in the zip
77 if(QFileInfo(contents
.at(index
)).baseName() == m_lang
) {
81 if(index
< contents
.size()) {
82 qDebug() << "[VoiceFile] extracting strings file from zip";
84 QTemporaryFile stringsfile
;
86 QString sfn
= stringsfile
.fileName();
87 // ZipUtil::extractArchive() only compares the filename.
88 if(z
.extractArchive(sfn
, QFileInfo(contents
.at(index
)).fileName())) {
89 emit
logItem(tr("Extracted voice strings from installation"), LOGINFO
);
92 QByteArray data
= stringsfile
.readAll();
93 const char* buf
= data
.constData();
95 // header (4 bytes): cookie = 9a, version = 06, targetid, options
96 // subheader for each user. Only "core" for now.
97 // subheader (6 bytes): count (2bytes), size (2bytes), offset (2bytes)
98 if(buf
[0] != (char)0x9a || buf
[1] != 0x06 || buf
[2] != m_targetid
) {
99 emit
logItem(tr("Extracted voice strings incompatible"), LOGINFO
);
102 QMap
<int, QString
> voicestrings
;
107 unsigned int id
= ((unsigned char)buf
[idx
])<<8
108 | ((unsigned char)buf
[idx
+1]);
109 // need to use strlen here, since QString::size()
110 // returns number of characters, not bytes.
111 int len
= strlen(&buf
[idx
+ 2]);
112 voicestrings
[id
] = QString::fromUtf8(&buf
[idx
+2]);
115 } while(idx
< data
.size());
119 // create input file suitable for voicefont from strings.
120 QTemporaryFile voicefontlist
;
121 voicefontlist
.open();
122 m_filename
= voicefontlist
.fileName();
123 for(int i
= 0; i
< voicestrings
.size(); ++i
) {
125 qba
= QString("id: %1_%2\n")
126 .arg(voicestrings
.keys().at(i
) < 0x8000 ? "LANG" : "VOICE")
127 .arg(voicestrings
.keys().at(i
)).toUtf8();
128 voicefontlist
.write(qba
);
129 qba
= QString("voice: \"%1\"\n").arg(
130 voicestrings
[voicestrings
.keys().at(i
)]).toUtf8();
131 voicefontlist
.write(qba
);
133 voicefontlist
.close();
135 // everything successful, now create the actual voice file.
144 emit
logItem(tr("Could not retrieve strings from installation, downloading"), LOGINFO
);
145 // if either no zip with voice strings is found or something went wrong
146 // retrieving the necessary files we'll end up here, trying to get the
147 // genlang output as previously from the webserver.
149 // prepare download url
150 QString genlang
= SystemInfo::value(SystemInfo::GenlangUrl
).toString();
151 genlang
.replace("%LANG%", m_lang
);
152 genlang
.replace("%TARGET%", target
);
153 genlang
.replace("%REVISION%", version
);
154 genlang
.replace("%FEATURES%", features
);
155 QUrl
genlangUrl(genlang
);
156 qDebug() << "[VoiceFileCreator] downloading" << genlangUrl
;
158 //download the correct genlang output
159 QTemporaryFile
*downloadFile
= new QTemporaryFile(this);
160 downloadFile
->open();
161 m_filename
= downloadFile
->fileName();
162 downloadFile
->close();
163 // get the real file.
164 getter
= new HttpGet(this);
165 getter
->setFile(downloadFile
);
167 connect(getter
, SIGNAL(done(bool)), this, SLOT(downloadDone(bool)));
168 connect(getter
, SIGNAL(dataReadProgress(int, int)), this, SIGNAL(logProgress(int, int)));
169 connect(this, SIGNAL(aborted()), getter
, SLOT(abort()));
170 emit
logItem(tr("Downloading voice info..."),LOGINFO
);
171 getter
->getFile(genlangUrl
);
176 void VoiceFileCreator::downloadDone(bool error
)
178 qDebug() << "[VoiceFileCreator] download done, error:" << error
;
180 // update progress bar
181 emit
logProgress(1,1);
182 if(getter
->httpResponse() != 200 && !getter
->isCached()) {
183 emit
logItem(tr("Download error: received HTTP error %1.")
184 .arg(getter
->httpResponse()),LOGERROR
);
189 if(getter
->isCached())
190 emit
logItem(tr("Cached file used."), LOGINFO
);
193 emit
logItem(tr("Download error: %1").arg(getter
->errorString()),LOGERROR
);
198 emit
logItem(tr("Download finished."),LOGINFO
);
200 QCoreApplication::processEvents();
205 void VoiceFileCreator::create(void)
207 //open downloaded file
208 QFile
genlang(m_filename
);
209 if(!genlang
.open(QIODevice::ReadOnly
))
211 emit
logItem(tr("failed to open downloaded file"),LOGERROR
);
216 //read in downloaded file
217 emit
logItem(tr("Reading strings..."),LOGINFO
);
218 QTextStream
in(&genlang
);
219 in
.setCodec("UTF-8");
221 bool idfound
= false;
222 bool voicefound
=false;
223 bool useCorrection
= RbSettings::value(RbSettings::UseTtsCorrections
).toBool();
226 QString line
= in
.readLine();
227 if(line
.contains("id:")) //ID found
229 id
= line
.remove("id:").remove('"').trimmed();
232 else if(line
.contains("voice:")) // voice found
234 voice
= line
.remove("voice:").remove('"').trimmed();
235 voice
= voice
.remove("<").remove(">");
239 if(idfound
&& voicefound
)
241 TalkGenerator::TalkEntry entry
;
242 entry
.toSpeak
= voice
;
243 entry
.wavfilename
= m_path
+ "/" + id
+ ".wav";
244 //voicefont wants them with .mp3 extension
245 entry
.talkfilename
= m_path
+ "/" + id
+ ".mp3";
246 entry
.voiced
= false;
247 entry
.encoded
= false;
248 if(id
== "VOICE_PAUSE")
250 QFile::copy(":/builtin/VOICE_PAUSE.wav",m_path
+ "/VOICE_PAUSE.wav");
251 entry
.wavfilename
= m_path
+ "/VOICE_PAUSE.wav";
253 m_talkList
.append(entry
);
255 else if(entry
.toSpeak
.isEmpty()) {
256 qDebug() << "[Voicefile] Empty voice string for ID" << id
;
259 m_talkList
.append(entry
);
267 // check for empty list
268 if(m_talkList
.size() == 0)
270 emit
logItem(tr("The downloaded file was empty!"),LOGERROR
);
277 TalkGenerator
generator(this);
278 // set language for string correction. If not set no correction will be made.
280 generator
.setLang(m_lang
);
281 connect(&generator
,SIGNAL(done(bool)),this,SIGNAL(done(bool)));
282 connect(&generator
,SIGNAL(logItem(QString
,int)),this,SIGNAL(logItem(QString
,int)));
283 connect(&generator
,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int)));
284 connect(this,SIGNAL(aborted()),&generator
,SLOT(abort()));
286 if(generator
.process(&m_talkList
, m_wavtrimThreshold
) == TalkGenerator::eERROR
)
289 emit
logProgress(0,1);
296 emit
logItem(tr("Creating voicefiles..."),LOGINFO
);
297 FILE* ids2
= fopen(m_filename
.toLocal8Bit(), "r");
301 emit
logItem(tr("Error opening downloaded file"),LOGERROR
);
306 FILE* output
= fopen(QString(m_mountpoint
+ "/.rockbox/langs/" + m_lang
307 + ".voice").toLocal8Bit(), "wb");
312 emit
logItem(tr("Error opening output file"),LOGERROR
);
317 qDebug() << "[VoiceFile] Running voicefont, format" << m_voiceformat
;
318 voicefont(ids2
,m_targetid
,m_path
.toLocal8Bit().data(), output
, m_voiceformat
);
319 // ids2 and output are closed by voicefont().
324 // Add Voice file to the install log
325 QSettings
installlog(m_mountpoint
+ "/.rockbox/rbutil.log", QSettings::IniFormat
, 0);
326 installlog
.beginGroup(QString("Voice (self created, %1)").arg(m_lang
));
327 installlog
.setValue("/.rockbox/langs/" + m_lang
+ ".voice", m_versionstring
);
328 installlog
.endGroup();
331 emit
logProgress(1,1);
332 emit
logItem(tr("successfully created."),LOGOK
);
337 //! \brief Cleans up Files potentially left in the temp dir
339 void VoiceFileCreator::cleanup()
341 emit
logItem(tr("Cleaning up..."),LOGINFO
);
343 for(int i
=0; i
< m_talkList
.size(); i
++)
345 if(QFile::exists(m_talkList
[i
].wavfilename
))
346 QFile::remove(m_talkList
[i
].wavfilename
);
347 if(QFile::exists(m_talkList
[i
].talkfilename
))
348 QFile::remove(m_talkList
[i
].talkfilename
);
350 QCoreApplication::processEvents();
352 emit
logItem(tr("Finished"),LOGINFO
);