]> err.no Git - mapper/blob - src/speak.c
Merge branch 'master' of ssh://git.tal.org/home/git/mapper
[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 *caps;
45         GstElement *sink;
46         gboolean done;
47         gshort  *buffer;
48         gint size;
49 };
50 static gst_espeak ge;
51
52 static gint erate;
53 static GstBus *bus;
54 static gboolean speaking=FALSE;
55 static gboolean speak_ok;
56 static gboolean espeak_ok;
57
58 static gboolean
59 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
60 {
61 gchar *debug;
62 GError *err;
63
64 switch (GST_MESSAGE_TYPE (msg)) {
65         case GST_MESSAGE_EOS:
66         g_print ("EOS\n");
67                 speaking=FALSE;
68                 speak_ok=TRUE;
69                 speak_stop();
70         break;
71         case GST_MESSAGE_ERROR:
72                 gst_message_parse_error (msg, &err, &debug);
73                 g_free (debug);
74
75                 g_printf ("Error: %s\n", err->message);
76                 g_error_free (err);
77
78                 speak_stop();
79                 speaking=FALSE;
80                 speak_ok=FALSE;
81         break;
82         case GST_MESSAGE_STATE_CHANGED:
83         g_print ("GST: state changed\n");
84         break;
85     default:
86                 g_printf("GST: %s\n", gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
87         break;
88         }
89 return TRUE;
90 }
91
92 static gboolean
93 speak_create_pipeline(void)
94 {
95 ge.pipeline=gst_pipeline_new("pipeline");
96 ge.src=gst_element_factory_make("appsrc", "source");
97 ge.caps=gst_element_factory_make("capsfilter", "caps");
98 ge.srccaps=gst_caps_new_simple ("audio/x-raw-int",
99                         "depth", G_TYPE_INT, 16,
100                         "signed", G_TYPE_BOOLEAN, TRUE, 
101                         "width", G_TYPE_INT,  16,
102                         "rate", G_TYPE_INT, erate,
103                         "channels", G_TYPE_INT, 1,
104                         NULL);
105
106 ge.queue=gst_element_factory_make("queue", "queue");
107 ge.sink=gst_element_factory_make(AUDIO_SINK, "sink");
108
109 g_assert(ge.pipeline);
110 g_assert(ge.src);
111 g_assert(ge.caps);
112 g_assert(ge.srccaps);
113 g_assert(ge.queue);
114 g_assert(ge.sink);
115
116 g_object_set(ge.caps, "caps", ge.srccaps, NULL);
117
118 gst_bin_add_many (GST_BIN(ge.pipeline), ge.src, ge.queue, ge.sink, NULL);
119
120 if (!gst_element_link_filtered(ge.src, ge.queue, ge.srccaps)) {
121         g_warning ("Failed to link elements 1!");
122         return FALSE;
123 }
124
125 if (!gst_element_link_filtered(ge.queue, ge.sink, ge.srccaps)) {
126         g_warning ("Failed to link elements 2!");
127         return FALSE;
128 }
129
130 return TRUE;
131 }
132
133 static void
134 espeak_buffer_free(void *p)
135 {
136 g_free(p);
137 }
138
139 static int 
140 espeak_cb(short *wav, int numsamples, espeak_EVENT *events)
141 {
142 GstBuffer *buf;
143 gchar *data;
144
145 g_print("Adding buffer %d\n", numsamples);
146
147 if (wav==NULL) {
148         gst_app_src_end_of_stream (GST_APP_SRC (ge.src));
149         return 0;
150 } else if (numsamples>0) {
151         numsamples=numsamples*2;
152         data=g_memdup(wav, numsamples);
153         buf=gst_app_buffer_new (data, numsamples, espeak_buffer_free, data);
154         gst_buffer_set_caps(buf, ge.srccaps);
155         gst_app_src_push_buffer(GST_APP_SRC (ge.src), buf);
156 }
157
158 return 0;
159 }
160
161 void
162 speak_set_parameters(guint speed, guint pitch)
163 {
164 espeak_SetParameter(espeakRATE, speed, 0);
165 espeak_SetParameter(espeakPITCH, pitch, 0);
166 }
167
168 gboolean
169 speak_init(gchar *voice, guint speed, guint pitch)
170 {
171 espeak_ok=FALSE;
172 erate=espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, 100, NULL, 0);
173 if (erate==-1)
174         return FALSE;
175
176 espeak_SetSynthCallback(espeak_cb);
177 espeak_SetVoiceByName(voice);
178 speak_set_parameters(speed, pitch);
179 espeak_SetParameter(espeakVOLUME, 100, 0);
180 if (speak_create_pipeline()==FALSE)
181         return FALSE;
182
183 bus=gst_pipeline_get_bus(GST_PIPELINE (ge.pipeline));
184 g_assert(bus);
185 gst_bus_add_watch(bus, bus_call, NULL);
186 espeak_ok=TRUE;
187 return TRUE;
188 }
189
190 void
191 speak_deinit(void)
192 {
193 if (espeak_ok==FALSE)
194         return;
195
196 gst_element_set_state(ge.pipeline, GST_STATE_NULL);
197 gst_object_unref(ge.pipeline);
198 ge.pipeline=NULL;
199 speaking=FALSE;
200 espeak_Terminate();
201 }
202
203 gboolean
204 speak_text(gchar *text)
205 {
206 #if 0
207 if (speaking==TRUE)
208         return FALSE;
209 #endif
210
211 g_printf("Speaking: %s (%d)\n", text, strlen(text));
212 espeak_Synth(text, strlen(text)+1, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL, NULL);
213
214 if (speaking!=TRUE) {
215         if (gst_element_set_state (ge.pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
216                 g_print("Failed to play\n");
217                 speaking=FALSE;
218                 return FALSE;
219         }
220         speaking=TRUE;
221 }
222 g_print("Playing...\n");
223 return TRUE;
224 }
225
226 gboolean
227 speak_speaking(void)
228 {
229 return speaking;
230 }
231
232 gboolean
233 speak_stop(void)
234 {
235 if (gst_element_set_state(ge.pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
236     return FALSE;
237 return TRUE;
238 }
239
240 #else
241
242 gboolean speak_init(gchar voice, guint speed, guint pitch) {return TRUE;}
243 void speak_deinit(void) {}
244 gboolean speak_stop(void) {return TRUE;}
245 gboolean speak_speaking(void) {return FALSE;}
246 void speak_set_parameters(guint speed, guint pitch) {}
247
248 gboolean 
249 speak_text(gchar *text)
250 {
251 #ifdef WITH_ESPEAK
252 #define _voice_synth_path "/usr/bin/espeak"
253 #else
254 #define _voice_synth_path "/usr/bin/flite"
255 #endif
256
257 if (!fork()) {
258         /* We are the fork child.  Synthesize the voice. */
259         sound_noise();
260         sleep(1);
261         printf("%s %s\n", _voice_synth_path, text);
262 #ifdef WITH_ESPEAK
263         execl(_voice_synth_path, _voice_synth_path, "-t", text, (char *)NULL);
264 #else
265         execl(_voice_synth_path, _voice_synth_path, "-s", "150", text, (char *)NULL);
266 #endif
267         exit(0);
268 }
269
270 return TRUE;
271 }
272
273 #endif