rbutil: Rework TTS classes to use a stringlist for arguments

Instead of passing the entire cmdline as a single string that was
treated as the exectuable name on some platforms

Change-Id: Ic4f043a3a48e1b1bfab82bbfa8983c2d224606ec
This commit is contained in:
Solomon Peachy 2026-07-09 14:41:54 -04:00
parent a9a4b3316f
commit 390d17893f
10 changed files with 87 additions and 53 deletions

View file

@ -33,8 +33,15 @@ class TTSEspeak : public TTSExes
m_name = "espeak"; m_name = "espeak";
/* default to espeak */ /* default to espeak */
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\""; m_TTSTemplate << "%options";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\""; m_TTSTemplate << "-w";
m_TTSTemplate << "%wavfile";
m_TTSTemplate << "--";
m_TTSTemplate << "%text";
m_TTSSpeakTemplate << "%options";
m_TTSSpeakTemplate << "--";
m_TTSSpeakTemplate << "%text";
m_capabilities = TTSBase::CanSpeak; m_capabilities = TTSBase::CanSpeak;
} }
}; };

View file

@ -32,8 +32,15 @@ class TTSEspeakNG : public TTSExes
{ {
m_name = "espeak-ng"; m_name = "espeak-ng";
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\""; m_TTSTemplate << "%options";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\""; m_TTSTemplate << "-w";
m_TTSTemplate << "%wavfile";
m_TTSTemplate << "--";
m_TTSTemplate << "%text";
m_TTSSpeakTemplate << "%options";
m_TTSSpeakTemplate << "--";
m_TTSSpeakTemplate << "%text";
m_capabilities = TTSBase::CanSpeak; m_capabilities = TTSBase::CanSpeak;
} }
}; };

View file

@ -24,14 +24,10 @@
TTSExes::TTSExes(QObject* parent) : TTSBase(parent) TTSExes::TTSExes(QObject* parent) : TTSBase(parent)
{ {
/* default to espeak */ m_name = "false";
m_name = "espeak"; m_capabilities = TTSBase::None;
m_capabilities = TTSBase::CanSpeak;
m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\"";
} }
TTSBase::Capabilities TTSExes::capabilities() TTSBase::Capabilities TTSExes::capabilities()
{ {
return m_capabilities; return m_capabilities;
@ -83,14 +79,14 @@ bool TTSExes::start(QString *errStr)
TTSStatus TTSExes::voice(const QString& text, const QString& wavfile, QString *errStr) TTSStatus TTSExes::voice(const QString& text, const QString& wavfile, QString *errStr)
{ {
(void) errStr; (void) errStr;
QString execstring; QStringList exec;
if(wavfile.isEmpty() && m_capabilities & TTSBase::CanSpeak) { if(wavfile.isEmpty() && m_capabilities & TTSBase::CanSpeak) {
if(m_TTSSpeakTemplate.isEmpty()) { if(m_TTSSpeakTemplate.isEmpty()) {
LOG_ERROR() << "internal error: TTS announces CanSpeak " LOG_ERROR() << "internal error: TTS announces CanSpeak "
"but template empty!"; "but template empty!";
return FatalError; return FatalError;
} }
execstring = m_TTSSpeakTemplate; exec = m_TTSSpeakTemplate;
} }
else if(wavfile.isEmpty()) { else if(wavfile.isEmpty()) {
LOG_ERROR() << "no output file passed to voice() " LOG_ERROR() << "no output file passed to voice() "
@ -98,15 +94,19 @@ TTSStatus TTSExes::voice(const QString& text, const QString& wavfile, QString *e
return FatalError; return FatalError;
} }
else { else {
execstring = m_TTSTemplate; exec = m_TTSTemplate;
} }
execstring.replace("%exe",m_TTSexec); exec.replaceInStrings("%options",m_TTSOpts);
execstring.replace("%options",m_TTSOpts); exec.replaceInStrings("%wavfile",wavfile);
execstring.replace("%wavfile",wavfile); exec.replaceInStrings("%text",text);
execstring.replace("%text",text);
QProcess::execute(execstring); int ret;
if ((ret = QProcess::execute(m_TTSexec, exec))) {
LOG_ERROR() << "TTS exec failed (" << ret << "): " << m_TTSexec << " " << exec;
return FatalError;
}
if(!wavfile.isEmpty() && !QFileInfo(wavfile).isFile()) { if(!wavfile.isEmpty() && !QFileInfo(wavfile).isFile()) {
LOG_ERROR() << "output file does not exist:" << wavfile; LOG_ERROR() << "output file does not exist:" << wavfile;
@ -124,4 +124,3 @@ bool TTSExes::configOk()
else else
return false; return false;
} }

View file

@ -50,8 +50,8 @@ class TTSExes : public TTSBase
void loadSettings(void); void loadSettings(void);
protected: protected:
QString m_TTSTemplate; QStringList m_TTSTemplate;
QString m_TTSSpeakTemplate; QStringList m_TTSSpeakTemplate;
QString m_name; QString m_name;
QString m_TTSexec; QString m_TTSexec;
QString m_TTSOpts; QString m_TTSOpts;

View file

@ -32,11 +32,14 @@ class TTSFlite : public TTSExes
{ {
m_name = "flite"; m_name = "flite";
/* default to espeak */ m_TTSTemplate << "%options";
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\""; m_TTSTemplate << "-o";
m_TTSSpeakTemplate = ""; m_TTSTemplate << "%wavfile";
m_capabilities = TTSBase::None; m_TTSTemplate << "-t";
m_TTSTemplate << "%text";
//m_TTSSpeakTemplate << "";
m_capabilities = TTSBase::None;
} }
}; };

View file

@ -32,8 +32,15 @@ class TTSMimic : public TTSExes
{ {
m_name = "mimic"; m_name = "mimic";
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\""; m_TTSTemplate << "%options";
m_TTSSpeakTemplate = "\"%exe\" %options -t \"%text\""; m_TTSTemplate << "-o";
m_TTSTemplate << "%wavfile";
m_TTSTemplate << "-t";
m_TTSTemplate << "%text";
m_TTSSpeakTemplate << "%options";
m_TTSSpeakTemplate << "-t";
m_TTSSpeakTemplate << "%text";
m_capabilities = TTSBase::CanSpeak; m_capabilities = TTSBase::CanSpeak;
} }
}; };

View file

@ -30,14 +30,10 @@ class TTSMssp: public TTSSapi
public: public:
TTSMssp(QObject* parent=nullptr) : TTSSapi(parent) TTSMssp(QObject* parent=nullptr) : TTSSapi(parent)
{ {
m_TTSTemplate = "cscript //nologo \"%exe\" " m_TTSTemplate << "/mssp";
"/language:%lang /voice:\"%voice\" " m_TTSVoiceTemplate << "/mssp";
"/speed:%speed \"%options\" /mssp";
m_TTSVoiceTemplate = "cscript //nologo \"%exe\" "
"/language:%lang /listvoices /mssp";
m_TTSType = "mssp"; m_TTSType = "mssp";
} }
}; };
#endif #endif

