1 /*
2 * Copyright (C) 2007, Gilles Casse <gcasse@oralux.org>
3 * Copyright (C) 2013-2016 Reece H. Dunn
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see: <http://www.gnu.org/licenses/>.
17 */
18
19 // This source file is only used for asynchronious modes
20
21 #include "config.h"
22
23 #include <assert.h>
24 #include <errno.h>
25 #include <pthread.h>
26 //#include <stdbool.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <unistd.h>
33
34 #include "espeak_ng.h"
35
36 #include "speech.h"
~RecyclingAllocator()37 #include "espeak_command.h"
38 #include "fifo.h"
39
40 #ifdef USE_ASYNC
41
42 // my_mutex: protects my_thread_is_talking,
43 // my_stop_is_required, and the command fifo
44 static pthread_mutex_t my_mutex;
Allocate()45 static bool my_command_is_running = false;
46 static pthread_cond_t my_cond_command_is_running;
47 static bool my_stop_is_required = false;
48
49 // my_thread: reads commands from the fifo, and runs them.
50 static pthread_t my_thread;
Deallocate(SubClass * E)51
52 static pthread_cond_t my_cond_start_is_required;
PrintStats()53 static bool my_start_is_required = false;
54
55 static pthread_cond_t my_cond_stop_is_acknowledged;
56 static bool my_stop_is_acknowledged = false;
57
58 static void *say_thread(void *);
59
60 static espeak_ng_STATUS push(t_espeak_command *the_command);
61 static t_espeak_command *pop(void);
new(size_t size,llvm::RecyclingAllocator<AllocatorType,T,Size,Align> & Allocator)62 static void init(int process_parameters);
63 static int node_counter = 0;
64
65 enum {
66 MAX_NODE_COUNTER = 400,
67 INACTIVITY_TIMEOUT = 50, // in ms, check that the stream is inactive
68 MAX_INACTIVITY_CHECK = 2
69 };
delete(void * E,llvm::RecyclingAllocator<AllocatorType,T,Size,Align> & A)70
71 void fifo_init()
72 {
73 // security
74 pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL);
75 init(0);
76
77 assert(-1 != pthread_cond_init(&my_cond_command_is_running, NULL));
78 assert(-1 != pthread_cond_init(&my_cond_start_is_required, NULL));
79 assert(-1 != pthread_cond_init(&my_cond_stop_is_acknowledged, NULL));
80
81 pthread_attr_t a_attrib;
82 if (pthread_attr_init(&a_attrib)
83 || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
84 || pthread_create(&my_thread,
85 &a_attrib,
86 say_thread,
87 (void *)NULL)) {
88 assert(0);
89 }
90
91 pthread_attr_destroy(&a_attrib);
92
93 // leave once the thread is actually started
94 assert(-1 != pthread_mutex_lock(&my_mutex));
95 while (my_stop_is_acknowledged == false) {
96 while ((pthread_cond_wait(&my_cond_stop_is_acknowledged, &my_mutex) == -1) && errno == EINTR)
97 ;
98 }
99 my_stop_is_acknowledged = false;
100 pthread_mutex_unlock(&my_mutex);
101 }
102
103 espeak_ng_STATUS fifo_add_command(t_espeak_command *the_command)
104 {
105 espeak_ng_STATUS status;
106 if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK)
107 return status;
108
109 if ((status = push(the_command)) != ENS_OK) {
110 pthread_mutex_unlock(&my_mutex);
111 return status;
112 }
113
114 my_start_is_required = true;
115 pthread_cond_signal(&my_cond_start_is_required);
116
117 while (my_start_is_required && !my_command_is_running) {
118 if((status = pthread_cond_wait(&my_cond_command_is_running, &my_mutex)) != ENS_OK && errno != EINTR) {
119 pthread_mutex_unlock(&my_mutex);
120 return status;
121 }
122 }
123 if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK)
124 return status;
125
126 return ENS_OK;
127 }
128
129 espeak_ng_STATUS fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2)
130 {
131 espeak_ng_STATUS status;
132 if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK)
133 return status;
134
135 if (node_counter+1 >= MAX_NODE_COUNTER) {
136 pthread_mutex_unlock(&my_mutex);
137 return ENS_FIFO_BUFFER_FULL;
138 }
139
140 if ((status = push(command1)) != ENS_OK) {
141 pthread_mutex_unlock(&my_mutex);
142 return status;
143 }
144
145 if ((status = push(command2)) != ENS_OK) {
146 pthread_mutex_unlock(&my_mutex);
147 return status;
148 }
149
150 my_start_is_required = true;
151 pthread_cond_signal(&my_cond_start_is_required);
152
153 while (my_start_is_required && !my_command_is_running) {
154 if((status = pthread_cond_wait(&my_cond_command_is_running, &my_mutex)) != ENS_OK && errno != EINTR) {
155 pthread_mutex_unlock(&my_mutex);
156 return status;
157 }
158 }
159 if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK)
160 return status;
161
162 return ENS_OK;
163 }
164
165 espeak_ng_STATUS fifo_stop()
166 {
167 espeak_ng_STATUS status;
168 if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK)
169 return status;
170
171 bool a_command_is_running = false;
172 if (my_command_is_running) {
173 a_command_is_running = true;
174 my_stop_is_required = true;
175 my_stop_is_acknowledged = false;
176 }
177
178 if (a_command_is_running) {
179 while (my_stop_is_acknowledged == false) {
180 while ((pthread_cond_wait(&my_cond_stop_is_acknowledged, &my_mutex) == -1) && errno == EINTR)
181 continue; // Restart when interrupted by handler
182 }
183 }
184
185 my_stop_is_required = false;
186 if ((status = pthread_mutex_unlock(&my_mutex)) != ENS_OK)
187 return status;
188
189 return ENS_OK;
190 }
191
192 int fifo_is_busy()
193 {
194 return my_command_is_running;
195 }
196
197 static int sleep_until_start_request_or_inactivity()
198 {
199 int a_start_is_required = false;
200
201 // Wait for the start request (my_cond_start_is_required).
202 // Besides this, if the audio stream is still busy,
203 // check from time to time its end.
204 // The end of the stream is confirmed by several checks
205 // for filtering underflow.
206 //
207 int i = 0;
208 int err = pthread_mutex_lock(&my_mutex);
209 assert(err != -1);
210 while ((i <= MAX_INACTIVITY_CHECK) && !a_start_is_required) {
211 i++;
212
213 struct timespec ts;
214 struct timeval tv;
215
216 clock_gettime2(&ts);
217
218 add_time_in_ms(&ts, INACTIVITY_TIMEOUT);
219
220 while ((err = pthread_cond_timedwait(&my_cond_start_is_required, &my_mutex, &ts)) == -1
221 && errno == EINTR)
222 continue;
223
224 assert(gettimeofday(&tv, NULL) != -1);
225
226 if (err == 0)
227 a_start_is_required = true;
228 }
229 pthread_mutex_unlock(&my_mutex);
230 return a_start_is_required;
231 }
232
233 static espeak_ng_STATUS close_stream()
234 {
235 espeak_ng_STATUS status = pthread_mutex_lock(&my_mutex);
236 if (status != ENS_OK)
237 return status;
238
239 bool a_stop_is_required = my_stop_is_required;
240 if (!a_stop_is_required)
241 my_command_is_running = true;
242
243 status = pthread_mutex_unlock(&my_mutex);
244
245 if (!a_stop_is_required) {
246 int a_status = pthread_mutex_lock(&my_mutex);
247 if (status == ENS_OK)
248 status = a_status;
249
250 my_command_is_running = false;
251 a_stop_is_required = my_stop_is_required;
252
253 a_status = pthread_mutex_unlock(&my_mutex);
254 if (status == ENS_OK)
255 status = a_status;
256
257 if (a_stop_is_required) {
258 // cancel the audio early, to be more responsive when using eSpeak NG
259 // for audio.
260 cancel_audio();
261
262 // acknowledge the stop request
263 if((a_status = pthread_mutex_lock(&my_mutex)) != ENS_OK)
264 return a_status;
265
266 my_stop_is_acknowledged = true;
267 a_status = pthread_cond_signal(&my_cond_stop_is_acknowledged);
268 if(a_status != ENS_OK)
269 return a_status;
270 a_status = pthread_mutex_unlock(&my_mutex);
271 if (status == ENS_OK)
272 status = a_status;
273
274 }
275 }
276
277 return status;
278 }
279
280 static void *say_thread(void *p)
281 {
282 (void)p; // unused
283
284 // announce that thread is started
285 assert(-1 != pthread_mutex_lock(&my_mutex));
286 my_stop_is_acknowledged = true;
287 assert(-1 != pthread_cond_signal(&my_cond_stop_is_acknowledged));
288 assert(-1 != pthread_mutex_unlock(&my_mutex));
289
290 bool look_for_inactivity = false;
291
292 while (1) {
293 bool a_start_is_required = false;
294 if (look_for_inactivity) {
295 a_start_is_required = sleep_until_start_request_or_inactivity();
296 if (!a_start_is_required)
297 close_stream();
298 }
299 look_for_inactivity = true;
300
301 int a_status = pthread_mutex_lock(&my_mutex);
302 assert(!a_status);
303
304 if (!a_start_is_required) {
305 while (my_start_is_required == false) {
306 while ((pthread_cond_wait(&my_cond_start_is_required, &my_mutex) == -1) && errno == EINTR)
307 continue; // Restart when interrupted by handler
308 }
309 }
310
311
312 my_command_is_running = true;
313
314 assert(-1 != pthread_cond_broadcast(&my_cond_command_is_running));
315 assert(-1 != pthread_mutex_unlock(&my_mutex));
316
317 while (my_command_is_running) {
318 int a_status = pthread_mutex_lock(&my_mutex);
319 assert(!a_status);
320 t_espeak_command *a_command = (t_espeak_command *)pop();
321
322 if (a_command == NULL) {
323 my_command_is_running = false;
324 a_status = pthread_mutex_unlock(&my_mutex);
325 } else {
326 my_start_is_required = false;
327
328 if (my_stop_is_required)
329 my_command_is_running = false;
330 a_status = pthread_mutex_unlock(&my_mutex);
331
332 if (my_command_is_running)
333 process_espeak_command(a_command);
334 delete_espeak_command(a_command);
335 }
336 }
337
338 if (my_stop_is_required) {
339 // no mutex required since the stop command is synchronous
340 // and waiting for my_cond_stop_is_acknowledged
341 init(1);
342
343 assert(-1 != pthread_mutex_lock(&my_mutex));
344 my_start_is_required = false;
345
346 // acknowledge the stop request
347 my_stop_is_acknowledged = true;
348 int a_status = pthread_cond_signal(&my_cond_stop_is_acknowledged);
349 assert(a_status != -1);
350 pthread_mutex_unlock(&my_mutex);
351
352 }
353 // and wait for the next start
354 }
355
356 return NULL;
357 }
358
359 int fifo_is_command_enabled()
360 {
361 return 0 == my_stop_is_required;
362 }
363
364 typedef struct t_node {
365 t_espeak_command *data;
366 struct t_node *next;
367 } node;
368
369 static node *head = NULL;
370 static node *tail = NULL;
371
372 static espeak_ng_STATUS push(t_espeak_command *the_command)
373 {
374 assert((!head && !tail) || (head && tail));
375
376 if (the_command == NULL)
377 return EINVAL;
378
379 if (node_counter >= MAX_NODE_COUNTER)
380 return ENS_FIFO_BUFFER_FULL;
381
382 node *n = (node *)malloc(sizeof(node));
383 if (n == NULL)
384 return ENOMEM;
385
386 if (head == NULL) {
387 head = n;
388 tail = n;
389 } else {
390 tail->next = n;
391 tail = n;
392 }
393
394 tail->next = NULL;
395 tail->data = the_command;
396
397 node_counter++;
398
399 the_command->state = CS_PENDING;
400
401 return ENS_OK;
402 }
403
404 static t_espeak_command *pop()
405 {
406 t_espeak_command *the_command = NULL;
407
408 assert((!head && !tail) || (head && tail));
409
410 if (head != NULL) {
411 node *n = head;
412 the_command = n->data;
413 head = n->next;
414 free(n);
415 node_counter--;
416 }
417
418 if (head == NULL)
419 tail = NULL;
420
421 return the_command;
422 }
423
424 static void init(int process_parameters)
425 {
426 t_espeak_command *c = NULL;
427 c = pop();
428 while (c != NULL) {
429 if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC))
430 process_espeak_command(c);
431 delete_espeak_command(c);
432 c = pop();
433 }
434 node_counter = 0;
435 }
436
437 void fifo_terminate()
438 {
439 pthread_cancel(my_thread);
440 pthread_join(my_thread, NULL);
441 pthread_mutex_destroy(&my_mutex);
442 pthread_cond_destroy(&my_cond_start_is_required);
443 pthread_cond_destroy(&my_cond_stop_is_acknowledged);
444
445 init(0); // purge fifo
446 }
447
448 #endif
449