From 390d17893fc257aa1d501d0f5b5d098939b78d28 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Thu, 9 Jul 2026 14:41:54 -0400 Subject: [PATCH] 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 --- utils/rbutilqt/base/ttsespeak.h | 11 +++++++-- utils/rbutilqt/base/ttsespeakng.h | 11 +++++++-- utils/rbutilqt/base/ttsexes.cpp | 29 +++++++++++----------- utils/rbutilqt/base/ttsexes.h | 4 +-- utils/rbutilqt/base/ttsflite.h | 11 ++++++--- utils/rbutilqt/base/ttsmimic.h | 11 +++++++-- utils/rbutilqt/base/ttsmssp.h | 8 ++---- utils/rbutilqt/base/ttssapi.cpp | 41 +++++++++++++++++++------------ utils/rbutilqt/base/ttssapi.h | 4 +-- utils/rbutilqt/base/ttsswift.h | 10 ++++++-- 10 files changed, 87 insertions(+), 53 deletions(-) diff --git a/utils/rbutilqt/base/ttsespeak.h b/utils/rbutilqt/base/ttsespeak.h index afe19fbdac..c069d5bbfb 100644 --- a/utils/rbutilqt/base/ttsespeak.h +++ b/utils/rbutilqt/base/ttsespeak.h @@ -33,8 +33,15 @@ class TTSEspeak : public TTSExes m_name = "espeak"; /* default to espeak */ - m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\""; - m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\""; + m_TTSTemplate << "%options"; + m_TTSTemplate << "-w"; + m_TTSTemplate << "%wavfile"; + m_TTSTemplate << "--"; + m_TTSTemplate << "%text"; + m_TTSSpeakTemplate << "%options"; + m_TTSSpeakTemplate << "--"; + m_TTSSpeakTemplate << "%text"; + m_capabilities = TTSBase::CanSpeak; } }; diff --git a/utils/rbutilqt/base/ttsespeakng.h b/utils/rbutilqt/base/ttsespeakng.h index 55aba62e7d..d47eb7cd42 100644 --- a/utils/rbutilqt/base/ttsespeakng.h +++ b/utils/rbutilqt/base/ttsespeakng.h @@ -32,8 +32,15 @@ class TTSEspeakNG : public TTSExes { m_name = "espeak-ng"; - m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\""; - m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\""; + m_TTSTemplate << "%options"; + m_TTSTemplate << "-w"; + m_TTSTemplate << "%wavfile"; + m_TTSTemplate << "--"; + m_TTSTemplate << "%text"; + m_TTSSpeakTemplate << "%options"; + m_TTSSpeakTemplate << "--"; + m_TTSSpeakTemplate << "%text"; + m_capabilities = TTSBase::CanSpeak; } }; diff --git a/utils/rbutilqt/base/ttsexes.cpp b/utils/rbutilqt/base/ttsexes.cpp index 3eae930eef..a4ecf985b7 100644 --- a/utils/rbutilqt/base/ttsexes.cpp +++ b/utils/rbutilqt/base/ttsexes.cpp @@ -24,14 +24,10 @@ TTSExes::TTSExes(QObject* parent) : TTSBase(parent) { - /* default to espeak */ - m_name = "espeak"; - m_capabilities = TTSBase::CanSpeak; - m_TTSTemplate = "\"%exe\" %options -w \"%wavfile\" -- \"%text\""; - m_TTSSpeakTemplate = "\"%exe\" %options -- \"%text\""; + m_name = "false"; + m_capabilities = TTSBase::None; } - TTSBase::Capabilities TTSExes::capabilities() { return m_capabilities; @@ -83,14 +79,14 @@ bool TTSExes::start(QString *errStr) TTSStatus TTSExes::voice(const QString& text, const QString& wavfile, QString *errStr) { (void) errStr; - QString execstring; + QStringList exec; if(wavfile.isEmpty() && m_capabilities & TTSBase::CanSpeak) { if(m_TTSSpeakTemplate.isEmpty()) { LOG_ERROR() << "internal error: TTS announces CanSpeak " "but template empty!"; return FatalError; } - execstring = m_TTSSpeakTemplate; + exec = m_TTSSpeakTemplate; } else if(wavfile.isEmpty()) { 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; } else { - execstring = m_TTSTemplate; + exec = m_TTSTemplate; } - execstring.replace("%exe",m_TTSexec); - execstring.replace("%options",m_TTSOpts); - execstring.replace("%wavfile",wavfile); - execstring.replace("%text",text); + exec.replaceInStrings("%options",m_TTSOpts); + exec.replaceInStrings("%wavfile",wavfile); + exec.replaceInStrings("%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()) { LOG_ERROR() << "output file does not exist:" << wavfile; @@ -124,4 +124,3 @@ bool TTSExes::configOk() else return false; } - diff --git a/utils/rbutilqt/base/ttsexes.h b/utils/rbutilqt/base/ttsexes.h index 470b007e92..c9b1bb185d 100644 --- a/utils/rbutilqt/base/ttsexes.h +++ b/utils/rbutilqt/base/ttsexes.h @@ -50,8 +50,8 @@ class TTSExes : public TTSBase void loadSettings(void); protected: - QString m_TTSTemplate; - QString m_TTSSpeakTemplate; + QStringList m_TTSTemplate; + QStringList m_TTSSpeakTemplate; QString m_name; QString m_TTSexec; QString m_TTSOpts; diff --git a/utils/rbutilqt/base/ttsflite.h b/utils/rbutilqt/base/ttsflite.h index 717c311a34..6d6c36740d 100644 --- a/utils/rbutilqt/base/ttsflite.h +++ b/utils/rbutilqt/base/ttsflite.h @@ -32,11 +32,14 @@ class TTSFlite : public TTSExes { m_name = "flite"; - /* default to espeak */ - m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\""; - m_TTSSpeakTemplate = ""; - m_capabilities = TTSBase::None; + m_TTSTemplate << "%options"; + m_TTSTemplate << "-o"; + m_TTSTemplate << "%wavfile"; + m_TTSTemplate << "-t"; + m_TTSTemplate << "%text"; + //m_TTSSpeakTemplate << ""; + m_capabilities = TTSBase::None; } }; diff --git a/utils/rbutilqt/base/ttsmimic.h b/utils/rbutilqt/base/ttsmimic.h index f4d7a8beac..3b0295f44e 100644 --- a/utils/rbutilqt/base/ttsmimic.h +++ b/utils/rbutilqt/base/ttsmimic.h @@ -32,8 +32,15 @@ class TTSMimic : public TTSExes { m_name = "mimic"; - m_TTSTemplate = "\"%exe\" %options -o \"%wavfile\" -t \"%text\""; - m_TTSSpeakTemplate = "\"%exe\" %options -t \"%text\""; + m_TTSTemplate << "%options"; + 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; } }; diff --git a/utils/rbutilqt/base/ttsmssp.h b/utils/rbutilqt/base/ttsmssp.h index 817b9fde58..755841338d 100644 --- a/utils/rbutilqt/base/ttsmssp.h +++ b/utils/rbutilqt/base/ttsmssp.h @@ -30,14 +30,10 @@ class TTSMssp: public TTSSapi public: TTSMssp(QObject* parent=nullptr) : TTSSapi(parent) { - m_TTSTemplate = "cscript //nologo \"%exe\" " - "/language:%lang /voice:\"%voice\" " - "/speed:%speed \"%options\" /mssp"; - m_TTSVoiceTemplate = "cscript //nologo \"%exe\" " - "/language:%lang /listvoices /mssp"; + m_TTSTemplate << "/mssp"; + m_TTSVoiceTemplate << "/mssp"; m_TTSType = "mssp"; } - }; #endif diff --git a/utils/rbutilqt/base/ttssapi.cpp b/utils/rbutilqt/base/ttssapi.cpp index 96ca3cb498..14c0cae70e 100644 --- a/utils/rbutilqt/base/ttssapi.cpp +++ b/utils/rbutilqt/base/ttssapi.cpp @@ -24,9 +24,18 @@ TTSSapi::TTSSapi(QObject* parent) : TTSBase(parent) { - m_TTSTemplate = "cscript //nologo \"%exe\" /language:%lang " - "/voice:\"%voice\" /speed:%speed \"%options\""; - m_TTSVoiceTemplate = "cscript //nologo \"%exe\" /language:%lang /listvoices"; + m_TTSTemplate << "//nologo"; + m_TTSTemplate << "%exe"; + 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"; defaultLanguage = "english"; m_started = false; @@ -113,17 +122,17 @@ bool TTSSapi::start(QString *errStr) return false; } // create the voice process - QString execstring = m_TTSTemplate; - execstring.replace("%exe",m_TTSexec); - execstring.replace("%options",m_TTSOpts); - execstring.replace("%lang",m_TTSLanguage); - execstring.replace("%voice",m_TTSVoice); - execstring.replace("%speed",m_TTSSpeed); + QStringList exec = m_TTSTemplate; + exec.replaceInStrings("%exe",m_TTSexec); + exec.replaceInStrings("%options",m_TTSOpts); + exec.replaceInStrings("%lang",m_TTSLanguage); + exec.replaceInStrings("%voice",m_TTSVoice); + exec.replaceInStrings("%speed",m_TTSSpeed); - LOG_INFO() << "Start:" << execstring; + LOG_INFO() << "Start: cscript " << exec; voicescript = new QProcess(nullptr); //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error())); - voicescript->start(execstring); + voicescript->start("cscript", exec); LOG_INFO() << "wait for process"; if(!voicescript->waitForStarted()) { @@ -178,13 +187,13 @@ QStringList TTSSapi::getVoiceList(QString language) return result; // create the voice process - QString execstring = m_TTSVoiceTemplate; - execstring.replace("%exe",m_TTSexec); - execstring.replace("%lang",language); + QStringList exec = m_TTSVoiceTemplate; + exec.replaceInStrings("%exe",m_TTSexec); + exec.replaceInStrings("%lang",language); - LOG_INFO() << "Start:" << execstring; + LOG_INFO() << "Start: cscript " << exec; voicescript = new QProcess(nullptr); - voicescript->start(execstring); + voicescript->start("cscript", exec); LOG_INFO() << "wait for process"; if(!voicescript->waitForStarted()) { LOG_INFO() << "process startup timed out!"; diff --git a/utils/rbutilqt/base/ttssapi.h b/utils/rbutilqt/base/ttssapi.h index b30b9d5fac..8ea1370133 100644 --- a/utils/rbutilqt/base/ttssapi.h +++ b/utils/rbutilqt/base/ttssapi.h @@ -67,8 +67,8 @@ class TTSSapi : public TTSBase bool m_started; protected: - QString m_TTSTemplate; - QString m_TTSVoiceTemplate; + QStringList m_TTSTemplate; + QStringList m_TTSVoiceTemplate; QString m_TTSType; }; diff --git a/utils/rbutilqt/base/ttsswift.h b/utils/rbutilqt/base/ttsswift.h index adbc674d78..78237bea2e 100644 --- a/utils/rbutilqt/base/ttsswift.h +++ b/utils/rbutilqt/base/ttsswift.h @@ -31,8 +31,14 @@ class TTSSwift : public TTSExes TTSSwift(QObject* parent=nullptr) : TTSExes(parent) { 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; } };