From: Torsten Dreyer Date: Thu, 24 Apr 2014 18:59:08 +0000 (+0200) Subject: use flite+hts for metar X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=cadb77b18d13bb0d45672ac607801bda222454e9;p=flightgear.git use flite+hts for metar --- diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx index 341488671..14cf4e8c2 100644 --- a/src/Instrumentation/commradio.cxx +++ b/src/Instrumentation/commradio.cxx @@ -39,6 +39,12 @@ #include
#include +#if defined(ENABLE_FLITE) +#include +#include +#include +#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); -SignalQualityComputer::~SignalQualityComputer() + bool hasSpokenMetar() { return _spokenMetar.empty() == false; } + SGSharedPtr getSpokenMetar() { return _spokenMetar.pop(); } +private: + SynthesizeRequest _synthesizeRequest; + SGLockedQueue > _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(globals->get_soundmgr()); + assert(smgr != NULL); + + string voice = globals->get_fg_root() + "/ATC/cmu_us_arctic_slt.htsvoice"; + FLITEVoiceSynthesizer * synthesizer = dynamic_cast(smgr->getSynthesizer(voice)); + + synthesizer->synthesize(_synthesizeRequest); +} + +void MetarSpeaker::SoundSampleReady(SGSharedPtr 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 _PO_stationType; - PropertyObject _PO_stationName; - PropertyObject _PO_airportId; - PropertyObject _PO_signalQuality_norm; - PropertyObject _PO_slantDistance_m; - PropertyObject _PO_trueBearingTo_deg; - PropertyObject _PO_trueBearingFrom_deg; - PropertyObject _PO_trackDistance_m; - PropertyObject _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 _PO_stationType; + PropertyObject _PO_stationName; + PropertyObject _PO_airportId; + PropertyObject _PO_signalQuality_norm; + PropertyObject _PO_slantDistance_m; + PropertyObject _PO_trueBearingTo_deg; + PropertyObject _PO_trueBearingFrom_deg; + PropertyObject _PO_trackDistance_m; + PropertyObject _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 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 _serviceable; - PropertyObject _power_btn; - PropertyObject _power_good; + PropertyObject _serviceable; + PropertyObject _power_btn; + PropertyObject _power_good; PropertyObject _volume_norm; PropertyObject _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(_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(_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 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 diff --git a/src/Sound/CMakeLists.txt b/src/Sound/CMakeLists.txt index fc3960d5b..8b342435f 100644 --- a/src/Sound/CMakeLists.txt +++ b/src/Sound/CMakeLists.txt @@ -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 index 000000000..7a8d7ed14 --- /dev/null +++ b/src/Sound/VoiceSynthesizer.cxx @@ -0,0 +1,107 @@ +/* + * VoiceSynthesizer.cxx + * + * Created on: Apr 24, 2014 + * Author: flightgear + */ + +#include "VoiceSynthesizer.hxx" +#include
+#include +#include +#include +#include +#include + +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 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 index 000000000..4e49a38bf --- /dev/null +++ b/src/Sound/VoiceSynthesizer.hxx @@ -0,0 +1,81 @@ +/* + * VoiceSynthesizer.hxx + * + * Created on: Apr 24, 2014 + * Author: flightgear + */ + +#ifndef VOICESYNTHESIZER_HXX_ +#define VOICESYNTHESIZER_HXX_ + +#include +#include + +#include +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 ) = 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 SynthesizeRequestList; + SynthesizeRequestList _requests; +}; + +#endif /* VOICESYNTHESIZER_HXX_ */ diff --git a/src/Sound/flitevoice.cxx b/src/Sound/flitevoice.cxx new file mode 100644 index 000000000..f554c2e8e --- /dev/null +++ b/src/Sound/flitevoice.cxx @@ -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
+#include +#include + +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 index 000000000..4322b9d18 --- /dev/null +++ b/src/Sound/flitevoice.hxx @@ -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 + +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 _sgr; + VoiceSynthesizer * _synthesizer; +}; + +#endif // _FLITEVOICE_HXX diff --git a/src/Sound/soundmanager.cxx b/src/Sound/soundmanager.cxx index 012642f66..906ffd798 100644 --- a/src/Sound/soundmanager.cxx +++ b/src/Sound/soundmanager.cxx @@ -23,6 +23,9 @@ #include +#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::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 diff --git a/src/Sound/soundmanager.hxx b/src/Sound/soundmanager.hxx index 7bec8b8f5..7f5a16499 100644 --- a/src/Sound/soundmanager.hxx +++ b/src/Sound/soundmanager.hxx @@ -21,12 +21,16 @@ #define __FG_SOUNDMGR_HXX 1 #include +#include #include #include #include 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; +#if defined(ENABLE_FLITE) + std::map _synthesizers; +#endif }; #else #include "Main/fg_props.hxx"