]> git.mxchange.org Git - flightgear.git/commitdiff
use flite+hts for metar
authorTorsten Dreyer <torsten@t3r.de>
Thu, 24 Apr 2014 18:59:08 +0000 (20:59 +0200)
committerTorsten Dreyer <torsten@t3r.de>
Thu, 24 Apr 2014 18:59:08 +0000 (20:59 +0200)
src/Instrumentation/commradio.cxx
src/Sound/CMakeLists.txt
src/Sound/VoiceSynthesizer.cxx [new file with mode: 0644]
src/Sound/VoiceSynthesizer.hxx [new file with mode: 0644]
src/Sound/flitevoice.cxx [new file with mode: 0644]
src/Sound/flitevoice.hxx [new file with mode: 0644]
src/Sound/soundmanager.cxx
src/Sound/soundmanager.hxx

index 341488671ae102021db4d315d7b65c8730865654..14cf4e8c2c8872b5305570afd61183d7a8a4c662 100644 (file)
 #include <Main/fg_props.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"
 
 namespace Instrumentation {
@@ -46,129 +52,185 @@ namespace Instrumentation {
 using simgear::PropertyObject;
 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>);
 
-SignalQualityComputer::~SignalQualityComputer()
+  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
 
-class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
+SignalQualityComputer::~SignalQualityComputer()
 {
+}
+
+class SimpleDistanceSquareSignalQualityComputer: public SignalQualityComputer {
 public:
-  SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {}
-  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;
+  SimpleDistanceSquareSignalQualityComputer(double range)
+      : _rangeM(range), _rangeM2(range * range)
+  {
+  }
+  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:
   double _rangeM;
   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 {
-  public:
-    virtual void onExit() = 0;
-    virtual ~OnExitHandler() {}
+public:
+  virtual void onExit() = 0;
+  virtual ~OnExitHandler()
+  {
+  }
 };
 
 class OnExit {
-  public:
-    OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
-    ~OnExit() { _onExitHandler->onExit(); }
-  private:
-    OnExitHandler * _onExitHandler;
+public:
+  OnExit(OnExitHandler * onExitHandler)
+      : _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),
-
-      _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() {}
+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),
+
+      _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:
-    SGPropertyNode_ptr _rootNode;
-
-    std::string  _stationType;
-    std::string  _stationName;
-    std::string  _airportId;
-    double       _signalQuality_norm;
-    double       _slantDistance_m;
-    double       _trueBearingTo_deg;
-    double       _trueBearingFrom_deg;
-    double       _trackDistance_m;
-    double       _heightAboveStation_ft;
+  SGPropertyNode_ptr _rootNode;
+
+  std::string _stationType;
+  std::string _stationName;
+  std::string _airportId;
+  double _signalQuality_norm;
+  double _slantDistance_m;
+  double _trueBearingTo_deg;
+  double _trueBearingFrom_deg;
+  double _trackDistance_m;
+  double _heightAboveStation_ft;
 
 private:
-    PropertyObject<string> _PO_stationType;
-    PropertyObject<string> _PO_stationName;
-    PropertyObject<string> _PO_airportId;
-    PropertyObject<double> _PO_signalQuality_norm;
-    PropertyObject<double> _PO_slantDistance_m;
-    PropertyObject<double> _PO_trueBearingTo_deg;
-    PropertyObject<double> _PO_trueBearingFrom_deg;
-    PropertyObject<double> _PO_trackDistance_m;
-    PropertyObject<double> _PO_heightAboveStation_ft;
-
-    virtual void onExit() {
-        _PO_stationType = _stationType;
-        _PO_stationName = _stationName;
-        _PO_airportId = _airportId;
-        _PO_signalQuality_norm = _signalQuality_norm;
-        _PO_slantDistance_m = _slantDistance_m;
-        _PO_trueBearingTo_deg = _trueBearingTo_deg;
-        _PO_trueBearingFrom_deg = _trueBearingFrom_deg;
-        _PO_trackDistance_m = _trackDistance_m;
-        _PO_heightAboveStation_ft = _heightAboveStation_ft;
-    }
+  PropertyObject<string> _PO_stationType;
+  PropertyObject<string> _PO_stationName;
+  PropertyObject<string> _PO_airportId;
+  PropertyObject<double> _PO_signalQuality_norm;
+  PropertyObject<double> _PO_slantDistance_m;
+  PropertyObject<double> _PO_trueBearingTo_deg;
+  PropertyObject<double> _PO_trueBearingFrom_deg;
+  PropertyObject<double> _PO_trackDistance_m;
+  PropertyObject<double> _PO_heightAboveStation_ft;
+
+  virtual void onExit()
+  {
+    _PO_stationType = _stationType;
+    _PO_stationName = _stationName;
+    _PO_airportId = _airportId;
+    _PO_signalQuality_norm = _signalQuality_norm;
+    _PO_slantDistance_m = _slantDistance_m;
+    _PO_trueBearingTo_deg = _trueBearingTo_deg;
+    _PO_trueBearingFrom_deg = _trueBearingFrom_deg;
+    _PO_trackDistance_m = _trackDistance_m;
+    _PO_heightAboveStation_ft = _heightAboveStation_ft;
+  }
 };
 
 /* ------------- The CommRadio implementation ---------------------- */
 
