FestivalSynthesizer.cpp
Go to the documentation of this file.
00001 /*******************************************************************************
00002  *  TalkingHead - A talking head for robots
00003  *  Copyright (C) 2012 AG Aktives Sehen <agas@uni-koblenz.de>
00004  *                     Universitaet Koblenz-Landau
00005  *
00006  *  This program is free software: you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation, either version 3 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014  *  Library General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Library General Public
00017  *  License along with this library; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00019  *  MA 02110-1301  USA or see <http://www.gnu.org/licenses/>.
00020  *******************************************************************************/
00021 
00022 #include <pulse/error.h>
00023 #include <pulse/gccmacro.h>
00024 #include <pulse/simple.h>
00025 
00026 #include <string>
00027 
00028 #include "Config.h"
00029 #include "FestivalSynthesizer.h"
00030 
00031 FestivalSynthesizer::FestivalSynthesizer() :
00032     festival_initialized_(false),
00033     synth_speech_(false),
00034     punctuation_characters_(0)
00035 {
00036     if (!festival_initialized_)
00037     {
00038         initFestival();
00039         festival_initialized_ = true;
00040     }
00041 
00042     smileys_.push_back(">:");
00043     smileys_.push_back(":)");
00044     smileys_.push_back(":(");
00045     smileys_.push_back(":O");
00046     smileys_.push_back(":o");
00047     smileys_.push_back(":!");
00048     smileys_.push_back(":&");
00049 
00050     punctuation_characters_.push_back(":");
00051     punctuation_characters_.push_back(")");
00052     punctuation_characters_.push_back("(");
00053     punctuation_characters_.push_back(".");
00054     punctuation_characters_.push_back("'");
00055     punctuation_characters_.push_back("\"");
00056     punctuation_characters_.push_back("}");
00057     punctuation_characters_.push_back("{");
00058     punctuation_characters_.push_back("§");
00059     punctuation_characters_.push_back("!");
00060     punctuation_characters_.push_back("?");
00061     punctuation_characters_.push_back("`");
00062     punctuation_characters_.push_back("´");
00063     punctuation_characters_.push_back("-");
00064     punctuation_characters_.push_back(";");
00065     punctuation_characters_.push_back(",");
00066     punctuation_characters_.push_back("€");
00067     punctuation_characters_.push_back("°");
00068     punctuation_characters_.push_back("<");
00069     punctuation_characters_.push_back(">");
00070     punctuation_characters_.push_back(".");
00071 }
00072 
00073 FestivalSynthesizer::~FestivalSynthesizer()
00074 {
00075     festival_tidy_up();
00076 }
00077 
00078 void FestivalSynthesizer::initFestival()
00079 {
00080     int heap_size = 210000;   // default scheme heap size
00081     int load_init_files = 1;  // we want the festival init files loaded
00082 
00083     festival_initialize(load_init_files, heap_size);
00084 
00085     try
00086     {
00087         const char* cfgFilename = "../config.cfg";
00088 
00089         Config cfg(cfgFilename);
00090         std::string voice = cfg.get("Voice");
00091 
00092         if ( voice == "female" )
00093         {
00094             festival_eval_command( "(voice_us1_mbrola)" );
00095             festival_eval_command( "(defvar Styles '((default 140 22 1.0)) \"Available voice styles\")" );
00096             festival_eval_command( "(defvar style_default 'default \"Default voice style\")" );
00097             festival_eval_command( "(defvar current_style 'none \"Current voice style\")" );
00098             festival_eval_command( "(define (Style selected_style) (let ((style (assoc selected_style Styles)))"
00099                                    "(if (not style) (set! style (assoc 'default Styles)))"
00100                                    "(let ((model_mean (cadr (assoc 'model_f0_mean  int_lr_params)))"
00101                                    "(model_std  (cadr (assoc 'model_f0_std   int_lr_params)))"
00102                                    "(new_mean (cadr style)) (new_std (cadr (cdr style)))"
00103                                    "(new_stretch (cadr (cdr (cdr style)))))"
00104                                    "(set! int_lr_params (list (list 'target_f0_mean new_mean)"
00105                                    "(list 'target_f0_std  new_std) (list 'model_f0_mean  model_mean)"
00106                                    "(list 'model_f0_std   model_std)))"
00107                                    "(Parameter.set 'Duration_Stretch new_stretch)"
00108                                    "(set! current_style (car style)) (list (car style) new_mean new_std new_stretch) ) ) )" );
00109             festival_eval_command( "(define (NewStyle style_name mean std stretch)"
00110                                    "(set! Styles (cons (list style_name mean std stretch) Styles)))" );
00111             festival_eval_command( "(NewStyle 'lisa 280 50 1.2)" );
00112             festival_eval_command( "(Style 'lisa)" );
00113         }
00114     }
00115     catch(const std::exception& e)
00116     {
00117         std::cerr << e.what() << std::endl;
00118     }
00119 
00120     // Check sampling rate
00121     EST_Wave wave;
00122     festival_text_to_wave( "init", wave );
00123 
00124     // Init pulse audio
00125     pa_sample_spec sample_spec;
00126     sample_spec.format = PA_SAMPLE_S16NE;
00127     sample_spec.channels = 1;
00128     sample_spec.rate = wave.sample_rate();
00129 
00130     pa_simple_ = pa_simple_new( NULL,                                   // Use the default server.
00131                                 "FestivalSynthesizer",                  // Our application's name.
00132                                 PA_STREAM_PLAYBACK,                     // Playback stream.
00133                                 NULL,                                   // Use the default device.
00134                                 "Festival Synthesizer (TalkingHead)",   // Description of our stream.
00135                                 &sample_spec,                           // Our sample format.
00136                                 NULL,                                   // Use default channel map
00137                                 NULL,                                   // Use default buffering attributes.
00138                                 NULL                                    // Ignore error code.
00139                                 );
00140 
00141     if ( !pa_simple_ )
00142     {
00143         ROS_ERROR ( "Error initializing PulseAudio!" );
00144     }
00145 }
00146 
00147 void FestivalSynthesizer::run()
00148 {
00149     ros::NodeHandle node_handle;
00150     subscriber_ = node_handle.subscribe( "robot_face/text_out", 1, &FestivalSynthesizer::callbackSynth, this );
00151 
00152     // Subscribe to ROS message
00153     talking_finished_publisher_ = node_handle.advertise<std_msgs::String>( "robot_face/talking_finished", 1 );
00154 
00155     while( ros::ok() )
00156     {
00157        ros::getGlobalCallbackQueue()->callOne( ros::WallDuration( 3 ) );
00158 
00159         if( synth_speech_ )
00160         {
00161             synthSpeech( text_for_synth_ );
00162             festival_wait_for_spooler();
00163 
00164             std_msgs::String msg;
00165 
00166             std::stringstream string_stream;
00167             string_stream << "talking_finished";
00168             msg.data = string_stream.str();
00169 
00170             talking_finished_publisher_.publish( msg );
00171 
00172             synth_speech_ = false;
00173         }
00174     }
00175 }
00176 
00177 void FestivalSynthesizer::callbackSynth( const std_msgs::String::ConstPtr& msg )
00178 {
00179     text_for_synth_ = prepareText( msg->data );
00180     if( text_for_synth_.length() > 0 )
00181     {
00182         synth_speech_ = true;
00183     }
00184 }
00185 
00186 void FestivalSynthesizer::synthSpeech ( std::string text )
00187 {
00188     if ( pa_simple_ )
00189     {
00190         EST_Wave wave;
00191         festival_text_to_wave( text.c_str(), wave );
00192         int error;
00193         pa_simple_write( pa_simple_, &(wave.a(0)), wave.length()*2, &error );
00194     }
00195     else
00196     {
00197         ROS_ERROR( "Cannot snyth speec. Initilization failed." );
00198     }
00199 }
00200 
00201 std::string FestivalSynthesizer::prepareText( std::string text )
00202 {
00203     std::string tmp_text = text;
00204 
00205     tmp_text = trimSpaces( tmp_text );
00206     tmp_text = clearSmileys( tmp_text );
00207 
00208     size_t i_symbol = std::string::npos;
00209 
00210     for( unsigned int j = 0; j < punctuation_characters_.size(); j++ )
00211     {
00212     for( unsigned int i = 0; i < tmp_text.length(); i++ )
00213     {
00214             i_symbol = tmp_text.find( punctuation_characters_.at( j ), 0 );
00215             if( i_symbol != std::string::npos )
00216             {
00217                 tmp_text.erase(i_symbol, punctuation_characters_.at( j ).length());
00218             }
00219         }
00220     }
00221     tmp_text = trimSpaces( tmp_text );
00222 
00223     return tmp_text;
00224 }
00225 
00226 std::string FestivalSynthesizer::clearSmileys( std::string text )
00227 {
00228     std::string tmp_text = text;
00229 
00230     size_t i_smiley = std::string::npos;
00231 
00232     for( unsigned int j = 0; j < smileys_.size(); j++ )
00233     {
00234     for( unsigned int i = 0; i < tmp_text.length(); i++ )
00235     {
00236             i_smiley = tmp_text.find( smileys_.at( j ), 0 );
00237             if( i_smiley != std::string::npos )
00238             {
00239                 tmp_text.erase(i_smiley, smileys_.at( j ).length());
00240             }
00241         }
00242     }
00243 
00244     return tmp_text;
00245 }
00246 
00247 std::string FestivalSynthesizer::trimSpaces( std::string text )
00248 {
00249     std::string tmp_text = text;
00250 
00251     size_t startpos = tmp_text.find_first_not_of(" \t");
00252     size_t endpos = tmp_text.find_last_not_of(" \t");
00253 
00254     if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
00255     {
00256         tmp_text = "";
00257     }
00258     else
00259     {
00260         tmp_text = tmp_text.substr( startpos, endpos-startpos+1 );
00261     }
00262 
00263     return tmp_text;
00264 }
00265 
00266 int main( int argc, char *argv[] )
00267 {
00268     ros::init(argc, argv, "FestivalSynthesizer");
00269     FestivalSynthesizer fs;
00270     fs.run();
00271 
00272     return 0;
00273 }


robot_face
Author(s): AGAS, Julian Giesen, David Gossow
autogenerated on Mon Jan 6 2014 11:41:09