]> git.mxchange.org Git - flightgear.git/blob - src/Sound/VoiceSynthesizer.cxx
Add GPL header
[flightgear.git] / src / Sound / VoiceSynthesizer.cxx
1 /*
2  * VoiceSynthesizer.cxx - wraps flite+hts_engine
3  * Copyright (C) 2014  Torsten Dreyer - torsten (at) t3r (dot) de
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include "VoiceSynthesizer.hxx"
20 #include <Main/globals.hxx>
21 #include <Main/fg_props.hxx>
22 #include <simgear/debug/logstream.hxx>
23 #include <simgear/sound/readwav.hxx>
24 #include <simgear/misc/sg_path.hxx>
25 #include <OpenThreads/Thread>
26 #include <flite_hts_engine.h>
27
28 class ScopedTempfile {
29 public:
30   ScopedTempfile( bool keep = false ) : _keep(keep)
31   {
32     _name = ::tempnam(globals->get_fg_home().c_str(), "fgvox");
33
34   }
35   ~ScopedTempfile()
36   {
37     if (_name && !_keep) ::unlink(_name);
38     ::free(_name);
39   }
40
41   const char * getName() const
42   {
43     return _name;
44   }
45   SGPath getPath()
46   {
47     return SGPath(_name);
48   }
49 private:
50   char * _name;
51   bool _keep;
52 };
53
54 class FLITEVoiceSynthesizer::WorkerThread: public OpenThreads::Thread {
55 public:
56   WorkerThread(FLITEVoiceSynthesizer * synthesizer)
57       : _synthesizer(synthesizer)
58   {
59   }
60   virtual void run();
61 private:
62   FLITEVoiceSynthesizer * _synthesizer;
63 };
64
65 void FLITEVoiceSynthesizer::WorkerThread::run()
66 {
67   for (;;) {
68     SynthesizeRequest request = _synthesizer->_requests.pop();
69     if ( NULL != request.listener) {
70       SGSharedPtr<SGSoundSample> sample = _synthesizer->synthesize(request.text);
71       request.listener->SoundSampleReady( sample );
72     }
73   }
74 }
75
76 void FLITEVoiceSynthesizer::synthesize( SynthesizeRequest & request)
77 {
78   _requests.push(request);
79 }
80
81 FLITEVoiceSynthesizer::FLITEVoiceSynthesizer(const std::string & voice)
82     : _engine(new Flite_HTS_Engine), _worker(new FLITEVoiceSynthesizer::WorkerThread(this)), _volume(6.0), _keepScratchFile(false)
83 {
84   _volume = fgGetDouble("/sim/sound/voice-synthesizer/volume", _volume );
85   _keepScratchFile = fgGetBool("/sim/sound/voice-synthesizer/keep-scratch-file", _keepScratchFile);
86   Flite_HTS_Engine_initialize(_engine);
87   Flite_HTS_Engine_load(_engine, voice.c_str());
88   _worker->start();
89 }
90
91 FLITEVoiceSynthesizer::~FLITEVoiceSynthesizer()
92 {
93   _worker->cancel();
94   _worker->join();
95   Flite_HTS_Engine_clear(_engine);
96 }
97
98 SGSoundSample * FLITEVoiceSynthesizer::synthesize(const std::string & text)
99 {
100   ScopedTempfile scratch(_keepScratchFile);
101   HTS_Engine_set_volume( &_engine->engine, _volume );
102
103   if ( FALSE == Flite_HTS_Engine_synthesize(_engine, text.c_str(), scratch.getName())) return NULL;
104
105   ALenum format;
106   ALsizei size;
107   ALfloat freqf;
108   ALvoid * data = simgear::loadWAVFromFile(scratch.getPath(), format, size, freqf);
109
110   if (data == NULL) {
111     SG_LOG(SG_SOUND, SG_ALERT, "Failed to load wav file " << scratch.getPath());
112   }
113
114   if (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) {
115     free(data);
116     SG_LOG(SG_SOUND, SG_ALERT, "Warning: STEREO files are not supported for 3D audio effects: " << scratch.getPath());
117   }
118
119   return new SGSoundSample(&data, size, (ALsizei) freqf, format);
120 }
121