synth_thread.cpp

00001 
00002 /***************************************************************************
00003  *  synth_thread.cpp - Festival synthesis thread
00004  *
00005  *  Created: Tue Oct 28 14:34:14 2008
00006  *  Copyright  2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include "synth_thread.h"
00024 
00025 #include <interfaces/SpeechSynthInterface.h>
00026 #include <utils/time/wait.h>
00027 
00028 #include <festival/festival.h>
00029 
00030 using namespace fawkes;
00031 
00032 /** @class FestivalSynthThread "synth_thread.h"
00033  * Festival Synthesis Thread.
00034  * This thread synthesises audio for text-to-speech using Festival.
00035  * @author Tim Niemueller
00036  */
00037 
00038 
00039 /** Constructor. */
00040 FestivalSynthThread::FestivalSynthThread()
00041   : Thread("FestivalSynthThread", Thread::OPMODE_WAITFORWAKEUP),
00042     BlackBoardInterfaceListener("FestivalSynthThread")
00043 {
00044 }
00045 
00046 
00047 void
00048 FestivalSynthThread::init()
00049 {
00050   try {
00051     __cfg_voice = config->get_string("/plugins/festival/voice");
00052   } catch (Exception &e) {
00053     __cfg_voice = "";
00054   }
00055   try {
00056     __cfg_extra_code = config->get_string("/plugins/festival/extra_code");
00057   } catch (Exception &e) {
00058     __cfg_extra_code = "";
00059   }
00060 
00061   __speechsynth_if = blackboard->open_for_writing<SpeechSynthInterface>("Festival");
00062 
00063   bbil_add_message_interface(__speechsynth_if);
00064   blackboard->register_listener(this, BlackBoard::BBIL_FLAG_MESSAGES);
00065 
00066 }
00067 
00068 
00069 void FestivalSynthThread::once()
00070 {
00071   festival_initialize(/* load init files */ 1, FESTIVAL_HEAP_SIZE);
00072   if (__cfg_voice != "") {
00073     std::string voice_cmd = "(voice_" + __cfg_voice + ")";
00074     if (! festival_eval_command(voice_cmd.c_str())) {
00075       logger->log_error(name(), "Failed to load voice %s", __cfg_voice.c_str());
00076     }
00077   }
00078 
00079   if (__cfg_extra_code != "") {
00080     logger->log_debug(name(), "Executing extra code '%s'", __cfg_extra_code.c_str());
00081     if (! festival_eval_command(__cfg_extra_code.c_str())) {
00082       logger->log_error(name(), "Failed to execute extra code '%s'", __cfg_extra_code.c_str());
00083     }
00084   }
00085 
00086   say("Festival speech synth loaded");
00087 }
00088 
00089 void
00090 FestivalSynthThread::finalize()
00091 {
00092   festival_tidy_up();
00093   blackboard->unregister_listener(this);
00094   blackboard->close(__speechsynth_if);
00095 }
00096 
00097 void
00098 FestivalSynthThread::loop()
00099 {
00100   // wait for message(s) to arrive, could take a (little) while after the wakeup
00101   while ( __speechsynth_if->msgq_empty() ) {
00102     usleep(100);
00103   }
00104 
00105   // process messages, blocking
00106   if ( ! __speechsynth_if->msgq_empty() ) {
00107     if ( __speechsynth_if->msgq_first_is<SpeechSynthInterface::SayMessage>() ) {
00108       SpeechSynthInterface::SayMessage *msg = __speechsynth_if->msgq_first<SpeechSynthInterface::SayMessage>();
00109       __speechsynth_if->set_msgid(msg->id());
00110       say(msg->text());
00111     }
00112 
00113     __speechsynth_if->msgq_pop();
00114   }
00115 }
00116 
00117 
00118 bool
00119 FestivalSynthThread::bb_interface_message_received(Interface *interface,
00120                                                 Message *message) throw()
00121 {
00122   wakeup();
00123   return true;
00124 }
00125 
00126 
00127 /** Say something.
00128  * @param text text to synthesize and speak.
00129  */
00130 void
00131 FestivalSynthThread::say(const char *text)
00132 {
00133   EST_Wave wave;
00134   festival_text_to_wave(text, wave);
00135 
00136   float duration = (float)wave.num_samples() / (float)wave.sample_rate();
00137 
00138   __speechsynth_if->set_text(text);
00139   __speechsynth_if->set_final(false);
00140   __speechsynth_if->set_duration(duration);
00141   __speechsynth_if->write();
00142 
00143   Time start;
00144   clock->get_systime(start);
00145 
00146   EST_Option al;
00147   play_wave(wave, al);
00148 
00149   // compensate for data in buffer that still needs to be player, should be
00150   // replaced with a call that actually determines the size of the buffer...
00151   Time now;
00152   clock->get_systime(now);
00153   float remaining = duration - (now - &start);
00154   if (remaining > 0) {
00155     Time waittime(remaining);
00156     waittime.wait_systime();
00157   }
00158 
00159   __speechsynth_if->set_final(true);
00160   __speechsynth_if->write();
00161 }

Generated on Tue Feb 22 13:31:28 2011 for Fawkes API by  doxygen 1.4.7