synth_thread.cpp

00001 
00002 /***************************************************************************
00003  *  synth_thread.cpp - Flite 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 #include <asoundlib.h>
00028 #include <cmath>
00029 
00030 using namespace fawkes;
00031 
00032 extern "C" {
00033   extern cst_voice *register_cmu_us_kal(const char *voxdir);
00034   extern void       unregister_cmu_us_kal(cst_voice *voice);
00035 }
00036 
00037 /** @class FliteSynthThread "synth_thread.h"
00038  * Flite Synthesis Thread.
00039  * This thread synthesises audio for text-to-speech using Flite.
00040  * @author Tim Niemueller
00041  */
00042 
00043 
00044 /** Constructor. */
00045 FliteSynthThread::FliteSynthThread()
00046   : Thread("FliteSynthThread", Thread::OPMODE_WAITFORWAKEUP),
00047     BlackBoardInterfaceListener("FliteSynthThread")
00048 {
00049 }
00050 
00051 
00052 void
00053 FliteSynthThread::init()
00054 {
00055   __speechsynth_if = blackboard->open_for_writing<SpeechSynthInterface>("Flite");
00056   __voice = register_cmu_us_kal(NULL);
00057 
00058   bbil_add_message_interface(__speechsynth_if);
00059   blackboard->register_listener(this, BlackBoard::BBIL_FLAG_MESSAGES);
00060 
00061   say("Speech synth loaded");
00062 }
00063 
00064 
00065 void
00066 FliteSynthThread::finalize()
00067 {
00068   unregister_cmu_us_kal(__voice);
00069   blackboard->unregister_listener(this);
00070   blackboard->close(__speechsynth_if);
00071 }
00072 
00073 void
00074 FliteSynthThread::loop()
00075 {
00076   // wait for message(s) to arrive, could take a (little) while after the wakeup
00077   while ( __speechsynth_if->msgq_empty() ) {
00078     usleep(100);
00079   }
00080 
00081   // process message, blocking
00082   // only one at a time, loop() will be run as many times as wakeup() was called
00083   if ( ! __speechsynth_if->msgq_empty() ) {
00084     if ( __speechsynth_if->msgq_first_is<SpeechSynthInterface::SayMessage>() ) {
00085       SpeechSynthInterface::SayMessage *msg = __speechsynth_if->msgq_first<SpeechSynthInterface::SayMessage>();
00086       __speechsynth_if->set_msgid(msg->id());
00087       say(msg->text());
00088     }
00089 
00090     __speechsynth_if->msgq_pop();
00091   }
00092 }
00093 
00094 
00095 bool
00096 FliteSynthThread::bb_interface_message_received(Interface *interface,
00097                                                 Message *message) throw()
00098 {
00099   wakeup();
00100   return true;
00101 }
00102 
00103 
00104 /** Say something.
00105  * @param text text to synthesize and speak.
00106  */
00107 void
00108 FliteSynthThread::say(const char *text)
00109 {
00110   cst_wave *wave = flite_text_to_wave(text, __voice);
00111   cst_wave_save_riff(wave, "/tmp/test.wav");
00112 
00113   __speechsynth_if->set_text(text);
00114   __speechsynth_if->set_final(false);
00115   __speechsynth_if->set_duration(get_duration(wave));
00116   __speechsynth_if->write();
00117 
00118   play_wave(wave);
00119   delete_wave(wave);
00120 
00121   __speechsynth_if->set_final(true);
00122   __speechsynth_if->write();
00123 }
00124 
00125 
00126 float
00127 FliteSynthThread::get_duration(cst_wave *wave)
00128 {
00129   return (float)cst_wave_num_samples(wave) / (float)cst_wave_sample_rate(wave);
00130 }
00131 
00132 
00133 /** Play a Flite wave to the default ALSA audio out.
00134  * @param wave the wave form to play
00135  */
00136 void
00137 FliteSynthThread::play_wave(cst_wave *wave)
00138 {
00139   snd_pcm_t *pcm;
00140   float duration = get_duration(wave);
00141   int err;
00142   if ((err = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
00143     throw Exception("Failed to open PCM: %s", snd_strerror(err));
00144   }
00145   snd_pcm_nonblock(pcm, 0);
00146   if ((err = snd_pcm_set_params(pcm,
00147                                     SND_PCM_FORMAT_S16_LE,
00148                                     SND_PCM_ACCESS_RW_INTERLEAVED,
00149                                     cst_wave_num_channels(wave),
00150                                     cst_wave_sample_rate(wave),
00151                                     1,
00152                                     (unsigned int)roundf(duration * 1000000.))) < 0) {
00153     throw Exception("Playback to set params: %s", snd_strerror(err));
00154   }
00155 
00156   snd_pcm_sframes_t frames;
00157   frames = snd_pcm_writei(pcm, cst_wave_samples(wave), cst_wave_num_samples(wave));
00158   if (frames < 0) {
00159     logger->log_warn(name(), "snd_pcm_writei failed (frames < 0)");
00160     frames = snd_pcm_recover(pcm, frames, 0);
00161   }
00162   if (frames < 0) {
00163     logger->log_warn(name(), "snd_pcm_writei failed: %s", snd_strerror(err));
00164   } else if ( frames < (long)cst_wave_num_samples(wave)) {
00165     logger->log_warn(name(), "Short write (expected %li, wrote %li)",
00166                      (long)cst_wave_num_samples(wave), frames);
00167   }
00168 
00169   TimeWait::wait_systime((unsigned int)roundf(duration * 1000000.f));
00170   snd_pcm_close(pcm);
00171 }

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