1
0
Fork 0

use flite+hts for metar

This commit is contained in:
Torsten Dreyer 2014-04-24 20:59:08 +02:00
parent f6b6cfc645
commit cadb77b18d
8 changed files with 607 additions and 191 deletions

View file

@ -39,6 +39,12 @@
#include <Main/fg_props.hxx> #include <Main/fg_props.hxx>
#include <Navaids/navlist.hxx> #include <Navaids/navlist.hxx>
#if defined(ENABLE_FLITE)
#include <Sound/soundmanager.hxx>
#include <simgear/sound/sample_group.hxx>
#include <Sound/VoiceSynthesizer.hxx>
#endif
#include "frequencyformatter.hxx" #include "frequencyformatter.hxx"
namespace Instrumentation { namespace Instrumentation {
@ -46,129 +52,185 @@ namespace Instrumentation {
using simgear::PropertyObject; using simgear::PropertyObject;
using std::string; using std::string;
#if defined(ENABLE_FLITE)
class MetarSpeaker: public SGPropertyChangeListener, SoundSampleReadyListener {
public:
MetarSpeaker();
virtual ~MetarSpeaker();
virtual void valueChanged(SGPropertyNode * node);
virtual void SoundSampleReady(SGSharedPtr<SGSoundSample>);
bool hasSpokenMetar() { return _spokenMetar.empty() == false; }
SGSharedPtr<SGSoundSample> getSpokenMetar() { return _spokenMetar.pop(); }
private:
SynthesizeRequest _synthesizeRequest;
SGLockedQueue<SGSharedPtr<SGSoundSample> > _spokenMetar;
};
MetarSpeaker::MetarSpeaker()
{
_synthesizeRequest.listener = this;
}
MetarSpeaker::~MetarSpeaker()
{
}
void MetarSpeaker::valueChanged(SGPropertyNode * node)
{
string newText = node->getStringValue();
if (_synthesizeRequest.text == newText) return;
_synthesizeRequest.text = newText;
FGSoundManager * smgr = dynamic_cast<FGSoundManager*>(globals->get_soundmgr());
assert(smgr != NULL);
string voice = globals->get_fg_root() + "/ATC/cmu_us_arctic_slt.htsvoice";
FLITEVoiceSynthesizer * synthesizer = dynamic_cast<FLITEVoiceSynthesizer*>(smgr->getSynthesizer(voice));
synthesizer->synthesize(_synthesizeRequest);
}
void MetarSpeaker::SoundSampleReady(SGSharedPtr<SGSoundSample> sample)
{
// we are now in the synthesizers worker thread!
_spokenMetar.push(sample);
}
#endif
SignalQualityComputer::~SignalQualityComputer() SignalQualityComputer::~SignalQualityComputer()
{ {
} }
class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer class SimpleDistanceSquareSignalQualityComputer: public SignalQualityComputer {
{
public: public:
SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {} SimpleDistanceSquareSignalQualityComputer(double range)
virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const; : _rangeM(range), _rangeM2(range * range)
virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const; {
virtual double computeSignalQuality( double slantDistanceM ) const; }
virtual double computeSignalQuality(const SGGeod & sender, const SGGeod & receiver) const;
virtual double computeSignalQuality(const SGVec3d & sender, const SGVec3d & receiver) const;
virtual double computeSignalQuality(double slantDistanceM) const;
private: private:
double _rangeM; double _rangeM;
double _rangeM2; double _rangeM2;
}; };
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality(const SGVec3d & sender, const SGVec3d & receiver) const
{ {
return computeSignalQuality( dist( sender, receiver ) ); return computeSignalQuality(dist(sender, receiver));
} }
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality(const SGGeod & sender, const SGGeod & receiver) const
{ {
return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) ); return computeSignalQuality(SGGeodesy::distanceM(sender, receiver));
} }
double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality(double distanceM) const
{ {
return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) ); return distanceM < _rangeM ? 1.0 : (_rangeM2 / (distanceM * distanceM));
} }
class OnExitHandler { class OnExitHandler {
public: public:
virtual void onExit() = 0; virtual void onExit() = 0;
virtual ~OnExitHandler() {} virtual ~OnExitHandler()
{
}
}; };
class OnExit { class OnExit {
public: public:
OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {} OnExit(OnExitHandler * onExitHandler)
~OnExit() { _onExitHandler->onExit(); } : _onExitHandler(onExitHandler)
private: {
OnExitHandler * _onExitHandler; }
~OnExit()
{
_onExitHandler->onExit();
}
private:
OnExitHandler * _onExitHandler;
}; };
class OutputProperties: public OnExitHandler {
public:
OutputProperties(SGPropertyNode_ptr rootNode)
: _rootNode(rootNode), _signalQuality_norm(0.0), _slantDistance_m(0.0), _trueBearingTo_deg(0.0), _trueBearingFrom_deg(0.0), _trackDistance_m(
0.0), _heightAboveStation_ft(0.0),
class OutputProperties : public OnExitHandler { _PO_stationType(rootNode->getNode("station-type", true)), _PO_stationName(rootNode->getNode("station-name", true)), _PO_airportId(
public: rootNode->getNode("airport-id", true)), _PO_signalQuality_norm(rootNode->getNode("signal-quality-norm", true)), _PO_slantDistance_m(
OutputProperties( SGPropertyNode_ptr rootNode ) : rootNode->getNode("slant-distance-m", true)), _PO_trueBearingTo_deg(rootNode->getNode("true-bearing-to-deg", true)), _PO_trueBearingFrom_deg(
_rootNode(rootNode), rootNode->getNode("true-bearing-from-deg", true)), _PO_trackDistance_m(rootNode->getNode("track-distance-m", true)), _PO_heightAboveStation_ft(
_signalQuality_norm(0.0), rootNode->getNode("height-above-station-ft", true))
_slantDistance_m(0.0), {
_trueBearingTo_deg(0.0), }
_trueBearingFrom_deg(0.0), virtual ~OutputProperties()
_trackDistance_m(0.0), {
_heightAboveStation_ft(0.0), }
_PO_stationType( rootNode->getNode("station-type", true ) ),
_PO_stationName( rootNode->getNode("station-name", true ) ),
_PO_airportId( rootNode->getNode("airport-id", true ) ),
_PO_signalQuality_norm( rootNode->getNode("signal-quality-norm",true) ),
_PO_slantDistance_m( rootNode->getNode("slant-distance-m",true) ),
_PO_trueBearingTo_deg( rootNode->getNode("true-bearing-to-deg",true) ),
_PO_trueBearingFrom_deg( rootNode->getNode("true-bearing-from-deg",true) ),
_PO_trackDistance_m( rootNode->getNode("track-distance-m",true) ),
_PO_heightAboveStation_ft( rootNode->getNode("height-above-station-ft",true) )
{}
virtual ~OutputProperties() {}
protected: protected:
SGPropertyNode_ptr _rootNode; SGPropertyNode_ptr _rootNode;
std::string _stationType; std::string _stationType;
std::string _stationName; std::string _stationName;
std::string _airportId; std::string _airportId;
double _signalQuality_norm; double _signalQuality_norm;
double _slantDistance_m; double _slantDistance_m;
double _trueBearingTo_deg; double _trueBearingTo_deg;
double _trueBearingFrom_deg; double _trueBearingFrom_deg;
double _trackDistance_m; double _trackDistance_m;
double _heightAboveStation_ft; double _heightAboveStation_ft;
private: private:
PropertyObject<string> _PO_stationType; PropertyObject<string> _PO_stationType;
PropertyObject<string> _PO_stationName; PropertyObject<string> _PO_stationName;
PropertyObject<string> _PO_airportId; PropertyObject<string> _PO_airportId;
PropertyObject<double> _PO_signalQuality_norm; PropertyObject<double> _PO_signalQuality_norm;
PropertyObject<double> _PO_slantDistance_m; PropertyObject<double> _PO_slantDistance_m;
PropertyObject<double> _PO_trueBearingTo_deg; PropertyObject<double> _PO_trueBearingTo_deg;
PropertyObject<double> _PO_trueBearingFrom_deg; PropertyObject<double> _PO_trueBearingFrom_deg;
PropertyObject<double> _PO_trackDistance_m; PropertyObject<double> _PO_trackDistance_m;
PropertyObject<double> _PO_heightAboveStation_ft; PropertyObject<double> _PO_heightAboveStation_ft;
virtual void onExit() { virtual void onExit()
_PO_stationType = _stationType; {
_PO_stationName = _stationName; _PO_stationType = _stationType;
_PO_airportId = _airportId; _PO_stationName = _stationName;
_PO_signalQuality_norm = _signalQuality_norm; _PO_airportId = _airportId;
_PO_slantDistance_m = _slantDistance_m; _PO_signalQuality_norm = _signalQuality_norm;
_PO_trueBearingTo_deg = _trueBearingTo_deg; _PO_slantDistance_m = _slantDistance_m;
_PO_trueBearingFrom_deg = _trueBearingFrom_deg; _PO_trueBearingTo_deg = _trueBearingTo_deg;
_PO_trackDistance_m = _trackDistance_m; _PO_trueBearingFrom_deg = _trueBearingFrom_deg;
_PO_heightAboveStation_ft = _heightAboveStation_ft; _PO_trackDistance_m = _trackDistance_m;
} _PO_heightAboveStation_ft = _heightAboveStation_ft;
}
}; };
/* ------------- The CommRadio implementation ---------------------- */ /* ------------- The CommRadio implementation ---------------------- */
class MetarBridge : public SGReferenced, public SGPropertyChangeListener { class MetarBridge: public SGReferenced, public SGPropertyChangeListener {
public: public:
MetarBridge(); MetarBridge();
~MetarBridge(); ~MetarBridge();
void bind(); void bind();
void unbind(); void unbind();
void requestMetarForId( std::string & id ); void requestMetarForId(std::string & id);
void clearMetar(); void clearMetar();
void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; } void setMetarPropertiesRoot(SGPropertyNode_ptr n)
void setAtisNode( SGPropertyNode * n ) { _atisNode = n; } {
_metarPropertiesNode = n;
}
void setAtisNode(SGPropertyNode * n)
{
_atisNode = n;
}
protected: protected:
virtual void valueChanged(SGPropertyNode * ); virtual void valueChanged(SGPropertyNode *);
private: private:
std::string _requestedId; std::string _requestedId;
@ -179,8 +241,8 @@ private:
}; };
typedef SGSharedPtr<MetarBridge> MetarBridgeRef; typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
MetarBridge::MetarBridge() : MetarBridge::MetarBridge()
_atisNode(0) : _atisNode(0)
{ {
} }
@ -190,31 +252,31 @@ MetarBridge::~MetarBridge()
void MetarBridge::bind() void MetarBridge::bind()
{ {
_realWxEnabledNode = fgGetNode( "/environment/realwx/enabled", true ); _realWxEnabledNode = fgGetNode("/environment/realwx/enabled", true);
_metarPropertiesNode->getNode( "valid", true )->addChangeListener( this ); _metarPropertiesNode->getNode("valid", true)->addChangeListener(this);
} }
void MetarBridge::unbind() void MetarBridge::unbind()
{ {
_metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this ); _metarPropertiesNode->getNode("valid", true)->removeChangeListener(this);
} }
void MetarBridge::requestMetarForId( std::string & id ) void MetarBridge::requestMetarForId(std::string & id)
{ {
std::string uppercaseId = simgear::strutils::uppercase( id ); std::string uppercaseId = simgear::strutils::uppercase(id);
if( _requestedId == uppercaseId ) return; if (_requestedId == uppercaseId) return;
_requestedId = uppercaseId; _requestedId = uppercaseId;
if( _realWxEnabledNode->getBoolValue() ) { if (_realWxEnabledNode->getBoolValue()) {
// trigger a METAR request for the associated metarproperties // trigger a METAR request for the associated metarproperties
_metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId ); _metarPropertiesNode->getNode("station-id", true)->setStringValue(uppercaseId);
_metarPropertiesNode->getNode( "valid", true )->setBoolValue( false ); _metarPropertiesNode->getNode("valid", true)->setBoolValue(false);
_metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 ); _metarPropertiesNode->getNode("time-to-live", true)->setDoubleValue(0.0);
} else { } else {
// use the present weather to generate the ATIS. // use the present weather to generate the ATIS.
if( NULL != _atisNode && false == _requestedId.empty() ) { if ( NULL != _atisNode && false == _requestedId.empty()) {
CurrentWeatherATISInformationProvider provider( _requestedId ); CurrentWeatherATISInformationProvider provider(_requestedId);
_atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) ); _atisNode->setStringValue(_atisEncoder.encodeATIS(&provider));
} }
} }
} }
@ -222,82 +284,74 @@ void MetarBridge::requestMetarForId( std::string & id )
void MetarBridge::clearMetar() void MetarBridge::clearMetar()
{ {
string empty; string empty;
requestMetarForId( empty ); requestMetarForId(empty);
} }
void MetarBridge::valueChanged(SGPropertyNode * node ) void MetarBridge::valueChanged(SGPropertyNode * node)
{ {
// check for raising edge of valid flag // check for raising edge of valid flag
if( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue() ) if ( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue()) return;
return;
std::string responseId = simgear::strutils::uppercase( std::string responseId = simgear::strutils::uppercase(_metarPropertiesNode->getNode("station-id", true)->getStringValue());
_metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
// unrequested metar!? // unrequested metar!?
if( responseId != _requestedId ) if (responseId != _requestedId) return;
return;
if( NULL != _atisNode ) { if ( NULL != _atisNode) {
MetarPropertiesATISInformationProvider provider( _metarPropertiesNode ); MetarPropertiesATISInformationProvider provider(_metarPropertiesNode);
_atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) ); _atisNode->setStringValue(_atisEncoder.encodeATIS(&provider));
} }
} }
/* ------------- The CommRadio implementation ---------------------- */ /* ------------- The CommRadio implementation ---------------------- */
class CommRadioImpl : public CommRadio, OutputProperties { class CommRadioImpl: public CommRadio, OutputProperties {
public: public:
CommRadioImpl( SGPropertyNode_ptr node ); CommRadioImpl(SGPropertyNode_ptr node);
virtual ~CommRadioImpl(); virtual ~CommRadioImpl();
virtual void update( double dt ); virtual void update(double dt);
virtual void init(); virtual void init();
void bind(); void bind();
void unbind(); void unbind();
private: private:
int _num; int _num;
MetarBridgeRef _metarBridge; MetarBridgeRef _metarBridge;
#if defined(ENABLE_FLITE)
MetarSpeaker _metarSpeaker;
#endif
FrequencyFormatter _useFrequencyFormatter; FrequencyFormatter _useFrequencyFormatter;
FrequencyFormatter _stbyFrequencyFormatter; FrequencyFormatter _stbyFrequencyFormatter;
const SignalQualityComputerRef _signalQualityComputer; const SignalQualityComputerRef _signalQualityComputer;
double _stationTTL; double _stationTTL;
double _frequency; double _frequency;
flightgear::CommStationRef _commStationForFrequency; flightgear::CommStationRef _commStationForFrequency;
PropertyObject<bool> _serviceable; PropertyObject<bool> _serviceable;
PropertyObject<bool> _power_btn; PropertyObject<bool> _power_btn;
PropertyObject<bool> _power_good; PropertyObject<bool> _power_good;
PropertyObject<double> _volume_norm; PropertyObject<double> _volume_norm;
PropertyObject<string> _atis; PropertyObject<string> _atis;
}; };
CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) : CommRadioImpl::CommRadioImpl(SGPropertyNode_ptr node)
OutputProperties( fgGetNode("/instrumentation",true)->getNode( : OutputProperties(
node->getStringValue("name", "comm"), fgGetNode("/instrumentation", true)->getNode(node->getStringValue("name", "comm"), node->getIntValue("number", 0), true)), _num(
node->getIntValue("number", 0), true)), node->getIntValue("number", 0)), _metarBridge(new MetarBridge()), _useFrequencyFormatter(
_num( node->getIntValue("number",0)), _rootNode->getNode("frequencies/selected-mhz", true), _rootNode->getNode("frequencies/selected-mhz-fmt", true), 0.025,
_metarBridge( new MetarBridge() ), 118.0, 136.0), _stbyFrequencyFormatter(_rootNode->getNode("frequencies/standby-mhz", true),
_useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true), _rootNode->getNode("frequencies/standby-mhz-fmt", true), 0.025, 118.0, 136.0), _signalQualityComputer(
_rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025, 118.0, 136.0 ), new SimpleDistanceSquareSignalQualityComputer(10 * SG_NM_TO_METER)),
_stbyFrequencyFormatter( _rootNode->getNode("frequencies/standby-mhz",true),
_rootNode->getNode("frequencies/standby-mhz-fmt",true), 0.025, 118.0, 136.0 ),
_signalQualityComputer( new SimpleDistanceSquareSignalQualityComputer(10*SG_NM_TO_METER) ),
_stationTTL(0.0), _stationTTL(0.0), _frequency(-1.0), _commStationForFrequency(NULL),
_frequency(-1.0),
_commStationForFrequency(NULL),
_serviceable( _rootNode->getNode("serviceable",true) ), _serviceable(_rootNode->getNode("serviceable", true)), _power_btn(_rootNode->getNode("power-btn", true)), _power_good(
_power_btn( _rootNode->getNode("power-btn",true) ), _rootNode->getNode("power-good", true)), _volume_norm(_rootNode->getNode("volume", true)), _atis(
_power_good( _rootNode->getNode("power-good",true) ), _rootNode->getNode("atis", true))
_volume_norm( _rootNode->getNode("volume",true) ),
_atis( _rootNode->getNode("atis",true) )
{ {
} }
@ -307,15 +361,21 @@ CommRadioImpl::~CommRadioImpl()
void CommRadioImpl::bind() void CommRadioImpl::bind()
{ {
_metarBridge->setAtisNode( _atis.node() ); _metarBridge->setAtisNode(_atis.node());
// link the metar node. /environment/metar[3] is comm1 and /environment[4] is comm2. #if defined(ENABLE_FLITE)
// see FGDATA/Environment/environment.xml _atis.node()->addChangeListener( &_metarSpeaker );
_metarBridge->setMetarPropertiesRoot( fgGetNode( "/environment",true)->getNode("metar", _num+3, true ) ); #endif
// link the metar node. /environment/metar[3] is comm1 and /environment[4] is comm2.
// see FGDATA/Environment/environment.xml
_metarBridge->setMetarPropertiesRoot(fgGetNode("/environment", true)->getNode("metar", _num + 3, true));
_metarBridge->bind(); _metarBridge->bind();
} }
void CommRadioImpl::unbind() void CommRadioImpl::unbind()
{ {
#if defined(ENABLE_FLITE)
_atis.node()->removeChangeListener( &_metarSpeaker );
#endif
_metarBridge->unbind(); _metarBridge->unbind();
} }
@ -323,80 +383,88 @@ void CommRadioImpl::init()
{ {
} }
void CommRadioImpl::update( double dt ) void CommRadioImpl::update(double dt)
{ {
if( dt < SGLimitsd::min() ) return; if (dt < SGLimitsd::min()) return;
_stationTTL -= dt; _stationTTL -= dt;
// Ensure all output properties get written on exit of this method // Ensure all output properties get written on exit of this method
OnExit onExit(this); OnExit onExit(this);
SGGeod position; SGGeod position;
try { position = globals->get_aircraft_position(); } try {
catch( std::exception & ) { return; } position = globals->get_aircraft_position();
}
catch (std::exception &) {
return;
}
if( false == (_power_btn )) { if (false == (_power_btn)) {
_stationTTL = 0.0; _stationTTL = 0.0;
return; return;
} }
if (_frequency != _useFrequencyFormatter.getFrequency()) {
_frequency = _useFrequencyFormatter.getFrequency();
_stationTTL = 0.0;
}
if( _frequency != _useFrequencyFormatter.getFrequency() ) { if (_stationTTL <= 0.0) {
_frequency = _useFrequencyFormatter.getFrequency(); _stationTTL = 30.0;
_stationTTL = 0.0;
}
if( _stationTTL <= 0.0 ) { // Note: 122.375 must be rounded DOWN to 122370
_stationTTL = 30.0; // in order to be consistent with apt.dat et cetera.
int freqKhz = 10 * static_cast<int>(_frequency * 100 + 0.25);
_commStationForFrequency = flightgear::CommStation::findByFreq(freqKhz, position, NULL);
// Note: 122.375 must be rounded DOWN to 122370 }
// in order to be consistent with apt.dat et cetera.
int freqKhz = 10 * static_cast<int>(_frequency * 100 + 0.25);
_commStationForFrequency = flightgear::CommStation::findByFreq( freqKhz, position, NULL );
} if (false == _commStationForFrequency.valid()) return;
if( false == _commStationForFrequency.valid() ) return; _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
_slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position)); SGGeodesy::inverse(position, _commStationForFrequency->geod(), _trueBearingTo_deg, _trueBearingFrom_deg, _trackDistance_m);
SGGeodesy::inverse(position, _commStationForFrequency->geod(), _heightAboveStation_ft = SGMiscd::max(0.0, position.getElevationFt() - _commStationForFrequency->airport()->elevation());
_trueBearingTo_deg,
_trueBearingFrom_deg,
_trackDistance_m );
_heightAboveStation_ft = _signalQuality_norm = _signalQualityComputer->computeSignalQuality(_slantDistance_m);
SGMiscd::max(0.0, position.getElevationFt() _stationType = _commStationForFrequency->nameForType(_commStationForFrequency->type());
- _commStationForFrequency->airport()->elevation()); _stationName = _commStationForFrequency->ident();
_airportId = _commStationForFrequency->airport()->getId();
_signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
_stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
_stationName = _commStationForFrequency->ident();
_airportId = _commStationForFrequency->airport()->getId();
switch( _commStationForFrequency->type() ) { switch (_commStationForFrequency->type()) {
case FGPositioned::FREQ_ATIS: case FGPositioned::FREQ_ATIS:
case FGPositioned::FREQ_AWOS: { case FGPositioned::FREQ_AWOS: {
if( _signalQuality_norm > 0.01 ) { if (_signalQuality_norm > 0.01) {
_metarBridge->requestMetarForId( _airportId ); _metarBridge->requestMetarForId(_airportId);
} else { } else {
_metarBridge->clearMetar();
_atis = "";
}
}
break;
default:
_metarBridge->clearMetar(); _metarBridge->clearMetar();
_atis = ""; _atis = "";
break; }
} }
break;
default:
_metarBridge->clearMetar();
_atis = "";
break;
}
#if defined(ENABLE_FLITE)
if( _metarSpeaker.hasSpokenMetar() ) {
SGSharedPtr<SGSoundSample> sample = _metarSpeaker.getSpokenMetar();
SGSoundMgr * _smgr = globals->get_soundmgr();
SGSampleGroup * _sgr = _smgr->find("comm", true );
_sgr->tie_to_listener();
_sgr->remove("metar");
_sgr->add(sample,"metar");
_sgr->play_looped( "metar" );
}
#endif
} }
SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode ) SGSubsystem * CommRadio::createInstance(SGPropertyNode_ptr rootNode)
{ {
return new CommRadioImpl( rootNode ); return new CommRadioImpl(rootNode);
} }
} // namespace Instrumentation } // namespace Instrumentation