-class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
+class MetarBridge: public SGReferenced, public SGPropertyChangeListener {
 public:
   MetarBridge();
   ~MetarBridge();
 
   void bind();
   void unbind();
-  void requestMetarForId( std::string & id );
+  void requestMetarForId(std::string & id);
   void clearMetar();
-  void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
-  void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
+  void setMetarPropertiesRoot(SGPropertyNode_ptr n)
+  {
+    _metarPropertiesNode = n;
+  }
+  void setAtisNode(SGPropertyNode * n)
+  {
+    _atisNode = n;
+  }
 
 protected:
-  virtual void valueChanged(SGPropertyNode * );
+  virtual void valueChanged(SGPropertyNode *);
 
 private:
   std::string _requestedId;
@@ -179,8 +241,8 @@ private:
 };
 typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
 
-MetarBridge::MetarBridge() :
-  _atisNode(0)
+MetarBridge::MetarBridge()
+    : _atisNode(0)
 {
 }
 
@@ -190,31 +252,31 @@ MetarBridge::~MetarBridge()
 
 void MetarBridge::bind()
 {
-  _realWxEnabledNode = fgGetNode( "/environment/realwx/enabled", true );
-  _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
+  _realWxEnabledNode = fgGetNode("/environment/realwx/enabled", true);
+  _metarPropertiesNode->getNode("valid", true)->addChangeListener(this);
 }
 
 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 );
-  if( _requestedId == uppercaseId ) return;
+  std::string uppercaseId = simgear::strutils::uppercase(id);
+  if (_requestedId == uppercaseId) return;
   _requestedId = uppercaseId;
 
-  if( _realWxEnabledNode->getBoolValue() ) {
+  if (_realWxEnabledNode->getBoolValue()) {
     //  trigger a METAR request for the associated metarproperties
-    _metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId );
-    _metarPropertiesNode->getNode( "valid", true )->setBoolValue( false );
-    _metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 );
-  } else  {
+    _metarPropertiesNode->getNode("station-id", true)->setStringValue(uppercaseId);
+    _metarPropertiesNode->getNode("valid", true)->setBoolValue(false);
+    _metarPropertiesNode->getNode("time-to-live", true)->setDoubleValue(0.0);
+  } else {
     // use the present weather to generate the ATIS. 
-    if( NULL != _atisNode && false == _requestedId.empty() ) {
-      CurrentWeatherATISInformationProvider provider( _requestedId );
-      _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
+    if ( NULL != _atisNode && false == _requestedId.empty()) {
+      CurrentWeatherATISInformationProvider provider(_requestedId);
+      _atisNode->setStringValue(_atisEncoder.encodeATIS(&provider));
     }
   }
 }
