FlightGear next
VoiceSynthesizer.cxx
Go to the documentation of this file.
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
20#include <utility>
21
22#include "VoiceSynthesizer.hxx"
23#include <Main/globals.hxx>
24#include <Main/fg_props.hxx>
25#include <simgear/sg_inlines.h>
26#include <simgear/debug/logstream.hxx>
27#include <simgear/misc/sg_path.hxx>
28#include <simgear/threads/SGThread.hxx>
29
30#include <flite_hts_engine.h>
31
32using std::string;
33
34static const char * VOICE_FILES[] = {
35 "cmu_us_arctic_slt.htsvoice",
36 "cstr_uk_female-1.0.htsvoice"
37};
38
40{
41public:
43 : _synthesizer(synthesizer)
44 {
45 }
46 virtual void run();
47private:
48 FLITEVoiceSynthesizer * _synthesizer;
49};
50
52{
53 for (;;) {
54 SynthesizeRequest request = _synthesizer->_requests.pop();
55
56 // marker value indicating termination requested
57 if ((request.speed < 0.0) && (request.volume < 0.0)) {
58 SG_LOG(SG_SOUND, SG_DEBUG, "FLITE synthesis thread exiting");
59 return;
60 }
61
62 if ( NULL != request.listener) {
63 SGSharedPtr<SGSoundSample> sample = _synthesizer->synthesize(request.text, request.volume, request.speed, request.pitch);
64 request.listener->SoundSampleReady( sample );
65 }
66 }
67}
68
70{
71 SynthesizeRequest marker;
72 marker.volume = -999.0;
73 marker.speed = -999.0;
74 return marker;
75}
76
78{
79 if( voice < 0 || voice >= VOICE_UNKNOWN ) return string("");
80 SGPath voicePath = globals->get_fg_root() / "ATC" / VOICE_FILES[voice];
81 return voicePath.utf8Str();
82}
83
84string FLITEVoiceSynthesizer::getVoicePath( const string & voice )
85{
86 if( voice == "cmu_us_arctic_slt" ) return getVoicePath(CMU_US_ARCTIC_SLT);
87 if( voice == "cstr_uk_female" ) return getVoicePath(CSTR_UK_FEMALE);
89}
90
91
93{
94 _requests.push(request);
95}
96
98 // REVIEW: Memory Leak - 1,696 bytes in 4 blocks are definitely lost in loss record 6,145 of 6,440
99 : _engine(new Flite_HTS_Engine), _worker(new FLITEVoiceSynthesizer::WorkerThread(this)), _volume(6.0)
100{
101 _volume = fgGetDouble("/sim/sound/voice-synthesizer/volume", _volume );
102 Flite_HTS_Engine_initialize(_engine);
103 Flite_HTS_Engine_load(_engine, voice.c_str());
104 _worker->start();
105}
106
108{
109 // push the special marker value
111 _worker->join();
112 Flite_HTS_Engine_clear(_engine);
113}
114
115SGSoundSample * FLITEVoiceSynthesizer::synthesize(const std::string & text, double volume, double speed, double pitch )
116{
117 SG_CLAMP_RANGE( volume, 0.0, 1.0 );
118 SG_CLAMP_RANGE( speed, 0.0, 10.0 );
119 SG_CLAMP_RANGE( pitch, 0.0, 10.0 );
120 HTS_Engine_set_volume( &_engine->engine, _volume );
121 HTS_Engine_set_speed( &_engine->engine, 0.8 + 0.4 * speed );
122 HTS_Engine_add_half_tone(&_engine->engine, -4.0 + 8.0 * pitch );
123
124
125 void* data;
126 int rate, count;
127 if ( FALSE == Flite_HTS_Engine_synthesize_samples_mono16(_engine, text.c_str(), &data, &count, &rate)) return NULL;
128
129 auto buf = std::unique_ptr<unsigned char, decltype(free)*>{
130 reinterpret_cast<unsigned char*>( data ),
131 free
132 };
133 return new SGSoundSample(std::move(buf),
134 count * sizeof(short),
135 rate,
136 SG_SAMPLE_MONO16);
137}
138
static const char * VOICE_FILES[]
WorkerThread(FLITEVoiceSynthesizer *synthesizer)
virtual SGSoundSample * synthesize(const std::string &text, double volume, double speed, double pitch)
FLITEVoiceSynthesizer(const std::string &voice)
static std::string getVoicePath(voice_t voice)
virtual void SoundSampleReady(SGSharedPtr< SGSoundSample >)=0
FGGlobals * globals
Definition globals.cxx:142
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
SoundSampleReadyListener * listener
static SynthesizeRequest cancelThreadRequest()