PageRenderTime 46ms CodeModel.GetById 12ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/native/external/espeak/src/event.cpp

http://eyes-free.googlecode.com/
C++ | 725 lines | 503 code | 123 blank | 99 comment | 90 complexity | 820d44bf865425221a59ab022d75d86b MD5 | raw file
  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
 20#include "speech.h"
 21
 22#ifdef USE_ASYNC
 23// This source file is only used for asynchronious modes
 24
 25
 26//<includes
 27#include <unistd.h>
 28#include <assert.h>
 29#include <string.h>
 30#include <stdlib.h>
 31#include <pthread.h>
 32#include <semaphore.h>
 33#include <sys/time.h>
 34#include <errno.h>
 35
 36#include "speak_lib.h"
 37#include "event.h"
 38#include "wave.h"
 39#include "debug.h"
 40//>
 41//<decls and function prototypes
 42
 43
 44// my_mutex: protects my_thread_is_talking, 
 45static pthread_mutex_t my_mutex;
 46static sem_t my_sem_start_is_required;
 47static sem_t my_sem_stop_is_required;
 48static sem_t my_sem_stop_is_acknowledged;
 49// my_thread: polls the audio duration and compares it to the duration of the first event.
 50static pthread_t my_thread;
 51
 52static t_espeak_callback* my_callback = NULL;
 53static int my_event_is_running=0;
 54
 55enum {MIN_TIMEOUT_IN_MS=10,
 56      ACTIVITY_TIMEOUT=50, // in ms, check that the stream is active
 57      MAX_ACTIVITY_CHECK=6 
 58};
 59
 60
 61typedef struct t_node
 62{
 63  void* data;
 64  t_node *next;
 65} node;
 66
 67static node* head=NULL;
 68static node* tail=NULL;
 69static int node_counter=0;
 70static espeak_ERROR push(void* data);
 71static void* pop();
 72static void init();
 73static void* polling_thread(void*);
 74
 75//>
 76//<event_init
 77
 78void event_set_callback(t_espeak_callback* SynthCallback)
 79{
 80  my_callback = SynthCallback;
 81}
 82
 83void event_init(void)
 84{
 85  ENTER("event_init");
 86
 87  my_event_is_running=0;
 88
 89  // security
 90  pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL);
 91  init();
 92
 93  assert(-1 != sem_init(&my_sem_start_is_required, 0, 0));
 94  assert(-1 != sem_init(&my_sem_stop_is_required, 0, 0));
 95  assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
 96
 97  pthread_attr_t a_attrib;    
 98  if (pthread_attr_init (& a_attrib)
 99      || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
100      || pthread_create( &my_thread, 
101			 & a_attrib, 
102			 polling_thread, 
103			 (void*)NULL))
104    {
105      assert(0);
106    }
107
108  pthread_attr_destroy(&a_attrib);
109}
110//>
111//<event_display
112void event_display(espeak_EVENT* event)
113{
114ENTER("event_display");
115
116#ifdef DEBUG_ENABLED
117	if (event==NULL)
118	{
119		SHOW("event_display > event=%s\n","NULL");
120	}
121	else
122	{
123		static const char* label[] = {
124		"LIST_TERMINATED",
125		"WORD",
126		"SENTENCE",
127		"MARK",
128		"PLAY",
129		"END",
130		"MSG_TERMINATED"  
131		};
132
133		SHOW("event_display > event=0x%x\n",event);
134		SHOW("event_display >   type=%s\n",label[event->type]);
135		SHOW("event_display >   uid=%d\n",event->unique_identifier);
136		SHOW("event_display >   text_position=%d\n",event->text_position);
137		SHOW("event_display >   length=%d\n",event->length);
138		SHOW("event_display >   audio_position=%d\n",event->audio_position);
139		SHOW("event_display >   sample=%d\n",event->sample);
140		SHOW("event_display >   user_data=0x%x\n",event->user_data);
141	}
142#endif
143}
144//>
145//<event_copy
146
147static espeak_EVENT* event_copy (espeak_EVENT* event)
148{
149	ENTER("event_copy");
150	
151	if (event==NULL)
152	{
153		return NULL;
154	}
155	
156	espeak_EVENT* a_event=(espeak_EVENT*)malloc(sizeof(espeak_EVENT));
157	if (a_event)
158	{
159		memcpy(a_event, event, sizeof(espeak_EVENT));
160	
161		switch(event->type)
162		{
163		case espeakEVENT_MARK:
164		case espeakEVENT_PLAY:
165			if (event->id.name)
166			{
167				a_event->id.name = strdup(event->id.name);
168			}
169		break;
170		
171		default:
172			break;
173		}
174	}
175	
176	event_display(a_event);
177	
178	return a_event;
179}
180
181//>
182//<event_notify
183
184// Call the user supplied callback
185//
186// Note: the current sequence is:
187//
188// * First call with: event->type = espeakEVENT_SENTENCE
189// * 0, 1 or several calls: event->type = espeakEVENT_WORD
190// * Last call: event->type = espeakEVENT_MSG_TERMINATED
191//
192
193static void event_notify(espeak_EVENT* event)
194{
195ENTER("event_notify");
196	static unsigned int a_old_uid = 0;
197
198	espeak_EVENT events[2];
199	memcpy(&events[0],event,sizeof(espeak_EVENT));     // the event parameter in the callback function should be an array of eventd
200	memcpy(&events[1],event,sizeof(espeak_EVENT));
201	events[1].type = espeakEVENT_LIST_TERMINATED;           // ... terminated by an event type=0
202
203	if (event && my_callback)
204	{
205		event_display(event);
206
207		switch(event->type)
208		{
209		case espeakEVENT_SENTENCE:
210			my_callback(NULL, 0, events);
211			a_old_uid = event->unique_identifier;
212			break;
213
214		case espeakEVENT_MSG_TERMINATED:
215		case espeakEVENT_MARK:
216		case espeakEVENT_WORD:
217		case espeakEVENT_END:
218		case espeakEVENT_PHONEME:
219		{
220// jonsd - I'm not sure what this is for. gilles says it's for when Gnome Speech reads a file of blank lines
221			if (a_old_uid != event->unique_identifier)
222			{
223				espeak_EVENT_TYPE a_new_type = events[0].type;
224				events[0].type = espeakEVENT_SENTENCE;
225				my_callback(NULL, 0, events);
226				events[0].type = a_new_type;
227				usleep(50000);
228			}
229			my_callback(NULL, 0, events);
230			a_old_uid = event->unique_identifier;
231		}
232		break;
233
234		default:
235			case espeakEVENT_LIST_TERMINATED:
236			case espeakEVENT_PLAY:
237		break;
238		}
239	}
240}
241//>
242//<event_delete
243
244static int event_delete(espeak_EVENT* event)
245{
246ENTER("event_delete");
247
248	event_display(event);
249
250	if(event==NULL)
251	{
252		return 0;
253	}
254
255	switch(event->type)
256	{
257	case espeakEVENT_MSG_TERMINATED:
258		event_notify(event);
259		break;
260
261	case espeakEVENT_MARK:
262	case espeakEVENT_PLAY:
263		if(event->id.name)
264		{
265			free((void*)(event->id.name));
266		}
267		break;
268
269	default:
270		break;
271	}
272
273	free(event);
274	return 1;
275}
276
277//>
278//<event_declare
279
280espeak_ERROR event_declare (espeak_EVENT* event)
281{
282ENTER("event_declare");
283
284	event_display(event);
285
286	if (!event)
287	{
288		return EE_INTERNAL_ERROR;
289	}
290
291	int a_status = pthread_mutex_lock(&my_mutex); 
292	espeak_ERROR a_error = EE_OK;
293
294	if (!a_status)
295	{
296		SHOW_TIME("event_declare > locked\n");
297		espeak_EVENT* a_event = event_copy(event);
298		a_error = push(a_event);
299		if (a_error != EE_OK)
300		{
301			event_delete(a_event);
302		}
303		SHOW_TIME("event_declare > unlocking\n");
304		a_status = pthread_mutex_unlock(&my_mutex);
305	}
306
307  // TBD: remove the comment
308  // reminder: code in comment.
309  // This wait can lead to an underrun
310  //
311//   if (!a_status && !my_event_is_running && (a_error == EE_OK))
312//   {
313//       // quit when command is actually started 
314//       // (for possible forthcoming 'end of command' checks)
315	SHOW_TIME("event_declare > post my_sem_start_is_required\n");
316	sem_post(&my_sem_start_is_required);
317//       int val=1;
318//       while (val)
319// 	{
320// 	  usleep(50000); // TBD: event?
321// 	  sem_getvalue(&my_sem_start_is_required, &val);
322// 	}
323//     }
324
325	if (a_status != 0)
326	{
327		a_error = EE_INTERNAL_ERROR;
328	}
329
330	return a_error;
331}
332
333//>
334//<event_clear_all
335
336espeak_ERROR event_clear_all ()
337{
338	ENTER("event_clear_all");
339
340	int a_status = pthread_mutex_lock(&my_mutex);
341	int a_event_is_running = 0;
342
343	SHOW_TIME("event_stop > locked\n");
344	if (a_status != 0)
345	{
346		return EE_INTERNAL_ERROR;
347	}
348
349	if (my_event_is_running)
350	{
351		SHOW_TIME("event_stop > post my_sem_stop_is_required\n");
352		sem_post(&my_sem_stop_is_required);
353		a_event_is_running = 1;
354	}
355	else
356	{
357		init(); // clear pending events
358	}
359	SHOW_TIME("event_stop > unlocking\n");
360	a_status = pthread_mutex_unlock(&my_mutex);
361	if (a_status != 0)
362	{
363		return EE_INTERNAL_ERROR;
364	}
365
366	if (a_event_is_running)
367	{
368		SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n");
369		while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
370		{
371			continue; // Restart when interrupted by handler
372		}
373		SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n");
374	}
375
376	SHOW_TIME("LEAVE event_stop\n");
377
378	return EE_OK;
379}
380
381//>
382//<sleep_until_timeout_or_stop_request
383
384static int sleep_until_timeout_or_stop_request(uint32_t time_in_ms)
385{
386ENTER("sleep_until_timeout_or_stop_request");
387
388	int a_stop_is_required=0;
389	struct timespec ts, to;
390	struct timeval tv;
391	int err=0;
392
393	clock_gettime2( &ts);
394	to.tv_sec = ts.tv_sec;
395	to.tv_nsec = ts.tv_nsec;
396
397	add_time_in_ms( &ts, time_in_ms);
398
399	SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n", 
400       to.tv_sec, to.tv_nsec,
401       ts.tv_sec, ts.tv_nsec);
402
403	while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 
404		&& errno == EINTR)
405	{
406		continue; // Restart when interrupted by handler
407	}
408
409	assert (gettimeofday(&tv, NULL) != -1);
410	SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", 
411       tv.tv_sec, tv.tv_usec*1000);
412
413	if (err == 0)
414	{
415		SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n","stop required!");
416		a_stop_is_required=1; // stop required
417	}
418	return a_stop_is_required;
419}
420
421//>
422//<get_remaining_time
423// Asked for the time interval required for reaching the sample.
424// If the stream is opened but the audio samples are not played, 
425// a timeout is started.
426
427static int get_remaining_time(uint32_t sample, uint32_t* time_in_ms, int* stop_is_required)
428{
429ENTER("get_remaining_time");
430
431	int err = 0;
432	*stop_is_required = 0;
433	int i=0;
434
435	for (i=0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++)
436	{
437		err = wave_get_remaining_time( sample, time_in_ms);
438
439		if (err || wave_is_busy(NULL) || (*time_in_ms == 0))
440		{ // if err, stream not available: quit
441	  // if wave is busy, time_in_ms is known: quit
442	  // if wave is not busy but remaining time == 0, event is reached: quit
443		break;
444	}
445
446      // stream opened but not active
447      //
448      // Several possible states: 
449      //   * the stream is opened but not yet started: 
450      //
451      //       wait for the start of stream
452      //
453      //   * some samples have already been played, 
454      //      ** the end of stream is reached
455      //      ** or there is an underrun
456      //      
457      //       wait for the close of stream
458
459		*stop_is_required = sleep_until_timeout_or_stop_request( ACTIVITY_TIMEOUT);      
460	}
461
462	return err;
463}
464
465//>
466//<polling_thread
467
468static void* polling_thread(void*)
469{
470ENTER("polling_thread");
471
472	while(1)
473	{
474		int a_stop_is_required=0;
475
476		SHOW_TIME("polling_thread > locking\n");
477		int a_status = pthread_mutex_lock(&my_mutex);
478		SHOW_TIME("polling_thread > locked (my_event_is_running = 0)\n");
479		my_event_is_running = 0;
480		pthread_mutex_unlock(&my_mutex);
481		SHOW_TIME("polling_thread > unlocked\n");
482
483		SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n");
484
485		while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
486		{
487			continue; // Restart when interrupted by handler
488		}
489
490		SHOW_TIME("polling_thread > get my_sem_start_is_required\n");
491
492		a_status = pthread_mutex_lock(&my_mutex);
493		SHOW_TIME("polling_thread > locked (my_event_is_running = 1)\n");
494		my_event_is_running = 1;
495		pthread_mutex_unlock(&my_mutex);
496		SHOW_TIME("polling_thread > unlocked\n");
497
498		a_stop_is_required=0;
499		a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
500		if ((a_status==0) && a_stop_is_required)
501		{
502			SHOW("polling_thread > stop required (%d)\n", __LINE__);
503			while(0 == sem_trywait(&my_sem_stop_is_required))
504			{
505			};
506		}
507		else
508		{
509			a_stop_is_required=0;
510		}
511
512		// In this loop, my_event_is_running = 1
513		while (head && !a_stop_is_required)
514		{
515			SHOW_TIME("polling_thread > check head\n");
516			while(0 == sem_trywait(&my_sem_start_is_required))
517			{
518			};
519	
520			espeak_EVENT* event = (espeak_EVENT*)(head->data);
521			assert(event);
522	
523			uint32_t time_in_ms = 0;
524	
525			int err = get_remaining_time((uint32_t)event->sample, 
526							&time_in_ms, 
527							&a_stop_is_required);
528			if (a_stop_is_required)
529			{
530				break;
531			}
532			else if (err != 0)
533			{ 
534				// No available time: the event is deleted.
535				SHOW("polling_thread > %s\n","audio device down");
536				a_status = pthread_mutex_lock(&my_mutex);
537				SHOW_TIME("polling_thread > locked\n");
538				event_delete( (espeak_EVENT*)pop());
539				a_status = pthread_mutex_unlock(&my_mutex);
540				SHOW_TIME("polling_thread > unlocked\n");
541			}
542			else if (time_in_ms==0)
543			{ // the event is already reached.
544				if (my_callback)
545				{
546					event_notify(event);
547					// the user_data (and the type) are cleaned to be sure 
548					// that MSG_TERMINATED is called twice (at delete time too).
549					event->type=espeakEVENT_LIST_TERMINATED;
550					event->user_data=NULL;
551				}
552	
553				a_status = pthread_mutex_lock(&my_mutex);
554				SHOW_TIME("polling_thread > locked\n");
555				event_delete( (espeak_EVENT*)pop());
556				a_status = pthread_mutex_unlock(&my_mutex);
557				SHOW_TIME("polling_thread > unlocked\n");
558			
559				a_stop_is_required=0;
560				a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
561	
562				if ((a_status==0) && a_stop_is_required)
563				{
564					SHOW("polling_thread > stop required (%d)\n", __LINE__);
565					while(0 == sem_trywait(&my_sem_stop_is_required))
566					{
567					};
568				}
569				else
570				{
571					a_stop_is_required=0;
572				}
573			}
574			else
575			{ // The event will be notified soon: sleep until timeout or stop request
576				a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms);
577			}
578		}
579	
580		a_status = pthread_mutex_lock(&my_mutex);
581		SHOW_TIME("polling_thread > locked\n");
582	
583		SHOW_TIME("polling_thread > my_event_is_running = 0\n");
584		my_event_is_running = 0;
585	
586		if(!a_stop_is_required)
587		{
588			a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
589			if ((a_status==0) && a_stop_is_required)
590			{
591				SHOW("polling_thread > stop required (%d)\n", __LINE__);
592				while(0 == sem_trywait(&my_sem_stop_is_required))
593				{
594				};
595			}
596			else
597			{
598				a_stop_is_required=0;
599			}
600		}
601
602		a_status = pthread_mutex_unlock(&my_mutex);
603		SHOW_TIME("polling_thread > unlocked\n");
604
605		if (a_stop_is_required)
606		{ 
607			SHOW("polling_thread > %s\n","stop required!");
608			// no mutex required since the stop command is synchronous
609			// and waiting for my_sem_stop_is_acknowledged
610			init();
611
612			// acknowledge the stop request
613			SHOW_TIME("polling_thread > post my_sem_stop_is_acknowledged\n");
614			a_status = sem_post(&my_sem_stop_is_acknowledged);
615		}
616	}
617
618	return NULL;
619}
620
621//>
622//<push, pop, init
623enum {MAX_NODE_COUNTER=1000};
624// return 1 if ok, 0 otherwise
625static espeak_ERROR push(void* the_data)
626{
627	ENTER("event > push");
628
629	assert((!head && !tail) || (head && tail));
630
631	if (the_data == NULL)
632	{
633		SHOW("event > push > event=0x%x\n", NULL);
634		return EE_INTERNAL_ERROR;
635	}
636
637	if (node_counter >= MAX_NODE_COUNTER)
638	{
639		SHOW("event > push > %s\n", "EE_BUFFER_FULL");
640		return EE_BUFFER_FULL;
641	}
642	
643	node *n = (node *)malloc(sizeof(node));
644	if (n == NULL)
645	{
646		return EE_INTERNAL_ERROR;
647	}
648	
649	if (head == NULL)
650	{
651		head = n;
652		tail = n;
653	}
654	else
655	{
656		tail->next = n;
657		tail = n;
658	}
659	
660	tail->next = NULL;
661	tail->data = the_data;
662	
663	node_counter++;
664	SHOW("event > push > counter=%d (uid=%d)\n",node_counter,((espeak_EVENT*)the_data)->unique_identifier);
665	
666	return EE_OK;
667}
668
669static void* pop()
670{
671	ENTER("event > pop");
672	void* the_data = NULL;
673	
674	assert((!head && !tail) || (head && tail));
675	
676	if (head != NULL)
677	{
678		node* n = head;
679		the_data = n->data;
680		head = n->next;
681		free(n);
682		node_counter--;
683		SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n",the_data, node_counter,((espeak_EVENT*)the_data)->unique_identifier);
684	}
685	
686	if(head == NULL)
687	{
688		tail = NULL;
689	}
690	
691	return the_data;
692}
693
694
695static void init()
696{
697	ENTER("event > init");
698	
699	while (event_delete( (espeak_EVENT*)pop() ))
700	{}
701	
702	node_counter = 0;
703}
704
705//>
706//<event_terminate
707void event_terminate()
708{
709ENTER("event_terminate");
710	
711	if (my_thread)
712	{
713		pthread_cancel(my_thread);
714		pthread_join(my_thread,NULL);
715		pthread_mutex_destroy(&my_mutex);
716		sem_destroy(&my_sem_start_is_required);
717		sem_destroy(&my_sem_stop_is_required);
718		sem_destroy(&my_sem_stop_is_acknowledged);
719		init(); // purge event
720	}
721}
722
723#endif
724//>
725