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 <alsa/asoundlib.h>
26#include <interfaces/SpeechSynthInterface.h>
27#include <utils/time/wait.h>
28
29#include <cmath>
30
31using namespace fawkes;
32
33extern "C" {
34extern cst_voice *register_cmu_us_kal(const char *voxdir);
35extern void unregister_cmu_us_kal(cst_voice *voice);
36}
37
38/** @class FliteSynthThread "synth_thread.h"
39 * Flite Synthesis Thread.
40 * This thread synthesises audio for text-to-speech using Flite.
41 * @author Tim Niemueller
42 */
43
44/** Constructor. */
46: Thread("FliteSynthThread", Thread::OPMODE_WAITFORWAKEUP),
47 BlackBoardInterfaceListener("FliteSynthThread")
48{
49}
50
51void
53{
54 speechsynth_if_ = blackboard->open_for_writing<SpeechSynthInterface>("Flite");
55 voice_ = register_cmu_us_kal(NULL);
56
57 cfg_soundcard_ = config->get_string("/flite/soundcard");
58
59 bbil_add_message_interface(speechsynth_if_);
61
62 say("Speech synth loaded");
63}
64
65void
67{
68 unregister_cmu_us_kal(voice_);
70 blackboard->close(speechsynth_if_);
71}
72
73void
75{
76 // wait for message(s) to arrive, could take a (little) while after the wakeup
77 while (speechsynth_if_->msgq_empty()) {
78 usleep(100);
79 }
80
81 // process message, blocking
82 // only one at a time, loop() will be run as many times as wakeup() was called
83 if (!speechsynth_if_->msgq_empty()) {
84 if (speechsynth_if_->msgq_first_is<SpeechSynthInterface::SayMessage>()) {
87 speechsynth_if_->set_msgid(msg->id());
88 say(msg->text());
89 }
90
91 speechsynth_if_->msgq_pop();
92 }
93}
94
95bool
97{
98 wakeup();
99 return true;
100}
101
102/** Say something.
103 * @param text text to synthesize and speak.
104 */
105void
106FliteSynthThread::say(const char *text)
107{
108 cst_wave *wave = flite_text_to_wave(text, voice_);
109 cst_wave_save_riff(wave, "/tmp/test.wav");
110
111 speechsynth_if_->set_text(text);
112 speechsynth_if_->set_final(false);
113 speechsynth_if_->set_duration(get_duration(wave));
114 speechsynth_if_->write();
115
116 play_wave(wave);
117 delete_wave(wave);
118
119 speechsynth_if_->set_final(true);
120 speechsynth_if_->write();
121}
122
123float
124FliteSynthThread::get_duration(cst_wave *wave)
125{
126 return (float)cst_wave_num_samples(wave) / (float)cst_wave_sample_rate(wave);
127}
128
129/** Play a Flite wave to the default ALSA audio out.
130 * @param wave the wave form to play
131 */
132void
133FliteSynthThread::play_wave(cst_wave *wave)
134{
135 snd_pcm_t *pcm;
136 float duration = get_duration(wave);
137 int err;
138 if ((err = snd_pcm_open(&pcm, cfg_soundcard_.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
139 throw Exception("Failed to open PCM: %s", snd_strerror(err));
140 }
141 snd_pcm_nonblock(pcm, 0);
142 if ((err = snd_pcm_set_params(pcm,
143 SND_PCM_FORMAT_S16_LE,
144 SND_PCM_ACCESS_RW_INTERLEAVED,
145 cst_wave_num_channels(wave),
146 cst_wave_sample_rate(wave),
147 1,
148 (unsigned int)roundf(duration * 1000000.)))
149 < 0) {
150 throw Exception("Playback to set params: %s", snd_strerror(err));
151 }
152
153 snd_pcm_sframes_t frames;
154 frames = snd_pcm_writei(pcm, cst_wave_samples(wave), cst_wave_num_samples(wave));
155 if (frames < 0) {
156 logger->log_warn(name(), "snd_pcm_writei failed (frames < 0)");
157 frames = snd_pcm_recover(pcm, frames, 0);
158 }
159 if (frames < 0) {
160 logger->log_warn(name(), "snd_pcm_writei failed: %s", snd_strerror(err));
161 } else if (frames < (long)cst_wave_num_samples(wave)) {
163 "Short write (expected %li, wrote %li)",
164 (long)cst_wave_num_samples(wave),
165 frames);
166 }
167
168 TimeWait::wait_systime((unsigned int)roundf(duration * 1000000.f));
169 snd_pcm_close(pcm);
170}
void say(const char *text)
Say something.
FliteSynthThread()
Constructor.
virtual void init()
Initialize the thread.
virtual bool bb_interface_message_received(fawkes::Interface *interface, fawkes::Message *message) noexcept
BlackBoard message received notification.
virtual void loop()
Code to execute in the thread.
virtual void finalize()
Finalize the thread.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
BlackBoard interface listener.
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
virtual void unregister_listener(BlackBoardInterfaceListener *listener)
Unregister BB interface listener.
Definition: blackboard.cpp:212
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual void register_listener(BlackBoardInterfaceListener *listener, ListenerRegisterFlag flag=BBIL_FLAG_ALL)
Register BB event listener.
Definition: blackboard.cpp:185
virtual void close(Interface *interface)=0
Close interface.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
bool msgq_first_is()
Check if first message has desired type.
Definition: interface.h:351
void msgq_pop()
Erase first message from queue.
Definition: interface.cpp:1215
Message * msgq_first()
Get the first message from the message queue.
Definition: interface.cpp:1200
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:501
bool msgq_empty()
Check if queue is empty.
Definition: interface.cpp:1062
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
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:181
SayMessage Fawkes BlackBoard Interface Message.
SpeechSynthInterface Fawkes BlackBoard Interface.
void set_duration(const float new_duration)
Set duration value.
void set_text(const char *new_text)
Set text value.
void set_msgid(const uint32_t new_msgid)
Set msgid value.
void set_final(const bool new_final)
Set final value.
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
Fawkes library namespace.