Newer
Older
#include <QSettings>
#include <QDebug>
Lorenz Meier
committed
#include <QCoreApplication>
#include <QFile>
dogmaphobic
committed
#include <QRegularExpression>
#include "QGC.h"
#include "QGCAudioWorker.h"
#include <ApplicationServices/ApplicationServices.h>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;
#if (defined __ios__) && defined QGC_SPEECH_ENABLED
extern void iOSSpeak(QString msg);
#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 __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
#define QGC_GAUDIOOUTPUT_KEY QString("QGC_AUDIOOUTPUT_")
QGCAudioWorker::QGCAudioWorker(QObject *parent) :
QObject(parent),
voiceIndex(0),
Lorenz Meier
committed
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
pVoice(NULL),
#endif
emergency(false),
muted(false)
{
// Load settings
QSettings settings;
muted = settings.value(QGC_GAUDIOOUTPUT_KEY + "muted", muted).toBool();
Lorenz Meier
committed
}
Lorenz Meier
committed
void QGCAudioWorker::init()
{
#if defined Q_OS_LINUX && !defined __android__ && 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)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
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
}
QGCAudioWorker::~QGCAudioWorker()
{
#if defined _MSC_VER && defined QGC_SPEECH_ENABLED
if (pVoice) {
pVoice->Release();
pVoice = NULL;
}
::CoUninitialize();
#endif
}
#ifdef __android__
Q_UNUSED(inText);
#else
static bool threadInit = false;
if (!threadInit) {
threadInit = true;
init();
}
Lorenz Meier
committed
#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);
}
Lorenz Meier
committed
#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 __ios__) && defined QGC_SPEECH_ENABLED
iOSSpeak(text);
Lorenz Meier
committed
#else
// Make sure there isn't an unused variable warning when speech output is disabled
Lorenz Meier
committed
#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;
}
dogmaphobic
committed
bool QGCAudioWorker::_getMillisecondString(const QString& string, QString& match, int& number) {
dogmaphobic
committed
QRegularExpressionMatchIterator i = re.globalMatch(string);
while (i.hasNext()) {
QRegularExpressionMatch qmatch = i.next();
if (qmatch.hasMatch()) {
match = qmatch.captured(0);
number = qmatch.captured(0).replace("ms", "").toInt();
return true;
}
}
return false;
}
QString QGCAudioWorker::fixTextMessageForAudio(const QString& string) {
dogmaphobic
committed
QString match;
QString newNumber;
QString result = string;
//-- Look for codified terms
if(result.contains("ERR ", Qt::CaseInsensitive)) {
result.replace("ERR ", "error ", Qt::CaseInsensitive);
}
if(result.contains("ERR:", Qt::CaseInsensitive)) {
result.replace("ERR:", "error.", Qt::CaseInsensitive);
}
if(result.contains("POSCTL", Qt::CaseInsensitive)) {
result.replace("POSCTL", "Position Control", Qt::CaseInsensitive);
}
if(result.contains("ALTCTL", Qt::CaseInsensitive)) {
result.replace("ALTCTL", "Altitude Control", 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)) {
result.replace("ACCEL ", "accelerometer ", Qt::CaseInsensitive);
}
if(result.contains("RC_MAP_MODE_SW", Qt::CaseInsensitive)) {
result.replace("RC_MAP_MODE_SW", "RC mode switch", Qt::CaseInsensitive);
}
if(result.contains("REJ.", Qt::CaseInsensitive)) {
result.replace("REJ.", "Rejected", Qt::CaseInsensitive);
}
if(result.contains("WP", Qt::CaseInsensitive)) {
result.replace("WP", "way point", Qt::CaseInsensitive);
}
if(result.contains("CMD", Qt::CaseInsensitive)) {
result.replace("CMD", "command", Qt::CaseInsensitive);
}
if(result.contains("COMPID", Qt::CaseInsensitive)) {
result.replace("COMPID", "component eye dee", Qt::CaseInsensitive);
}
if(result.contains(" params ", Qt::CaseInsensitive)) {
result.replace(" params ", " parameters ", Qt::CaseInsensitive);
}
if(result.contains(" id ", Qt::CaseInsensitive)) {
result.replace(" id ", " eye dee ", Qt::CaseInsensitive);
}
dogmaphobic
committed
int number;
if(_getMillisecondString(string, match, number) && number > 1000) {
if(number < 60000) {
int seconds = number / 1000;
newNumber = QString("%1 second%2").arg(seconds).arg(seconds > 1 ? "s" : "");
} else {
int minutes = number / 60000;
int seconds = (number - (minutes * 60000)) / 1000;
if (!seconds) {
newNumber = QString("%1 minute%2").arg(minutes).arg(minutes > 1 ? "s" : "");
} else {
newNumber = QString("%1 minute%2 and %3 second%4").arg(minutes).arg(minutes > 1 ? "s" : "").arg(seconds).arg(seconds > 1 ? "s" : "");
}
}
result.replace(match, newNumber);
}
dogmaphobic
committed
return result;
}