From b9d1de963a78a203c02df5c5502e28cefaae8a1e Mon Sep 17 00:00:00 2001 From: Kaj-Michael Lang Date: Thu, 9 Aug 2007 12:53:35 +0300 Subject: [PATCH] Add speach output using espeak+gstreamer. If not enabled, then fallback to using the old fork-flite/espeak mode. --- configure.ac | 5 +- src/Makefile.am | 5 +- src/mapper.c | 9 ++ src/speak.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++ src/speak.h | 12 +++ src/utils.c | 42 --------- 6 files changed, 256 insertions(+), 46 deletions(-) create mode 100644 src/speak.c create mode 100644 src/speak.h diff --git a/configure.ac b/configure.ac index 75da136..d6f9248 100644 --- a/configure.ac +++ b/configure.ac @@ -151,6 +151,7 @@ AC_SUBST(SQLITE_CFLAGS) PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10, HAVE_GST=yes, HAVE_GST=no) if test "x$HAVE_GST" = "xyes"; then AC_DEFINE(WITH_GST, 1, [build with Gstreamer]) + GSTREAMER_LIBS="$GSTREAMER_LIBS -lgstapp-0.10" AC_SUBST(GSTREAMER_LIBS) AC_SUBST(GSTREAMER_CFLAGS) fi @@ -171,9 +172,9 @@ AC_CHECK_HEADER(espeak/speak_lib.h) ESPEAK_LIBS="-lespeak" ESPEAK_CFLAGS="-I/usr/include/espeak" AC_CHECK_LIB([espeak], [espeak_Initialize], - AC_DEFINE(HAVE_ESPEAK, 1, [Defined if you have espeak]) + AC_DEFINE(WITH_ESPEAK, 1, [Defined if you have espeak]) AC_SUBST(ESPEAK_CFLAGS) - AC_SUBST(ESPEAK_LIB) + AC_SUBST(ESPEAK_LIBS) ) # Localisation diff --git a/src/Makefile.am b/src/Makefile.am index 7095dee..f97e8e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,11 +21,11 @@ bin_PROGRAMS = mapper osm2db mapper_CFLAGS = $(GLIBGTK_CFLAGS) $(OSSO_CFLAGS) $(HILDON_CFLAGS) $(HILDONFM_CFLAGS) \ - $(GNOME_VFS_CFLAGS) $(GCONF_CFLAGS) $(LIBXML2_CFLAGS) $(GSTREAMER_CFLAGS) \ + $(GNOME_VFS_CFLAGS) $(GCONF_CFLAGS) $(LIBXML2_CFLAGS) $(GSTREAMER_CFLAGS) $(ESPEAK_CFLAGS) \ $(SQLITE_CFLAGS) $(LIBCURL_CFLAGS) $(DBUS_GLIB_CFLAGS) $(BLUEZ_CFLAGS) mapper_LDADD = $(GLIBGTK_LIBS) $(OSSO_LIBS) $(HILDON_LIBS) $(HILDONFM_LIBS) \ - $(GNOME_VFS_LIBS) $(GCONF_LIBS) $(LIBXML2_LIBS) $(GSTREAMER_LIBS) \ + $(GNOME_VFS_LIBS) $(GCONF_LIBS) $(LIBXML2_LIBS) $(GSTREAMER_LIBS) $(ESPEAK_LIBS) \ $(SQLITE_LIBS) $(LIBCURL_LIBS) $(DBUS_GLIB_LIBS) $(BLUEZ_LIBS) -lm mapper_SOURCES = utils.c \ @@ -55,6 +55,7 @@ mapper_SOURCES = utils.c \ hildon-wrappers.c \ ui-common.c \ config-gconf.c \ + speak.c \ mapper.c osm2db_CFLAGS = $(EXPAT_CFLAGS) $(SQLITE_CFLAGS) $(GLIBGTK_CFLAGS) diff --git a/src/mapper.c b/src/mapper.c index 77210ef..47fc90e 100644 --- a/src/mapper.c +++ b/src/mapper.c @@ -36,7 +36,9 @@ #include #include #include +#ifdef WITH_GST #include +#endif #include #include #include @@ -60,6 +62,7 @@ #include "ui-common.h" #include "db.h" #include "cb.h" +#include "speak.h" gfloat UNITS_CONVERT[] = {1.85200,1.15077945,1.f,}; @@ -205,7 +208,10 @@ gint main(gint argc, gchar * argv[]) return 1; #endif set_var_defaults(); +#ifdef WITH_GST gst_init(&argc, &argv); + speak_init(); +#endif gtk_init(&argc, &argv); gconf_init(argc, argv, NULL); gnome_vfs_init(); @@ -219,6 +225,9 @@ gint main(gint argc, gchar * argv[]) #endif gtk_main(); mapper_destroy(); +#ifdef WITH_GST + speak_deinit(); +#endif #ifdef WITH_OSSO osso_deinitialize(_osso); #endif diff --git a/src/speak.c b/src/speak.c new file mode 100644 index 0000000..11d1bad --- /dev/null +++ b/src/speak.c @@ -0,0 +1,229 @@ +#include "config.h" + +#if defined (WITH_GST) && defined (WITH_ESPEAK) + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WITH_DEVICE_770 +#define AUDIO_SINK "autoaudiosink" +#else +#define AUDIO_SINK "dsppcmsink" +#endif + +#include "speak.h" + +typedef struct _gst_espeak gst_espeak; +struct _gst_espeak { + GstCaps *srccaps; + GstElement *pipeline; + GstElement *src; + GstElement *queue; + GstElement *caps; + GstElement *conv; + GstElement *sink; + gboolean done; + gshort *buffer; + gint size; +}; +static gst_espeak ge; + +static gint erate; +static GstBus *bus; +static gboolean speaking=FALSE; + +static gboolean +bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ +gchar *debug; +GError *err; + +switch (GST_MESSAGE_TYPE (msg)) { + case GST_MESSAGE_EOS: + g_print ("EOS\n"); + speaking=FALSE; + break; + case GST_MESSAGE_ERROR: + gst_message_parse_error (msg, &err, &debug); + g_free (debug); + + g_printf ("Error: %s\n", err->message); + g_error_free (err); + + speaking=FALSE; + break; + case GST_MESSAGE_STATE_CHANGED: + + break; + default: + g_print("HUH: %s\n", gst_message_type_get_name(GST_MESSAGE_TYPE(msg))); + break; + } +return TRUE; +} + +static gboolean +speak_create_pipeline(void) +{ +ge.pipeline=gst_pipeline_new("pipeline"); +ge.src=gst_element_factory_make("appsrc", "source"); +ge.caps=gst_element_factory_make("capsfilter", "caps"); +ge.srccaps=gst_caps_new_simple ("audio/x-raw-int", + "depth", G_TYPE_INT, 16, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, 16, + "rate", G_TYPE_INT, erate, + "channels", G_TYPE_INT, 1, + NULL); + +ge.conv=gst_element_factory_make("audioconverter", "converter"); +ge.queue=gst_element_factory_make("queue", "queue"); +ge.sink=gst_element_factory_make(AUDIO_SINK, "sink"); + +g_object_set(ge.caps, "caps", ge.srccaps, NULL); + +gst_bin_add_many (GST_BIN(ge.pipeline), ge.src, ge.queue, ge.sink, NULL); + +if (!gst_element_link_filtered(ge.src, ge.queue, ge.srccaps)) { + g_warning ("Failed to link elements 1!"); + return FALSE; +} + +if (!gst_element_link_filtered(ge.queue, ge.sink, ge.srccaps)) { + g_warning ("Failed to link elements 2!"); + return FALSE; +} + +return TRUE; +} + +static void +espeak_buffer_free(void *p) +{ +g_free(p); +} + +static int +espeak_cb(short *wav, int numsamples, espeak_EVENT *events) +{ +GstBuffer *buf; +gchar *data; + +g_print("Adding buffer %d\n", numsamples); + +if (wav==NULL) { + gst_app_src_end_of_stream (GST_APP_SRC (ge.src)); + return 0; +} else if (numsamples>0) { + numsamples=numsamples*2; + data=g_memdup(wav, numsamples); + buf=gst_app_buffer_new (data, numsamples, espeak_buffer_free, data); + gst_buffer_set_caps(buf, ge.srccaps); + gst_app_src_push_buffer (GST_APP_SRC (ge.src), buf); +} + +return 0; +} + +gboolean +speak_init(void) +{ +erate=espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, 1000, NULL, 0); +if (erate==-1) + return FALSE; + +espeak_SetSynthCallback(espeak_cb); +espeak_SetVoiceByName("en"); +espeak_SetParameter(espeakRATE,140,0); +espeak_SetParameter(espeakVOLUME,100,0); +if (speak_create_pipeline()==FALSE) + return FALSE; + +bus = gst_pipeline_get_bus (GST_PIPELINE (ge.pipeline)); +gst_bus_add_watch (bus, bus_call, NULL); + +return TRUE; +} + +void +speak_deinit(void) +{ +gst_element_set_state (ge.pipeline, GST_STATE_NULL); +gst_object_unref (ge.pipeline); +ge.pipeline=NULL; +speaking=FALSE; +} + +gboolean +speak_text(gchar *text) +{ +#if 0 +if (speaking==TRUE) + return FALSE; +#endif +g_printf("Speaking: %s (%d)\n", text, strlen(text)); +espeak_Synth(text, strlen(text)+1, 0, POS_CHARACTER, 0, espeakCHARS_8BIT, NULL, NULL); + +if (gst_element_set_state (ge.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_print("Failed to play\n"); + speaking=FALSE; + return FALSE; +} +speaking=TRUE; +g_print("Playing...\n"); +return TRUE; +} + +gboolean +speak_speaking(void) +{ +return speaking; +} + +gboolean +speak_stop(void) +{ +if (gst_element_set_state(ge.pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) + return FALSE; +return TRUE; +} + +#else + +gboolean speak_init(void) {return TRUE;} +void speak_deinit(void) {} +gboolean speak_stop(void) {return TRUE;} +gboolean speak_speaking(void) {return FALSE;} + +gboolean +speak_text(gchar *text) +{ +#ifdef WITH_ESPEAK +#define _voice_synth_path "/usr/bin/espeak" +#else +#define _voice_synth_path "/usr/bin/flite" +#endif + +if (!fork()) { + /* We are the fork child. Synthesize the voice. */ + sound_noise(); + sleep(1); + printf("%s %s\n", _voice_synth_path, phrase); +#ifdef WITH_ESPEAK + execl(_voice_synth_path, _voice_synth_path, "-t", text, (char *)NULL); +#else + execl(_voice_synth_path, _voice_synth_path, "-s", "150", text, (char *)NULL); +#endif + exit(0); +} + +return TRUE; +} + +#endif diff --git a/src/speak.h b/src/speak.h new file mode 100644 index 0000000..7bb4b46 --- /dev/null +++ b/src/speak.h @@ -0,0 +1,12 @@ +#ifndef _SPEAK_H +#define _SPEAK_H + +#include + +gboolean speak_init(void); +void speak_deinit(void); +gboolean speak_stop(void); +gboolean speak_speaking(void); +gboolean speak_text(gchar *text); + +#endif diff --git a/src/utils.c b/src/utils.c index 3f3ee3e..0c3f263 100644 --- a/src/utils.c +++ b/src/utils.c @@ -33,48 +33,6 @@ gdk_beep(); #endif } -void sound_speak(gchar * phrase) -{ -#ifdef WITH_ESPEAK -#define _voice_synth_path "/usr/bin/espeak" -#else -#define _voice_synth_path "/usr/bin/flite" -#endif - -if (!fork()) { - /* We are the fork child. Synthesize the voice. */ - sound_noise(); - sleep(1); - printf("%s %s\n", _voice_synth_path, phrase); - execl(_voice_synth_path, _voice_synth_path, "-t", phrase, (char *)NULL); - exit(0); -} - -} - -#if 0 -void latlon2unit(gdouble lat, gdouble lon, gint *unitx_, gint *unity_) -{ - gdouble tmp; - - gint unitx = &unitx_; - gint unity = &unity_; - - unitx = (lon + 180.f) * (WORLD_SIZE_UNITS / 360.f) + 0.5f; - tmp = sinf(lat * (PI / 180.f)); - unity = 0.5f + (WORLD_SIZE_UNITS / MERCATOR_SPAN) - * (logf((1.f + tmp) / (1.f - tmp)) * 0.5f - MERCATOR_TOP); -} - -void unit2latlon(gint unitx, gint unity, gdouble *lat, gdouble *lon) -{ - (lon) = ((unitx) * (360.f / WORLD_SIZE_UNITS)) - 180.f; - (lat) = (360.f * (atanf(expf(((unity) - * (MERCATOR_SPAN / WORLD_SIZE_UNITS)) - + MERCATOR_TOP)))) * (1.f / PI) - 90.f; -} -#endif - void deg_format(DegFormat degformat, gdouble coor, gchar * scoor, gchar neg_char, gchar pos_char) -- 2.39.5