Browse Source

Added Windows Speech SDK / SAPI support

QGC4.4
pixhawk 15 years ago
parent
commit
261dcf6eea
  1. 586
      src/GAudioOutput.cc
  2. 1
      src/GAudioOutput.h

586
src/GAudioOutput.cc

@ -1,275 +1,311 @@
/*===================================================================== /*=====================================================================
QGroundControl Open Source Ground Control Station QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> (c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful, QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>. along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/ ======================================================================*/
/** /**
* @file * @file
* @brief Implementation of audio output * @brief Implementation of audio output
* *
* @author Lorenz Meier <mavteam@student.ethz.ch> * @author Lorenz Meier <mavteam@student.ethz.ch>
* *
*/ */
#include <QApplication> #include <QApplication>
#include <QTemporaryFile> #include <QTemporaryFile>
#include "GAudioOutput.h" #include "GAudioOutput.h"
#include "MG.h" #include "MG.h"
#include <QDebug> #include <QDebug>
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#endif #endif
// Speech synthesis is only supported with MSVC compiler // Speech synthesis is only supported with MSVC compiler
#if _MSC_VER2 #if _MSC_VER
// Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx // Documentation: http://msdn.microsoft.com/en-us/library/ee125082%28v=VS.85%29.aspx
#include <sapi.h> #define _ATL_APARTMENT_THREADED
using System;
using System.Speech.Synthesis; #include <atlbase.h>
#endif //You may derive a class from CComModule and use it if you want to override something,
//but do not change the name of _Module
#ifdef Q_OS_LINUX extern CComModule _Module;
extern "C" { #include <atlcom.h>
#include <flite/flite.h>
cst_voice* register_cmu_us_kal(const char* voxdir); #include <sapi.h>
};
#endif //using System;
//using System.Speech.Synthesis;
#endif
/** #ifdef Q_OS_LINUX
* This class follows the singleton design pattern extern "C" {
* @see http://en.wikipedia.org/wiki/Singleton_pattern #include <flite/flite.h>
* A call to this function thus returns the only instance of this object cst_voice* register_cmu_us_kal(const char* voxdir);
* the call can occur at any place in the code, no reference to the };
* GAudioOutput object has to be passed. #endif
*/
GAudioOutput* GAudioOutput::instance()
{
static GAudioOutput* _instance = 0; /**
if(_instance == 0) { * This class follows the singleton design pattern
_instance = new GAudioOutput(); * @see http://en.wikipedia.org/wiki/Singleton_pattern
// Set the application as parent to ensure that this object * A call to this function thus returns the only instance of this object
// will be destroyed when the main application exits * the call can occur at any place in the code, no reference to the
_instance->setParent(qApp); * GAudioOutput object has to be passed.
} */
return _instance; GAudioOutput* GAudioOutput::instance()
} {
static GAudioOutput* _instance = 0;
GAudioOutput::GAudioOutput(QObject* parent) : QObject(parent), if(_instance == 0) {
voiceIndex(0), _instance = new GAudioOutput();
emergency(false) // Set the application as parent to ensure that this object
{ // will be destroyed when the main application exits
#ifdef Q_OS_LINUX _instance->setParent(qApp);
flite_init(); }
#endif return _instance;
// Initialize audio output }
m_media = new Phonon::MediaObject(this);
Phonon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this); GAudioOutput::GAudioOutput(QObject* parent) : QObject(parent),
createPath(m_media, audioOutput); voiceIndex(0),
emergency(false)
// Prepare regular emergency signal, will be fired off on calling startEmergency() {
emergencyTimer = new QTimer(); #ifdef Q_OS_LINUX
connect(emergencyTimer, SIGNAL(timeout()), this, SLOT(beep())); flite_init();
#endif
switch (voiceIndex)
{ #if _MSC_VER
case 0:
selectFemaleVoice(); ISpVoice * pVoice = NULL;
break; if (FAILED(::CoInitialize(NULL)))
default: {
selectMaleVoice(); qDebug("Creating COM object for audio output failed!");
break; }
} else
} {
bool GAudioOutput::say(QString text, int severity) HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice;);
{ if( SUCCEEDED( hr ) )
// TODO Add severity filter {
Q_UNUSED(severity); hr = pVoice->Speak(L"Hello world", 0, NULL);
bool res = false; pVoice->Release();
if (!emergency) pVoice = NULL;
{ }
}
// Speech synthesis is only supported with MSVC compiler #endif
#ifdef _MSC_VER2 // Initialize audio output
SpeechSynthesizer synth = new SpeechSynthesizer(); m_media = new Phonon::MediaObject(this);
synth.SelectVoice("Microsoft Anna"); Phonon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
synth.SpeakText(text.toStdString().c_str()); createPath(m_media, audioOutput);
res = true;
#endif // Prepare regular emergency signal, will be fired off on calling startEmergency()
emergencyTimer = new QTimer();
#ifdef Q_OS_LINUX connect(emergencyTimer, SIGNAL(timeout()), this, SLOT(beep()));
QTemporaryFile file;
file.setFileTemplate("XXXXXX.wav"); switch (voiceIndex)
if (file.open()) {
{ case 0:
cst_voice* v = register_cmu_us_kal(NULL); selectFemaleVoice();
cst_wave* wav = flite_text_to_wave(text.toStdString().c_str(), v); break;
// file.fileName() returns the unique file name default:
cst_wave_save(wav, file.fileName().toStdString().c_str(), "riff"); selectMaleVoice();
m_media->setCurrentSource(Phonon::MediaSource(file.fileName().toStdString().c_str())); break;
m_media->play(); }
res = true; }
}
#endif GAudioOutput::~GAudioOutput()
{
#ifdef Q_OS_MAC #ifdef _MSC_VER
// Slashes necessary to have the right start to the sentence ::CoUninitialize();
// copying data prevents SpeakString from reading additional chars #endif
text = "\\" + text; }
QStdWString str = text.toStdWString();
unsigned char str2[1024] = {}; bool GAudioOutput::say(QString text, int severity)
memcpy(str2, text.toAscii().data(), str.length()); {
SpeakString(str2); // TODO Add severity filter
res = true; Q_UNUSED(severity);
#endif bool res = false;
} if (!emergency)
return res; {
}
// Speech synthesis is only supported with MSVC compiler
/** #ifdef _MSC_VER2
* @param text This message will be played after the alert beep SpeechSynthesizer synth = new SpeechSynthesizer();
*/ synth.SelectVoice("Microsoft Anna");
bool GAudioOutput::alert(QString text) synth.SpeakText(text.toStdString().c_str());
{ res = true;
if (!emergency) #endif
{
// Play alert sound #ifdef Q_OS_LINUX
beep(); QTemporaryFile file;
// Say alert message file.setFileTemplate("XXXXXX.wav");
say(text, 2); if (file.open())
return true; {
} cst_voice* v = register_cmu_us_kal(NULL);
else cst_wave* wav = flite_text_to_wave(text.toStdString().c_str(), v);
{ // file.fileName() returns the unique file name
return false; cst_wave_save(wav, file.fileName().toStdString().c_str(), "riff");
} m_media->setCurrentSource(Phonon::MediaSource(file.fileName().toStdString().c_str()));
} m_media->play();
res = true;
void GAudioOutput::notifyPositive() }
{ #endif
// Use QFile to transform path for all OS
QFile f(QCoreApplication::applicationDirPath()+QString("/audio/double_notify.wav")); #ifdef Q_OS_MAC
m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str())); // Slashes necessary to have the right start to the sentence
m_media->play(); // copying data prevents SpeakString from reading additional chars
} text = "\\" + text;
QStdWString str = text.toStdWString();
void GAudioOutput::notifyNegative() unsigned char str2[1024] = {};
{ memcpy(str2, text.toAscii().data(), str.length());
// Use QFile to transform path for all OS SpeakString(str2);
QFile f(QCoreApplication::applicationDirPath()+QString("/audio/flat_notify.wav")); res = true;
m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str())); #endif
m_media->play(); }
} return res;
}
/**
* The emergency sound will be played continously during the emergency. /**
* call stopEmergency() to disable it again. No speech synthesis or other * @param text This message will be played after the alert beep
* audio output is available during the emergency. */
* bool GAudioOutput::alert(QString text)
* @return true if the emergency could be started, false else {
*/ if (!emergency)
bool GAudioOutput::startEmergency() {
{ // Play alert sound
if (!emergency) beep();
{ // Say alert message
emergency = true; say(text, 2);
// Beep immediately and then start timer return true;
beep(); }
emergencyTimer->start(1500); else
QTimer::singleShot(5000, this, SLOT(stopEmergency())); {
} return false;
return true; }
} }
/** void GAudioOutput::notifyPositive()
* Stops the continous emergency sound. Use startEmergency() to start {
* the emergency sound. // Use QFile to transform path for all OS
* QFile f(QCoreApplication::applicationDirPath()+QString("/audio/double_notify.wav"));
* @return true if the emergency could be stopped, false else m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str()));
*/ m_media->play();
bool GAudioOutput::stopEmergency() }
{
if (emergency) void GAudioOutput::notifyNegative()
{ {
emergency = false; // Use QFile to transform path for all OS
emergencyTimer->stop(); QFile f(QCoreApplication::applicationDirPath()+QString("/audio/flat_notify.wav"));
} m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str()));
return true; m_media->play();
} }
void GAudioOutput::beep() /**
{ * The emergency sound will be played continously during the emergency.
// Use QFile to transform path for all OS * call stopEmergency() to disable it again. No speech synthesis or other
QFile f(QCoreApplication::applicationDirPath()+QString("/audio/alert.wav")); * audio output is available during the emergency.
m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str())); *
m_media->play(); * @return true if the emergency could be started, false else
} */
bool GAudioOutput::startEmergency()
void GAudioOutput::selectFemaleVoice() {
{ if (!emergency)
#ifdef Q_OS_LINUX {
//this->voice = register_cmu_us_slt(NULL); emergency = true;
#endif // Beep immediately and then start timer
} beep();
emergencyTimer->start(1500);
void GAudioOutput::selectMaleVoice() QTimer::singleShot(5000, this, SLOT(stopEmergency()));
{ }
#ifdef Q_OS_LINUX return true;
//this->voice = register_cmu_us_rms(NULL); }
#endif
} /**
* Stops the continous emergency sound. Use startEmergency() to start
/* * the emergency sound.
void GAudioOutput::selectNeutralVoice() *
{ * @return true if the emergency could be stopped, false else
#ifdef Q_OS_LINUX */
this->voice = register_cmu_us_awb(NULL); bool GAudioOutput::stopEmergency()
#endif {
}*/ if (emergency)
{
QStringList GAudioOutput::listVoices(void) emergency = false;
{ emergencyTimer->stop();
QStringList l; }
#ifdef Q_OS_LINUX2 return true;
cst_voice *voice; }
const cst_val *v;
void GAudioOutput::beep()
{
// Use QFile to transform path for all OS
printf("Voices available: "); QFile f(QCoreApplication::applicationDirPath()+QString("/audio/alert.wav"));
for (v=flite_voice_list; v; v=val_cdr(v)) m_media->setCurrentSource(Phonon::MediaSource(f.fileName().toStdString().c_str()));
{ m_media->play();
voice = val_voice(val_car(v)); }
QString s;
s.sprintf("%s",voice->name); void GAudioOutput::selectFemaleVoice()
printf("%s",voice->name); {
l.append(s); #ifdef Q_OS_LINUX
} //this->voice = register_cmu_us_slt(NULL);
printf("\n"); #endif
}
#endif
return l; void GAudioOutput::selectMaleVoice()
{
} #ifdef Q_OS_LINUX
//this->voice = register_cmu_us_rms(NULL);
#endif
}
/*
void GAudioOutput::selectNeutralVoice()
{
#ifdef Q_OS_LINUX
this->voice = register_cmu_us_awb(NULL);
#endif
}*/
QStringList GAudioOutput::listVoices(void)
{
QStringList l;
#ifdef Q_OS_LINUX2
cst_voice *voice;
const cst_val *v;
printf("Voices available: ");
for (v=flite_voice_list; v; v=val_cdr(v))
{
voice = val_voice(val_car(v));
QString s;
s.sprintf("%s",voice->name);
printf("%s",voice->name);
l.append(s);
}
printf("\n");
#endif
return l;
}

1
src/GAudioOutput.h

@ -114,6 +114,7 @@ protected:
QTimer* emergencyTimer; QTimer* emergencyTimer;
private: private:
GAudioOutput(QObject* parent=NULL); GAudioOutput(QObject* parent=NULL);
~GAudioOutput();
}; };
#endif // AUDIOOUTPUT_H #endif // AUDIOOUTPUT_H

Loading…
Cancel
Save