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]), "<", 4)) {
665 i += 3;
666 out[n++] = '<';
667 } else if (!strncmp(&(message[i]), ">", 4)) {
668 i += 3;
669 out[n++] = '>';
670 } else if (!strncmp(&(message[i]), "&", 5)) {
671 i += 4;
672 out[n++] = '&';
673 } else if (!strncmp(&(message[i]), """, 6)) {
674 i += 5;
675 out[n++] = '"';
676 } else if (!strncmp(&(message[i]), "'", 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