View file

@ -24,9 +24,18 @@
TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent) TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent)
{ {
m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang " m_TTSTemplate << "//nologo";
"/voice:\"%voice\" /speed:%speed \"%options\""; m_TTSTemplate << "%exe";
m_TTSVoiceTemplate = "cscript //nologo \"%exe\" /language:%lang /listvoices"; m_TTSTemplate << "/language:%lang";
m_TTSTemplate << "/voice:%voice";
m_TTSTemplate << "/speed:%speed";
m_TTSTemplate << "%options";
m_TTSVoiceTemplate << "//nologo";
m_TTSVoiceTemplate << "%exe";
m_TTSVoiceTemplate << "/language:%lang";
m_TTSVoiceTemplate << "/listvoices";
m_TTSType = "sapi"; m_TTSType = "sapi";
defaultLanguage = "english"; defaultLanguage = "english";
m_started = false; m_started = false;
@ -113,17 +122,17 @@ bool TTSSapi::start(QString *errStr)
return false; return false;
} }
// create the voice process // create the voice process
QString execstring = m_TTSTemplate; QStringList exec = m_TTSTemplate;
execstring.replace("%exe",m_TTSexec); exec.replaceInStrings("%exe",m_TTSexec);
execstring.replace("%options",m_TTSOpts); exec.replaceInStrings("%options",m_TTSOpts);
execstring.replace("%lang",m_TTSLanguage); exec.replaceInStrings("%lang",m_TTSLanguage);
execstring.replace("%voice",m_TTSVoice); exec.replaceInStrings("%voice",m_TTSVoice);
execstring.replace("%speed",m_TTSSpeed); exec.replaceInStrings("%speed",m_TTSSpeed);
LOG_INFO() << "Start:" << execstring; LOG_INFO() << "Start: cscript " << exec;
voicescript = new QProcess(nullptr); voicescript = new QProcess(nullptr);
//connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error())); //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error()));
voicescript->start(execstring); voicescript->start("cscript", exec);
LOG_INFO() << "wait for process"; LOG_INFO() << "wait for process";
if(!voicescript->waitForStarted()) if(!voicescript->waitForStarted())
{ {
@ -178,13 +187,13 @@ QStringList TTSSapi::getVoiceList(QString language)
return result; return result;
// create the voice process // create the voice process
QString execstring = m_TTSVoiceTemplate; QStringList exec = m_TTSVoiceTemplate;
execstring.replace("%exe",m_TTSexec); exec.replaceInStrings("%exe",m_TTSexec);
execstring.replace("%lang",language); exec.replaceInStrings("%lang",language);
LOG_INFO() << "Start:" << execstring; LOG_INFO() << "Start: cscript " << exec;
voicescript = new QProcess(nullptr); voicescript = new QProcess(nullptr);
voicescript->start(execstring); voicescript->start("cscript", exec);
LOG_INFO() << "wait for process"; LOG_INFO() << "wait for process";
if(!voicescript->waitForStarted()) { if(!voicescript->waitForStarted()) {
LOG_INFO() << "process startup timed out!"; LOG_INFO() << "process startup timed out!";

View file

@ -67,8 +67,8 @@ class TTSSapi : public TTSBase
bool m_started; bool m_started;
protected: protected:
QString m_TTSTemplate; QStringList m_TTSTemplate;
QString m_TTSVoiceTemplate; QStringList m_TTSVoiceTemplate;
QString m_TTSType; QString m_TTSType;
}; };

View file

@ -31,8 +31,14 @@ class TTSSwift : public TTSExes
TTSSwift(QObject* parent=nullptr) : TTSExes(parent) TTSSwift(QObject* parent=nullptr) : TTSExes(parent)
{ {
m_name = "swift"; m_name = "swift";
m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -- \"%text\"";
m_TTSSpeakTemplate = ""; m_TTSTemplate << "%options";
m_TTSTemplate << "-o";
m_TTSTemplate << "%wavfile";
m_TTSTemplate << "--";
m_TTSTemplate << "%text";
//m_TTSSpeakTemplate << "";
m_capabilities = TTSBase::None; m_capabilities = TTSBase::None;
} }
}; };