2 * Copyright (c) 2012-2014, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
34 #include <murphy/common/debug.h>
35 #include <murphy/common/log.h>
36 #include <murphy/common/mm.h>
37 #include <murphy/common/mainloop.h>
39 #include <espeak/speak_lib.h>
41 #include "srs/daemon/plugin.h"
42 #include "srs/daemon/voice.h"
43 #include "srs/daemon/iso-6391.h"
44 #include "srs/daemon/pulse.h"
46 #include "espeak-voice.h"
48 #define PLUGIN_NAME "espeak-voice"
49 #define PLUGIN_DESCR "An espeak-based voice synthesizer plugin for SRS."
50 #define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
51 #define PLUGIN_VERSION "0.0.1"
53 #define CONFIG_VOICEDIR "espeak.voicedir"
55 #define ESPEAK_CONTINUE 0
56 #define ESPEAK_ABORT 1
64 static void stream_event_cb(srs_pulse_t *p, srs_voice_event_t *event,
67 espeak_t *e = (espeak_t *)user_data;
71 e->voice.notify(event, e->voice.notify_data);
75 static int espeak_synth_cb(short *samples, int nsample, espeak_EVENT *events)
77 synth_data_t *data = events->user_data;
80 return ESPEAK_CONTINUE;
82 if (mrp_realloc(data->samples, 2 * (data->nsample + nsample)) == NULL)
85 memcpy(data->samples + 2 * data->nsample, samples, 2 * nsample);
86 data->nsample += nsample;
88 return ESPEAK_CONTINUE;
92 static int espeak_setrate(double drate)
94 int min, max, step, rate, orig;
96 if (0.0 < drate && drate <= 2.0) {
98 rate = espeakRATE_NORMAL;
99 else if (drate < 1.0) {
100 min = espeakRATE_MINIMUM;
101 max = espeakRATE_NORMAL;
102 step = (max - min) / 1.0;
103 rate = (int)(min + drate * step);
105 else { /*drate > 1.0*/
106 min = espeakRATE_NORMAL;
107 max = espeakRATE_MAXIMUM;
108 step = (max - min) / 1.0;
109 rate = (int)(min + (drate - 1.0) * step);
112 orig = espeak_GetParameter(espeakRATE, 1);
113 espeak_SetParameter(espeakRATE, rate, 0);
122 static int espeak_setpitch(double dpitch)
124 int min, max, step, pitch, orig;
126 if (0.0 < dpitch && dpitch <= 2.0) {
127 pitch = (int)(50 * dpitch);
128 orig = espeak_GetParameter(espeakPITCH, 1);
129 espeak_SetParameter(espeakPITCH, pitch, 0);
138 static uint32_t espeak_render(const char *msg, char **tags, int actor,
139 double rate, double pitch, int notify_events,
142 espeak_t *e = (espeak_t *)api_data;
143 int size, start, end, type, orate, opitch;
144 unsigned int flags, uid;
152 if (0 <= actor && actor <= e->nactor) {
153 if (espeak_SetVoiceByName(e->actors[actor].name) != EE_OK) {
154 mrp_log_error("espeak: failed to activate espeak voice #%d ('%s').",
155 actor, e->actors[actor].name);
156 return SRS_VOICE_INVALID;
160 mrp_log_error("espeak: invalid espeak voice #%d requested.", actor);
161 return SRS_VOICE_INVALID;
165 type = POS_CHARACTER;
168 flags = espeakCHARS_UTF8;
170 data = (synth_data_t) { NULL, 0 };
172 orate = espeak_setrate(rate);
173 opitch = espeak_setpitch(pitch);
175 r = espeak_Synth(msg, size, start, type, end, flags, &uid, &data);
177 espeak_setrate(orate);
178 espeak_setpitch(opitch);
180 if (r != EE_OK || data.samples == NULL) {
181 mrp_log_error("espeak: failed to synthesize message with espeak.");
182 return SRS_VOICE_INVALID;
185 id = srs_play_stream(e->srs->pulse, data.samples, e->config.rate, 1,
186 data.nsample, tags, notify_events,
189 if (id == SRS_VOICE_INVALID)
190 mrp_free(data.samples);
196 static void espeak_cancel(uint32_t id, void *api_data)
198 espeak_t *e = (espeak_t *)api_data;
200 srs_stop_stream(e->srs->pulse, id, FALSE, FALSE);
204 static int create_espeak(srs_plugin_t *plugin)
208 mrp_debug("creating espeak voice plugin");
210 e = mrp_allocz(sizeof(*e));
214 e->srs = plugin->srs;
216 plugin->plugin_data = e;
225 static int config_espeak(srs_plugin_t *plugin, srs_cfg_t *cfg)
227 espeak_t *e = (espeak_t *)plugin->plugin_data;
231 mrp_debug("configure espeak voice plugin");
233 e->config.voicedir = srs_get_string_config(cfg, CONFIG_VOICEDIR, NULL);
235 out = AUDIO_OUTPUT_SYNCHRONOUS;
236 path = e->config.voicedir;
239 rate = espeak_Initialize(out, blen, path, 0);
242 mrp_log_error("espeak: failed to initialize espeak.");
246 mrp_log_info("espeak: chose %d Hz for sample rate.", rate);
248 e->config.rate = rate;
250 espeak_SetSynthCallback(espeak_synth_cb);
251 /*espeak_SetParameter(espeakRATE, espeakRATE_NORMAL, 0);
252 espeak_SetParameter(espeakPITCH, 50, 0);*/
258 static const char *espeak_parse_dialect(const char *lang, const char **dialect)
260 const char *dial, *l, *c, *d;
265 d = strchr(lang, '-');
267 if (d == NULL || d - c > 3)
271 strncpy(code_buf, c, n);
277 mrp_debug("parsed '%s' into code '%s', dialect '%s'", lang, c, d ? d : "-");
279 l = srs_iso6391_language(c);
286 if (d != NULL && strcmp(c, d))
287 dial = srs_iso6391_dialect(d);
297 static inline int espeak_gender(int gender)
300 case 1: return SRS_VOICE_GENDER_MALE;
301 case 2: return SRS_VOICE_GENDER_FEMALE;
302 default: return SRS_VOICE_GENDER_MALE;
307 static inline char *espeak_description(espeak_VOICE *v)
309 static char descr[256];
311 snprintf(descr, sizeof(descr), "espeak %s voice (%s).", v->languages,
312 v->identifier ? v->identifier : "-");
318 static int start_espeak(srs_plugin_t *plugin)
320 static srs_voice_api_t api = {
321 .render = espeak_render,
322 .cancel = espeak_cancel
325 espeak_t *e = (espeak_t *)plugin->plugin_data;
326 espeak_VOICE **voices, *v;
328 const char *lang, *language, *dialect;
331 if (e->srs->pulse == NULL)
334 voices = (espeak_VOICE **)espeak_ListVoices(NULL);
336 if (voices == NULL) {
337 mrp_log_error("espeak: could not find any voices.");
341 for (nvoice = 0; voices[nvoice] != NULL; nvoice++)
344 if ((e->actors = mrp_allocz_array(typeof(*e->actors), nvoice)) == NULL)
347 mrp_log_info("espeak: found %d available voices.", nvoice);
349 for (i = 0; i < nvoice; i++) {
352 mrp_log_info(" %s (%s)", v->name, v->identifier);
354 for (lang = v->languages + 1; *lang; lang += strlen(lang)) {
355 mrp_log_info(" %s (priority %d)", lang, lang[-1]);
357 language = espeak_parse_dialect(lang, &dialect);
359 e->actors[i].name = mrp_strdup(v->name);
360 e->actors[i].lang = mrp_strdup(language);
361 e->actors[i].dialect = mrp_strdup(dialect);
362 e->actors[i].gender = espeak_gender(v->gender);
363 e->actors[i].description = mrp_strdup(espeak_description(v));
365 if (e->actors[i].name == NULL || e->actors[i].lang == NULL)
372 if (srs_register_voice(e->self->srs, "espeak", &api, e,
373 e->actors, e->nactor,
374 &e->voice.notify, &e->voice.notify_data) == 0)
378 for (i = 0; i < e->nactor; i++) {
379 mrp_free(e->actors[i].name);
380 mrp_free(e->actors[i].lang);
381 mrp_free(e->actors[i].description);
388 static void stop_espeak(srs_plugin_t *plugin)
394 static void destroy_espeak(srs_plugin_t *plugin)
396 espeak_t *e = (espeak_t *)plugin->plugin_data;
399 srs_unregister_voice(e->self->srs, "espeak");
402 for (i = 0; i < e->nactor; i++) {
403 mrp_free(e->actors[i].name);
404 mrp_free(e->actors[i].lang);
405 mrp_free(e->actors[i].description);
412 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
413 create_espeak, config_espeak, start_espeak, stop_espeak,