View file

@ -24,4 +24,19 @@ set(HEADERS
soundmanager.hxx soundmanager.hxx
) )
if (ENABLE_FLITE)
set(SOURCES
"${SOURCES}"
VoiceSynthesizer.cxx
flitevoice.cxx
)
set(HEADERS
"${HEADERS}"
VoiceSynthesizer.hxx
flitevoice.hxx
)
endif()
flightgear_component(Sound "${SOURCES}" "${HEADERS}") flightgear_component(Sound "${SOURCES}" "${HEADERS}")

View file

@ -0,0 +1,107 @@
/*
* VoiceSynthesizer.cxx
*
* Created on: Apr 24, 2014
* Author: flightgear
*/
#include "VoiceSynthesizer.hxx"
#include <Main/globals.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/sound/readwav.hxx>
#include <simgear/misc/sg_path.hxx>
#include <OpenThreads/Thread>
#include <flite_hts_engine.h>
class ScopedTempfile {
public:
ScopedTempfile()
{
_name = ::tempnam(globals->get_fg_home().c_str(), "fgvox");
}
~ScopedTempfile()
{
if (_name) ::unlink(_name);
::free(_name);
}
const char * getName() const
{
return _name;
}
SGPath getPath()
{
return SGPath(_name);
}
private:
char * _name;
};
class FLITEVoiceSynthesizer::WorkerThread: public OpenThreads::Thread {
public:
WorkerThread(FLITEVoiceSynthesizer * synthesizer)
: _synthesizer(synthesizer)
{
}
virtual void run();
private:
FLITEVoiceSynthesizer * _synthesizer;
};
void FLITEVoiceSynthesizer::WorkerThread::run()
{
for (;;) {
SynthesizeRequest request = _synthesizer->_requests.pop();
if ( NULL != request.listener) {
SGSharedPtr<SGSoundSample> sample = _synthesizer->synthesize(request.text);
request.listener->SoundSampleReady( sample );
}
}
}
void FLITEVoiceSynthesizer::synthesize( SynthesizeRequest & request)
{
_requests.push(request);
}
FLITEVoiceSynthesizer::FLITEVoiceSynthesizer(const std::string & voice)
: _engine(new Flite_HTS_Engine), _worker(new FLITEVoiceSynthesizer::WorkerThread(this))
{
Flite_HTS_Engine_initialize(_engine);
Flite_HTS_Engine_load(_engine, voice.c_str());
_worker->start();
}
FLITEVoiceSynthesizer::~FLITEVoiceSynthesizer()
{
_worker->cancel();
_worker->join();
Flite_HTS_Engine_clear(_engine);
}
SGSoundSample * FLITEVoiceSynthesizer::synthesize(const std::string & text)
{
ScopedTempfile scratch;
if ( FALSE == Flite_HTS_Engine_synthesize(_engine, text.c_str(), scratch.getName())) return NULL;
SG_LOG(SG_SOUND, SG_ALERT, "created wav at " << scratch.getPath());
ALenum format;
ALsizei size;
ALfloat freqf;
ALvoid * data = simgear::loadWAVFromFile(scratch.getPath(), format, size, freqf);
SG_LOG(SG_ALL, SG_ALERT, "loaded wav at " << freqf << "Hz size=" << size << " format=" << format);
if (data == NULL) {
SG_LOG(SG_SOUND, SG_ALERT, "Failed to load wav file " << scratch.getPath());
}
if (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) {
free(data);
SG_LOG(SG_SOUND, SG_ALERT, "Warning: STEREO files are not supported for 3D audio effects: " << scratch.getPath());
}
return new SGSoundSample(&data, size, (ALsizei) freqf, format);
}

