Fawkes API  Fawkes Development Version
synth_thread.cpp
1 
2 /***************************************************************************
3  * synth_thread.cpp - Flite synthesis thread
4  *
5  * Created: Tue Oct 28 14:34:14 2008
6  * Copyright 2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "synth_thread.h"
24 
25 #include <interfaces/SpeechSynthInterface.h>
26 #include <utils/time/wait.h>
27 #include <asoundlib.h>
28 #include <cmath>
29 
30 using namespace fawkes;
31 
32 extern "C" {
33  extern cst_voice *register_cmu_us_kal(const char *voxdir);
34  extern void unregister_cmu_us_kal(cst_voice *voice);
35 }
36 
37 /** @class FliteSynthThread "synth_thread.h"
38  * Flite Synthesis Thread.
39  * This thread synthesises audio for text-to-speech using Flite.
40  * @author Tim Niemueller
41  */
42 
43 
44 /** Constructor. */
46  : Thread("FliteSynthThread", Thread::OPMODE_WAITFORWAKEUP),
47  BlackBoardInterfaceListener("FliteSynthThread")
48 {
49 }
50 
51 
52 void
54 {
55  __speechsynth_if = blackboard->open_for_writing<SpeechSynthInterface>("Flite");
56  __voice = register_cmu_us_kal(NULL);
57 
58  __cfg_soundcard = config->get_string("/flite/soundcard");
59 
60  bbil_add_message_interface(__speechsynth_if);
62 
63  say("Speech synth loaded");
64 }
65 
66 
67 void
69 {
70  unregister_cmu_us_kal(__voice);
72  blackboard->close(__speechsynth_if);
73 }
74 
75 void
77 {
78  // wait for message(s) to arrive, could take a (little) while after the wakeup
79  while ( __speechsynth_if->msgq_empty() ) {
80  usleep(100);
81  }
82 
83  // process message, blocking
84  // only one at a time, loop() will be run as many times as wakeup() was called
85  if ( ! __speechsynth_if->msgq_empty() ) {
86  if ( __speechsynth_if->msgq_first_is<SpeechSynthInterface::SayMessage>() ) {
88  __speechsynth_if->set_msgid(msg->id());
89  say(msg->text());
90  }
91 
92  __speechsynth_if->msgq_pop();
93  }
94 }
95 
96 
97 bool
99  Message *message) throw()
100 {
101  wakeup();
102  return true;
103 }
104 
105 
106 /** Say something.
107  * @param text text to synthesize and speak.
108  */
109 void
110 FliteSynthThread::say(const char *text)
111 {
112  cst_wave *wave = flite_text_to_wave(text, __voice);
113  cst_wave_save_riff(wave, "/tmp/test.wav");
114 
115  __speechsynth_if->set_text(text);
116  __speechsynth_if->set_final(false);
117  __speechsynth_if->set_duration(get_duration(wave));
118  __speechsynth_if->write();
119 
120  play_wave(wave);
121  delete_wave(wave);
122 
123  __speechsynth_if->set_final(true);
124  __speechsynth_if->write();
125 }
126 
127 
128 float
129 FliteSynthThread::get_duration(cst_wave *wave)
130 {
131  return (float)cst_wave_num_samples(wave) / (float)cst_wave_sample_rate(wave);
132 }
133 
134 
135 /** Play a Flite wave to the default ALSA audio out.
136  * @param wave the wave form to play
137  */
138 void
139 FliteSynthThread::play_wave(cst_wave *wave)
140 {
141  snd_pcm_t *pcm;
142  float duration = get_duration(wave);
143  int err;
144  if ((err = snd_pcm_open(&pcm, __cfg_soundcard.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
145  throw Exception("Failed to open PCM: %s", snd_strerror(err));
146  }
147  snd_pcm_nonblock(pcm, 0);
148  if ((err = snd_pcm_set_params(pcm,
149  SND_PCM_FORMAT_S16_LE,
150  SND_PCM_ACCESS_RW_INTERLEAVED,
151  cst_wave_num_channels(wave),
152  cst_wave_sample_rate(wave),
153  1,
154  (unsigned int)roundf(duration * 1000000.))) < 0) {
155  throw Exception("Playback to set params: %s", snd_strerror(err));
156  }
157 
158  snd_pcm_sframes_t frames;
159  frames = snd_pcm_writei(pcm, cst_wave_samples(wave), cst_wave_num_samples(wave));
160  if (frames < 0) {
161  logger->log_warn(name(), "snd_pcm_writei failed (frames < 0)");
162  frames = snd_pcm_recover(pcm, frames, 0);
163  }
164  if (frames < 0) {
165  logger->log_warn(name(), "snd_pcm_writei failed: %s", snd_strerror(err));
166  } else if ( frames < (long)cst_wave_num_samples(wave)) {
167  logger->log_warn(name(), "Short write (expected %li, wrote %li)",
168  (long)cst_wave_num_samples(wave), frames);
169  }
170 
171  TimeWait::wait_systime((unsigned int)roundf(duration * 1000000.f));
172  snd_pcm_close(pcm);
173 }
void set_duration(const float new_duration)
Set duration value.
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:44
unsigned int id() const
Get message ID.
Definition: message.cpp:197
void set_final(const bool new_final)
Set final value.
bool msgq_empty()
Check if queue is empty.
Definition: interface.cpp:1048
virtual bool bb_interface_message_received(fawkes::Interface *interface, fawkes::Message *message)
BlackBoard message received notification.
Fawkes library namespace.
virtual void init()
Initialize the thread.
FliteSynthThread()
Constructor.
SayMessage Fawkes BlackBoard Interface Message.
virtual void unregister_listener(BlackBoardInterfaceListener *listener)
Unregister BB interface listener.
Definition: blackboard.cpp:218
Thread class encapsulation of pthreads.
Definition: thread.h:42
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:500
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:79
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:44
void set_msgid(const uint32_t new_msgid)
Set msgid value.
virtual void register_listener(BlackBoardInterfaceListener *listener, ListenerRegisterFlag flag=BBIL_FLAG_ALL)
Register BB event listener.
Definition: blackboard.cpp:190
void msgq_pop()
Erase first message from queue.
Definition: interface.cpp:1193
void wakeup()
Wake up thread.
Definition: thread.cpp:1000
Base class for exceptions in Fawkes.
Definition: exception.h:36
void set_text(const char *new_text)
Set text value.
Message * msgq_first()
Get the first message from the message queue.
Definition: interface.cpp:1180
virtual void finalize()
Finalize the thread.
const char * name() const
Get name of thread.
Definition: thread.h:95
bool msgq_first_is()
Check if first message has desired type.
Definition: interface.h:314
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
void say(const char *text)
Say something.
virtual void loop()
Code to execute in the thread.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:44
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
BlackBoard interface listener.
SpeechSynthInterface Fawkes BlackBoard Interface.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
virtual void close(Interface *interface)=0
Close interface.