@@ -222,82 +284,74 @@ void MetarBridge::requestMetarForId( std::string & id )
 void MetarBridge::clearMetar()
 {
   string empty;
-  requestMetarForId( empty );
+  requestMetarForId(empty);
 }
 
-void MetarBridge::valueChanged(SGPropertyNode * node )
+void MetarBridge::valueChanged(SGPropertyNode * node)
 {
   // check for raising edge of valid flag
-  if( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue() )
-    return;
+  if ( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue()) return;
 
-  std::string responseId = simgear::strutils::uppercase(
-     _metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
+  std::string responseId = simgear::strutils::uppercase(_metarPropertiesNode->getNode("station-id", true)->getStringValue());
 
   // unrequested metar!?
-  if( responseId != _requestedId )
-    return;
+  if (responseId != _requestedId) return;
 
-  if( NULL != _atisNode ) {
-    MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
-    _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
+  if ( NULL != _atisNode) {
+    MetarPropertiesATISInformationProvider provider(_metarPropertiesNode);
+    _atisNode->setStringValue(_atisEncoder.encodeATIS(&provider));
   }
 }
 
 /* ------------- The CommRadio implementation ---------------------- */
 
-class CommRadioImpl : public CommRadio, OutputProperties {
+class CommRadioImpl: public CommRadio, OutputProperties {
 
 public:
-  CommRadioImpl( SGPropertyNode_ptr node );
+  CommRadioImpl(SGPropertyNode_ptr node);
   virtual ~CommRadioImpl();
 
-  virtual void update( double dt );
+  virtual void update(double dt);
   virtual void init();
   void bind();
   void unbind();
 
 private:
-  int                _num;
-  MetarBridgeRef     _metarBridge;
+  int _num;
+  MetarBridgeRef _metarBridge;
+#if defined(ENABLE_FLITE)
+  MetarSpeaker _metarSpeaker;
+#endif
   FrequencyFormatter _useFrequencyFormatter;
   FrequencyFormatter _stbyFrequencyFormatter;
-  const SignalQualityComputerRef _signalQualityComputer; 
+  const SignalQualityComputerRef _signalQualityComputer;
 
   double _stationTTL;
   double _frequency;
   flightgear::CommStationRef _commStationForFrequency;
 
-  PropertyObject<bool>   _serviceable;
-  PropertyObject<bool>   _power_btn;
-  PropertyObject<bool>   _power_good;
+  PropertyObject<bool> _serviceable;
+  PropertyObject<bool> _power_btn;
+  PropertyObject<bool> _power_good;
   PropertyObject<double> _volume_norm;
   PropertyObject<string> _atis;
 
-
 };
 
-CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) :
-    OutputProperties( fgGetNode("/instrumentation",true)->getNode(
-                        node->getStringValue("name", "comm"),
-                        node->getIntValue("number", 0), true)),
-    _num( node->getIntValue("number",0)),
-    _metarBridge( new MetarBridge() ),
-    _useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true), 
-                            _rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025, 118.0, 136.0 ),
-    _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),
-    _frequency(-1.0),
-    _commStationForFrequency(NULL),
-
-    _serviceable( _rootNode->getNode("serviceable",true) ),
-    _power_btn( _rootNode->getNode("power-btn",true) ),
-    _power_good( _rootNode->getNode("power-good",true) ),
-    _volume_norm( _rootNode->getNode("volume",true) ),
-    _atis( _rootNode->getNode("atis",true) )
+CommRadioImpl::CommRadioImpl(SGPropertyNode_ptr node)
+    : OutputProperties(
+        fgGetNode("/instrumentation", true)->getNode(node->getStringValue("name", "comm"), node->getIntValue("number", 0), true)), _num(
+        node->getIntValue("number", 0)), _metarBridge(new MetarBridge()), _useFrequencyFormatter(
+        _rootNode->getNode("frequencies/selected-mhz", true), _rootNode->getNode("frequencies/selected-mhz-fmt", true), 0.025,
+        118.0, 136.0), _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), _frequency(-1.0), _commStationForFrequency(NULL),
+
+    _serviceable(_rootNode->getNode("serviceable", true)), _power_btn(_rootNode->getNode("power-btn", true)), _power_good(
+        _rootNode->getNode("power-good", true)), _volume_norm(_rootNode->getNode("volume", true)), _atis(
+        _rootNode->getNode("atis", true))
 {
 }
 