View file

@ -0,0 +1,81 @@
/*
* VoiceSynthesizer.hxx
*
* Created on: Apr 24, 2014
* Author: flightgear
*/
#ifndef VOICESYNTHESIZER_HXX_
#define VOICESYNTHESIZER_HXX_
#include <simgear/sound/sample_openal.hxx>
#include <simgear/threads/SGQueue.hxx>
#include <string>
struct _Flite_HTS_Engine;
/**
* A Voice Synthesizer Interface
*/
class VoiceSynthesizer {
public:
virtual ~VoiceSynthesizer() {};
virtual SGSoundSample * synthesize( const std::string & text ) = 0;
};
class SoundSampleReadyListener {
public:
virtual ~SoundSampleReadyListener() {}
virtual void SoundSampleReady( SGSharedPtr<SGSoundSample> ) = 0;
};
struct SynthesizeRequest {
SynthesizeRequest() {
speed = volume = pitch = 1.0;
listener = NULL;
}
SynthesizeRequest( const SynthesizeRequest & other ) {
text = other.text;
speed = other.speed;
volume = other.volume;
pitch = other.pitch;
listener = other.listener;
}
SynthesizeRequest & operator = ( const SynthesizeRequest & other ) {
text = other.text;
speed = other.speed;
volume = other.volume;
pitch = other.pitch;
listener = other.listener;
return *this;
}
std::string text;
double speed;
double volume;
double pitch;
SoundSampleReadyListener * listener;
};
/**
* A Voice Synthesizer using FLITE+HTS
*/
class FLITEVoiceSynthesizer : public VoiceSynthesizer {
public:
FLITEVoiceSynthesizer( const std::string & voice );
~FLITEVoiceSynthesizer();
virtual SGSoundSample * synthesize( const std::string & text );
virtual void synthesize( SynthesizeRequest & request );
private:
struct _Flite_HTS_Engine * _engine;
class WorkerThread;
WorkerThread * _worker;
typedef SGBlockingQueue<SynthesizeRequest> SynthesizeRequestList;
SynthesizeRequestList _requests;
};
#endif /* VOICESYNTHESIZER_HXX_ */

