1 /*
2  * module_utils.c - Module utilities
3  *           Functions to help writing output modules for Speech Dispatcher
4  * Copyright (C) 2003,2006, 2007 Brailcom, o.p.s.
5  *
6  * This is free software; you can redistribute it and/or modify it under the
7  * terms of the GNU Lesser General Public License as published by the Free
8  * Software Foundation; either version 2.1, or (at your option) any later
9  * 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 Lesser General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  * $Id: module_utils.c,v 1.55 2008-07-10 15:37:18 hanke Exp $
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <sndfile.h>
27 
28 #include <fdsetconv.h>
29 #include <spd_utils.h>
30 #include "module_utils.h"
31 
32 static char *module_audio_pars[10];
33 
34 extern char *module_index_mark;
35 
36 pthread_mutex_t module_stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
37 
38 int log_level;
39 
40 AudioID *module_audio_id;
41 
42 SPDMsgSettings msg_settings;
43 SPDMsgSettings msg_settings_old;
44 
45 int current_index_mark;
46 
47 int Debug;
48 FILE *CustomDebugFile;
49 
50 configfile_t *configfile;
51 configoption_t *module_dc_options;
52 int module_num_dc_options;
53 
54 const char *module_name;
55 
56 char *module_index_mark;
57 
do_message(SPDMessageType msgtype)58 char *do_message(SPDMessageType msgtype)
59 {
60 	int ret;
61 	char *cur_line;
62 	GString *msg;
63 	size_t n;
64 	int nlines = 0;
65 
66 	msg = g_string_new("");
67 
68 	printf("202 OK RECEIVING MESSAGE\n");
69 	fflush(stdout);
70 
71 	while (1) {
72 		cur_line = NULL;
73 		n = 0;
74 		ret = spd_getline(&cur_line, &n, stdin);
75 		nlines++;
76 		if (ret == -1)
77 			return g_strdup("401 ERROR INTERNAL");
78 
79 		if (!strcmp(cur_line, "..\n")) {
80 			g_free(cur_line);
81 			cur_line = g_strdup(".\n");
82 		} else if (!strcmp(cur_line, ".\n")) {
83 			/* Strip the trailing \n */
84 			msg->str[strlen(msg->str) - 1] = 0;
85 			g_free(cur_line);
86 			break;
87 		}
88 		g_string_append(msg, cur_line);
89 		g_free(cur_line);
90 	}
91 
92 	if ((msgtype != SPD_MSGTYPE_TEXT) && (nlines > 2)) {
93 		return g_strdup("305 DATA MORE THAN ONE LINE");
94 	}
95 
96 	if ((msgtype == SPD_MSGTYPE_CHAR) && (!strcmp(msg->str, "space"))) {
97 		g_string_free(msg, 1);
98 		msg = g_string_new(" ");
99 	}
100 
101 	/* no sure we need this check here at all */
102 	if (msg->str == NULL || msg->str[0] == 0) {
103 		DBG("requested data NULL or empty\n");
104 		g_string_free(msg, TRUE);
105 		return g_strdup("301 ERROR CANT SPEAK");
106 	}
107 
108 	/* check voice and synthesis_voice settings for consistency */
109 	if (msg_settings.voice.name == NULL
110 	    && msg_settings_old.voice.name != NULL
111 	    && msg_settings.voice_type == msg_settings_old.voice_type) {
112 		/* force to set voice again, since synthesis_voice changed to NULL */
113 		msg_settings_old.voice_type = -1;
114 	}
115 
116 	/* Volume is controlled by the synthesizer. Always play at normal on audio device. */
117 	if (spd_audio_set_volume(module_audio_id, 85) < 0) {
118 		DBG("Can't set volume. audio not initialized?");
119 	}
120 
121 	ret = module_speak(msg->str, strlen(msg->str), msgtype);
122 
123 	g_string_free(msg, 1);
124 	if (ret <= 0)
125 		return g_strdup("301 ERROR CANT SPEAK");
126 
127 	return g_strdup("200 OK SPEAKING");
128 }
129 
do_speak(void)130 char *do_speak(void)
131 {
132 	return do_message(SPD_MSGTYPE_TEXT);
133 }
134 
do_sound_icon(void)135 char *do_sound_icon(void)
136 {
137 	return do_message(SPD_MSGTYPE_SOUND_ICON);
138 }
139 
do_char(void)140 char *do_char(void)
141 {
142 	return do_message(SPD_MSGTYPE_CHAR);
143 }
144 
do_key(void)145 char *do_key(void)
146 {
147 	return do_message(SPD_MSGTYPE_KEY);
148 }
149 
do_stop(void)150 void do_stop(void)
151 {
152 	module_stop();
153 	return;
154 }
155 
do_pause(void)156 void do_pause(void)
157 {
158 	int ret;
159 
160 	ret = module_pause();
161 	if (ret) {
162 		DBG("WARNING: Can't pause");
163 		return;
164 	}
165 
166 	return;
167 }
168 
169 #define SET_PARAM_NUM(name, cond) \
170 	if(!strcmp(cur_item, #name)){ \
171 		number = strtol(cur_value, &tptr, 10); \
172 		if(!(cond)){ err = 2; continue; } \
173 		if (tptr == cur_value){ err = 2; continue; } \
174 		msg_settings.name = number; \
175 	}
176 
177 #define SET_PARAM_STR(name) \
178 	if(!strcmp(cur_item, #name)){ \
179 		g_free(msg_settings.name); \
180 		if(!strcmp(cur_value, "NULL")) msg_settings.name = NULL; \
181 		else msg_settings.name = g_strdup(cur_value); \
182 	}
183 
184 #define SET_PARAM_STR_C(name, fconv) \
185 	if(!strcmp(cur_item, #name)){ \
186 		ret = fconv(cur_value); \
187 		if (ret != -1) msg_settings.name = ret; \
188 		else err = 2; \
189 	}
190 
do_set(void)191 char *do_set(void)
192 {
193 	char *cur_item = NULL;
194 	char *cur_value = NULL;
195 	char *line = NULL;
196 	int ret;
197 	size_t n;
198 	int number;
199 	char *tptr;
200 	int err = 0;		/* Error status */
201 
202 	printf("203 OK RECEIVING SETTINGS\n");
203 	fflush(stdout);
204 
205 	while (1) {
206 		line = NULL;
207 		n = 0;
208 		ret = spd_getline(&line, &n, stdin);
209 		if (ret == -1) {
210 			err = 1;
211 			break;
212 		}
213 		if (!strcmp(line, ".\n")) {
214 			g_free(line);
215 			break;
216 		}
217 		if (!err) {
218 			cur_item = strtok(line, "=");
219 			if (cur_item == NULL) {
220 				err = 1;
221 				continue;
222 			}
223 			cur_value = strtok(NULL, "\n");
224 			if (cur_value == NULL) {
225 				err = 1;
226 				continue;
227 			}
228 
229 			SET_PARAM_NUM(rate,
230 				      ((number >= -100) && (number <= 100)))
231 			    else
232 				SET_PARAM_NUM(pitch,
233 					      ((number >= -100)
234 					       && (number <= 100)))
235 				    else
236 				SET_PARAM_NUM(pitch_range,
237 					      ((number >= -100)
238 					       && (number <= 100)))
239 				    else
240 				SET_PARAM_NUM(volume,
241 					      ((number >= -100)
242 					       && (number <= 100)))
243 				    else
244 				SET_PARAM_STR_C(punctuation_mode,
245 						str2EPunctMode)
246 				    else
247 				SET_PARAM_STR_C(spelling_mode, str2ESpellMode)
248 				    else
249 				SET_PARAM_STR_C(cap_let_recogn,
250 						str2ECapLetRecogn)
251 				    else
252 			if (!strcmp(cur_item, "voice")) {
253 				ret = str2EVoice(cur_value);
254 				if (ret != -1)
255 					msg_settings.voice_type = ret;
256 				else
257 					err = 2;
258 			} else if (!strcmp(cur_item, "synthesis_voice")) {
259 				g_free(msg_settings.voice.name);
260 				if (!strcmp(cur_value, "NULL"))
261 					msg_settings.voice.name = NULL;
262 				else
263 					msg_settings.voice.name =
264 					    g_strdup(cur_value);
265 			} else if (!strcmp(cur_item, "language")) {
266 				g_free(msg_settings.voice.language);
267 				if (!strcmp(cur_value, "NULL"))
268 					msg_settings.voice.language = NULL;
269 				else
270 					msg_settings.voice.language =
271 					    g_strdup(cur_value);
272 			} else
273 				err = 2;	/* Unknown parameter */
274 		}
275 		g_free(line);
276 	}
277 
278 	if (err == 0)
279 		return g_strdup("203 OK SETTINGS RECEIVED");
280 	if (err == 1)
281 		return g_strdup("302 ERROR BAD SYNTAX");
282 	if (err == 2)
283 		return g_strdup("303 ERROR INVALID PARAMETER OR VALUE");
284 
285 	return g_strdup("401 ERROR INTERNAL");	/* Can't be reached */
286 }
287 
288 #define SET_AUDIO_STR(name,idx) \
289 	if(!strcmp(cur_item, #name)){ \
290 		g_free(module_audio_pars[idx]); \
291 		if(!strcmp(cur_value, "NULL")) module_audio_pars[idx] = NULL; \
292 		else module_audio_pars[idx] = g_strdup(cur_value); \
293 	}
294 
do_audio(void)295 char *do_audio(void)
296 {
297 	char *cur_item = NULL;
298 	char *cur_value = NULL;
299 	char *line = NULL;
300 	int ret;
301 	size_t n;
302 	int err = 0;		/* Error status */
303 	char *status = NULL;
304 	char *msg;
305 
306 	printf("207 OK RECEIVING AUDIO SETTINGS\n");
307 	fflush(stdout);
308 
309 	while (1) {
310 		line = NULL;
311 		n = 0;
312 		ret = spd_getline(&line, &n, stdin);
313 		if (ret == -1) {
314 			err = 1;
315 			break;
316 		}
317 		if (!strcmp(line, ".\n")) {
318 			g_free(line);
319 			break;
320 		}
321 		if (!err) {
322 			cur_item = strtok(line, "=");
323 			if (cur_item == NULL) {
324 				err = 1;
325 				continue;
326 			}
327 			cur_value = strtok(NULL, "\n");
328 			if (cur_value == NULL) {
329 				err = 1;
330 				continue;
331 			}
332 
333 			SET_AUDIO_STR(audio_output_method, 0)
334 			    else
335 				SET_AUDIO_STR(audio_oss_device, 1)
336 				    else
337 				SET_AUDIO_STR(audio_alsa_device, 2)
338 				    else
339 				SET_AUDIO_STR(audio_nas_server, 3)
340 				    else
341 				      /* TODO: restore AudioPulseServer option
342 				SET_AUDIO_STR(audio_pulse_server, 4)
343 				    else
344 				    */
345 				SET_AUDIO_STR(audio_pulse_device, 4)
346 				    else
347 				SET_AUDIO_STR(audio_pulse_min_length, 5)
348 				    else
349 				/* 6 reserved for speech-dispatcher module name */
350 				err = 2;	/* Unknown parameter */
351 		}
352 		g_free(line);
353 	}
354 
355 	if (err == 1)
356 		return g_strdup("302 ERROR BAD SYNTAX");
357 	if (err == 2)
358 		return g_strdup("303 ERROR INVALID PARAMETER OR VALUE");
359 
360 	err = module_audio_init(&status);
361 
362 	if (err == 0)
363 		msg = g_strdup_printf("203 OK AUDIO INITIALIZED");
364 	else
365 		msg = g_strdup_printf("300-%s\n300 UNKNOWN ERROR", status);
366 
367 	g_free(status);
368 	return msg;
369 }
370 
371 #define SET_LOGLEVEL_NUM(name, cond) \
372 	if(!strcmp(cur_item, #name)){ \
373 		number = strtol(cur_value, &tptr, 10); \
374 		if(!(cond)){ err = 2; continue; } \
375 		if (tptr == cur_value){ err = 2; continue; } \
376 		log_level = number; \
377 		spd_audio_set_loglevel(module_audio_id, number); \
378 	}
379 
do_loglevel(void)380 char *do_loglevel(void)
381 {
382 	char *cur_item = NULL;
383 	char *cur_value = NULL;
384 	char *line = NULL;
385 	int ret;
386 	size_t n;
387 	int number;
388 	char *tptr;
389 	int err = 0;		/* Error status */
390 	char *msg;
391 
392 	printf("207 OK RECEIVING LOGLEVEL SETTINGS\n");
393 	fflush(stdout);
394 
395 	while (1) {
396 		line = NULL;
397 		n = 0;
398 		ret = spd_getline(&line, &n, stdin);
399 		if (ret == -1) {
400 			err = 1;
401 			break;
402 		}
403 		if (!strcmp(line, ".\n")) {
404 			g_free(line);
405 			break;
406 		}
407 		if (!err) {
408 			cur_item = strtok(line, "=");
409 			if (cur_item == NULL) {
410 				err = 1;
411 				continue;
412 			}
413 			cur_value = strtok(NULL, "\n");
414 			if (cur_value == NULL) {
415 				err = 1;
416 				continue;
417 			}
418 
419 			SET_LOGLEVEL_NUM(log_level, 1)
420 			    else
421 				err = 2;	/* Unknown parameter */
422 		}
423 		g_free(line);
424 	}
425 
426 	if (err == 1)
427 		return g_strdup("302 ERROR BAD SYNTAX");
428 	if (err == 2)
429 		return g_strdup("303 ERROR INVALID PARAMETER OR VALUE");
430 
431 	msg = g_strdup_printf("203 OK LOG LEVEL SET");
432 
433 	return msg;
434 }
435 
do_debug(char * cmd_buf)436 char *do_debug(char *cmd_buf)
437 {
438 	/* TODO: Develop the full on/off logic etc. */
439 
440 	char **cmd;
441 	char *filename;
442 
443 	cmd = g_strsplit(cmd_buf, " ", -1);
444 
445 	if (!cmd[1]) {
446 		g_strfreev(cmd);
447 		return g_strdup("302 ERROR BAD SYNTAX");
448 	}
449 
450 	if (!strcmp(cmd[1], "ON")) {
451 		if (!cmd[2]) {
452 			g_strfreev(cmd);
453 			return g_strdup("302 ERROR BAD SYNTAX");
454 		}
455 
456 		filename = cmd[2];
457 		DBG("Additional logging into specific path %s requested",
458 		    filename);
459 		FILE *new_CustomDebugFile = fopen(filename, "w+");
460 		if (new_CustomDebugFile == NULL) {
461 			DBG("ERROR: Can't open custom debug file for logging: %d (%s)", errno, strerror(errno));
462 			return g_strdup("303 CANT OPEN CUSTOM DEBUG FILE");
463 		}
464 		if (CustomDebugFile != NULL)
465 			fclose(CustomDebugFile);
466 		CustomDebugFile = new_CustomDebugFile;
467 		if (Debug == 1)
468 			Debug = 3;
469 		else
470 			Debug = 2;
471 
472 		DBG("Additional logging initialized");
473 	} else if (!strcmp(cmd[1], "OFF")) {
474 		if (Debug == 3)
475 			Debug = 1;
476 		else
477 			Debug = 0;
478 
479 		if (CustomDebugFile != NULL)
480 			fclose(CustomDebugFile);
481 		CustomDebugFile = NULL;
482 		DBG("Additional logging into specific path terminated");
483 	} else {
484 		return g_strdup("302 ERROR BAD SYNTAX");
485 	}
486 
487 	g_strfreev(cmd);
488 	return g_strdup("200 OK DEBUGGING ON");
489 }
490 
do_list_voices(void)491 char *do_list_voices(void)
492 {
493 	SPDVoice **voices;
494 	int i;
495 	char *lang, *variant;
496 	GString *voice_list;
497 
498 	voices = module_list_voices();
499 	if (voices == NULL) {
500 		return g_strdup("304 CANT LIST VOICES");
501 	}
502 
503 	voice_list = g_string_new("");
504 	for (i = 0; voices[i] != NULL; i++) {
505 		if (voices[i]->name == NULL) {	/* Shouldn't happen! */
506 			DBG("Unnamed voice found; ignoring it.");
507 			continue;
508 		}
509 		if (voices[i]->language == NULL)
510 			lang = "none";
511 		else
512 			lang = voices[i]->language;
513 		if (voices[i]->variant == NULL)
514 			variant = "none";
515 		else
516 			variant = voices[i]->variant;
517 		g_string_append_printf(voice_list, "200-%s\t%s\t%s\n",
518 				       voices[i]->name, lang, variant);
519 	}
520 
521 	/* check whether we found at least one voice */
522 	if (voice_list->len == 0) {
523 		g_string_free(voice_list, TRUE);
524 		return g_strdup("304 CANT LIST VOICES");
525 	}
526 
527 	g_string_append(voice_list, "200 OK VOICE LIST SENT");
528 
529 	DBG("Voice prepared to  send to speechd");
530 
531 	return g_string_free(voice_list, FALSE);
532 }
533 
534 #undef SET_PARAM_NUM
535 #undef SET_PARAM_STR
536 
537 /* This has to return int (although it doesn't return at all) so that we could
538  * call it from PROCESS_CMD() macro like the other commands that return
539  * something */
do_quit(void)540 void do_quit(void)
541 {
542 	printf("210 OK QUIT\n");
543 	fflush(stdout);
544 
545 	module_close();
546 
547 	spd_audio_close(module_audio_id);
548 	module_audio_id = NULL;
549 	return;
550 }
551 
552 int
module_get_message_part(const char * message,char * part,unsigned int * pos,size_t maxlen,const char * dividers)553 module_get_message_part(const char *message, char *part, unsigned int *pos,
554 			size_t maxlen, const char *dividers)
555 {
556 	int i, n;
557 	int num_dividers;
558 	int len;
559 
560 	assert(part != NULL);
561 	assert(message != NULL);
562 
563 	len = strlen(message);
564 
565 	if (message[*pos] == 0)
566 		return -1;
567 
568 	if (dividers != NULL) {
569 		num_dividers = strlen(dividers);
570 	} else {
571 		num_dividers = 0;
572 	}
573 
574 	for (i = 0; i <= maxlen - 1; i++) {
575 		part[i] = message[*pos];
576 
577 		if (part[i] == 0) {
578 			return i;
579 		}
580 		// DBG("pos: %d", *pos);
581 
582 		if ((len - 1 - i) > 2) {
583 			if ((message[*pos + 1] == ' ')
584 			    || (message[*pos + 1] == '\n')
585 			    || (message[*pos + 1] == '\r')) {
586 				for (n = 0; n <= num_dividers - 1; n++) {
587 					if (part[i] == dividers[n]) {
588 						part[i + 1] = 0;
589 						(*pos)++;
590 						return i + 1;
591 					}
592 				}
593 				if ((message[*pos] == '\n')
594 				    && (message[*pos + 1] == '\n')) {
595 					part[i + 1] = 0;
596 					(*pos)++;
597 					return i + 1;
598 				}
599 				if ((len - 1 - i) > 4) {
600 					if (((message[*pos] == '\r')
601 					     && (message[*pos + 1] == '\n'))
602 					    && ((message[*pos + 2] == '\r')
603 						&& (message[*pos + 3] ==
604 						    '\n'))) {
605 						part[i + 1] = 0;
606 						(*pos)++;
607 						return i + 1;
608 					}
609 				}
610 			}
611 		}
612 
613 		(*pos)++;
614 	}
615 	part[i] = 0;
616 
617 	return i;
618 }
619 
module_strip_punctuation_some(char * message,char * punct_chars)620 void module_strip_punctuation_some(char *message, char *punct_chars)
621 {
622 	int len;
623 	char *p = message;
624 	int i;
625 	assert(message != NULL);
626 
627 	if (punct_chars == NULL)
628 		return;
629 
630 	len = strlen(message);
631 	for (i = 0; i <= len - 1; i++) {
632 		if (strchr(punct_chars, *p)) {
633 			DBG("Substitution %d: char -%c- for whitespace\n", i,
634 			    *p);
635 			*p = ' ';
636 		}
637 		p++;
638 	}
639 }
640 
module_strip_ssml(char * message)641 char *module_strip_ssml(char *message)
642 {
643 
644 	int len;
645 	char *out;
646 	int i, n;
647 	int omit = 0;
648 
649 	assert(message != NULL);
650 
651 	len = strlen(message);
652 	out = (char *)g_malloc(sizeof(char) * (len + 1));
653 
654 	for (i = 0, n = 0; i <= len; i++) {
655 
656 		if (message[i] == '<') {
657 			omit = 1;
658 			continue;
659 		}
660 		if (message[i] == '>') {
661 			omit = 0;
662 			continue;
663 		}
664 		if (!strncmp(&(message[i]), "&lt;", 4)) {
665 			i += 3;
666 			out[n++] = '<';
667 		} else if (!strncmp(&(message[i]), "&gt;", 4)) {
668 			i += 3;
669 			out[n++] = '>';
670 		} else if (!strncmp(&(message[i]), "&amp;", 5)) {
671 			i += 4;
672 			out[n++] = '&';
673 		} else if (!strncmp(&(message[i]), "&quot;", 6)) {
674 			i += 5;
675 			out[n++] = '"';
676 		} else if (!strncmp(&(message[i]), "&apos;", 6)) {
677 			i += 5;
678 			out[n++] = '\'';
679 		} else if (!omit || i == len)
680 			out[n++] = message[i];
681 	}
682 	DBG("In stripping ssml: |%s|", out);
683 
684 	return out;
685 }
686 
module_strip_punctuation_default(char * buf)687 void module_strip_punctuation_default(char *buf)
688 {
689 	assert(buf != NULL);
690 	module_strip_punctuation_some(buf, "~#$%^&*+=|<>[]_");
691 }
692 
693 size_t
module_parent_wfork(TModuleDoublePipe dpipe,const char * message,SPDMessageType msgtype,const size_t maxlen,const char * dividers,int * pause_requested)694 module_parent_wfork(TModuleDoublePipe dpipe, const char *message,
695 		    SPDMessageType msgtype, const size_t maxlen,
696 		    const char *dividers, int *pause_requested)
697 {
698 	unsigned int pos = 0;
699 	char msg[16];
700 	char *buf;
701 	int bytes;
702 	size_t read_bytes = 0;
703 
704 	DBG("Entering parent process, closing pipes");
705 
706 	buf = (char *)g_malloc((maxlen + 1) * sizeof(char));
707 
708 	module_parent_dp_init(dpipe);
709 
710 	pos = 0;
711 	while (1) {
712 		DBG("  Looping...\n");
713 
714 		bytes =
715 		    module_get_message_part(message, buf, &pos, maxlen,
716 					    dividers);
717 
718 		DBG("Returned %d bytes from get_part\n", bytes);
719 
720 		if (*pause_requested) {
721 			DBG("Pause requested in parent");
722 			module_parent_dp_close(dpipe);
723 			*pause_requested = 0;
724 			return 0;
725 		}
726 
727 		if (bytes > 0) {
728 			DBG("Sending buf to child:|%s| %d\n", buf, bytes);
729 			module_parent_dp_write(dpipe, buf, bytes);
730 
731 			DBG("Waiting for response from child...\n");
732 			while (1) {
733 				read_bytes =
734 				    module_parent_dp_read(dpipe, msg, 8);
735 				if (read_bytes == 0) {
736 					DBG("parent: Read bytes 0, child stopped\n");
737 					break;
738 				}
739 				if (msg[0] == 'C') {
740 					DBG("Ok, received report to continue...\n");
741 					break;
742 				}
743 			}
744 		}
745 
746 		if ((bytes == -1) || (read_bytes == 0)) {
747 			DBG("End of data in parent, closing pipes");
748 			module_parent_dp_close(dpipe);
749 			break;
750 		}
751 
752 	}
753 	return 0;
754 }
755 
module_parent_wait_continue(TModuleDoublePipe dpipe)756 int module_parent_wait_continue(TModuleDoublePipe dpipe)
757 {
758 	char msg[16];
759 	int bytes;
760 
761 	DBG("parent: Waiting for response from child...\n");
762 	while (1) {
763 		bytes = module_parent_dp_read(dpipe, msg, 8);
764 		if (bytes == 0) {
765 			DBG("parent: Read bytes 0, child stopped\n");
766 			return 1;
767 		}
768 		if (msg[0] == 'C') {
769 			DBG("parent: Ok, received report to continue...\n");
770 			return 0;
771 		}
772 	}
773 }
774 
module_parent_dp_init(TModuleDoublePipe dpipe)775 void module_parent_dp_init(TModuleDoublePipe dpipe)
776 {
777 	close(dpipe.pc[0]);
778 	close(dpipe.cp[1]);
779 }
780 
module_parent_dp_close(TModuleDoublePipe dpipe)781 void module_parent_dp_close(TModuleDoublePipe dpipe)
782 {
783 	close(dpipe.pc[1]);
784 	close(dpipe.cp[0]);
785 }
786 
module_child_dp_init(TModuleDoublePipe dpipe)787 void module_child_dp_init(TModuleDoublePipe dpipe)
788 {
789 	close(dpipe.pc[1]);
790 	close(dpipe.cp[0]);
791 }
792 
module_child_dp_close(TModuleDoublePipe dpipe)793 void module_child_dp_close(TModuleDoublePipe dpipe)
794 {
795 	close(dpipe.pc[0]);
796 	close(dpipe.cp[1]);
797 }
798 
799 void
module_child_dp_write(TModuleDoublePipe dpipe,const char * msg,size_t bytes)800 module_child_dp_write(TModuleDoublePipe dpipe, const char *msg, size_t bytes)
801 {
802 	int ret;
803 	assert(msg != NULL);
804 	ret = write(dpipe.cp[1], msg, bytes);
805 	assert(ret);
806 }
807 
808 int
module_parent_dp_write(TModuleDoublePipe dpipe,const char * msg,size_t bytes)809 module_parent_dp_write(TModuleDoublePipe dpipe, const char *msg, size_t bytes)
810 {
811 	ssize_t ret;
812 	assert(msg != NULL);
813 	DBG("going to write %lu bytes", (long unsigned)bytes);
814 	ret = write(dpipe.pc[1], msg, bytes);
815 	DBG("written %ld bytes", (long)ret);
816 	return ret;
817 }
818 
module_child_dp_read(TModuleDoublePipe dpipe,char * msg,size_t maxlen)819 int module_child_dp_read(TModuleDoublePipe dpipe, char *msg, size_t maxlen)
820 {
821 	int bytes;
822 	while ((bytes = read(dpipe.pc[0], msg, maxlen)) < 0) {
823 		if (errno != EINTR) {
824 			FATAL("Unable to read data");
825 		}
826 	}
827 	return bytes;
828 }
829 
module_parent_dp_read(TModuleDoublePipe dpipe,char * msg,size_t maxlen)830 int module_parent_dp_read(TModuleDoublePipe dpipe, char *msg, size_t maxlen)
831 {
832 	int bytes;
833 	while ((bytes = read(dpipe.cp[0], msg, maxlen)) < 0) {
834 		if (errno != EINTR) {
835 			FATAL("Unable to read data");
836 		}
837 	}
838 	return bytes;
839 }
840 
module_sigblockall(void)841 void module_sigblockall(void)
842 {
843 	int ret;
844 	sigset_t all_signals;
845 
846 	DBG("Blocking all signals");
847 
848 	sigfillset(&all_signals);
849 
850 	ret = sigprocmask(SIG_BLOCK, &all_signals, NULL);
851 	if (ret != 0)
852 		DBG("Can't block signals, expect problems with terminating!\n");
853 }
854 
module_sigunblockusr(sigset_t * some_signals)855 void module_sigunblockusr(sigset_t * some_signals)
856 {
857 	int ret;
858 
859 	DBG("UnBlocking user signal");
860 
861 	sigdelset(some_signals, SIGUSR1);
862 	ret = sigprocmask(SIG_SETMASK, some_signals, NULL);
863 	if (ret != 0)
864 		DBG("Can't block signal set, expect problems with terminating!\n");
865 }
866 
module_sigblockusr(sigset_t * some_signals)867 void module_sigblockusr(sigset_t * some_signals)
868 {
869 	int ret;
870 
871 	DBG("Blocking user signal");
872 
873 	sigaddset(some_signals, SIGUSR1);
874 	ret = sigprocmask(SIG_SETMASK, some_signals, NULL);
875 	if (ret != 0)
876 		DBG("Can't block signal set, expect problems when terminating!\n");
877 
878 }
879 
set_speaking_thread_parameters()880 void set_speaking_thread_parameters()
881 {
882 	int ret;
883 	sigset_t all_signals;
884 
885 	ret = sigfillset(&all_signals);
886 	if (ret == 0) {
887 		ret = pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
888 		if (ret != 0)
889 			DBG("Can't set signal set, expect problems when terminating!\n");
890 	} else {
891 		DBG("Can't fill signal set, expect problems when terminating!\n");
892 	}
893 
894 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
895 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
896 }
897 
module_terminate_thread(pthread_t thread)898 int module_terminate_thread(pthread_t thread)
899 {
900 	int ret;
901 
902 	ret = pthread_cancel(thread);
903 	if (ret != 0) {
904 		DBG("Cancellation of speak thread failed");
905 		return 1;
906 	}
907 	ret = pthread_join(thread, NULL);
908 	if (ret != 0) {
909 		DBG("join failed!\n");
910 		return 1;
911 	}
912 
913 	return 0;
914 }
915 
module_recode_to_iso(char * data,int bytes,char * language,char * fallback)916 char *module_recode_to_iso(char *data, int bytes, char *language,
917 			   char *fallback)
918 {
919 	char *recoded;
920 
921 	if (language == NULL)
922 		recoded = g_strdup(data);
923 	else if (!strcmp(language, "cs") || !strcmp(language, "cs-CZ"))
924 		recoded =
925 		    (char *)g_convert_with_fallback(data, bytes, "ISO8859-2",
926 						    "UTF-8", fallback, NULL,
927 						    NULL, NULL);
928 	else
929 		recoded =
930 		    (char *)g_convert_with_fallback(data, bytes, "ISO8859-1",
931 						    "UTF-8", fallback, NULL,
932 						    NULL, NULL);
933 
934 	if (recoded == NULL)
935 		DBG("festival: Conversion to ISO coding failed\n");
936 
937 	return recoded;
938 }
939 
module_send_asynchronous(char * text)940 void module_send_asynchronous(char *text)
941 {
942 	pthread_mutex_lock(&module_stdout_mutex);
943 	DBG("Printing reply: %s", text);
944 	fputs(text, stdout);
945 	fflush(stdout);
946 	DBG("Printed");
947 	pthread_mutex_unlock(&module_stdout_mutex);
948 }
949 
module_report_index_mark(char * mark)950 void module_report_index_mark(char *mark)
951 {
952 	char *reply;
953 	DBG("Event: Index mark %s", mark);
954 	if (mark != NULL)
955 		reply = g_strdup_printf("700-%s\n700 INDEX MARK\n", mark);
956 	else
957 		return;
958 
959 	module_send_asynchronous(reply);
960 
961 	g_free(reply);
962 }
963 
module_report_event_begin(void)964 void module_report_event_begin(void)
965 {
966 	module_send_asynchronous("701 BEGIN\n");
967 }
968 
module_report_event_end(void)969 void module_report_event_end(void)
970 {
971 	module_send_asynchronous("702 END\n");
972 }
973 
module_report_event_stop(void)974 void module_report_event_stop(void)
975 {
976 	module_send_asynchronous("703 STOP\n");
977 }
978 
module_report_event_pause(void)979 void module_report_event_pause(void)
980 {
981 	module_send_asynchronous("704 PAUSE\n");
982 }
983 
984 /* --- CONFIGURATION --- */
module_add_config_option(configoption_t * options,int * num_options,const char * name,int type,dotconf_callback_t callback,info_t * info,unsigned long context)985 configoption_t *module_add_config_option(configoption_t * options,
986 					 int *num_options, const char *name, int type,
987 					 dotconf_callback_t callback,
988 					 info_t * info, unsigned long context)
989 {
990 	configoption_t *opts;
991 	int num_config_options = *num_options;
992 
993 	assert(name != NULL);
994 
995 	num_config_options++;
996 	opts =
997 	    (configoption_t *) g_realloc(options,
998 					 (num_config_options +
999 					  1) * sizeof(configoption_t));
1000 	opts[num_config_options - 1].name = (char *)g_strdup(name);
1001 	opts[num_config_options - 1].type = type;
1002 	opts[num_config_options - 1].callback = callback;
1003 	opts[num_config_options - 1].info = info;
1004 	opts[num_config_options - 1].context = context;
1005 
1006 	*num_options = num_config_options;
1007 	return opts;
1008 }
1009 
module_audio_init(char ** status_info)1010 int module_audio_init(char **status_info)
1011 {
1012 	char *error = 0;
1013 	gchar **outputs;
1014 	int i = 0;
1015 
1016 	DBG("Opening audio output system");
1017 	if (NULL == module_audio_pars[0]) {
1018 		*status_info =
1019 		    g_strdup
1020 		    ("Sound output method specified in configuration not supported. "
1021 		     "Please choose 'oss', 'alsa', 'nas', 'libao' or 'pulse'.");
1022 		return -1;
1023 	}
1024 
1025 	g_free(module_audio_pars[6]);
1026 	module_audio_pars[6] = strdup(module_name);
1027 
1028 	outputs = g_strsplit(module_audio_pars[0], ",", 0);
1029 	while (NULL != outputs[i]) {
1030 		module_audio_id =
1031 		    spd_audio_open(outputs[i], (void **)&module_audio_pars[1],
1032 				   &error);
1033 		if (module_audio_id) {
1034 			DBG("Using %s audio output method", outputs[i]);
1035 			g_strfreev(outputs);
1036 			*status_info =
1037 			    g_strdup("audio initialized successfully.");
1038 			return 0;
1039 		}
1040 		i++;
1041 	}
1042 
1043 	*status_info =
1044 	    g_strdup_printf("Opening sound device failed. Reason: %s. ", error);
1045 	g_free(error);		/* g_malloc'ed, in spd_audio_open. */
1046 
1047 	g_strfreev(outputs);
1048 	return -1;
1049 
1050 }
1051 
module_tts_output(AudioTrack track,AudioFormat format)1052 int module_tts_output(AudioTrack track, AudioFormat format)
1053 {
1054 
1055 	if (spd_audio_play(module_audio_id, track, format) < 0) {
1056 		DBG("Can't play track for unknown reason.");
1057 		return -1;
1058 	}
1059 	return 0;
1060 }
1061 
1062 /* Plays the specified audio file. */
module_play_file(const char * filename)1063 int module_play_file(const char *filename)
1064 {
1065 	int result = 0;
1066 	int subformat;
1067 	sf_count_t items;
1068 	sf_count_t readcount;
1069 	SNDFILE *sf;
1070 	SF_INFO sfinfo;
1071 
1072 	DBG("Playing |%s|", filename);
1073 	memset(&sfinfo, 0, sizeof(sfinfo));
1074 	sf = sf_open(filename, SFM_READ, &sfinfo);
1075 	if (NULL == sf) {
1076 		DBG("%s", sf_strerror(NULL));
1077 		return -1;
1078 	}
1079 	if (sfinfo.channels < 1 || sfinfo.channels > 2) {
1080 		DBG("ERROR: channels = %d.\n", sfinfo.channels);
1081 		result = FALSE;
1082 		goto cleanup1;
1083 	}
1084 	if (sfinfo.frames > 0x7FFFFFFF || sfinfo.frames == 0) {
1085 		DBG("ERROR: Unknown number of frames.");
1086 		result = FALSE;
1087 		goto cleanup1;
1088 	}
1089 
1090 	subformat = sfinfo.format & SF_FORMAT_SUBMASK;
1091 	items = sfinfo.channels * sfinfo.frames;
1092 	DBG("Frames = %jd, channels = %ld", sfinfo.frames,
1093 	    (long)sfinfo.channels);
1094 	DBG("Samplerate = %i, items = %lld", sfinfo.samplerate,
1095 	    (long long)items);
1096 	DBG("Major format = 0x%08X, subformat = 0x%08X, endian = 0x%08X",
1097 	    sfinfo.format & SF_FORMAT_TYPEMASK, subformat,
1098 	    sfinfo.format & SF_FORMAT_ENDMASK);
1099 
1100 	if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) {
1101 		/* Set scaling for float to integer conversion. */
1102 		sf_command(sf, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
1103 	}
1104 	AudioTrack track;
1105 	track.num_samples = sfinfo.frames;
1106 	track.num_channels = sfinfo.channels;
1107 	track.sample_rate = sfinfo.samplerate;
1108 	track.bits = 16;
1109 	track.samples = g_malloc(items * sizeof(short));
1110 	readcount = sf_read_short(sf, (short *)track.samples, items);
1111 	DBG("Read %lld items from audio file.", (long long)readcount);
1112 
1113 	if (readcount > 0) {
1114 		track.num_samples = readcount / sfinfo.channels;
1115 		DBG("Sending %i samples to audio.", track.num_samples);
1116 		int ret = module_tts_output(track, SPD_AUDIO_LE);
1117 		if (ret < 0) {
1118 			DBG("ERROR: Can't play track for unknown reason.");
1119 			result = -1;
1120 			goto cleanup2;
1121 		}
1122 		DBG("Sent to audio.");
1123 	}
1124 cleanup2:
1125 	g_free(track.samples);
1126 cleanup1:
1127 	sf_close(sf);
1128 	return result;
1129 }
module_marks_init(SPDMarks * marks)1130 int module_marks_init(SPDMarks *marks)
1131 {
1132 	marks->num = 0;
1133 	marks->allocated = 0;
1134 	marks->samples = NULL;
1135 	marks->names = NULL;
1136 	marks->stop = 0;
1137 
1138 	return 0;
1139 }
1140 
module_marks_add(SPDMarks * marks,unsigned sample,const char * name)1141 int module_marks_add(SPDMarks *marks, unsigned sample, const char *name)
1142 {
1143 	marks->num++;
1144 	if (marks->num >= marks->allocated) {
1145 		/* Amortized reallocation */
1146 		marks->allocated = marks->num * 2;
1147 		marks->samples = g_realloc(marks->samples, marks->allocated * sizeof(marks->samples[0]));
1148 		marks->names = g_realloc(marks->names, marks->allocated * sizeof(marks->names[0]));
1149 	}
1150 	marks->samples[marks->num - 1] = sample;
1151 	marks->names[marks->num - 1] = g_strdup(name);
1152 
1153 	return 0;
1154 }
1155 
module_marks_clear(SPDMarks * marks)1156 int module_marks_clear(SPDMarks *marks)
1157 {
1158 	unsigned i;
1159 
1160 	for (i = 0; i < marks->num; i++)
1161 		g_free(marks->names[i]);
1162 
1163 	marks->num = 0;
1164 	marks->allocated = 0;
1165 	g_free(marks->samples);
1166 	marks->samples = NULL;
1167 	g_free(marks->names);
1168 	marks->names = NULL;
1169 	marks->stop = 0;
1170 
1171 	return 0;
1172 }
1173 
module_tts_output_marks(AudioTrack track,AudioFormat format,SPDMarks * marks)1174 int module_tts_output_marks(AudioTrack track, AudioFormat format, SPDMarks *marks)
1175 {
1176 	AudioTrack cur = track;
1177 	int current_sample = 0;
1178 
1179 	/* Loop control */
1180 	int start = 0;
1181 	int end = marks->num;
1182 	int delta = 0; /* loop below figures out the delta */
1183 	int i;
1184 	for (i = 1; i < marks->num; i++) {
1185 		if (marks->samples[i - 1] > marks->samples[i]) {
1186 			/* Decreasing mark order */
1187 			if (delta > 0) {
1188 				DBG("WARNING: Mixed samples order");
1189 			} else {
1190 				start = marks->num - 1;
1191 				end = -1;
1192 				delta = -1;
1193 			}
1194 		} else if (marks->samples[i - 1] < marks->samples[i]) {
1195 			/* Increasing mark order */
1196 			if (delta < 0) {
1197 				DBG("WARNING: Mixed samples order");
1198 			} else {
1199 				delta = 1;
1200 			}
1201 		}
1202 	}
1203 	if (delta == 0) {
1204 		/* All marks are at the same sample */
1205 		delta = 1;
1206 	}
1207 
1208 	/* Alternate speaking and reporting mark */
1209 	for (i = start; i != end; i += delta) {
1210 		unsigned end_sample = marks->samples[i];
1211 
1212 		cur.samples = &track.samples[current_sample];
1213 		cur.num_samples = end_sample - current_sample;
1214 		current_sample = end_sample;
1215 
1216 		if (cur.num_samples && module_tts_output(cur, format))
1217 			return -1;
1218 		if (marks->stop)
1219 			return 1;
1220 		module_report_index_mark(marks->names[i]);
1221 	}
1222 
1223 	/* Finish with remaining bits if any */
1224 	if (track.num_samples > current_sample) {
1225 		cur.samples = &track.samples[current_sample];
1226 		cur.num_samples = track.num_samples - current_sample;
1227 		if (module_tts_output(cur, format))
1228 			return -1;
1229 	}
1230 
1231 	return 0;
1232 }
1233 
module_marks_stop(SPDMarks * marks)1234 int module_marks_stop(SPDMarks *marks)
1235 {
1236 	marks->stop = 1;
1237 	return 0;
1238 }
1239