@@ -307,15 +361,21 @@ CommRadioImpl::~CommRadioImpl()
 
 void CommRadioImpl::bind()
 {
-  _metarBridge->setAtisNode( _atis.node() );
-   // 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->setAtisNode(_atis.node());
+#if defined(ENABLE_FLITE)
+  _atis.node()->addChangeListener( &_metarSpeaker );
+#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();
 }
 
 void CommRadioImpl::unbind()
 {
+#if defined(ENABLE_FLITE)
+  _atis.node()->removeChangeListener( &_metarSpeaker );
+#endif
   _metarBridge->unbind();
 }
 
@@ -323,80 +383,88 @@ void CommRadioImpl::init()
 {
 }
 
-void CommRadioImpl::update( double dt )
+void CommRadioImpl::update(double dt)
 {
-    if( dt < SGLimitsd::min() ) return;
-    _stationTTL -= dt;
+  if (dt < SGLimitsd::min()) return;
+  _stationTTL -= dt;
 
-    // Ensure all output properties get written on exit of this method
-    OnExit onExit(this);
+  // Ensure all output properties get written on exit of this method
+  OnExit onExit(this);
 
-    SGGeod position;
-    try { position = globals->get_aircraft_position(); }
-    catch( std::exception & ) { return; }
+  SGGeod position;
+  try {
+    position = globals->get_aircraft_position();
+  }
+  catch (std::exception &) {
+    return;
+  }
 
-    if( false == (_power_btn )) {
-        _stationTTL = 0.0;
-        return;
-    }
+  if (false == (_power_btn)) {
+    _stationTTL = 0.0;
+    return;
+  }
 
+  if (_frequency != _useFrequencyFormatter.getFrequency()) {
+    _frequency = _useFrequencyFormatter.getFrequency();
+    _stationTTL = 0.0;
+  }
 
-    if( _frequency != _useFrequencyFormatter.getFrequency() ) {
-        _frequency = _useFrequencyFormatter.getFrequency();
-        _stationTTL = 0.0;
-    }
+  if (_stationTTL <= 0.0) {
+    _stationTTL = 30.0;
 
-    if( _stationTTL <= 0.0 ) {
-        _stationTTL = 30.0;
+    // 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);
 
-        // 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));
-
-    SGGeodesy::inverse(position, _commStationForFrequency->geod(), 
-        _trueBearingTo_deg,
-        _trueBearingFrom_deg,
-        _trackDistance_m );
-
-    _heightAboveStation_ft = 
-         SGMiscd::max(0.0, position.getElevationFt() 
-           - _commStationForFrequency->airport()->elevation());
-    
-    _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
-    _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
-    _stationName = _commStationForFrequency->ident();
-    _airportId = _commStationForFrequency->airport()->getId();
-
-    switch( _commStationForFrequency->type() ) {
-      case FGPositioned::FREQ_ATIS:
-      case FGPositioned::FREQ_AWOS: {
-        if( _signalQuality_norm > 0.01 ) {
-          _metarBridge->requestMetarForId( _airportId );
-        } else {
-          _metarBridge->clearMetar();
-          _atis = "";
-        }
-      }
-      break;
+  _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
+
+  SGGeodesy::inverse(position, _commStationForFrequency->geod(), _trueBearingTo_deg, _trueBearingFrom_deg, _trackDistance_m);
 
-      default:
+  _heightAboveStation_ft = SGMiscd::max(0.0, position.getElevationFt() - _commStationForFrequency->airport()->elevation());
+
+  _signalQuality_norm = _signalQualityComputer->computeSignalQuality(_slantDistance_m);
+  _stationType = _commStationForFrequency->nameForType(_commStationForFrequency->type());
+  _stationName = _commStationForFrequency->ident();
+  _airportId = _commStationForFrequency->airport()->getId();
+
+  switch (_commStationForFrequency->type()) {
+    case FGPositioned::FREQ_ATIS:
+    case FGPositioned::FREQ_AWOS: {
+      if (_signalQuality_norm > 0.01) {
+        _metarBridge->requestMetarForId(_airportId);
+      } else {
         _metarBridge->clearMetar();
         _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
index fc3960d5b0de6e23392257c57e24fb27820f83ee..8b342435f102efc1560ed33e431389b3a51b39cf 100644 (file)
@@ -24,4 +24,19 @@ set(HEADERS
        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}")
diff --git a/src/Sound/VoiceSynthesizer.cxx b/src/Sound/VoiceSynthesizer.cxx
new file mode 100644 (file)
index 0000000..7a8d7ed
--- /dev/null
@@ -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);
+}
+
diff --git a/src/Sound/VoiceSynthesizer.hxx b/src/Sound/VoiceSynthesizer.hxx
new file mode 100644 (file)
index 0000000..4e49a38
--- /dev/null
@@ -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_ */
diff --git a/src/Sound/flitevoice.cxx b/src/Sound/flitevoice.cxx
new file mode 100644 (file)
index 0000000..f554c2e
--- /dev/null
@@ -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()
+{
+
+}
+
diff --git a/src/Sound/flitevoice.hxx b/src/Sound/flitevoice.hxx
new file mode 100644 (file)
index 0000000..4322b9d
--- /dev/null
@@ -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
index 012642f669ddddb5dacd35ff74723b1f7f773dbc..906ffd7983ac7bff876444d5b50adb5a9bedcfba 100644 (file)
@@ -23,6 +23,9 @@
 
 #include <simgear/sound/soundmgr_openal.hxx>
 
+#if defined(ENABLE_FLITE)
+#include "VoiceSynthesizer.hxx"
+#endif
 #include "soundmanager.hxx"
 #include "Main/globals.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
index 7bec8b8f587330c64d3f4f042d2abf80b5c5ed73..7f5a164990a1936ff7ca5dc6b56c66a3bb2499e0 100644 (file)
 #define __FG_SOUNDMGR_HXX 1
 
 #include <memory>
+#include <map>
 #include <simgear/props/props.hxx>
 #include <simgear/structure/subsystem_mgr.hxx>
 #include <simgear/sound/soundmgr_openal.hxx>
 
 class SGSoundMgr;
 class Listener;
+#if defined(ENABLE_FLITE)
+class VoiceSynthesizer;
+#endif
 
 #ifdef ENABLE_AUDIO_SUPPORT
 class FGSoundManager : public SGSoundMgr
@@ -42,7 +46,9 @@ public:
 
     void activate(bool State);
     void update_device_list();
-
+#if defined(ENABLE_FLITE)
+    VoiceSynthesizer * getSynthesizer( const std::string & voice );
+#endif
 private:
     bool stationaryView() const;
   
@@ -53,6 +59,9 @@ private:
     SGPropertyNode_ptr _velocityNorthFPS, _velocityEastFPS, _velocityDownFPS;
     SGPropertyNode_ptr _viewXoffset, _viewYoffset, _viewZoffset;
     std::auto_ptr<Listener> _listener;
+#if defined(ENABLE_FLITE)
+    std::map<std::string,VoiceSynthesizer*> _synthesizers;
+#endif
 };
 #else
 #include "Main/fg_props.hxx"