76
src/Sound/flitevoice.cxx Normal file
View file

@ -0,0 +1,76 @@
// speech synthesis interface subsystem
//
// Written by Torsten Dreyer, started April 2014
//
// Copyright (C) 2014 Torsten Dreyer - torsten (at) t3r (dot) de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "flitevoice.hxx"
#include <Main/fg_props.hxx>
#include <simgear/sound/sample_group.hxx>
#include <flite_hts_engine.h>
using std::string;
#include "VoiceSynthesizer.hxx"
FGFLITEVoice::FGFLITEVoice(FGVoiceMgr * mgr, const SGPropertyNode_ptr node)
: FGVoice(mgr), _synthesizer( NULL )
{
string voice = globals->get_fg_root() + "/ATC/cmu_us_arctic_slt.htsvoice";
_synthesizer = new FLITEVoiceSynthesizer( voice.c_str() );
SGSoundMgr *smgr = globals->get_soundmgr();
_sgr = smgr->find("atc", true);
_sgr->tie_to_listener();
node->getNode("text", true)->addChangeListener(this);
SG_LOG(SG_ALL, SG_ALERT, "FLITEVoice initialized");
}
FGFLITEVoice::~FGFLITEVoice()
{
delete _synthesizer;
SG_LOG(SG_ALL, SG_ALERT, "FLITEVoice dtor()");
}
void FGFLITEVoice::speak(const string & msg)
{
SG_LOG(SG_ALL, SG_ALERT, "FLITEVoice speak(" << msg << ")");
_sgr->remove("flite");
string s = simgear::strutils::strip( msg );
if( false == s.empty() ) {
SGSoundSample * sample = _synthesizer->synthesize( msg );
_sgr->add(sample, "flite");
_sgr->set_volume(1.0);
_sgr->resume();
_sgr->play("flite", true);
}
}
void FGFLITEVoice::update()
{
}

