1 /***************************************************************************
2  *   Copyright (C) 2007, Gilles Casse <gcasse@oralux.org>                  *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 3 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #include "speech.h"
20 
21 #ifdef USE_ASYNC
22 // This source file is only used for asynchronious modes
23 
24 
25 //<includes
26 
27 #ifndef PLATFORM_WINDOWS
28 #include <unistd.h>
29 #endif
30 #include <assert.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <pthread.h>
34 #include <semaphore.h>
35 #include <wchar.h>
36 #include <errno.h>
37 #include <sys/time.h>
38 #include <time.h>
39 
40 #include "fifo.h"
41 #include "wave.h"
42 #include "debug.h"
43 
44 
45 //>
46 //<decls and function prototypes
47 
48 // my_mutex: protects my_thread_is_talking,
49 // my_stop_is_required, and the command fifo
50 static pthread_mutex_t my_mutex;
51 static int my_command_is_running = 0;
52 static int my_stop_is_required = 0;
53 // + fifo
54 //
55 
56 // my_thread: reads commands from the fifo, and runs them.
57 static pthread_t my_thread;
58 static sem_t my_sem_start_is_required;
59 static sem_t my_sem_stop_is_acknowledged;
60 
61 static void* say_thread(void*);
62 
63 static espeak_ERROR push(t_espeak_command* the_command);
64 static t_espeak_command* pop();
65 static void init(int process_parameters);
66 static int node_counter=0;
67 enum {MAX_NODE_COUNTER=400,
68       INACTIVITY_TIMEOUT=50, // in ms, check that the stream is inactive
69       MAX_INACTIVITY_CHECK=2
70 };
71 
72 //>
73 //<fifo_init
fifo_init()74 void fifo_init()
75 {
76   ENTER("fifo_init");
77 
78   // security
79   pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL);
80   init(0);
81 
82   assert(-1 != sem_init(&my_sem_start_is_required, 0, 0));
83   assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
84 
85   pthread_attr_t a_attrib;
86   if (pthread_attr_init (& a_attrib)
87       || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
88       || pthread_create( &my_thread,
89 			 & a_attrib,
90 			 say_thread,
91 			 (void*)NULL))
92     {
93       assert(0);
94     }
95 
96   pthread_attr_destroy(&a_attrib);
97 
98   // leave once the thread is actually started
99   SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n");
100   while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
101     {
102       continue; // Restart when interrupted by handler
103     }
104   SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n");
105 }
106 //>
107 //<fifo_add_command
108 
fifo_add_command(t_espeak_command * the_command)109 espeak_ERROR fifo_add_command (t_espeak_command* the_command)
110 {
111   ENTER("fifo_add_command");
112 
113   int a_status = pthread_mutex_lock(&my_mutex);
114   espeak_ERROR a_error = EE_OK;
115 
116   if (!a_status)
117     {
118       SHOW_TIME("fifo_add_command > locked\n");
119       a_error = push(the_command);
120       SHOW_TIME("fifo_add_command > unlocking\n");
121       a_status = pthread_mutex_unlock(&my_mutex);
122     }
123 
124   if (!a_status && !my_command_is_running && (a_error == EE_OK))
125     {
126       // quit when command is actually started
127       // (for possible forthcoming 'end of command' checks)
128       SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
129       sem_post(&my_sem_start_is_required);
130       int val=1;
131       while (val > 0)
132 	{
133 	  usleep(50000); // TBD: event?
134 	  sem_getvalue(&my_sem_start_is_required, &val);
135 	}
136     }
137 
138   if (a_status != 0)
139     {
140       a_error = EE_INTERNAL_ERROR;
141     }
142 
143   SHOW_TIME("LEAVE fifo_add_command");
144   return a_error;
145 }
146 
147 //>
148 //<fifo_add_commands
149 
fifo_add_commands(t_espeak_command * command1,t_espeak_command * command2)150 espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* command2)
151 {
152   ENTER("fifo_add_command");
153 
154   int a_status = pthread_mutex_lock(&my_mutex);
155   espeak_ERROR a_error = EE_OK;
156 
157   if (!a_status)
158     {
159       SHOW_TIME("fifo_add_commands > locked\n");
160 
161       if (node_counter+1 >= MAX_NODE_COUNTER)
162 	{
163 	  SHOW("push > %s\n", "EE_BUFFER_FULL");
164 	  a_error = EE_BUFFER_FULL;
165 	}
166       else
167 	{
168 	  push(command1);
169 	  push(command2);
170 	}
171       SHOW_TIME("fifo_add_command > unlocking\n");
172       a_status = pthread_mutex_unlock(&my_mutex);
173     }
174 
175   if (!a_status && !my_command_is_running && (a_error == EE_OK))
176     {
177       // quit when one command is actually started
178       // (for possible forthcoming 'end of command' checks)
179       SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
180       sem_post(&my_sem_start_is_required);
181       int val=1;
182       while (val > 0)
183 	{
184 	  usleep(50000); // TBD: event?
185 	  sem_getvalue(&my_sem_start_is_required, &val);
186 	}
187     }
188 
189   if (a_status != 0)
190     {
191       a_error = EE_INTERNAL_ERROR;
192     }
193 
194   SHOW_TIME("LEAVE fifo_add_commands");
195   return a_error;
196 }
197 
198 //>
199 //<fifo_stop
200 
fifo_stop()201 espeak_ERROR fifo_stop ()
202 {
203   ENTER("fifo_stop");
204 
205   int a_command_is_running = 0;
206   int a_status = pthread_mutex_lock(&my_mutex);
207   SHOW_TIME("fifo_stop > locked\n");
208   if (a_status != 0)
209     {
210       return EE_INTERNAL_ERROR;
211     }
212 
213   if (my_command_is_running)
214     {
215       a_command_is_running = 1;
216       my_stop_is_required = 1;
217       SHOW_TIME("fifo_stop > my_stop_is_required = 1\n");
218     }
219   SHOW_TIME("fifo_stop > unlocking\n");
220   a_status = pthread_mutex_unlock(&my_mutex);
221   if (a_status != 0)
222     {
223       return EE_INTERNAL_ERROR;
224     }
225 
226   if (a_command_is_running)
227     {
228       SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n");
229       while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
230 	{
231 	  continue; // Restart when interrupted by handler
232 	}
233       SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n");
234     }
235 
236   SHOW_TIME("fifo_stop > my_stop_is_required = 0\n");
237   my_stop_is_required = 0;
238   SHOW_TIME("LEAVE fifo_stop\n");
239 
240   return EE_OK;
241 }
242 
243 //>
244 
245 //<fifo_is_speaking
fifo_is_busy()246 int fifo_is_busy ()
247 {
248   //  ENTER("isSpeaking");
249   //  int aResult = (int) (my_command_is_running || WaveIsPlaying());
250   SHOW("fifo_is_busy > aResult = %d\n",my_command_is_running);
251   return my_command_is_running;
252 }
253 
254 // int pause ()
255 // {
256 //   ENTER("pause");
257 //   // TBD
258 //   //   if (espeakPause (espeakHandle, 1))
259 //   return true;
260 // }
261 
262 // int resume ()
263 // {
264 //   ENTER("resume");
265 //   // TBD
266 //   //   if (espeakPause (espeakHandle, 0))
267 //   return true;
268 // }
269 //>
270 
271 
272 //<sleep_until_start_request_or_inactivity
273 
sleep_until_start_request_or_inactivity()274 static int sleep_until_start_request_or_inactivity()
275 {
276   SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER");
277   int a_start_is_required=0;
278 
279   // Wait for the start request (my_sem_start_is_required).
280   // Besides this, if the audio stream is still busy,
281   // check from time to time its end.
282   // The end of the stream is confirmed by several checks
283   // for filtering underflow.
284   //
285   int i=0;
286 	while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required)
287 	{
288 		if (wave_is_busy( NULL) )
289 		{
290 			i = 0;
291 		}
292       else
293 		{
294 			i++;
295 		}
296 
297 		int err=0;
298 		struct timespec ts;
299 		struct timeval tv;
300 
301 		clock_gettime2( &ts);
302 
303 #ifdef DEBUG_ENABLED
304 		struct timespec to;
305 		to.tv_sec = ts.tv_sec;
306 		to.tv_nsec = ts.tv_nsec;
307 #endif
308 
309 		add_time_in_ms( &ts, INACTIVITY_TIMEOUT);
310 
311 		SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n",
312 			to.tv_sec, to.tv_nsec,
313 			ts.tv_sec, ts.tv_nsec);
314 
315 		while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1
316 			&& errno == EINTR)
317 		{
318 			continue;
319 		}
320 
321 		assert (gettimeofday(&tv, NULL) != -1);
322 		SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err,
323 			tv.tv_sec, tv.tv_usec*1000);
324 
325 		if (err==0)
326 		{
327 			a_start_is_required = 1;
328 		}
329 	}
330 	SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE");
331 	return a_start_is_required;
332 }
333 
334 //>
335 //<close_stream
336 
close_stream()337 static void close_stream()
338 {
339   SHOW_TIME("fifo > close_stream > ENTER\n");
340 
341   // Warning: a wave_close can be already required by
342   // an external command (espeak_Cancel + fifo_stop), if so:
343   // my_stop_is_required = 1;
344 
345   int a_status = pthread_mutex_lock(&my_mutex);
346   assert (!a_status);
347   int a_stop_is_required = my_stop_is_required;
348   if (!a_stop_is_required)
349     {
350       my_command_is_running = 1;
351     }
352   a_status = pthread_mutex_unlock(&my_mutex);
353 
354   if (!a_stop_is_required)
355     {
356       wave_close(NULL);
357 
358       int a_status = pthread_mutex_lock(&my_mutex);
359       assert (!a_status);
360       my_command_is_running = 0;
361 
362       a_stop_is_required = my_stop_is_required;
363       a_status = pthread_mutex_unlock(&my_mutex);
364 
365       if (a_stop_is_required)
366 	{
367 	  // acknowledge the stop request
368 	  SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n");
369 	  int a_status = sem_post(&my_sem_stop_is_acknowledged);
370 	  assert( a_status != -1);
371 	}
372     }
373 
374   SHOW_TIME("fifo > close_stream > LEAVE\n");
375 }
376 
377 //>
378 //<say_thread
379 
say_thread(void *)380 static void* say_thread(void*)
381 {
382   ENTER("say_thread");
383 
384   SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
385 
386   // announce that thread is started
387   sem_post(&my_sem_stop_is_acknowledged);
388 
389   int look_for_inactivity=0;
390 
391   while(1)
392     {
393       SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
394 
395       int a_start_is_required = 0;
396       if (look_for_inactivity)
397 	{
398 	  a_start_is_required = sleep_until_start_request_or_inactivity();
399 	  if (!a_start_is_required)
400 	    {
401 	      close_stream();
402 	    }
403 	}
404       look_for_inactivity = 1;
405 
406       if (!a_start_is_required)
407 	{
408 	  while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
409 	    {
410 	      continue; // Restart when interrupted by handler
411 	    }
412 	}
413       SHOW_TIME("say_thread > get my_sem_start_is_required\n");
414 
415       SHOW_TIME("say_thread > my_command_is_running = 1\n");
416       my_command_is_running = 1;
417 
418       while( my_command_is_running)
419 	{
420 	  SHOW_TIME("say_thread > locking\n");
421 	  int a_status = pthread_mutex_lock(&my_mutex);
422 	  assert (!a_status);
423 	  t_espeak_command* a_command = (t_espeak_command*)pop();
424 
425 	  if (a_command == NULL)
426 	    {
427 	      SHOW_TIME("say_thread > text empty (talking=0) \n");
428 	      a_status = pthread_mutex_unlock(&my_mutex);
429 	      SHOW_TIME("say_thread > unlocked\n");
430 	      SHOW_TIME("say_thread > my_command_is_running = 0\n");
431 	      my_command_is_running = 0;
432 	    }
433 	  else
434 	    {
435 	      display_espeak_command(a_command);
436 	      // purge start semaphore
437 	      SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
438 	      while(0 == sem_trywait(&my_sem_start_is_required))
439 		{
440 		};
441 
442 	      if (my_stop_is_required)
443 		{
444 		  SHOW_TIME("say_thread > my_command_is_running = 0\n");
445 		  my_command_is_running = 0;
446 		}
447 	      SHOW_TIME("say_thread > unlocking\n");
448 	      a_status = pthread_mutex_unlock(&my_mutex);
449 
450 	      if (my_command_is_running)
451 		{
452 		  process_espeak_command(a_command);
453 		}
454 	      delete_espeak_command(a_command);
455 	    }
456 	}
457 
458       if (my_stop_is_required)
459 	{
460 	  // no mutex required since the stop command is synchronous
461 	  // and waiting for my_sem_stop_is_acknowledged
462 	  init(1);
463 
464 	  // purge start semaphore
465 	  SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
466 	  while(0==sem_trywait(&my_sem_start_is_required))
467 	    {
468 	    };
469 
470 	  // acknowledge the stop request
471 	  SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
472 	  int a_status = sem_post(&my_sem_stop_is_acknowledged);
473 	  assert( a_status != -1);
474 	}
475       // and wait for the next start
476       SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
477     }
478 
479   return NULL;
480 }
481 
fifo_is_command_enabled()482 int fifo_is_command_enabled()
483 {
484   SHOW("ENTER fifo_is_command_enabled=%d\n",(int)(0 == my_stop_is_required));
485   return (0 == my_stop_is_required);
486 }
487 
488 //>
489 //<fifo
490 typedef struct t_node
491 {
492   t_espeak_command* data;
493   t_node *next;
494 } node;
495 
496 static node* head=NULL;
497 static node* tail=NULL;
498 // return 1 if ok, 0 otherwise
push(t_espeak_command * the_command)499 static espeak_ERROR push(t_espeak_command* the_command)
500 {
501   ENTER("fifo > push");
502 
503   assert((!head && !tail) || (head && tail));
504 
505   if (the_command == NULL)
506     {
507       SHOW("push > command=0x%x\n", NULL);
508       return EE_INTERNAL_ERROR;
509     }
510 
511   if (node_counter >= MAX_NODE_COUNTER)
512     {
513       SHOW("push > %s\n", "EE_BUFFER_FULL");
514       return EE_BUFFER_FULL;
515     }
516 
517   node *n = (node *)malloc(sizeof(node));
518   if (n == NULL)
519     {
520       return EE_INTERNAL_ERROR;
521     }
522 
523   if (head == NULL)
524     {
525       head = n;
526       tail = n;
527     }
528   else
529     {
530       tail->next = n;
531       tail = n;
532     }
533 
534   tail->next = NULL;
535   tail->data = the_command;
536 
537   node_counter++;
538   SHOW("push > counter=%d\n",node_counter);
539 
540   the_command->state = CS_PENDING;
541   display_espeak_command(the_command);
542 
543   return EE_OK;
544 }
545 
pop()546 static t_espeak_command* pop()
547 {
548   ENTER("fifo > pop");
549   t_espeak_command* the_command = NULL;
550 
551   assert((!head && !tail) || (head && tail));
552 
553   if (head != NULL)
554     {
555       node* n = head;
556       the_command = n->data;
557       head = n->next;
558       free(n);
559       node_counter--;
560       SHOW("pop > command=0x%x (counter=%d)\n",the_command, node_counter);
561     }
562 
563   if(head == NULL)
564     {
565       tail = NULL;
566     }
567 
568   display_espeak_command(the_command);
569 
570   return the_command;
571 }
572 
573 
init(int process_parameters)574 static void init(int process_parameters)
575 {
576 	// Changed by Tyler Spivey 30.Nov.2011
577 	t_espeak_command *c = NULL;
578 	ENTER("fifo > init");
579 	c = pop();
580 	while (c != NULL) {
581 		if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC))
582 		{
583 			process_espeak_command(c);
584 		}
585 		delete_espeak_command(c);
586 		c = pop();
587 	}
588 	node_counter = 0;
589 }
590 
591 
592 //>
593 //<fifo_init
fifo_terminate()594 void fifo_terminate()
595 {
596   ENTER("fifo_terminate");
597 
598   pthread_cancel(my_thread);
599   pthread_join(my_thread,NULL);
600   pthread_mutex_destroy(&my_mutex);
601   sem_destroy(&my_sem_start_is_required);
602   sem_destroy(&my_sem_stop_is_acknowledged);
603 
604   init(0); // purge fifo
605 }
606 
607 #endif
608 //>
609 
610