Port festival plugin to GStreamer-0.10 (#461377).
authorJonathan Matthew <jonathan@kaolin.wh9.net>
Thu, 23 Aug 2007 13:24:33 +0000 (13:24 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Thu, 23 Aug 2007 13:24:33 +0000 (13:24 +0000)
Original commit message from CVS:
Patch by: Jonathan Matthew  <jonathan at kaolin wh9 net>
* configure.ac:
* gst/festival/Makefile.am:
* gst/festival/gstfestival.c:
Port festival plugin to GStreamer-0.10 (#461377).

ChangeLog
configure.ac
gst/festival/Makefile.am
gst/festival/gstfestival.c

index 45aceb6..ea37b23 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-08-22  Tim-Philipp Müller  <tim at centricular dot net>
+
+       Patch by: Jonathan Matthew  <jonathan at kaolin wh9 net>
+
+       * configure.ac:
+       * gst/festival/Makefile.am:
+       * gst/festival/gstfestival.c:
+         Port festival plugin to GStreamer-0.10 (#461377).
+
 2007-08-22  Wim Taymans  <wim.taymans@gmail.com>
 
        * ext/faad/gstfaad.c: (gst_faad_chain):
index 1005371..1982616 100644 (file)
@@ -84,6 +84,7 @@ GST_PLUGINS_ALL="\
   cdxaparse \
   deinterlace \
   equalizer \
+  festival \
   filter \
   flv \
   freeze \
@@ -160,6 +161,13 @@ AC_SUBST(LIBM)
 
 dnl *** checks for header files ***
 
+AC_CHECK_HEADERS([unistd.h], HAVE_UNISTD_H=yes)
+AM_CONDITIONAL(HAVE_UNISTD_H, test "x$HAVE_UNISTD_H" = "xyes")
+
+if test "x$HAVE_UNISTD_H" != "xyes"; then
+  GST_PLUGINS_SELECTED=`echo $GST_PLUGINS_SELECTED | $SED -e s/festival//`
+fi
+
 dnl used by ext/dts
 AX_CREATE_STDINT_H
 
@@ -982,6 +990,7 @@ gst/bayer/Makefile
 gst/cdxaparse/Makefile
 gst/deinterlace/Makefile
 gst/equalizer/Makefile
+gst/festival/Makefile
 gst/filter/Makefile
 gst/flv/Makefile
 gst/freeze/Makefile
index d785fbe..4599442 100644 (file)
@@ -2,10 +2,10 @@
 plugin_LTLIBRARIES = libgstfestival.la
 
 libgstfestival_la_SOURCES = gstfestival.c 
-libgstfestival_la_CFLAGS = $(GST_CFLAGS)
+libgstfestival_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
 # we use net connection now... perhaps use the lib later
 # libgstfestival_la_LIBADD = $(FESTIVAL_LIBS)
-libgstfestival_la_LIBADD =
+libgstfestival_la_LIBADD = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstaudio-0.10
 libgstfestival_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS = gstfestival.h 
index f63e0b7..0d8395c 100644 (file)
 /*                                                                       */
 /*=======================================================================*/
 
+/**
+ * SECTION:element-festival
+ * 
+ * <refsect2>
+ * <para>
+ * This element connects to a
+ * <ulink url="http://www.festvox.org/festival/index.html">festival</ulink> server 
+ * process and uses it to synthesize speech.
+ * </para>
+ * <title>Example pipeline</title>
+ * <programlisting>
+ * echo "hi" | gst-launch fdsrc fd=0 ! festival ! wavparse ! audioconvert ! alsasink
+ * </programlisting>
+ * </refsect2>
+ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include "gstfestival.h"
 #include <gst/audio/audio.h>
 
+GST_DEBUG_CATEGORY_STATIC (festival_debug);
+#define GST_CAT_DEFAULT festival_debug
+
 static void gst_festival_base_init (gpointer g_class);
 static void gst_festival_class_init (GstFestivalClass * klass);
 static void gst_festival_init (GstFestival * festival);
 
-static void gst_festival_chain (GstPad * pad, GstData * _data);
+static GstFlowReturn gst_festival_chain (GstPad * pad, GstBuffer * buf);
 static GstStateChangeReturn gst_festival_change_state (GstElement * element,
     GstStateChange transition);
 
@@ -97,21 +117,17 @@ GST_ELEMENT_DETAILS ("Festival Text-to-Speech synthesizer",
     "Wim Taymans <wim.taymans@chello.be>");
 
 static GstStaticPadTemplate sink_template_factory =
-GST_STATIC_PAD_TEMPLATE ("festival_sink",
+GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("text/plain")
     );
 
 static GstStaticPadTemplate src_template_factory =
-GST_STATIC_PAD_TEMPLATE ("festival_src",
+GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-raw-int, "
-        "endianness = (int) BYTE_ORDER, "
-        "signed = (boolean) TRUE, "
-        "width = (int) 16, "
-        "depth = (int) 16, " "rate = (int) 16000, " "channels = (int) 1")
+    GST_STATIC_CAPS ("audio/x-wav")
     );
 
 /* Festival signals and args */
@@ -161,7 +177,7 @@ gst_festival_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  /* register src pads */
+  /* register pads */
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_template_factory));
   gst_element_class_add_pad_template (element_class,
@@ -197,73 +213,106 @@ gst_festival_init (GstFestival * festival)
   festival->info = festival_default_info ();
 }
 
-static void
-gst_festival_chain (GstPad * pad, GstData * _data)
+static gboolean
+read_response (GstFestival * festival)
 {
-  GstBuffer *buf = GST_BUFFER (_data);
-  gchar *wavefile;
-  int filesize;
-  FILE *fd;
-  char *p;
   char ack[4];
+  char *data;
+  int filesize;
+  int fd;
   int n;
+  gboolean ret = TRUE;
+
+  fd = festival->info->server_fd;
+  do {
+    for (n = 0; n < 3;)
+      n += read (fd, ack + n, 3 - n);
+    ack[3] = '\0';
+    GST_DEBUG_OBJECT (festival, "got response %s", ack);
+
+    if (strcmp (ack, "WV\n") == 0) {
+      GstBuffer *buffer;
+
+      /* receive a waveform */
+      data = socket_receive_file_to_buff (fd, &filesize);
+      GST_DEBUG_OBJECT (festival, "received %d bytes of waveform data",
+          filesize);
+
+      /* push contents as a buffer */
+      buffer = gst_buffer_new ();
+      GST_BUFFER_SIZE (buffer) = (filesize);
+      GST_BUFFER_DATA (buffer) = (guint8 *) data;
+      GST_BUFFER_MALLOCDATA (buffer) = (guint8 *) data;
+      GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+
+      gst_pad_push (festival->srcpad, buffer);
+
+    } else if (strcmp (ack, "LP\n") == 0) {
+      /* receive an s-expr */
+      data = client_accept_s_expr (fd);
+      GST_DEBUG_OBJECT (festival, "received s-expression: %s", data);
+      g_free (data);
+    } else if (strcmp (ack, "ER\n") == 0) {
+      /* server got an error */
+      GST_ELEMENT_ERROR (festival,
+          LIBRARY,
+          FAILED,
+          ("Festival speech server returned an error"),
+          ("Make sure you have voices/languages installed"));
+      ret = FALSE;
+      break;
+    }
+
+  } while (strcmp (ack, "OK\n") != 0);
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_festival_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
   GstFestival *festival;
-  GstBuffer *outbuf;
-  glong size;
+  guint8 *p, *ep;
+  FILE *fd;
 
-  g_return_if_fail (pad != NULL);
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (buf != NULL);
-  g_return_if_fail (GST_BUFFER_DATA (buf) != NULL);
+  festival = GST_FESTIVAL (GST_PAD_PARENT (pad));
 
-  festival = GST_FESTIVAL (gst_pad_get_parent (pad));
-  GST_DEBUG ("gst_festival_chain: got buffer in '%s'",
-      gst_object_get_name (GST_OBJECT (festival)));
+  GST_LOG_OBJECT (festival, "Got text buffer, %u bytes", GST_BUFFER_SIZE (buf));
 
   fd = fdopen (dup (festival->info->server_fd), "wb");
 
-  size = GST_BUFFER_SIZE (buf);
-
   /* Copy text over to server, escaping any quotes */
   fprintf (fd, "(Parameter.set 'Audio_Required_Rate 16000)\n");
-  fprintf (fd, "(tts_textall \"\n");
-  for (p = GST_BUFFER_DATA (buf); p && (*p != '\0') && size; p++, size--) {
-    if ((*p == '"') || (*p == '\\'))
+  fflush (fd);
+  GST_DEBUG_OBJECT (festival, "issued Parameter.set command");
+  if (read_response (festival) == FALSE) {
+    ret = GST_FLOW_ERROR;
+    goto out;
+  }
+
+  fprintf (fd, "(tts_textall \"");
+  p = GST_BUFFER_DATA (buf);
+  ep = p + GST_BUFFER_SIZE (buf);
+  for (; p < ep && (*p != '\0'); p++) {
+    if ((*p == '"') || (*p == '\\')) {
       putc ('\\', fd);
+    }
+
     putc (*p, fd);
   }
   fprintf (fd, "\" \"%s\")\n", festival->info->text_mode);
   fclose (fd);
 
-  /* Read back info from server */
-  /* This assumes only one waveform will come back, also LP is unlikely */
-  wavefile = NULL;
-  do {
-    for (n = 0; n < 3;)
-      n += read (festival->info->server_fd, ack + n, 3 - n);
-    ack[3] = '\0';
-    if (strcmp (ack, "WV\n") == 0)      /* receive a waveform */
-      wavefile =
-          socket_receive_file_to_buff (festival->info->server_fd, &filesize);
-    else if (strcmp (ack, "LP\n") == 0) /* receive an s-expr */
-      client_accept_s_expr (festival->info->server_fd);
-    else if (strcmp (ack, "ER\n") == 0) {       /* server got an error */
-      fprintf (stderr, "festival_client: server returned error\n");
-      break;
-    }
-
-    if (wavefile) {
-      outbuf = gst_buffer_new ();
-      GST_BUFFER_DATA (outbuf) = wavefile;
-      GST_BUFFER_SIZE (outbuf) = filesize;
+  GST_DEBUG_OBJECT (festival, "issued tts_textall command");
 
-      gst_pad_push (festival->srcpad, GST_DATA (outbuf));
-
-      wavefile = NULL;
-    }
-  } while (strcmp (ack, "OK\n") != 0);
+  /* Read back info from server */
+  if (read_response (festival) == FALSE)
+    ret = GST_FLOW_ERROR;
 
+out:
   gst_buffer_unref (buf);
+  return ret;
 }
 
 static FT_Info *
@@ -317,7 +366,6 @@ festival_socket_open (const char *host, int port)
   return fd;
 }
 
-
 static char *
 client_accept_s_expr (int fd)
 {
@@ -345,17 +393,18 @@ socket_receive_file_to_buff (int fd, int *size)
   char c;
 
   bufflen = 1024;
-  buff = (char *) malloc (bufflen);
+  buff = (char *) g_malloc (bufflen);
   *size = 0;
 
   for (k = 0; file_stuff_key[k] != '\0';) {
     n = read (fd, &c, 1);
     if (n == 0)
       break;                    /* hit stream eof before end of file */
+
     if ((*size) + k + 1 >= bufflen) {
       /* +1 so you can add a NULL if you want */
       bufflen += bufflen / 4;
-      buff = (char *) realloc (buff, bufflen);
+      buff = (char *) g_realloc (buff, bufflen);
     }
     if (file_stuff_key[k] == c)
       k++;
@@ -433,6 +482,9 @@ gst_festival_change_state (GstElement * element, GstStateChange transition)
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
+  GST_DEBUG_CATEGORY_INIT (festival_debug, "festival",
+      0, "Festival text-to-speech synthesizer");
+
   if (!gst_element_register (plugin, "festival", GST_RANK_NONE,
           GST_TYPE_FESTIVAL))
     return FALSE;