Commit f5d2c570 authored by Lorenz Meier's avatar Lorenz Meier

Put audio output into a worker thread.

parent fa69b34c
......@@ -243,6 +243,7 @@ INCLUDEPATH += \
src/ui/map \
src/uas \
src/comm \
src/audio \
include/ui \
src/input \
src/lib/qmapcontrol \
......@@ -479,7 +480,8 @@ HEADERS += \
src/QGCFileDialog.h \
src/QGCMessageBox.h \
src/QGCComboBox.h \
src/QGCTemporaryFile.h
src/QGCTemporaryFile.h \
src/audio/QGCAudioWorker.h
SOURCES += \
src/main.cc \
......@@ -618,7 +620,8 @@ SOURCES += \
src/uas/QGXPX4UAS.cc \
src/QGCFileDialog.cc \
src/QGCComboBox.cc \
src/QGCTemporaryFile.cc
src/QGCTemporaryFile.cc \
src/audio/QGCAudioWorker.cpp
#
......
......@@ -33,28 +33,8 @@ This file is part of the QGROUNDCONTROL project
#include <QApplication>
#include <QSettings>
#include "GAudioOutput.h"
#include "MG.h"
#include <QDebug>
#if defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
#include <ApplicationServices/ApplicationServices.h>
#endif
// Speech synthesis is only supported with MSVC compiler
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
// Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx
#include <sapi.h>
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// Using eSpeak for speech synthesis: following https://github.com/mondhs/espeak-sample/blob/master/sampleSpeak.cpp
#include <espeak/speak_lib.h>
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
ISpVoice *GAudioOutput::pVoice = NULL;
#endif
#include <QGC.h>
/**
* This class follows the singleton design pattern
......@@ -78,134 +58,44 @@ GAudioOutput *GAudioOutput::instance()
return _instance;
}
#define QGC_GAUDIOOUTPUT_KEY QString("QGC_AUDIOOUTPUT_")
GAudioOutput::GAudioOutput(QObject *parent) : QObject(parent),
voiceIndex(0),
emergency(false),
muted(false)
muted(false),
thread(new QThread()),
worker(new QGCAudioWorker())
{
// Load settings
QSettings settings;
muted = settings.value(QGC_GAUDIOOUTPUT_KEY + "muted", muted).toBool();
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 500, NULL, 0); // initialize for playback with 500ms buffer and no options (see speak_lib.h)
espeak_VOICE *espeak_voice = espeak_GetCurrentVoice();
espeak_voice->languages = "en-uk"; // Default to British English
espeak_voice->identifier = NULL; // no specific voice file specified
espeak_voice->name = "klatt"; // espeak voice name
espeak_voice->gender = 2; // Female
espeak_voice->age = 0; // age not specified
espeak_SetVoiceByProperties(espeak_voice);
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice = NULL;
if (FAILED(::CoInitialize(NULL)))
{
qDebug() << "ERROR: Creating COM object for audio output failed!";
}
else
{
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if (FAILED(hr))
{
qDebug() << "ERROR: Initializing voice for audio output failed!";
}
}
#endif
// Prepare regular emergency signal, will be fired off on calling startEmergency()
emergencyTimer = new QTimer();
connect(emergencyTimer, SIGNAL(timeout()), this, SLOT(beep()));
switch (voiceIndex)
{
case 0:
selectFemaleVoice();
break;
default:
selectMaleVoice();
break;
}
worker->moveToThread(thread);
connect(this, SIGNAL(textToSpeak(QString,int)), worker, SLOT(say(QString,int)));
thread->start();
}
GAudioOutput::~GAudioOutput()
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice->Release();
pVoice = NULL;
::CoUninitialize();
#endif
thread->quit();
while (thread->isRunning()) {
QGC::SLEEP::usleep(100);
}
worker->deleteLater();
thread->deleteLater();
worker = NULL;
thread = NULL;
}
void GAudioOutput::mute(bool mute)
{
if (mute != muted)
{
this->muted = mute;
QSettings settings;
settings.setValue(QGC_GAUDIOOUTPUT_KEY + "muted", this->muted);
emit mutedChanged(muted);
}
// XXX handle muting
}
bool GAudioOutput::isMuted()
{
return this->muted;
// XXX return right stuff
return false;
}
bool GAudioOutput::say(QString text, int severity)
{
if (!muted)
{
// TODO Add severity filter
Q_UNUSED(severity);
bool res = false;
if (!emergency)
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice->Speak(text.toStdWString().c_str(), SPF_ASYNC, NULL);
#elif defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// Set size of string for espeak: +1 for the null-character
unsigned int espeak_size = strlen(text.toStdString().c_str()) + 1;
espeak_Synth(text.toStdString().c_str(), espeak_size, 0, POS_CHARACTER, 0, espeakCHARS_AUTO, NULL, NULL);
#elif defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
// Slashes necessary to have the right start to the sentence
// copying data prevents SpeakString from reading additional chars
text = "\\" + text;
std::wstring str = text.toStdWString();
unsigned char str2[1024] = {};
memcpy(str2, text.toLatin1().data(), str.length());
SpeakString(str2);
res = true;
#else
// Make sure there isn't an unused variable warning when speech output is disabled
Q_UNUSED(text);
#endif
}
return res;
}
else
{
return false;
}
emit textToSpeak(text, severity);
return true;
}
/**
......@@ -213,19 +103,8 @@ bool GAudioOutput::say(QString text, int severity)
*/
bool GAudioOutput::alert(QString text)
{
if (!emergency || !muted)
{
// Play alert sound
beep();
// Say alert message
say(text, 2);
return true;
}
else
{
return false;
}
emit textToSpeak(text, 2);
return true;
}
void GAudioOutput::notifyPositive()
......@@ -261,16 +140,16 @@ void GAudioOutput::notifyNegative()
*/
bool GAudioOutput::startEmergency()
{
if (!emergency)
{
emergency = true;
// if (!emergency)
// {
// emergency = true;
// Beep immediately and then start timer
if (!muted) beep();
// // Beep immediately and then start timer
// if (!muted) beep();
emergencyTimer->start(1500);
QTimer::singleShot(5000, this, SLOT(stopEmergency()));
}
// emergencyTimer->start(1500);
// QTimer::singleShot(5000, this, SLOT(stopEmergency()));
// }
return true;
}
......@@ -283,11 +162,11 @@ bool GAudioOutput::startEmergency()
*/
bool GAudioOutput::stopEmergency()
{
if (emergency)
{
emergency = false;
emergencyTimer->stop();
}
// if (emergency)
// {
// emergency = false;
// emergencyTimer->stop();
// }
return true;
}
......@@ -304,26 +183,3 @@ void GAudioOutput::beep()
//m_media->play();
}
}
void GAudioOutput::selectFemaleVoice()
{
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// FIXME: Enable selecting a female voice on all platforms
//this->voice = register_cmu_us_slt(NULL);
#endif
}
void GAudioOutput::selectMaleVoice()
{
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// FIXME: Enable selecting a male voice on all platforms
//this->voice = register_cmu_us_rms(NULL);
#endif
}
QStringList GAudioOutput::listVoices(void)
{
// No voice selection is currently supported, so just return an empty list
QStringList l;
return l;
}
......@@ -34,31 +34,10 @@ This file is part of the PIXHAWK project
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QStringList>
#ifdef Q_OS_MAC
//#include <MediaObject>
//#include <AudioOutput>
#endif
#ifdef Q_OS_LINUX
//#include <phonon/MediaObject>
//#include <phonon/AudioOutput>
#endif
#ifdef Q_OS_WIN
//#include <Phonon/MediaObject>
//#include <Phonon/AudioOutput>
#endif
/* For Snow leopard and later
#if defined Q_OS_MAC & defined QGC_SPEECH_ENABLED
#include <NSSpeechSynthesizer.h>
#endif
*/
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
// Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx
#include <sapi.h>
#endif
#include <QGCAudioWorker.h>
/**
* @brief Audio Output (speech synthesizer and "beep" output)
......@@ -91,10 +70,10 @@ public slots:
bool startEmergency();
/** @brief Stop emergency sound */
bool stopEmergency();
/** @brief Select female voice */
void selectFemaleVoice();
/** @brief Select male voice */
void selectMaleVoice();
// /** @brief Select female voice */
// void selectFemaleVoice();
// /** @brief Select male voice */
// void selectMaleVoice();
/** @brief Play emergency sound once */
void beep();
/** @brief Notify about positive event */
......@@ -106,23 +85,12 @@ public slots:
signals:
void mutedChanged(bool);
bool textToSpeak(QString text, int severity = 1);
protected:
#if defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
//NSSpeechSynthesizer
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
//cst_voice* voice; ///< The flite voice object
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
static ISpVoice *pVoice;
#endif
int voiceIndex; ///< The index of the flite voice to use (awb, slt, rms)
//Phonon::MediaObject *m_media; ///< The output object for audio
//Phonon::AudioOutput *m_audioOutput;
bool emergency; ///< Emergency status flag
QTimer *emergencyTimer;
bool muted;
QThread* thread;
QGCAudioWorker* worker;
private:
GAudioOutput(QObject *parent = NULL);
~GAudioOutput();
......
#include <QSettings>
#include <QDebug>
#include "QGC.h"
#include "QGCAudioWorker.h"
#if defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
#include <ApplicationServices/ApplicationServices.h>
#endif
// Speech synthesis is only supported with MSVC compiler
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
// Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx
#include <sapi.h>
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// Using eSpeak for speech synthesis: following https://github.com/mondhs/espeak-sample/blob/master/sampleSpeak.cpp
#include <espeak/speak_lib.h>
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
ISpVoice *GAudioOutput::pVoice = NULL;
#endif
#define QGC_GAUDIOOUTPUT_KEY QString("QGC_AUDIOOUTPUT_")
QGCAudioWorker::QGCAudioWorker(QObject *parent) :
QObject(parent),
voiceIndex(0),
emergency(false),
muted(false)
{
// Load settings
QSettings settings;
muted = settings.value(QGC_GAUDIOOUTPUT_KEY + "muted", muted).toBool();
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 500, NULL, 0); // initialize for playback with 500ms buffer and no options (see speak_lib.h)
espeak_VOICE *espeak_voice = espeak_GetCurrentVoice();
espeak_voice->languages = "en-uk"; // Default to British English
espeak_voice->identifier = NULL; // no specific voice file specified
espeak_voice->name = "klatt"; // espeak voice name
espeak_voice->gender = 2; // Female
espeak_voice->age = 0; // age not specified
espeak_SetVoiceByProperties(espeak_voice);
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice = NULL;
if (FAILED(::CoInitialize(NULL)))
{
qDebug() << "ERROR: Creating COM object for audio output failed!";
}
else
{
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if (FAILED(hr))
{
qDebug() << "ERROR: Initializing voice for audio output failed!";
}
}
#endif
// Prepare regular emergency signal, will be fired off on calling startEmergency()
emergencyTimer = new QTimer();
connect(emergencyTimer, SIGNAL(timeout()), this, SLOT(beep()));
}
QGCAudioWorker::~QGCAudioWorker()
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice->Release();
pVoice = NULL;
::CoUninitialize();
#endif
}
void QGCAudioWorker::say(QString text, int severity)
{
qDebug() << "TEXT" << text;
if (!muted)
{
// TODO Add severity filter
Q_UNUSED(severity);
bool res = false;
if (!emergency)
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice->Speak(text.toStdWString().c_str(), SPF_ASYNC, NULL);
#elif defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
// Set size of string for espeak: +1 for the null-character
unsigned int espeak_size = strlen(text.toStdString().c_str()) + 1;
espeak_Synth(text.toStdString().c_str(), espeak_size, 0, POS_CHARACTER, 0, espeakCHARS_AUTO, NULL, NULL);
#elif defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
// Slashes necessary to have the right start to the sentence
// copying data prevents SpeakString from reading additional chars
text = "\\" + text;
std::wstring str = text.toStdWString();
unsigned char str2[1024] = {};
memcpy(str2, text.toLatin1().data(), str.length());
SpeakString(str2);
// Block the thread while busy
// because we run in our own thread, this doesn't
// halt the main application
while (SpeechBusy()) {
QGC::SLEEP::msleep(100);
}
#else
// Make sure there isn't an unused variable warning when speech output is disabled
Q_UNUSED(text);
#endif
}
}
}
void QGCAudioWorker::mute(bool mute)
{
if (mute != muted)
{
this->muted = mute;
QSettings settings;
settings.setValue(QGC_GAUDIOOUTPUT_KEY + "muted", this->muted);
// emit mutedChanged(muted);
}
}
bool QGCAudioWorker::isMuted()
{
return this->muted;
}
#ifndef QGCAUDIOWORKER_H
#define QGCAUDIOWORKER_H
#include <QObject>
#include <QTimer>
/* For Snow leopard and later
#if defined Q_OS_MAC & defined QGC_SPEECH_ENABLED
#include <NSSpeechSynthesizer.h>
#endif
*/
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
// Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx
#include <sapi.h>
#endif
class QGCAudioWorker : public QObject
{
Q_OBJECT
public:
explicit QGCAudioWorker(QObject *parent = 0);
~QGCAudioWorker();
void mute(bool mute);
bool isMuted();
signals:
public slots:
/** @brief Say this text if current output priority matches */
void say(QString text, int severity = 1);
protected:
#if defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
//NSSpeechSynthesizer
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
//cst_voice* voice; ///< The flite voice object
#endif
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
static ISpVoice *pVoice;
#endif
int voiceIndex; ///< The index of the flite voice to use (awb, slt, rms)
bool emergency; ///< Emergency status flag
QTimer *emergencyTimer;
bool muted;
};
#endif // QGCAUDIOWORKER_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment