Fawkes API
Fawkes Development Version
|
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 }