A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

Rockbox Utility: support reading voice strings from target.

If the Rockbox installation has voice strings included use them when generating
voice files. Fall back to querying the server if no voice strings are found or
the found strings are incompatible.

Change-Id: I9883829ab1757e55b1da9a434221a8dbfc702dd4

+89 -7
+89 -7
rbutil/rbutilqt/base/voicefile.cpp
··· 16 16 * 17 17 ****************************************************************************/ 18 18 19 + #include <QtCore> 19 20 #include "voicefile.h" 20 21 #include "utils.h" 21 22 #include "rockboxinfo.h" 22 23 #include "rbsettings.h" 23 24 #include "systeminfo.h" 25 + #include "ziputil.h" 24 26 25 27 VoiceFileCreator::VoiceFileCreator(QObject* parent) :QObject(parent) 26 28 { ··· 54 56 emit done(true); 55 57 return false; 56 58 } 57 - 58 59 QString target = info.target(); 59 60 QString features = info.features(); 60 61 m_targetid = info.targetID().toInt(); ··· 62 63 m_voiceformat = info.voicefmt(); 63 64 QString version = m_versionstring.left(m_versionstring.indexOf("-")).remove("r"); 64 65 65 - //prepare download url 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 71 + ZipUtil z(this); 72 + if(z.open(fn)) { 73 + QStringList contents = z.files(); 74 + int index; 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) { 78 + break; 79 + } 80 + } 81 + if(index < contents.size()) { 82 + qDebug() << "[VoiceFile] extracting strings file from zip"; 83 + // extract strings 84 + QTemporaryFile stringsfile; 85 + stringsfile.open(); 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); 90 + 91 + stringsfile.seek(0); 92 + QByteArray data = stringsfile.readAll(); 93 + const char* buf = data.constData(); 94 + // check file header 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); 100 + } 101 + else { 102 + QMap<int, QString> voicestrings; 103 + 104 + /* skip header */ 105 + int idx = 10; 106 + do { 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]); 113 + idx += 2 + len + 1; 114 + 115 + } while(idx < data.size()); 116 + 117 + stringsfile.close(); 118 + 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) { 124 + QByteArray qba; 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); 132 + } 133 + voicefontlist.close(); 134 + 135 + // everything successful, now create the actual voice file. 136 + create(); 137 + return true; 138 + } 139 + 140 + } 141 + } 142 + } 143 + } 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. 148 + 149 + // prepare download url 66 150 QString genlang = SystemInfo::value(SystemInfo::GenlangUrl).toString(); 67 151 genlang.replace("%LANG%", m_lang); 68 152 genlang.replace("%TARGET%", target); 69 153 genlang.replace("%REVISION%", version); 70 154 genlang.replace("%FEATURES%", features); 71 155 QUrl genlangUrl(genlang); 72 - qDebug() << "[VoiceFileCreator] downloading " << genlangUrl; 156 + qDebug() << "[VoiceFileCreator] downloading" << genlangUrl; 73 157 74 158 //download the correct genlang output 75 159 QTemporaryFile *downloadFile = new QTemporaryFile(this); ··· 129 213 return; 130 214 } 131 215 132 - QCoreApplication::processEvents(); 133 - 134 216 //read in downloaded file 135 217 emit logItem(tr("Reading strings..."),LOGINFO); 136 218 QTextStream in(&genlang); ··· 200 282 connect(&generator,SIGNAL(logItem(QString,int)),this,SIGNAL(logItem(QString,int))); 201 283 connect(&generator,SIGNAL(logProgress(int,int)),this,SIGNAL(logProgress(int,int))); 202 284 connect(this,SIGNAL(aborted()),&generator,SLOT(abort())); 203 - 285 + 204 286 if(generator.process(&m_talkList, m_wavtrimThreshold) == TalkGenerator::eERROR) 205 287 { 206 288 cleanup(); ··· 209 291 return; 210 292 } 211 293 } 212 - 294 + 213 295 //make voicefile 214 296 emit logItem(tr("Creating voicefiles..."),LOGINFO); 215 297 FILE* ids2 = fopen(m_filename.toLocal8Bit(), "r");