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