45
src/Sound/flitevoice.hxx Normal file
View file

@ -0,0 +1,45 @@
// speech synthesis interface subsystem
//
// Written by Torsten Dreyer, started April 2014
//
// Copyright (C) 2014 Torsten Dreyer - torsten (at) t3r (dot) de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FLITEVOICE_HXX
#define _FLITEVOICE_HXX
#include "voice.hxx"
#include <simgear/sound/soundmgr_openal.hxx>
class VoiceSynthesizer;
class FGFLITEVoice: public FGVoiceMgr::FGVoice {
public:
FGFLITEVoice(FGVoiceMgr *, const SGPropertyNode_ptr);
virtual ~FGFLITEVoice();
virtual void speak(const std::string & msg);
virtual void update();
private:
FGFLITEVoice(const FGFLITEVoice & other);
FGFLITEVoice & operator =(const FGFLITEVoice & other);
SGSharedPtr<SGSampleGroup> _sgr;
VoiceSynthesizer * _synthesizer;
};
#endif // _FLITEVOICE_HXX

View file

@ -23,6 +23,9 @@
#include <simgear/sound/soundmgr_openal.hxx> #include <simgear/sound/soundmgr_openal.hxx>
#if defined(ENABLE_FLITE)
#include "VoiceSynthesizer.hxx"
#endif
#include "soundmanager.hxx" #include "soundmanager.hxx"
#include "Main/globals.hxx" #include "Main/globals.hxx"
#include "Main/fg_props.hxx" #include "Main/fg_props.hxx"
@ -198,4 +201,16 @@ void FGSoundManager::update(double dt)
} }
} }
#if defined(ENABLE_FLITE)
VoiceSynthesizer * FGSoundManager::getSynthesizer( const std::string & voice )
{
std::map<std::string,VoiceSynthesizer*>::iterator it = _synthesizers.find(voice);
if( it == _synthesizers.end() ) {
VoiceSynthesizer * synthesizer = new FLITEVoiceSynthesizer( voice );
_synthesizers[voice] = synthesizer;
return synthesizer;
}
return it->second;
}
#endif
#endif // ENABLE_AUDIO_SUPPORT #endif // ENABLE_AUDIO_SUPPORT

