Commit dd587ce2 authored by dogmaphobic's avatar dogmaphobic

Text to Speech Work

parent c2a3cd0b
......@@ -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;
}
......@@ -88,8 +88,11 @@ signals:
protected:
bool muted;
#if !defined __android__
QThread* thread;
QGCAudioWorker* worker;
#endif
private:
GAudioOutput(QObject *parent = NULL);
......
......@@ -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
......@@ -92,6 +133,10 @@ QGCAudioWorker::~QGCAudioWorker()
void QGCAudioWorker::say(QString inText, int severity)
{
#ifdef __android__
Q_UNUSED(inText);
Q_UNUSED(severity);
#else
static bool threadInit = false;
if (!threadInit) {
threadInit = true;
......@@ -100,7 +145,7 @@ void QGCAudioWorker::say(QString inText, int severity)
if (!muted)
{
QString text = _fixTextMessageForAudio(inText);
QString text = fixTextMessageForAudio(inText);
// Prepend high priority text with alert beep
if (severity < GAudioOutput::AUDIO_SEVERITY_CRITICAL) {
beep();
......@@ -124,26 +169,13 @@ void QGCAudioWorker::say(QString inText, int severity)
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
......@@ -422,7 +422,7 @@ void UAS::receiveMessage(mavlink_message_t message)
modechanged = true;
this->base_mode = state.base_mode;
this->custom_mode = state.custom_mode;
modeAudio = " is now in " + audiomodeText + "flight mode";
modeAudio = " is now in " + audiomodeText + " flight mode";
}
// We got the mode
......@@ -2470,10 +2470,6 @@ void UAS::unsetRCToParameterMap()
void UAS::_say(const QString& text, int severity)
{
#ifndef UNITTEST_BUILD
if (!qgcApp()->runningUnitTests())
GAudioOutput::instance()->say(text, severity);
#else
Q_UNUSED(text)
Q_UNUSED(severity)
#endif
}
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