QProcess cannot write data to stdin
-
I want to use QProcess to start a small console program which reads a line from stdin and speaks the text read.
I start the process like this:
@
QProcess *cons = new QProcess();
cons->start("/home/elp/ConsoleTTS");
if (!cons->waitForStarted()) {
Print("Speech server not started");
} else {
Print("Speech server started");
server = 1;
}
@I subsequently (after a few seconds) try to send the text to be spoken like this (the text is terminated with '\n'):
@
int err = cons->write(text.c_str());if (err==-1) { QString qerr = cons->errorString(); Print("Err: %s", qerr.toStdString().c_str()); }
@
I know that the ConsoleTTS is started correcly. I see it appear in task manager.
I know that the call to 'write' fails. It returns -1. Then, very oddly I do not get to see the message returned by errorString as the call crashes the system.
I am using Qt 5.2.0 (GCC 4.6.1, 64 bit). The OS is lubuntu 13.04.
I am right out of ideas on what is wrong. Am I right in thinking that errorString refers to errors in QIODevice (from which it is inherited) ? I am not so concerned about errorString crashing, but I really need to get 'write' to send data to stdin of the process.
The full code of ConsoleTTS is here in case it helps shed light. The program works fine when run in a console.
#include <QCoreApplication>
#include <QTextStream>
#include <flite.h>@
extern "C" cst_voice register_cmu_us_kal(const char);int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);QTextStream cin(stdin);
QTextStream cout(stdout);cst_voice *v;
flite_init();
v = register_cmu_us_kal(NULL);do {
QString text = cin.readLine();
cout << text << "\n";
cout.flush();
flite_text_to_speech(text.toStdString().c_str(), v, "play");
} while(true);return a.exec();
}
@ -
PS I just tried this:
@
cons->setStandardInputFile("/home/elp/text.txt");
@I set a file to be connected to stdin before starting the process. The process starts correctly and speaks the contents of the file.
Conclusion: Most of QProcess functionality is working. It is just the 'write' function that is not working.
Writing to the file actually provides a workaround to get the speech functionality working, but it would be much neater to pipe the text via QProcess::write.
-
-
The line that crashes is:
@ QString qerr = cons->errorString();
@Although I would not need to get an error string if the QProcess::write invocation was not failing. That is the crux of my problems I think.
-
What kind of error are you getting ?
Are you sure that cons is initialized properly ? That you have not shadowed it at creation time ?
-
First of all many thanks to the people who are trying to help me.
The error that I got was a termination signal that threw me into the debugger. I have now solved the problem. Turns out that the QProcess object was created in a different thread to where 'write' and 'errorString' were called. I guess this sort of explains the writes failing and the errorString crashing the program.
I have rewritten the program very simply and free of any threading issues. The aim is to provide a completely non-blocking means of outputting speech. I include the full definition below and also the console program that QProcess communicates with.
-
The console program works correctly when operated from the keyboard.
-
The 'write' invocations indicate the correct number of bytes sent, but the console program does not seem to receive the data. It does not produce speech output.
@#include <QProcess>
class Voice {
public:QProcess *speechServer;
Voice() {
run();
}void run() {
string speechServerName = SessionDirectory() + "ConsoleTTS";speechServer = new QProcess(); speechServer->start(speechServerName.c_str(), QProcess::Unbuffered | QProcess::ReadWrite); if (!speechServer->waitForStarted()) { Print("Speech server not started"); }
}
void say(const string &text) {
int err1 = speechServer->write(text.c_str());
if (err1==-1) {
Print("QProcess::write failed");
}int err2 = speechServer->write("\n"); if (err2==-1) { Print("QProcess::write failed"); }
}
};
@The console program is:
@#include <QCoreApplication>
#include <QTextStream>
#include <flite.h>extern "C" cst_voice register_cmu_us_kal(const char);
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);QTextStream cin(stdin);
QTextStream cout(stdout);cst_voice *v;
flite_init();
v = register_cmu_us_kal(NULL);do {
QString text = cin.readLine();
cout << text << "\n";
cout.flush();
flite_text_to_speech(text.toStdString().c_str(), v, "play");
} while(true);return a.exec();
}@
-
-
Not that I don't want to help debug your situation but I just thought of something: there's the "QtSpeech":https://gitorious.org/qt-speech module (not part of Qt) that could be interesting for you
-
It is very interesting, as it does just what I wanted (non-blocking TTS). I'm a bit worried about installing it as I think it will destroy my current installation of Festival (took quite a bit of work to get it going with the high-quality voices). However I will give it a go before I update my OS as I will have to re-install everything after that.
Even so I would still like to understand why my very simple piece of code fails. I can think of several other uses for QProcess when I get a handle on it.
Is there much probability that the problem is due to my version of Lubuntu, the now unsupported 13.04 ?
-
Since you already have Festival installed, just skip that part and build QtSpeech directly.
Your QApplication never start since you have an infinite loop running before exec is called. I also don't see anywhere a call to Voice
-
I see what you mean about exec not been called, however the program works perfectly well when run in a console from the keyboard. I suppose it is possible that the event loop is needed when stdin is connected to something other than the keyboard _but isn't the source of the input stream totally irrelevant to a console program ?
I did not include the code that calls Voice::say as it did not seem relevant. The call is made though (I have set a breakpoint to check).
I will give QtSpeech a try. I actually got my program to work by using a named pipe to send the data instead of QProcess::write, but QtSpeech would be a better solution.
Thank you for your help and time. It is much appreciated.
-
What i'd try (I haven't thus far) is to have a QThread dedicated to read stdin and emit a signal that would write to QProcess's input so you can have the event loop running.