View file

@ -21,12 +21,16 @@
#define __FG_SOUNDMGR_HXX 1 #define __FG_SOUNDMGR_HXX 1
#include <memory> #include <memory>
#include <map>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx> #include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/sound/soundmgr_openal.hxx> #include <simgear/sound/soundmgr_openal.hxx>
class SGSoundMgr; class SGSoundMgr;
class Listener; class Listener;
#if defined(ENABLE_FLITE)
class VoiceSynthesizer;
#endif
#ifdef ENABLE_AUDIO_SUPPORT #ifdef ENABLE_AUDIO_SUPPORT
class FGSoundManager : public SGSoundMgr class FGSoundManager : public SGSoundMgr
@ -42,7 +46,9 @@ public:
void activate(bool State); void activate(bool State);
void update_device_list(); void update_device_list();
#if defined(ENABLE_FLITE)
VoiceSynthesizer * getSynthesizer( const std::string & voice );
#endif
private: private:
bool stationaryView() const; bool stationaryView() const;
@ -53,6 +59,9 @@ private:
SGPropertyNode_ptr _velocityNorthFPS, _velocityEastFPS, _velocityDownFPS; SGPropertyNode_ptr _velocityNorthFPS, _velocityEastFPS, _velocityDownFPS;
SGPropertyNode_ptr _viewXoffset, _viewYoffset, _viewZoffset; SGPropertyNode_ptr _viewXoffset, _viewYoffset, _viewZoffset;
std::auto_ptr<Listener> _listener; std::auto_ptr<Listener> _listener;
#if defined(ENABLE_FLITE)
std::map<std::string,VoiceSynthesizer*> _synthesizers;
#endif
}; };
#else #else
#include "Main/fg_props.hxx" #include "Main/fg_props.hxx"