} synth_data_t;
-static void stream_event_cb(espeak_t *e, srs_voice_event_t *event,
+static void stream_event_cb(pulse_t *p, srs_voice_event_t *event,
void *user_data)
{
- MRP_UNUSED(user_data);
+ espeak_t *e = (espeak_t *)user_data;
+
+ MRP_UNUSED(p);
e->voice.notify(event, e->voice.notify_data);
}
static int espeak_synth_cb(short *samples, int nsample, espeak_EVENT *events)
{
synth_data_t *data = events->user_data;
- espeak_EVENT *evt;
-
- if (samples == NULL) {
- if (data->samples != NULL) {
- int fd = open("espeak.data", O_CREAT|O_WRONLY, 0644);
-
- if (fd >= 0) {
- write(fd, data->samples, 2 * data->nsample);
- close(fd);
- }
- }
+ if (samples == NULL)
return ESPEAK_CONTINUE;
- }
-
- mrp_debug("got %d new samples from espeak", nsample);
if (mrp_realloc(data->samples, 2 * (data->nsample + nsample)) == NULL)
return ESPEAK_ABORT;
if (0 <= actor && actor <= e->nactor) {
if (espeak_SetVoiceByName(e->actors[actor].name) != EE_OK) {
- mrp_log_error("Failed to activate espeak voice #%d ('%s').",
+ mrp_log_error("espeak: failed to activate espeak voice #%d ('%s').",
actor, e->actors[actor].name);
return SRS_VOICE_INVALID;
}
}
else {
- mrp_log_error("Invalid espeak voice #%d requested.", actor);
+ mrp_log_error("espeak: invalid espeak voice #%d requested.", actor);
return SRS_VOICE_INVALID;
}
espeak_setpitch(opitch);
if (r != EE_OK || data.samples == NULL) {
- mrp_log_error("Failed to synthesize message with espeak.");
+ mrp_log_error("espeak: failed to synthesize message with espeak.");
return SRS_VOICE_INVALID;
}
- id = pulse_play_stream(e, data.samples, e->config.rate, 1, data.nsample,
- tags, notify_events, stream_event_cb, NULL);
+ id = pulse_play_stream(e->pulse, data.samples, e->config.rate, 1,
+ data.nsample, tags, notify_events,
+ stream_event_cb, e);
if (id == SRS_VOICE_INVALID)
mrp_free(data.samples);
{
espeak_t *e = (espeak_t *)api_data;
- pulse_stop_stream(e, id, FALSE, FALSE);
+ pulse_stop_stream(e->pulse, id, FALSE, FALSE);
}
rate = espeak_Initialize(out, blen, path, 0);
if (rate <= 0) {
- mrp_log_error("Failed to initialize espeak.");
+ mrp_log_error("espeak: failed to initialize espeak.");
return FALSE;
}
- mrp_log_info("espeak chose %d Hz for sample rate.", rate);
+ mrp_log_info("espeak: chose %d Hz for sample rate.", rate);
e->config.rate = rate;
int nvoice, i;
int nactor;
- if (pulse_setup(e) != 0)
+ if ((e->pulse = pulse_setup(e->srs->pa, "espeak")) == NULL)
return FALSE;
voices = (espeak_VOICE **)espeak_ListVoices(NULL);
if (voices == NULL) {
- mrp_log_error("Could not find any espeak voices.");
+ mrp_log_error("espeak: could not find any voices.");
return FALSE;
}
if ((e->actors = mrp_allocz_array(typeof(*e->actors), nvoice)) == NULL)
goto fail;
- mrp_log_info("Available espeak voices:");
+ mrp_log_info("espeak: found available voices:");
for (i = 0; i < nvoice; i++) {
v = voices[i];
mrp_free(e->actors[i].description);
}
- pulse_cleanup(e);
+ pulse_cleanup(e->pulse);
mrp_free(e);
}
SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
- create_espeak, config_espeak,
- start_espeak, stop_espeak,
+ create_espeak, config_espeak, start_espeak, stop_espeak,
destroy_espeak)
#define SPEECH "speech"
#define TTS "text-to-speech"
-typedef struct {
- espeak_t *e; /* espeak voice context */
+struct pulse_s {
pa_mainloop_api *pa; /* PA mainloop API */
+ char *name; /* PA context name */
pa_context *pc; /* PA context */
uint32_t strmid; /* next stream id */
mrp_list_hook_t streams; /* active streams */
int connected; /* whether connection is up */
- mrp_timer_t *reconn; /* reconnect timer */
-} pulse_t;
+ pa_time_event *reconn; /* reconnect timer */
+};
typedef struct {
static void stream_notify(stream_t *s, srs_voice_event_type_t event);
-int pulse_setup(espeak_t *e)
+pulse_t *pulse_setup(pa_mainloop_api *pa, const char *name)
{
pulse_t *p;
if ((p = mrp_allocz(sizeof(*p))) == NULL)
- return -1;
+ return NULL;
mrp_list_init(&p->streams);
- p->e = e;
- p->pa = e->srs->pa;
- p->pc = pa_context_new(p->pa, "festival");
+ p->pa = pa;
+ p->name = name ? mrp_strdup(name) : mrp_strdup("Winthorpe");
+ p->pc = pa_context_new(p->pa, p->name);
if (p->pc == NULL) {
mrp_free(p);
- return -1;
+ return NULL;
}
- e->pulse = p;
p->strmid = 1;
pa_context_set_state_callback(p->pc, context_state_cb, p);
pa_context_set_subscribe_callback(p->pc, context_event_cb, p);
pa_context_connect(p->pc, NULL, PA_CONTEXT_NOFAIL, NULL);
- return 0;
+ return p;
}
-void pulse_cleanup(espeak_t *e)
+void pulse_cleanup(pulse_t *p)
{
- pulse_t *p = (pulse_t *)e->pulse;
-
if (p->pc != NULL) {
pa_context_disconnect(p->pc);
p->pc = NULL;
+ mrp_free(p->name);
+ p->name = NULL;
+ mrp_free(p);
}
}
}
-uint32_t pulse_play_stream(espeak_t *e, void *sample_buf, int sample_rate,
+uint32_t pulse_play_stream(pulse_t *p, void *sample_buf, int sample_rate,
int nchannel, uint32_t nsample, char **tags,
int event_mask, pulse_stream_cb_t cb,
void *user_data)
{
- pulse_t *p = (pulse_t *)e->pulse;
char **t;
stream_t *s;
pa_sample_spec ss;
}
-int pulse_stop_stream(espeak_t *e, uint32_t id, int drain, int notify)
+int pulse_stop_stream(pulse_t *p, uint32_t id, int drain, int notify)
{
- pulse_t *p = (pulse_t *)e->pulse;
mrp_list_hook_t *sp, *sn;
stream_t *se, *s;
}
-static void connect_timer_cb(mrp_timer_t *t, void *user_data)
+static void connect_timer_cb(pa_mainloop_api *api, pa_time_event *e,
+ const struct timeval *tv, void *user_data)
{
pulse_t *p = (pulse_t *)user_data;
p->pc = NULL;
}
- p->pc = pa_context_new(p->pa, "festival");
+ p->pc = pa_context_new(p->pa, p->name);
pa_context_set_state_callback(p->pc, context_state_cb, p);
pa_context_set_subscribe_callback(p->pc, context_event_cb, p);
pa_context_connect(p->pc, NULL, PA_CONTEXT_NOFAIL, NULL);
+ p->pa->time_free(p->reconn);
p->reconn = NULL;
- mrp_del_timer(t);
}
static void stop_reconnect(pulse_t *p)
{
if (p->reconn != NULL) {
- mrp_del_timer(p->reconn);
+ p->pa->time_free(p->reconn);
p->reconn = NULL;
}
}
static void start_reconnect(pulse_t *p)
{
+ struct timeval tv;
+
stop_reconnect(p);
- p->reconn = mrp_add_timer(p->e->srs->ml, 5000, connect_timer_cb, p);
+ pa_timeval_add(pa_gettimeofday(&tv), 5000);
+
+ p->reconn = p->pa->time_new(p->pa, &tv, connect_timer_cb, p);
}
switch (pa_context_get_state(pc)) {
case PA_CONTEXT_CONNECTING:
- mrp_debug("PA connection: being established...");
+ mrp_debug("pulse: connection being established...");
p->connected = FALSE;
stop_reconnect(p);
break;
case PA_CONTEXT_AUTHORIZING:
- mrp_debug("PA connection: being authenticated...");
+ mrp_debug("pulse: connection being authenticated...");
p->connected = FALSE;
break;
case PA_CONTEXT_SETTING_NAME:
- mrp_debug("PA connection: setting name...");
+ mrp_debug("pulse: setting connection name...");
p->connected = FALSE;
break;
case PA_CONTEXT_READY:
- mrp_log_info("festival: PA connection up and ready");
+ mrp_log_info("pulse: connection up and ready");
p->connected = TRUE;
break;
case PA_CONTEXT_TERMINATED:
- mrp_log_info("festival: PA connection terminated");
+ mrp_log_info("pulse: connection terminated");
p->connected = FALSE;
start_reconnect(p);
break;
case PA_CONTEXT_FAILED:
- mrp_log_error("festival: PA connetion failed");
+ mrp_log_error("pulse: connetion failed");
default:
p->connected = FALSE;
start_reconnect(p);
}
stream_ref(s);
- s->cb(s->p->e, &e, s->user_data);
+ s->cb(s->p, &e, s->user_data);
stream_unref(s);
}
switch ((sst = pa_stream_get_state(s->s))) {
case PA_STREAM_CREATING:
- mrp_debug("stream #%u being created", s->id);
+ mrp_debug("pulse: stream #%u being created", s->id);
break;
case PA_STREAM_READY:
- mrp_debug("stream #%u ready", s->id);
+ mrp_debug("pulse: stream #%u ready", s->id);
stream_notify(s, PULSE_STREAM_STARTED);
break;
case PA_STREAM_TERMINATED:
case PA_STREAM_FAILED:
default:
- mrp_debug("stream #%u state %d", s->id, sst);
+ mrp_debug("pulse: stream #%u state %d", s->id, sst);
pa_stream_disconnect(s->s);
pa_stream_set_state_callback(s->s, NULL, NULL);
static void stream_drain(stream_t *s)
{
if (s->drain == NULL) {
- mrp_debug("stream #%u done, draining", s->id);
+ mrp_debug("pulse: stream #%u done, draining", s->id);
stream_ref(s);
s->drain = pa_stream_drain(s->s, stream_drain_cb, s);
}
{
stream_t *s = (stream_t *)user_data;
- mrp_debug("stream #%u drained %s", s->id,
+ mrp_debug("pulse: stream #%u drained %s", s->id,
success ? "successfully" : "failed");
pa_operation_unref(s->drain);
if (pa_stream_write(s->s, s->buf + s->offs, size, NULL, 0,
PA_SEEK_RELATIVE) < 0) {
- mrp_log_error("festival: failed to write %zd bytes", size);
+ mrp_log_error("pulse: failed to write %zd bytes to stream #%u",
+ size, s->id);
goto out;
}
else {
-#ifndef __SRS_FESTIVAL_PULSE_H__
-#define __SRS_FESTIVAL_PULSE_H__
+#ifndef __SRS_ESPEAK_PULSE_H__
+#define __SRS_ESPEAK_PULSE_H__
#include <stdint.h>
#include <murphy/common/macros.h>
#include <pulse/pulseaudio.h>
-#include "espeak-voice.h"
+#include "srs/daemon/voice.h"
MRP_CDECL_BEGIN
#define PULSE_MASK_ALL (PULSE_MASK_STARTED | PULSE_MASK_PROGRESS | \
PULSE_MASK_COMPLETED | PULSE_MASK_ABORTED)
+typedef struct pulse_s pulse_t;
+
typedef srs_voice_event_t pulse_stream_event_t;
-typedef void (*pulse_stream_cb_t)(espeak_t *e, pulse_stream_event_t *event,
+typedef void (*pulse_stream_cb_t)(pulse_t *p, pulse_stream_event_t *event,
void *user_data);
/** Set up the PulseAudio interface. */
-int pulse_setup(espeak_t *e);
+pulse_t *pulse_setup(pa_mainloop_api *pa, const char *name);
/** Clean up the audio interface. */
-void pulse_cleanup(espeak_t *e);
+void pulse_cleanup(pulse_t *p);
/** Render an stream (a buffer of audio samples). */
-uint32_t pulse_play_stream(espeak_t *e, void *sample_buf, int sample_rate,
+uint32_t pulse_play_stream(pulse_t *p, void *sample_buf, int sample_rate,
int nchannel, uint32_t nsample, char **tags,
int event_mask, pulse_stream_cb_t cb,
void *user_data);
/** Stop an ongoing stream. */
-int pulse_stop_stream(espeak_t *e, uint32_t id, int drain, int notify);
+int pulse_stop_stream(pulse_t *p, uint32_t id, int drain, int notify);
MRP_CDECL_END
-#endif /* __SRS_FESTIVAL_PULSE_H__ */
+#endif /* __SRS_ESPEAK_PULSE_H__ */