]> err.no Git - mapper/blob - src/speak.c
Fixes to gstreamer element and caps handlings.
[mapper] / src / speak.c
1 /*
2  * This file is part of mapper
3  *
4  * Copyright (C) 2007 Kaj-Michael Lang
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <glib.h>
25 #include <glib/gstdio.h>
26
27 #if defined (WITH_GST) && defined (WITH_ESPEAK)
28
29 #include <gst/gst.h>
30 #include <gst/app/gstappsrc.h>
31 #include <gst/app/gstappbuffer.h>
32 #include <gst/app/gstappsink.h>
33 #include <espeak/speak_lib.h>
34
35 #include "audio.h"
36 #include "speak.h"
37
38 typedef struct _gst_espeak gst_espeak;
39 struct _gst_espeak {
40         GstCaps *srccaps;
41         GstElement *pipeline;
42         GstElement *src;
43         GstElement *queue;
44         GstElement *sink;
45         gboolean done;
46         gshort  *buffer;
47         gint size;
48 };
49 static gst_espeak ge;
50
51 static gint erate;
52 static GstBus *bus;
53 static gboolean speaking=FALSE;
54 static gboolean speak_ok;
55 static gboolean espeak_ok;
56
57 static gboolean
58 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
59 {
60 gchar *debug;
61 GError *err;
62
63 switch (GST_MESSAGE_TYPE (msg)) {
64         case GST_MESSAGE_EOS:
65         g_print ("EOS\n");
66                 speaking=FALSE;
67                 speak_ok=TRUE;
68                 speak_stop();
69         break;
70         case GST_MESSAGE_ERROR:
71                 gst_message_parse_error (msg, &err, &debug);
72                 g_free (debug);
73
74                 g_debug("Error: %s", err->message);
75                 g_error_free (err);
76
77                 speak_stop();
78                 speaking=FALSE;
79                 speak_ok=FALSE;
80         break;
81         case GST_MESSAGE_STATE_CHANGED:
82         g_debug("GST: state changed");
83         break;
84     default:
85                 g_debug("GST: %s", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
86         break;
87         }
88 return TRUE;
89 }
90
91 static gboolean
92 speak_create_pipeline(void)
93 {
94 ge.pipeline=gst_pipeline_new("pipeline");
95 g_assert(ge.pipeline);
96
97 ge.src=gst_element_factory_make("appsrc", "source");
98 g_assert(ge.src);
99
100 ge.srccaps=gst_caps_new_simple ("audio/x-raw-int",
101                         "depth", G_TYPE_INT, 16,
102                         "signed", G_TYPE_BOOLEAN, TRUE, 
103                         "width", G_TYPE_INT,  16,
104                         "rate", G_TYPE_INT, erate,
105                         "channels", G_TYPE_INT, 1,
106                         NULL);
107 g_assert(ge.srccaps);
108
109 ge.queue=gst_element_factory_make("queue", "queue");
110 g_assert(ge.queue);
111
112 ge.sink=gst_element_factory_make(AUDIO_SINK, "sink");
113 g_assert(ge.sink);
114
115 gst_bin_add_many (GST_BIN(ge.pipeline), ge.src, ge.queue, ge.sink, NULL);
116
117 if (!gst_element_link_filtered(ge.src, ge.queue, ge.srccaps)) {
118         g_warning ("Failed to link source to queue with caps.");
119         return FALSE;
120 }
121 gst_caps_unref(ge.srccaps);
122
123 if (!gst_element_link(ge.queue, ge.sink)) {
124         g_warning ("Failed to link queue to sink.");
125         return FALSE;
126 }
127
128 return TRUE;
129 }
130
131 static void
132 espeak_buffer_free(void *p)
133 {
134 g_free(p);
135 }
136
137 static int 
138 espeak_cb(short *wav, int numsamples, espeak_EVENT *events)
139 {
140 GstBuffer *buf;
141 gchar *data;
142
143 g_print("Adding buffer %d\n", numsamples);
144
145 if (wav==NULL) {
146         gst_app_src_end_of_stream(GST_APP_SRC (ge.src));
147         return 0;
148 } else if (numsamples>0) {
149         numsamples=numsamples*2;
150         data=g_memdup(wav, numsamples);
151         buf=gst_app_buffer_new(data, numsamples, espeak_buffer_free, data);
152         gst_buffer_set_caps(buf, ge.srccaps);
153         gst_app_src_push_buffer(GST_APP_SRC (ge.src), buf);
154 }
155
156 return 0;
157 }
158
159 void
160 speak_set_parameters(guint speed, guint pitch)
161 {
162 espeak_SetParameter(espeakRATE, speed, 0);
163 espeak_SetParameter(espeakPITCH, pitch, 0);
164 }
165
166 gboolean
167 speak_init(gchar *voice, guint speed, guint pitch)
168 {
169 espeak_ok=FALSE;
170 erate=espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, 100, NULL, 0);
171 if (erate==-1)
172         return FALSE;
173
174 espeak_SetSynthCallback(espeak_cb);
175 espeak_SetVoiceByName(voice);
176 speak_set_parameters(speed, pitch);
177 espeak_SetParameter(espeakVOLUME, 100, 0);
178 if (speak_create_pipeline()==FALSE)
179         return FALSE;
180
181 bus=gst_pipeline_get_bus(GST_PIPELINE (ge.pipeline));
182 g_assert(bus);
183 gst_bus_add_watch(bus, bus_call, NULL);
184
185 espeak_ok=TRUE;
186 return TRUE;
187 }
188
189 void
190 speak_deinit(void)
191 {
192 if (espeak_ok==FALSE)
193         return;
194
195 gst_element_set_state(ge.pipeline, GST_STATE_NULL);
196 gst_object_unref(ge.pipeline);
197 ge.pipeline=NULL;
198 speaking=FALSE;
199 espeak_Terminate();
200 }
201
202 gboolean
203 speak_text(gchar *text)
204 {
205 #if 0
206 if (speaking==TRUE)
207         return FALSE;
208 #endif
209
210 g_debug("Speaking: %s (%d)\n", text, strlen(text));
211 espeak_Synth(text, strlen(text)+1, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL, NULL);
212
213 if (speaking!=TRUE) {
214         if (gst_element_set_state (ge.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
215                 g_print("Failed to play\n");
216                 speaking=FALSE;
217                 return FALSE;
218         }
219         speaking=TRUE;
220 }
221 g_print("Playing...\n");
222 return TRUE;
223 }
224
225 gboolean
226 speak_speaking(void)
227 {
228 return speaking;
229 }
230
231 gboolean
232 speak_stop(void)
233 {
234 if (gst_element_set_state(ge.pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
235     return FALSE;
236 return TRUE;
237 }
238
239 #else
240
241 gboolean speak_init(gchar voice, guint speed, guint pitch) {return TRUE;}
242 void speak_deinit(void) {}
243 gboolean speak_stop(void) {return TRUE;}
244 gboolean speak_speaking(void) {return FALSE;}
245 void speak_set_parameters(guint speed, guint pitch) {}
246
247 gboolean 
248 speak_text(gchar *text)
249 {
250 #ifdef WITH_ESPEAK
251 #define _voice_synth_path "/usr/bin/espeak"
252 #else
253 #define _voice_synth_path "/usr/bin/flite"
254 #endif
255
256 if (!fork()) {
257         /* We are the fork child.  Synthesize the voice. */
258         sound_noise();
259         sleep(1);
260         g_debug("%s %s\n", _voice_synth_path, text);
261 #ifdef WITH_ESPEAK
262         execl(_voice_synth_path, _voice_synth_path, "-t", text, (char *)NULL);
263 #else
264         execl(_voice_synth_path, _voice_synth_path, "-s", "150", text, (char *)NULL);
265 #endif
266         exit(0);
267 }
268
269 return TRUE;
270 }
271
272 #endif