Commit dd587ce2 authored by dogmaphobic's avatar dogmaphobic

Text to Speech Work

parent c2a3cd0b
......@@ -11,8 +11,8 @@ WindowsBuild {
# the selected autopilot system.
#
# If the user config file exists, it will be included. If this file
# specifies the MAVLINK_CONF variable with a MAVLink dialect, support
# for it will be compiled in to QGC. It will also create a
# specifies the MAVLINK_CONF variable with a MAVLink dialect, support
# for it will be compiled in to QGC. It will also create a
# QGC_USE_{AUTOPILOT_NAME}_MESSAGES macro for use within the actual code.
#
MAVLINKPATH_REL = libs/mavlink/include/mavlink/v1.0
......@@ -79,20 +79,20 @@ INCLUDEPATH += libs/qwt
# Uninstalling from Linux can be done with `sudo make uninstall`.
#
XBEE_DEPENDENT_HEADERS += \
src/comm/XbeeLinkInterface.h \
src/comm/XbeeLink.h \
src/comm/HexSpinBox.h \
src/ui/XbeeConfigurationWindow.h \
src/comm/CallConv.h
src/comm/XbeeLinkInterface.h \
src/comm/XbeeLink.h \
src/comm/HexSpinBox.h \
src/ui/XbeeConfigurationWindow.h \
src/comm/CallConv.h
XBEE_DEPENDENT_SOURCES += \
src/comm/XbeeLink.cpp \
src/comm/HexSpinBox.cpp \
src/ui/XbeeConfigurationWindow.cpp
src/comm/XbeeLink.cpp \
src/comm/HexSpinBox.cpp \
src/ui/XbeeConfigurationWindow.cpp
XBEE_DEFINES = QGC_XBEE_ENABLED
contains(DEFINES, DISABLE_XBEE) {
message("Skipping support for native XBee API (manual override from command line)")
DEFINES -= DISABLE_XBEE
message("Skipping support for native XBee API (manual override from command line)")
DEFINES -= DISABLE_XBEE
# Otherwise the user can still disable this feature in the user_config.pri file.
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_XBEE) {
message("Skipping support for native XBee API (manual override from user_config.pri)")
......@@ -100,50 +100,50 @@ contains(DEFINES, DISABLE_XBEE) {
linux-g++-64 {
message("Skipping support for XBee API (64-bit Linux builds not supported)")
} else:exists(/usr/include/xbee.h) {
message("Including support for XBee API")
message("Including support for XBee API")
HEADERS += $$XBEE_DEPENDENT_HEADERS
SOURCES += $$XBEE_DEPENDENT_SOURCES
DEFINES += $$XBEE_DEFINES
LIBS += -L/usr/lib -lxbee
} else {
warning("Skipping support for XBee API (missing libraries, see README)")
}
HEADERS += $$XBEE_DEPENDENT_HEADERS
SOURCES += $$XBEE_DEPENDENT_SOURCES
DEFINES += $$XBEE_DEFINES
LIBS += -L/usr/lib -lxbee
} else {
warning("Skipping support for XBee API (missing libraries, see README)")
}
} else:WindowsBuild {
message("Including support for XBee API")
HEADERS += $$XBEE_DEPENDENT_HEADERS
SOURCES += $$XBEE_DEPENDENT_SOURCES
DEFINES += $$XBEE_DEFINES
INCLUDEPATH += libs/thirdParty/libxbee
message("Including support for XBee API")
HEADERS += $$XBEE_DEPENDENT_HEADERS
SOURCES += $$XBEE_DEPENDENT_SOURCES
DEFINES += $$XBEE_DEFINES
INCLUDEPATH += libs/thirdParty/libxbee
LIBS += -l$$BASEDIR/libs/thirdParty/libxbee/lib/libxbee
} else {
message("Skipping support for XBee API (unsupported platform)")
message("Skipping support for XBee API (unsupported platform)")
}
#
# [OPTIONAL] Magellan 3DxWare library. Provides support for 3DConnexion's 3D mice.
#
contains(DEFINES, DISABLE_3DMOUSE) {
message("Skipping support for 3DConnexion mice (manual override from command line)")
DEFINES -= DISABLE_3DMOUSE
message("Skipping support for 3DConnexion mice (manual override from command line)")
DEFINES -= DISABLE_3DMOUSE
# Otherwise the user can still disable this feature in the user_config.pri file.
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_3DMOUSE) {
message("Skipping support for 3DConnexion mice (manual override from user_config.pri)")
} else:LinuxBuild {
exists(/usr/local/lib/libxdrvlib.so) {
message("Including support for 3DConnexion mice")
exists(/usr/local/lib/libxdrvlib.so) {
message("Including support for 3DConnexion mice")
DEFINES += \
QGC_MOUSE_ENABLED_LINUX \
QGC_MOUSE_ENABLED_LINUX \
ParameterCheck
# Hack: Has to be defined for magellan usage
HEADERS += src/input/Mouse6dofInput.h
SOURCES += src/input/Mouse6dofInput.cpp
LIBS += -L/usr/local/lib/ -lxdrvlib
} else {
warning("Skipping support for 3DConnexion mice (missing libraries, see README)")
}
HEADERS += src/input/Mouse6dofInput.h
SOURCES += src/input/Mouse6dofInput.cpp
LIBS += -L/usr/local/lib/ -lxdrvlib
} else {
warning("Skipping support for 3DConnexion mice (missing libraries, see README)")
}
} else:WindowsBuild {
message("Including support for 3DConnexion mice")
......@@ -162,52 +162,52 @@ contains(DEFINES, DISABLE_3DMOUSE) {
libs/thirdParty/3DMouse/win/Mouse3DInput.cpp \
src/input/Mouse6dofInput.cpp
} else {
message("Skipping support for 3DConnexion mice (unsupported platform)")
message("Skipping support for 3DConnexion mice (unsupported platform)")
}
#
# [OPTIONAL] Opal RT-LAB Library. Provides integration with Opal-RT's RT-LAB simulator.
#
contains(DEFINES, DISABLE_RTLAB) {
message("Skipping support for RT-LAB (manual override from command line)")
DEFINES -= DISABLE_RTLAB
message("Skipping support for RT-LAB (manual override from command line)")
DEFINES -= DISABLE_RTLAB
# Otherwise the user can still disable this feature in the user_config.pri file.
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_RTLAB) {
message("Skipping support for RT-LAB (manual override from user_config.pri)")
} else:WindowsBuild {
exists(src/lib/opalrt/OpalApi.h) : exists(C:/OPAL-RT/RT-LAB7.2.4/Common/bin) {
message("Including support for RT-LAB")
exists(src/lib/opalrt/OpalApi.h) : exists(C:/OPAL-RT/RT-LAB7.2.4/Common/bin) {
message("Including support for RT-LAB")
DEFINES += QGC_RTLAB_ENABLED
DEFINES += QGC_RTLAB_ENABLED
INCLUDEPATH +=
src/lib/opalrt
libs/lib/opal/include \
INCLUDEPATH +=
src/lib/opalrt
libs/lib/opal/include \
FORMS += src/ui/OpalLinkSettings.ui
FORMS += src/ui/OpalLinkSettings.ui
HEADERS += \
src/comm/OpalRT.h \
src/comm/OpalLink.h \
src/comm/Parameter.h \
src/comm/QGCParamID.h \
src/comm/ParameterList.h \
src/ui/OpalLinkConfigurationWindow.h
HEADERS += \
src/comm/OpalRT.h \
src/comm/OpalLink.h \
src/comm/Parameter.h \
src/comm/QGCParamID.h \
src/comm/ParameterList.h \
src/ui/OpalLinkConfigurationWindow.h
SOURCES += \
src/comm/OpalRT.cc \
src/comm/OpalLink.cc \
src/comm/Parameter.cc \
src/comm/QGCParamID.cc \
src/comm/ParameterList.cc \
src/ui/OpalLinkConfigurationWindow.cc
SOURCES += \
src/comm/OpalRT.cc \
src/comm/OpalLink.cc \
src/comm/Parameter.cc \
src/comm/QGCParamID.cc \
src/comm/ParameterList.cc \
src/ui/OpalLinkConfigurationWindow.cc
LIBS += \
-LC:/OPAL-RT/RT-LAB7.2.4/Common/bin \
-lOpalApi
} else {
warning("Skipping support for RT-LAB (missing libraries, see README)")
}
LIBS += \
-LC:/OPAL-RT/RT-LAB7.2.4/Common/bin \
-lOpalApi
} else {
warning("Skipping support for RT-LAB (missing libraries, see README)")
}
} else {
message("Skipping support for RT-LAB (unsupported platform)")
}
......@@ -225,12 +225,12 @@ MacBuild {
-F$$BASEDIR/libs/lib/Frameworks \
-framework SDL
} else:LinuxBuild {
PKGCONFIG = sdl
PKGCONFIG = sdl
} else:WindowsBuild {
INCLUDEPATH += \
INCLUDEPATH += \
$$BASEDIR/libs/lib/sdl/msvc/include \
LIBS += \
LIBS += \
-L$$BASEDIR/libs/lib/sdl/msvc/lib \
-lSDLmain \
-lSDL
......@@ -244,20 +244,20 @@ MacBuild {
# Windows is supported as of Windows 7
#
contains (DEFINES, DISABLE_SPEECH) {
message("Skipping support for speech output (manual override from command line)")
DEFINES -= DISABLE_SPEECH
message("Skipping support for speech output (manual override from command line)")
DEFINES -= DISABLE_SPEECH
# Otherwise the user can still disable this feature in the user_config.pri file.
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_SPEECH) {
message("Skipping support for speech output (manual override from user_config.pri)")
} else:LinuxBuild {
exists(/usr/include/espeak) | exists(/usr/local/include/espeak) {
message("Including support for speech output")
DEFINES += QGC_SPEECH_ENABLED
LIBS += \
-lespeak
} else {
warning("Skipping support for speech output (missing libraries, see README)")
}
exists(/usr/include/espeak) | exists(/usr/local/include/espeak) {
message("Including support for speech output")
DEFINES += QGC_SPEECH_ENABLED
LIBS += \
-lespeak
} else {
warning("Skipping support for speech output (missing libraries, see README)")
}
}
# Mac support is built into OS 10.6+.
else:MacBuild {
......@@ -270,6 +270,11 @@ else:WindowsBuild {
DEFINES += QGC_SPEECH_ENABLED
LIBS += -lOle32
}
# Android supports speech through native (Java) API.
else:AndroidBuild {
message("Including support for speech output")
DEFINES += QGC_SPEECH_ENABLED
}
#
# [OPTIONAL] Zeroconf for UDP links
......
......@@ -43,12 +43,15 @@ import android.content.IntentFilter;
import android.hardware.usb.*;
import android.widget.Toast;
import android.util.Log;
//-- Text To Speech
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import com.hoho.android.usbserial.driver.*;
import org.qtproject.qt5.android.bindings.QtActivity;
import org.qtproject.qt5.android.bindings.QtApplication;
public class UsbDeviceJNI extends QtActivity
public class UsbDeviceJNI extends QtActivity implements TextToSpeech.OnInitListener
{
public static int BAD_PORT = 0;
private static UsbDeviceJNI m_instance;
......@@ -61,6 +64,7 @@ public class UsbDeviceJNI extends QtActivity
private BroadcastReceiver m_UsbReceiver = null;
private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor();
private static final String TAG = "QGC_UsbDeviceJNI";
private static TextToSpeech m_tts;
private final static UsbIoManager.Listener m_Listener =
new UsbIoManager.Listener()
......@@ -98,6 +102,34 @@ public class UsbDeviceJNI extends QtActivity
Log.i(TAG, "Instance created");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Text To Speech
// Pigback a ride for providing TTS to QGC
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_tts = new TextToSpeech(this,this);
}
@Override
protected void onDestroy() {
super.onDestroy();
m_tts.shutdown();
}
public void onInit(int status) {
}
public static void say(String msg)
{
Log.i(TAG, "Say: " + msg);
m_tts.speak(msg, TextToSpeech.QUEUE_FLUSH, null);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Find all current devices that match the device filter described in the androidmanifest.xml and the
......
......@@ -38,42 +38,51 @@ This file is part of the QGROUNDCONTROL project
#include "QGCApplication.h"
#include "QGC.h"
#if defined __android__
#include <QtAndroidExtras/QtAndroidExtras>
#include <QtAndroidExtras/QAndroidJniObject>
#endif
IMPLEMENT_QGC_SINGLETON(GAudioOutput, GAudioOutput)
const char* GAudioOutput::_mutedKey = "AudioMuted";
GAudioOutput::GAudioOutput(QObject *parent) :
QGCSingleton(parent),
muted(false),
thread(new QThread()),
worker(new QGCAudioWorker())
GAudioOutput::GAudioOutput(QObject *parent)
: QGCSingleton(parent)
, muted(false)
#ifndef __android__
, thread(new QThread())
, worker(new QGCAudioWorker())
#endif
{
QSettings settings;
muted = settings.value(_mutedKey, false).toBool();
muted |= qgcApp()->runningUnitTests();
#ifndef __android__
worker->moveToThread(thread);
connect(this, &GAudioOutput::textToSpeak, worker, &QGCAudioWorker::say);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
thread->start();
#endif
}
GAudioOutput::~GAudioOutput()
{
#ifndef __android__
thread->quit();
#endif
}
void GAudioOutput::mute(bool mute)
{
QSettings settings;
muted = mute;
settings.setValue(_mutedKey, mute);
#ifndef __android__
emit mutedChanged(mute);
#endif
}
bool GAudioOutput::isMuted()
......@@ -81,10 +90,24 @@ bool GAudioOutput::isMuted()
return muted;
}
bool GAudioOutput::say(const QString& text, int severity)
bool GAudioOutput::say(const QString& inText, int severity)
{
if (!muted) {
emit textToSpeak(text, severity);
#if defined __android__
#if defined QGC_SPEECH_ENABLED
static const char V_jniClassName[] {"org/qgroundcontrol/qgchelper/UsbDeviceJNI"};
QAndroidJniEnvironment env;
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
QString text = QGCAudioWorker::fixTextMessageForAudio(inText);
QAndroidJniObject javaMessage = QAndroidJniObject::fromString(text);
QAndroidJniObject::callStaticMethod<void>(V_jniClassName, "say", "(Ljava/lang/String;)V", javaMessage.object<jstring>());
#endif
#else
emit textToSpeak(inText, severity);
#endif
}
return true;
}
......@@ -48,9 +48,9 @@ This file is part of the PIXHAWK project
class GAudioOutput : public QGCSingleton
{
Q_OBJECT
DECLARE_QGC_SINGLETON(GAudioOutput, GAudioOutput)
public:
/** @brief List available voices */
QStringList listVoices(void);
......@@ -88,13 +88,16 @@ signals:
protected:
bool muted;
#if !defined __android__
QThread* thread;
QGCAudioWorker* worker;
#endif
private:
GAudioOutput(QObject *parent = NULL);
~GAudioOutput();
static const char* _mutedKey;
};
......
......@@ -10,6 +10,47 @@
#if defined Q_OS_MAC && defined QGC_SPEECH_ENABLED
#include <ApplicationServices/ApplicationServices.h>
static SpeechChannel sc;
static Fixed volume;
static void speechDone(SpeechChannel sc2, void *) {
if (sc2 == sc)
{
DisposeSpeechChannel(sc);
}
}
class MacSpeech
{
public:
MacSpeech()
{
setVolume(100);
}
~MacSpeech()
{
}
void say(const char* words)
{
while (SpeechBusy()) {
QGC::SLEEP::msleep(100);
}
NewSpeechChannel(NULL, &sc);
SetSpeechInfo(sc, soVolume, &volume);
SetSpeechInfo(sc, soSpeechDoneCallBack, reinterpret_cast<void *>(speechDone));
CFStringRef cfstr = CFStringCreateWithCString(NULL, words, kCFStringEncodingUTF8);
SpeakCFString(sc, cfstr, NULL);
}
void setVolume(int v)
{
volume = FixRatio(v, 100);
}
};
//-- Singleton
MacSpeech macSpeech;
#endif
// Speech synthesis is only supported with MSVC compiler
......@@ -18,7 +59,7 @@
#include <sapi.h>
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
#if defined Q_OS_LINUX && !defined __android__ && 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
......@@ -48,7 +89,7 @@ void QGCAudioWorker::init()
sound = new QSound(":/res/Alert");
#endif
#if defined Q_OS_LINUX && defined QGC_SPEECH_ENABLED
#if defined Q_OS_LINUX && !defined __android__ && defined QGC_SPEECH_ENABLED
espeak_Initialize(AUDIO_OUTPUT_SYNCH_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
......@@ -82,25 +123,29 @@ void QGCAudioWorker::init()
QGCAudioWorker::~QGCAudioWorker()
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
if (pVoice) {
pVoice->Release();
pVoice = NULL;
}
if (pVoice) {
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
#endif
}
void QGCAudioWorker::say(QString inText, int severity)
{
static bool threadInit = false;
if (!threadInit) {
threadInit = true;
init();
}
#ifdef __android__
Q_UNUSED(inText);
Q_UNUSED(severity);
#else
static bool threadInit = false;
if (!threadInit) {
threadInit = true;
init();
}
if (!muted)
{
QString text = _fixTextMessageForAudio(inText);
QString text = fixTextMessageForAudio(inText);
// Prepend high priority text with alert beep
if (severity < GAudioOutput::AUDIO_SEVERITY_CRITICAL) {
beep();
......@@ -115,35 +160,22 @@ void QGCAudioWorker::say(QString inText, int severity)
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
HRESULT hr = pVoice->Speak(text.toStdWString().c_str(), SPF_DEFAULT, NULL);
if (FAILED(hr)) {
qDebug() << "Speak failed, HR:" << QString("%1").arg(hr, 0, 16);
}
if (FAILED(hr)) {
qDebug() << "Speak failed, HR:" << QString("%1").arg(hr, 0, 16);
}
#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);
}
macSpeech.say(text.toStdString().c_str());
#else
// Make sure there isn't an unused variable warning when speech output is disabled
Q_UNUSED(inText);
#endif
}
#endif // __android__
}
void QGCAudioWorker::mute(bool mute)
......@@ -187,7 +219,7 @@ bool QGCAudioWorker::_getMillisecondString(const QString& string, QString& match
return false;
}
QString QGCAudioWorker::_fixTextMessageForAudio(const QString& string) {
QString QGCAudioWorker::fixTextMessageForAudio(const QString& string) {
QString match;
QString newNumber;
QString result = string;
......@@ -204,7 +236,9 @@ QString QGCAudioWorker::_fixTextMessageForAudio(const QString& string) {
if(result.contains("ALTCTL", Qt::CaseInsensitive)) {
result.replace("ALTCTL", "Altitude Control", Qt::CaseInsensitive);
}
if(result.contains("RTL", Qt::CaseInsensitive)) {
if(result.contains("AUTO_RTL", Qt::CaseInsensitive)) {
result.replace("AUTO_RTL", "auto Return To Land", Qt::CaseInsensitive);
} else if(result.contains("RTL", Qt::CaseInsensitive)) {
result.replace("RTL", "Return To Land", Qt::CaseInsensitive);
}
if(result.contains("ACCEL ", Qt::CaseInsensitive)) {
......
......@@ -7,13 +7,6 @@
#include <QSound>
#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 <basetyps.h>
......@@ -31,6 +24,8 @@ public:
bool isMuted();
void init();
static QString fixTextMessageForAudio(const QString& string);
signals:
public slots:
......@@ -52,8 +47,7 @@ protected:
QTimer *emergencyTimer;
bool muted;
private:
QString _fixTextMessageForAudio(const QString& string);
bool _getMillisecondString(const QString& string, QString& match, int& number);
static bool _getMillisecondString(const QString& string, QString& match, int& number);
};
#endif // QGCAUDIOWORKER_H
This diff is collapsed.
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