1 /*
2  * festival.c - Speech Dispatcher backend for Festival
3  *
4  * Copyright (C) 2003, 2007 Brailcom, o.p.s.
5  *
6  * This is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This software 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 GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  * $Id: festival.c,v 1.82 2008-06-09 10:33:38 hanke Exp $
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <stdio.h>
27 #include <semaphore.h>
28 
29 #include <speechd_types.h>
30 #include "fdsetconv.h"
31 
32 #include "festival_client.h"
33 #include "module_utils.h"
34 
35 #define MODULE_NAME     "festival"
36 #define MODULE_VERSION  "0.5"
37 
38 DECLARE_DEBUG()
39 
40 /* Thread and process control */
41 static pthread_t festival_speak_thread;
42 static sem_t festival_semaphore;
43 static int festival_speaking = 0;
44 static int festival_pause_requested = 0;
45 
46 static char *festival_message;
47 static SPDMessageType festival_message_type;
48 signed int festival_volume = 0;
49 
50 int festival_stop_request = 0;
51 int festival_stop = 0;
52 
53 int festival_process_pid = 0;
54 
55 FT_Info *festival_info = NULL;
56 
57 SPDVoice **festival_voice_list = NULL;
58 
59 enum {
60 	FCT_SOCKET = 0,
61 	FCT_LOCAL = 1,
62 } FestivalComType;
63 
64 struct {
65 	int pipe_in[2];
66 	int pipe_out[2];
67 	int pid;
68 } module_p;
69 
70 #define COM_SOCKET ((FestivalComType == FCT_SOCKET) ? 1 : 0)
71 #define COM_LOCAL ((FestivalComType == FCT_LOCAL) ? 1 : 0)
72 
73 /* --- SETTINGS COMMANDS --- */
74 
75 #define FEST_SET_STR(name, fest_param) \
76 	int \
77 	name(FT_Info *info, char *param, char **resp) \
78 	{ \
79 		char *r; \
80 		int ret; \
81 		char *f; \
82 		if (festival_check_info(info, #name)) return -1; \
83 		if (param == NULL){ \
84 			FEST_SEND_CMD("("fest_param" nil)"); \
85 		}else{ \
86 			f = g_ascii_strdown(param, -1); \
87 			FEST_SEND_CMDA("("fest_param" \"%s\")", f); \
88 			g_free(f); \
89 		} \
90 		ret = festival_read_response(info, &r); \
91 		if (ret != 0) return -1; \
92 		if (r != NULL){ \
93 			if (resp != NULL) \
94 				*resp = r; \
95 			else \
96 				g_free(r); \
97 		} \
98 		return ret; \
99 	}
100 
101 #define FEST_SET_SYMB(name, fest_param) \
102 	int \
103 	name(FT_Info *info, char *param) \
104 	{ \
105 		char *f = NULL; \
106 		if (festival_check_info(info, #name)) return -1; \
107 		if (param == NULL) return -1; \
108 		FEST_SEND_CMDA("("fest_param" '%s)", f = g_ascii_strdown(param, -1)); \
109 		g_free(f); \
110 		return festival_read_response(info, NULL); \
111 	}
112 
113 #define FEST_SET_INT(name, fest_param) \
114 	int \
115 	name(FT_Info *info, int param) \
116 	{ \
117 		if (festival_check_info(info, #name)) return -1; \
118 		FEST_SEND_CMDA("("fest_param" %d)", param); \
119 		return festival_read_response(info, NULL); \
120 	}
121 
122 FEST_SET_SYMB(FestivalSetMultiMode, "speechd-enable-multi-mode")
123 
124     FEST_SET_INT(FestivalSetRate, "speechd-set-rate")
125     FEST_SET_INT(FestivalSetPitch, "speechd-set-pitch")
126     FEST_SET_SYMB(FestivalSetPunctuationMode, "speechd-set-punctuation-mode")
127     FEST_SET_STR(FestivalSetCapLetRecogn,
128 	     "speechd-set-capital-character-recognition-mode")
129     FEST_SET_STR(FestivalSetLanguage, "speechd-set-language")
130     FEST_SET_STR(FestivalSetVoice, "speechd-set-voice")
131     FEST_SET_SYMB(FestivalSetSynthesisVoice, "speechd-set-festival-voice")
132 
133 /* Internal functions prototypes */
134 static SPDVoice **festivalGetVoices(FT_Info * info);
135 void *_festival_speak(void *);
136 
137 void festival_parent_clean();
138 
139 void festival_set_rate(signed int rate);
140 void festival_set_pitch(signed int pitch);
141 void festival_set_voice(SPDVoiceType voice);
142 void festival_set_synthesis_voice(char *synthesis_voice);
143 void festival_set_language(char *language);
144 void festival_set_punctuation_mode(SPDPunctuation punct);
145 void festival_set_cap_let_recogn(SPDCapitalLetters recogn);
146 void festival_set_volume(signed int volume);
147 
148 int init_festival_standalone();
149 int init_festival_socket();
150 
151 int is_text(SPDMessageType msg_type);
152 
153 MOD_OPTION_1_INT(FestivalComunicationType)
154 
155     MOD_OPTION_1_INT(FestivalMaxChunkLength)
156     MOD_OPTION_1_STR(FestivalDelimiters)
157     MOD_OPTION_1_STR(FestivalServerHost)
158     MOD_OPTION_1_STR(FestivalStripPunctChars)
159     MOD_OPTION_1_INT(FestivalServerPort)
160     MOD_OPTION_1_INT(FestivalPitchDeviation)
161     MOD_OPTION_1_INT(FestivalDebugSaveOutput)
162     MOD_OPTION_1_STR(FestivalRecodeFallback)
163 
164     MOD_OPTION_1_INT(FestivalCacheOn)
165     MOD_OPTION_1_INT(FestivalCacheMaxKBytes)
166     MOD_OPTION_1_INT(FestivalCacheDistinguishVoices)
167     MOD_OPTION_1_INT(FestivalCacheDistinguishRate)
168     MOD_OPTION_1_INT(FestivalCacheDistinguishPitch)
169 
170     MOD_OPTION_1_INT(FestivalReopenSocket)
171 
172 typedef struct {
173 	size_t size;
174 	GHashTable *caches;
175 	GList *cache_counter;
176 } TCache;
177 
178 typedef struct {
179 	time_t start;
180 	int count;
181 	size_t size;
182 	GHashTable *p_caches;
183 	char *key;
184 } TCounterEntry;
185 
186 typedef struct {
187 	TCounterEntry *p_counter_entry;
188 	FT_Wave *fwave;
189 } TCacheEntry;
190 
191 TCache FestivalCache;
192 
193 int cache_init();
194 int cache_reset();
195 int cache_insert(char *key, SPDMessageType msgtype, FT_Wave * value);
196 FT_Wave *cache_lookup(const char *key, SPDMessageType msgtype, int add_counter);
197 
198 pthread_mutex_t sound_output_mutex;
199 
200 /* Public functions */
201 
module_load(void)202 int module_load(void)
203 {
204 
205 	INIT_SETTINGS_TABLES();
206 
207 	REGISTER_DEBUG();
208 
209 	MOD_OPTION_1_INT_REG(FestivalComunicationType, 0);
210 
211 	MOD_OPTION_1_STR_REG(FestivalServerHost, "localhost");
212 	MOD_OPTION_1_INT_REG(FestivalServerPort, 1314);
213 
214 	MOD_OPTION_1_INT_REG(FestivalDebugSaveOutput, 0);
215 
216 	MOD_OPTION_1_STR_REG(FestivalRecodeFallback, "?");
217 
218 	MOD_OPTION_1_INT_REG(FestivalCacheOn, 1);
219 	MOD_OPTION_1_INT_REG(FestivalCacheMaxKBytes, 5120);
220 	MOD_OPTION_1_INT_REG(FestivalCacheDistinguishVoices, 0);
221 	MOD_OPTION_1_INT_REG(FestivalCacheDistinguishRate, 0);
222 	MOD_OPTION_1_INT_REG(FestivalCacheDistinguishPitch, 0);
223 
224 	/* TODO: Maybe switch this option to 1 when the bug with the 40ms delay
225 	   in Festival is fixed */
226 	MOD_OPTION_1_INT_REG(FestivalReopenSocket, 0);
227 
228 	return 0;
229 }
230 
231 #define ABORT(msg) g_string_append(info, msg); \
232 	*status_info = info->str; \
233 	g_string_free(info, 0); \
234 	return -1;
235 
module_init(char ** status_info)236 int module_init(char **status_info)
237 {
238 	int ret;
239 
240 	GString *info;
241 
242 	info = g_string_new("");
243 
244 	DBG("module_init()");
245 
246 	INIT_INDEX_MARKING();
247 
248 	/* Initialize appropriate communication mechanism */
249 	FestivalComType = FestivalComunicationType;
250 	if (COM_SOCKET) {
251 		g_string_append(info,
252 				"Communicating with Festival through a socket. ");
253 		ret = init_festival_socket();
254 		if (ret == -1) {
255 			ABORT
256 			    ("Can't connect to Festival server. Check your configuration "
257 			     "in etc/speech-dispatcher/modules/festival.conf for the specified host and port "
258 			     "and check if Festival is really running there, e.g. with telnet. "
259 			     "Please see documentation for more info.");
260 		} else if (ret == -2) {
261 			ABORT("Connect to the Festival server was successful, "
262 			      "but I got disconnected immediately. This is most likely "
263 			      "because of authorization problems. Check the variable "
264 			      "server_access_list in etc/festival.scm and consult documentation "
265 			      "for more information.");
266 		}
267 	}
268 	if (COM_LOCAL) {
269 		g_string_append(info,
270 				"Communicating with Festival through a local child process.");
271 		if (init_festival_standalone()) {
272 			ABORT
273 			    ("Local connect to Festival failed for unknown reasons.");
274 		}
275 	}
276 
277 	/* Get festival voice list */
278 	festival_voice_list = festivalGetVoices(festival_info);
279 
280 	/* Initialize global variables */
281 	festival_message = NULL;
282 
283 	/* Initialize festival_speak thread to handle communication
284 	   with festival in a separate thread (to be faster in communication
285 	   with Speech Dispatcher) */
286 
287 	sem_init(&festival_semaphore, 0, 0);
288 
289 	DBG("Festival: creating new thread for festival_speak\n");
290 	festival_speaking = 0;
291 	ret =
292 	    pthread_create(&festival_speak_thread, NULL, _festival_speak, NULL);
293 	if (ret != 0) {
294 		DBG("Festival: thread failed\n");
295 		g_string_append(info, "The module couldn't initialize threads"
296 				"This can be either an internal problem or an"
297 				"architecture problem. If you are sure your architecture"
298 				"supports threads, please report a bug.");
299 		*status_info = info->str;
300 		g_string_free(info, 0);
301 		return -1;
302 	}
303 
304 	pthread_mutex_init(&sound_output_mutex, NULL);
305 
306 	*status_info = info->str;
307 	g_string_free(info, 0);
308 
309 	return 0;
310 }
311 
312 #undef ABORT
313 
module_list_voices(void)314 SPDVoice **module_list_voices(void)
315 {
316 	return festival_voice_list;
317 }
318 
module_speak(char * data,size_t bytes,SPDMessageType msgtype)319 int module_speak(char *data, size_t bytes, SPDMessageType msgtype)
320 {
321 	int ret;
322 
323 	DBG("module_speak()\n");
324 
325 	if (data == NULL)
326 		return -1;
327 
328 	if (festival_speaking) {
329 		DBG("Speaking when requested to write\n");
330 		return -1;
331 	}
332 
333 	festival_stop_request = 0;
334 
335 	festival_message_type = msgtype;
336 	if ((msgtype == SPD_MSGTYPE_TEXT)
337 	    && (msg_settings.spelling_mode == SPD_SPELL_ON))
338 		festival_message_type = SPD_MSGTYPE_SPELL;
339 
340 	/* If the connection crashed or language or voice
341 	   change, we will need to set all the parameters again */
342 	if (COM_SOCKET) {
343 		if (festival_connection_crashed) {
344 			DBG("Recovering after a connection loss");
345 			CLEAN_OLD_SETTINGS_TABLE();
346 			festival_info = festivalOpen(festival_info);
347 			if (festival_info)
348 				festival_connection_crashed = 0;
349 			else {
350 				DBG("Can't recover. Not possible to open connection to Festival.");
351 				return -1;
352 			}
353 			ret = FestivalSetMultiMode(festival_info, "t");
354 			if (ret != 0)
355 				return -1;
356 		}
357 	}
358 
359 	/* If the voice was changed, re-set all the parameters */
360 	// TODO: Handle synthesis_voice change too
361 	if ((msg_settings.voice_type != msg_settings_old.voice_type)
362 	    || ((msg_settings.voice.language != NULL)
363 		&& (msg_settings_old.voice.language != NULL)
364 		&&
365 		(strcmp
366 		 (msg_settings.voice.language,
367 		  msg_settings_old.voice.language)))) {
368 		DBG("Cleaning old settings table");
369 		CLEAN_OLD_SETTINGS_TABLE();
370 	}
371 
372 	/* Setting voice parameters */
373 	DBG("Updating parameters");
374 	UPDATE_STRING_PARAMETER(voice.language, festival_set_language);
375 	UPDATE_PARAMETER(voice_type, festival_set_voice);
376 	UPDATE_STRING_PARAMETER(voice.name, festival_set_synthesis_voice);
377 	UPDATE_PARAMETER(rate, festival_set_rate);
378 	UPDATE_PARAMETER(pitch, festival_set_pitch);
379 	UPDATE_PARAMETER(volume, festival_set_volume);
380 	UPDATE_PARAMETER(punctuation_mode, festival_set_punctuation_mode);
381 	UPDATE_PARAMETER(cap_let_recogn, festival_set_cap_let_recogn);
382 
383 	if (festival_connection_crashed) {
384 		DBG("ERROR: Festival connection not working!");
385 		return -1;
386 	}
387 
388 	DBG("Requested data: |%s| \n", data);
389 
390 	g_free(festival_message);
391 	festival_message = g_strdup(data);
392 	if (festival_message == NULL) {
393 		DBG("Error: Copying data unsuccessful.");
394 		return -1;
395 	}
396 
397 	/* Send semaphore signal to the speaking thread */
398 	festival_speaking = 1;
399 	sem_post(&festival_semaphore);
400 
401 	DBG("Festival: leaving write() normally\n\r");
402 	return bytes;
403 }
404 
module_stop(void)405 int module_stop(void)
406 {
407 	DBG("stop()\n");
408 
409 	if (festival_speaking) {
410 		/* if(COM_SOCKET) */
411 		if (0) {
412 			if (festival_info != 0)
413 				if ((festival_info->server_fd != -1)
414 				    && FestivalReopenSocket) {
415 					/* TODO: Maybe use shutdown here? */
416 					close(festival_info->server_fd);
417 					festival_info->server_fd = -1;
418 					festival_connection_crashed = 1;
419 					DBG("festival socket closed by module_stop()");
420 				}
421 		}
422 		if (COM_LOCAL) {
423 			DBG("festival local stopped by sending SIGINT");
424 			/* TODO: Write this function for local communication */
425 			//      festival_stop_local();
426 		}
427 
428 		if (!festival_stop) {
429 			pthread_mutex_lock(&sound_output_mutex);
430 			festival_stop = 1;
431 			if (festival_speaking && module_audio_id) {
432 				spd_audio_stop(module_audio_id);
433 			}
434 			pthread_mutex_unlock(&sound_output_mutex);
435 		}
436 	}
437 
438 	return 0;
439 }
440 
module_pause(void)441 size_t module_pause(void)
442 {
443 	DBG("pause requested\n");
444 	if (festival_speaking) {
445 		DBG("Sending request for pause to child\n");
446 		festival_pause_requested = 1;
447 		DBG("Signalled to pause");
448 		return 0;
449 	} else {
450 		return -1;
451 	}
452 }
453 
module_close(void)454 int module_close(void)
455 {
456 
457 	DBG("festival: close()\n");
458 
459 	DBG("Stopping the module");
460 	while (festival_speaking) {
461 		module_stop();
462 		usleep(50);
463 	}
464 
465 	// DBG("festivalClose()");
466 	// festivalClose(festival_info);
467 
468 	DBG("Terminating threads");
469 	if (festival_speak_thread)
470 		module_terminate_thread(festival_speak_thread);
471 
472 	if (festival_info)
473 		delete_FT_Info(festival_info);
474 
475 	/* TODO: Solve this */
476 	//    DBG("Removing junk files in tmp/");
477 	//    system("rm -f /tmp/est* 2> /dev/null");
478 
479 	sem_destroy(&festival_semaphore);
480 	return 0;
481 }
482 
483 /* Internal functions */
484 
485 #define CLEAN_UP(code, im) \
486 	{ \
487 		if(!wave_cached) if (fwave) delete_FT_Wave(fwave); \
488 		pthread_mutex_lock(&sound_output_mutex); \
489 		festival_stop = 0; \
490 		festival_speaking = 0; \
491 		pthread_mutex_unlock(&sound_output_mutex); \
492 		im(); \
493 		goto sem_wait; \
494 	}
495 
496 #define CLP(code, im) \
497 	{ \
498 		pthread_mutex_lock(&sound_output_mutex); \
499 		festival_stop = 0; \
500 		festival_speaking = 0; \
501 		pthread_mutex_unlock(&sound_output_mutex); \
502 		im(); \
503 		goto sem_wait; \
504 	}
505 
festivalGetVoices(FT_Info * info)506 static SPDVoice **festivalGetVoices(FT_Info * info)
507 {
508 	char *reply;
509 	char **voices;
510 	char *lang;
511 	char *region;
512 	int i, j;
513 	int num_voices = 0;
514 	SPDVoice **result;
515 
516 	FEST_SEND_CMD("(apply append (voice-list-language-codes))");
517 	festival_read_response(info, &reply);
518 	if (reply == NULL) {
519 		DBG("ERROR: Invalid reply for voice-list");
520 		return NULL;
521 	}
522 	/* Remove trailing newline */
523 	reply[strlen(reply) - 1] = 0;
524 	DBG("Voice list reply: |%s|", reply);
525 	voices = lisp_list_get_vect(reply);
526 	if (voices == NULL) {
527 		DBG("ERROR: Can't parse voice listing reply into vector");
528 		return NULL;
529 	}
530 
531 	/* Compute number of voices */
532 	for (i = 0;; i++, num_voices++)
533 		if (voices[i] == NULL)
534 			break;
535 	num_voices /= 3;
536 
537 	result = g_malloc((num_voices + 1) * sizeof(SPDVoice *));
538 
539 	for (i = 0, j = 0;; j++) {
540 		if (voices[i] == NULL)
541 			break;
542 		else if (strlen(voices[i]) == 0)
543 			continue;
544 		else {
545 			result[j] = g_malloc(sizeof(SPDVoice));
546 			result[j]->name = voices[i];
547 			lang = voices[i + 1];
548 			if (lang && !strcmp(lang, "nil"))
549 				lang = NULL;
550 			region = voices[i + 2];
551 			if (region && !strcmp(region, "nil"))
552 				region = NULL;
553 			if (lang && region)
554 				result[j]->language = g_strdup_printf("%s-%s", lang, region);
555 			else if (lang)
556 				result[j]->language = g_strdup(lang);
557 			else if (region)
558 				result[j]->language = g_strdup(region);
559 			else
560 				result[j]->language = NULL;
561 			result[j]->variant = NULL;
562 			i += 3;
563 		}
564 	}
565 	result[j] = NULL;
566 	return result;
567 }
568 
festival_send_to_audio(FT_Wave * fwave)569 int festival_send_to_audio(FT_Wave * fwave)
570 {
571 	AudioTrack track;
572 #if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
573 	AudioFormat format = SPD_AUDIO_BE;
574 #else
575 	AudioFormat format = SPD_AUDIO_LE;
576 #endif
577 	int ret = 0;
578 
579 	if (fwave->samples == NULL)
580 		return 0;
581 
582 	track.num_samples = fwave->num_samples;
583 	track.num_channels = 1;
584 	track.sample_rate = fwave->sample_rate;
585 	track.bits = 16;
586 	track.samples = fwave->samples;
587 
588 	DBG("Sending to audio");
589 
590 	ret = module_tts_output(track, format);
591 	if (ret < 0)
592 		DBG("ERROR: Can't play track for unknown reason.");
593 	DBG("Sent to audio.");
594 
595 	return 0;
596 }
597 
_festival_speak(void * nothing)598 void *_festival_speak(void *nothing)
599 {
600 
601 	int ret;
602 	int bytes;
603 	int wave_cached;
604 	FT_Wave *fwave;
605 	int debug_count = 0;
606 	int r;
607 	int terminate = 0;
608 
609 	char *callback;
610 
611 	DBG("festival: speaking thread starting.......\n");
612 
613 	cache_init();
614 
615 	set_speaking_thread_parameters();
616 
617 	while (1) {
618 sem_wait:
619 		sem_wait(&festival_semaphore);
620 		DBG("Semaphore on, speaking\n");
621 
622 		festival_stop = 0;
623 		festival_speaking = 1;
624 		wave_cached = 0;
625 		fwave = NULL;
626 
627 		terminate = 0;
628 
629 		bytes = strlen(festival_message);
630 
631 		module_report_event_begin();
632 
633 		DBG("Going to synthesize: |%s|", festival_message);
634 		if (bytes > 0) {
635 			if (!is_text(festival_message_type)) {	/* it is a raw text */
636 				DBG("Cache mechanisms...");
637 				fwave =
638 				    cache_lookup(festival_message,
639 						 festival_message_type, 1);
640 				if (fwave != NULL) {
641 					wave_cached = 1;
642 					if (fwave->num_samples != 0) {
643 						if (FestivalDebugSaveOutput) {
644 							char filename_debug
645 							    [256];
646 							sprintf(filename_debug,
647 								"/tmp/debug-festival-%d.snd",
648 								debug_count++);
649 							save_FT_Wave_snd(fwave,
650 									 filename_debug);
651 						}
652 
653 						festival_send_to_audio(fwave);
654 
655 						if (!festival_stop) {
656 							CLEAN_UP(0,
657 								 module_report_event_end);
658 						} else {
659 							CLEAN_UP(0,
660 								 module_report_event_stop);
661 						}
662 
663 					} else {
664 						CLEAN_UP(0,
665 							 module_report_event_end);
666 					}
667 				}
668 			}
669 
670 			/*  Set multi-mode for appropriate kind of events */
671 			if (is_text(festival_message_type)) {	/* it is a raw text */
672 				ret = FestivalSetMultiMode(festival_info, "t");
673 				if (ret != 0)
674 					CLP(0, module_report_event_stop);
675 			} else {	/* it is some kind of event */
676 				ret =
677 				    FestivalSetMultiMode(festival_info, "nil");
678 				if (ret != 0)
679 					CLP(0, module_report_event_stop);
680 			}
681 
682 			switch (festival_message_type) {
683 			case SPD_MSGTYPE_TEXT:
684 				r = festivalStringToWaveRequest(festival_info,
685 								festival_message);
686 				break;
687 			case SPD_MSGTYPE_SOUND_ICON:
688 				r = festivalSoundIcon(festival_info,
689 						      festival_message);
690 				break;
691 			case SPD_MSGTYPE_CHAR:
692 				r = festivalCharacter(festival_info,
693 						      festival_message);
694 				break;
695 			case SPD_MSGTYPE_KEY:
696 				/* TODO: make sure all SSIP cases are supported */
697 				r = festivalKey(festival_info,
698 						festival_message);
699 				break;
700 			case SPD_MSGTYPE_SPELL:
701 				r = festivalSpell(festival_info,
702 						  festival_message);
703 				break;
704 			default:
705 				r = -1;
706 			}
707 			if (r < 0) {
708 				DBG("Couldn't process the request to say the object.");
709 				CLP(0, module_report_event_stop);
710 			}
711 		}
712 
713 		while (1) {
714 
715 			wave_cached = 0;
716 			DBG("Retrieving data\n");
717 
718 			/* (speechd-next) */
719 			if (is_text(festival_message_type)) {
720 
721 				if (festival_stop) {
722 					DBG("Module stopped 1");
723 					CLEAN_UP(0, module_report_event_stop);
724 				}
725 
726 				DBG("Getting data in multi mode");
727 				fwave =
728 				    festivalGetDataMulti(festival_info,
729 							 &callback,
730 							 &festival_stop_request,
731 							 FestivalReopenSocket);
732 
733 				if (callback != NULL) {
734 					if ((festival_pause_requested)
735 					    &&
736 					    (!strncmp
737 					     (callback, INDEX_MARK_BODY,
738 					      INDEX_MARK_BODY_LEN))) {
739 						DBG("Pause requested, pausing.");
740 						module_report_index_mark
741 						    (callback);
742 						g_free(callback);
743 						festival_pause_requested = 0;
744 						CLEAN_UP(0,
745 							 module_report_event_pause);
746 					} else {
747 						module_report_index_mark
748 						    (callback);
749 						g_free(callback);
750 						continue;
751 					}
752 				}
753 			} else {	/* is event */
754 				DBG("Getting data in single mode");
755 				fwave =
756 				    festivalStringToWaveGetData(festival_info);
757 				terminate = 1;
758 				callback = NULL;
759 			}
760 
761 			if (fwave == NULL) {
762 				DBG("End of sound samples, terminating this message...");
763 				CLEAN_UP(0, module_report_event_end);
764 			}
765 
766 			if (festival_message_type == SPD_MSGTYPE_CHAR
767 			    || festival_message_type == SPD_MSGTYPE_KEY
768 			    || festival_message_type ==
769 			    SPD_MSGTYPE_SOUND_ICON) {
770 				DBG("Storing record for %s in cache\n",
771 				    festival_message);
772 				/* cache_insert takes care of not inserting the same
773 				   message again */
774 				cache_insert(g_strdup(festival_message),
775 					     festival_message_type, fwave);
776 				wave_cached = 1;
777 			}
778 
779 			if (festival_stop) {
780 				DBG("Module stopped 2");
781 				CLEAN_UP(0, module_report_event_stop);
782 			}
783 
784 			if (fwave->num_samples != 0) {
785 				DBG("Sending message to audio: %lu bytes\n",
786 				    (long unsigned)((fwave->num_samples) *
787 						    sizeof(short)));
788 
789 				if (FestivalDebugSaveOutput) {
790 					char filename_debug[256];
791 					sprintf(filename_debug,
792 						"/tmp/debug-festival-%d.snd",
793 						debug_count++);
794 					save_FT_Wave_snd(fwave, filename_debug);
795 				}
796 
797 				DBG("Playing sound samples");
798 				festival_send_to_audio(fwave);
799 
800 				if (!wave_cached)
801 					delete_FT_Wave(fwave);
802 				DBG("End of playing sound samples");
803 			}
804 
805 			if (terminate) {
806 				DBG("Ok, end of samples, returning");
807 				CLP(0, module_report_event_end);
808 			}
809 
810 			if (festival_stop) {
811 				DBG("Module stopped 3");
812 				CLP(0, module_report_event_stop);
813 			}
814 		}
815 
816 	}
817 
818 	festival_stop = 0;
819 	festival_speaking = 0;
820 
821 	DBG("festival: speaking thread ended.......\n");
822 
823 	pthread_exit(NULL);
824 }
825 
is_text(SPDMessageType msg_type)826 int is_text(SPDMessageType msg_type)
827 {
828 	if (msg_type == SPD_MSGTYPE_TEXT || msg_type == SPD_MSGTYPE_SPELL)
829 		return 1;
830 	else
831 		return 0;
832 }
833 
festival_set_language(char * language)834 void festival_set_language(char *language)
835 {
836 	FestivalSetLanguage(festival_info, language, NULL);
837 	g_free(festival_voice_list);
838 	festival_voice_list = festivalGetVoices(festival_info);
839 }
840 
festival_set_voice(SPDVoiceType voice)841 void festival_set_voice(SPDVoiceType voice)
842 {
843 	char *voice_name;
844 
845 	voice_name = EVoice2str(voice);
846 	FestivalSetVoice(festival_info, voice_name, NULL);
847 	g_free(voice_name);
848 }
849 
festival_set_synthesis_voice(char * voice_name)850 void festival_set_synthesis_voice(char *voice_name)
851 {
852 
853 	FestivalSetSynthesisVoice(festival_info, voice_name);
854 }
855 
festival_set_rate(signed int rate)856 void festival_set_rate(signed int rate)
857 {
858 	FestivalSetRate(festival_info, rate);
859 }
860 
festival_set_pitch(signed int pitch)861 void festival_set_pitch(signed int pitch)
862 {
863 	FestivalSetPitch(festival_info, pitch);
864 }
865 
festival_set_volume(signed int volume)866 void festival_set_volume(signed int volume)
867 {
868 	festival_volume = volume;
869 }
870 
festival_set_punctuation_mode(SPDPunctuation punct)871 void festival_set_punctuation_mode(SPDPunctuation punct)
872 {
873 	char *punct_mode;
874 	punct_mode = EPunctMode2str(punct);
875 	FestivalSetPunctuationMode(festival_info, punct_mode);
876 	g_free(punct_mode);
877 }
878 
festival_set_cap_let_recogn(SPDCapitalLetters recogn)879 void festival_set_cap_let_recogn(SPDCapitalLetters recogn)
880 {
881 	char *recogn_mode;
882 
883 	if (recogn == SPD_CAP_NONE)
884 		recogn_mode = NULL;
885 	else
886 		recogn_mode = ECapLetRecogn2str(recogn);
887 	FestivalSetCapLetRecogn(festival_info, recogn_mode, NULL);
888 	g_free(recogn_mode);
889 }
890 
891 /* --- Cache related functions --- */
892 
cache_destroy_entry(gpointer data)893 void cache_destroy_entry(gpointer data)
894 {
895 	TCacheEntry *entry = data;
896 	g_free(entry->fwave);
897 	g_free(entry);
898 }
899 
cache_destroy_table_entry(gpointer data)900 void cache_destroy_table_entry(gpointer data)
901 {
902 	g_hash_table_destroy(data);
903 }
904 
cache_free_counter_entry(gpointer data,gpointer user_data)905 void cache_free_counter_entry(gpointer data, gpointer user_data)
906 {
907 	g_free(((TCounterEntry *) data)->key);
908 	g_free(data);
909 }
910 
cache_init()911 int cache_init()
912 {
913 
914 	if (FestivalCacheOn == 0)
915 		return 0;
916 
917 	FestivalCache.size = 0;
918 	FestivalCache.caches =
919 	    g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
920 				  cache_destroy_table_entry);
921 	FestivalCache.cache_counter = NULL;
922 	DBG("Cache: initialized");
923 	return 0;
924 }
925 
cache_destroy()926 int cache_destroy()
927 {
928 	g_hash_table_destroy(FestivalCache.caches);
929 	g_list_foreach(FestivalCache.cache_counter, cache_free_counter_entry,
930 		       NULL);
931 	g_list_free(FestivalCache.cache_counter);
932 	return 0;
933 }
934 
cache_reset()935 int cache_reset()
936 {
937 	/* TODO: it could free everything in the cache and go from start,
938 	   but currently it isn't called by anybody */
939 	return 0;
940 }
941 
942 /* Compare two cache entries according to their score (how many
943    times the entry was requested divided by the time it exists
944    in the database) */
cache_counter_comp(gconstpointer a,gconstpointer b)945 gint cache_counter_comp(gconstpointer a, gconstpointer b)
946 {
947 	const TCounterEntry *A = a;
948 	const TCounterEntry *B = b;
949 	time_t t;
950 	float ret;
951 
952 	t = time(NULL);
953 	ret = (((float)A->count / (float)(t - A->start))
954 	       - ((float)B->count / (float)(t - B->start)));
955 
956 	if (ret > 0)
957 		return -1;
958 	if (ret == 0)
959 		return 0;
960 	if (ret < 0)
961 		return 1;
962 
963 	return 0;
964 }
965 
966 /* List scores of all entries in the cache*/
cache_debug_foreach_list_score(gpointer a,gpointer user)967 void cache_debug_foreach_list_score(gpointer a, gpointer user)
968 {
969 	const TCounterEntry *A = a;
970 
971 	DBG("key: %s      -> score %f (count: %d, dtime: %d)", A->key,
972 	    ((float)A->count / (float)(time(NULL) - A->start)), (int)A->count,
973 	    (int)(time(NULL) - A->start));
974 }
975 
976 /* Remove 1/3 of the least used (according to cache_counter_comp) entries
977    (measured by size) */
cache_clean(size_t new_element_size)978 int cache_clean(size_t new_element_size)
979 {
980 	size_t req_size;
981 	GList *gl;
982 	TCounterEntry *centry;
983 
984 	DBG("Cache: cleaning, cache size %lu kbytes (>max %d).",
985 	    (unsigned long)(FestivalCache.size / 1024), FestivalCacheMaxKBytes);
986 
987 	req_size = 2 * FestivalCache.size / 3;
988 
989 	FestivalCache.cache_counter =
990 	    g_list_sort(FestivalCache.cache_counter, cache_counter_comp);
991 	g_list_foreach(FestivalCache.cache_counter,
992 		       cache_debug_foreach_list_score, NULL);
993 
994 	while ((FestivalCache.size + new_element_size) > req_size) {
995 		gl = g_list_last(FestivalCache.cache_counter);
996 		if (gl == NULL)
997 			break;
998 		if (gl->data == NULL) {
999 			DBG("Error: Cache: gl->data in cache_clean is NULL, but shouldn't be.");
1000 			return -1;
1001 		}
1002 		centry = gl->data;
1003 		FestivalCache.size -= centry->size;
1004 		DBG("Cache: Removing element with key '%s'", centry->key);
1005 		if (FestivalCache.size < 0) {
1006 			DBG("Error: Cache: FestivalCache.size < 0, this shouldn't be.");
1007 			return -1;
1008 		}
1009 		/* Remove the data itself from the hash table */
1010 		g_hash_table_remove(centry->p_caches, centry->key);
1011 		/* Remove the associated entry in the counter list */
1012 		cache_free_counter_entry(centry, NULL);
1013 		FestivalCache.cache_counter =
1014 		    g_list_delete_link(FestivalCache.cache_counter, gl);
1015 	}
1016 
1017 	return 0;
1018 }
1019 
1020 /* Generate a key for searching between the different hash tables */
cache_gen_key(SPDMessageType type)1021 char *cache_gen_key(SPDMessageType type)
1022 {
1023 	char *key;
1024 	char ktype;
1025 	int kpitch = 0, krate = 0, kvoice = 0;
1026 
1027 	if (msg_settings.voice.language == NULL)
1028 		return NULL;
1029 
1030 	DBG("v, p, r = %d %d %d", FestivalCacheDistinguishVoices,
1031 	    FestivalCacheDistinguishPitch, FestivalCacheDistinguishRate);
1032 
1033 	if (FestivalCacheDistinguishVoices)
1034 		kvoice = msg_settings.voice_type;
1035 	if (FestivalCacheDistinguishPitch)
1036 		kpitch = msg_settings.pitch;
1037 	if (FestivalCacheDistinguishRate)
1038 		krate = msg_settings.rate;
1039 
1040 	if (type == SPD_MSGTYPE_CHAR)
1041 		ktype = 'c';
1042 	else if (type == SPD_MSGTYPE_KEY)
1043 		ktype = 'k';
1044 	else if (type == SPD_MSGTYPE_SOUND_ICON)
1045 		ktype = 's';
1046 	else {
1047 		DBG("Invalid message type for cache_gen_key()");
1048 		return NULL;
1049 	}
1050 
1051 	key =
1052 	    g_strdup_printf("%c_%s_%d_%d_%d", ktype,
1053 			    msg_settings.voice.language, kvoice, krate, kpitch);
1054 
1055 	return key;
1056 }
1057 
1058 /* Insert one entry into the cache */
cache_insert(char * key,SPDMessageType msgtype,FT_Wave * fwave)1059 int cache_insert(char *key, SPDMessageType msgtype, FT_Wave * fwave)
1060 {
1061 	GHashTable *cache;
1062 	TCacheEntry *entry;
1063 	TCounterEntry *centry;
1064 	char *key_table;
1065 
1066 	if (FestivalCacheOn == 0)
1067 		return 0;
1068 
1069 	if (key == NULL)
1070 		return -1;
1071 	if (fwave == NULL)
1072 		return -1;
1073 
1074 	/* Check if the entry isn't present already */
1075 	if (cache_lookup(key, msgtype, 0) != NULL)
1076 		return 0;
1077 
1078 	key_table = cache_gen_key(msgtype);
1079 
1080 	DBG("Cache: Inserting wave with key:'%s' into table '%s'", key,
1081 	    key_table);
1082 
1083 	/* Clean less used cache entries if the size would exceed max. size */
1084 	if ((FestivalCache.size + fwave->num_samples * sizeof(short))
1085 	    > (FestivalCacheMaxKBytes * 1024))
1086 		if (cache_clean(fwave->num_samples * sizeof(short)) != 0)
1087 			return -1;
1088 
1089 	/* Select the right table according to language, voice, etc. or create a new one */
1090 	cache = g_hash_table_lookup(FestivalCache.caches, key_table);
1091 	if (cache == NULL) {
1092 		cache = g_hash_table_new(g_str_hash, g_str_equal);
1093 		g_hash_table_insert(FestivalCache.caches, key_table, cache);
1094 	} else {
1095 		g_free(key_table);
1096 	}
1097 
1098 	/* Fill the CounterEntry structure that will later allow us to remove
1099 	   the less used entries from cache */
1100 	centry = (TCounterEntry *) g_malloc(sizeof(TCounterEntry));
1101 	centry->start = time(NULL);
1102 	centry->count = 1;
1103 	centry->size = fwave->num_samples * sizeof(short);
1104 	centry->p_caches = cache;
1105 	centry->key = g_strdup(key);
1106 	FestivalCache.cache_counter =
1107 	    g_list_append(FestivalCache.cache_counter, centry);
1108 
1109 	entry = (TCacheEntry *) g_malloc(sizeof(TCacheEntry));
1110 	entry->p_counter_entry = centry;
1111 	entry->fwave = fwave;
1112 
1113 	FestivalCache.size += centry->size;
1114 	g_hash_table_insert(cache, g_strdup(key), entry);
1115 
1116 	return 0;
1117 }
1118 
1119 /* Retrieve wave from the cache */
cache_lookup(const char * key,SPDMessageType msgtype,int add_counter)1120 FT_Wave *cache_lookup(const char *key, SPDMessageType msgtype, int add_counter)
1121 {
1122 	GHashTable *cache;
1123 	TCacheEntry *entry;
1124 	char *key_table;
1125 
1126 	if (FestivalCacheOn == 0)
1127 		return NULL;
1128 	if (key == NULL)
1129 		return NULL;
1130 
1131 	key_table = cache_gen_key(msgtype);
1132 
1133 	if (add_counter)
1134 		DBG("Cache: looking up a wave with key '%s' in '%s'", key,
1135 		    key_table);
1136 
1137 	if (key_table == NULL)
1138 		return NULL;
1139 	cache = g_hash_table_lookup(FestivalCache.caches, key_table);
1140 	g_free(key_table);
1141 	if (cache == NULL)
1142 		return NULL;
1143 
1144 	entry = g_hash_table_lookup(cache, key);
1145 	if (entry == NULL)
1146 		return NULL;
1147 	entry->p_counter_entry->count++;
1148 
1149 	DBG("Cache: corresponding wave found: %s", key);
1150 
1151 	return entry->fwave;
1152 }
1153 
init_festival_standalone()1154 int init_festival_standalone()
1155 {
1156 	int ret;
1157 	int fr;
1158 
1159 	if ((pipe(module_p.pipe_in) != 0)
1160 	    || (pipe(module_p.pipe_out) != 0)) {
1161 		DBG("Can't open pipe! Module not loaded.");
1162 		return -1;
1163 	}
1164 
1165 	DBG("Starting Festival as a child process");
1166 
1167 	fr = fork();
1168 	switch (fr) {
1169 	case -1:
1170 		DBG("ERROR: Can't fork! Module not loaded.");
1171 		return -1;
1172 	case 0:
1173 		ret = dup2(module_p.pipe_in[0], 0);
1174 		close(module_p.pipe_in[0]);
1175 		close(module_p.pipe_in[1]);
1176 
1177 		ret = dup2(module_p.pipe_out[1], 1);
1178 		close(module_p.pipe_out[1]);
1179 		close(module_p.pipe_out[0]);
1180 
1181 		/* TODO: fix festival hardcoded path */
1182 		if (execlp("festival", "", (char *)0) == -1)
1183 			exit(1);
1184 
1185 	default:
1186 		festival_process_pid = fr;
1187 		close(module_p.pipe_in[0]);
1188 		close(module_p.pipe_out[1]);
1189 
1190 		usleep(100);	/* So that the other child has at least time to fail
1191 				   with the execlp */
1192 		ret = waitpid(module_p.pid, NULL, WNOHANG);
1193 		if (ret != 0) {
1194 			DBG("Can't execute festival. Bad filename in configuration?");
1195 			return -1;
1196 		}
1197 
1198 		return 0;
1199 	}
1200 
1201 	assert(0);
1202 }
1203 
init_festival_socket()1204 int init_festival_socket()
1205 {
1206 	int r;
1207 
1208 	/* Init festival and register a new voice */
1209 	festival_info = festivalDefaultInfo();
1210 	festival_info->server_host = FestivalServerHost;
1211 	festival_info->server_port = FestivalServerPort;
1212 
1213 	festival_info = festivalOpen(festival_info);
1214 	if (festival_info == NULL)
1215 		return -1;
1216 	r = FestivalSetMultiMode(festival_info, "t");
1217 	if (r != 0)
1218 		return -2;
1219 
1220 	DBG("FestivalServerHost = %s\n", FestivalServerHost);
1221 	DBG("FestivalServerPort = %d\n", FestivalServerPort);
1222 
1223 	return 0;
1224 }
1225 
stop_festival_local()1226 int stop_festival_local()
1227 {
1228 	if (festival_process_pid > 0)
1229 		kill(festival_process_pid, SIGINT);
1230 	return 0;
1231 }
1232