1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2019, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Seven Du <seven@signalwire.com>
27  *
28  * fs_tts.c -- Use TTS to generate a sound file
29  *
30  */
31 
32 #ifndef _XOPEN_SOURCE
33 #define _XOPEN_SOURCE 600
34 #endif
35 
36 #ifndef WIN32
37 #ifdef HAVE_SETRLIMIT
38 #include <sys/resource.h>
39 #endif
40 #endif
41 
42 #include <switch.h>
43 
44 /* Picky compiler */
45 #ifdef __ICC
46 #pragma warning (disable:167)
47 #endif
48 
fs_tts_cleanup()49 static void fs_tts_cleanup()
50 {
51 	switch_safe_free(SWITCH_GLOBAL_dirs.conf_dir);
52 	switch_safe_free(SWITCH_GLOBAL_dirs.mod_dir);
53 	switch_safe_free(SWITCH_GLOBAL_dirs.log_dir);
54 }
55 
main(int argc,char * argv[])56 int main(int argc, char *argv[])
57 {
58 	int r = 1;
59 	switch_bool_t verbose = SWITCH_FALSE;
60 	const char *err = NULL;
61 	int i;
62 	char *extra_modules[100] = { 0 };
63 	int extra_modules_count = 0;
64 	int cmd_fail = 0;
65 	const char *tts_engine = "flite";
66 	const char *tts_voice = "default";
67 	const char *input = NULL;
68 	const char *output = NULL;
69 	const char *text = NULL;
70 	int channels = 1;
71 	int rate = 8000;
72 	switch_file_handle_t fh_input = { 0 }, fh_output = { 0 };
73 	char buf[2048];
74 	char txtbuf[2048] = { 0 };
75 	switch_size_t len = 0;
76 	switch_memory_pool_t *pool = NULL;
77 
78 	for (i = 1; i < argc; i++) {
79 		if (argv[i][0] == '-') {
80 			switch(argv[i][1]) {
81 				case 'c':
82 					i++;
83 					if((SWITCH_GLOBAL_dirs.conf_dir = (char *) malloc(strlen(argv[i]) + 1)) == NULL) {
84 						return 255;
85 					}
86 					strcpy(SWITCH_GLOBAL_dirs.conf_dir, argv[i]);
87 					break;
88 				case 'k':
89 					i++;
90 					if((SWITCH_GLOBAL_dirs.log_dir = (char *) malloc(strlen(argv[i]) + 1)) == NULL) {
91 						return 255;
92 					}
93 					strcpy(SWITCH_GLOBAL_dirs.log_dir, argv[i]);
94 					break;
95 				case 'm':
96 					i++;
97 					if((SWITCH_GLOBAL_dirs.mod_dir = (char *) malloc(strlen(argv[i]) + 1)) == NULL) {
98 						return 255;
99 					}
100 					strcpy(SWITCH_GLOBAL_dirs.mod_dir, argv[i]);
101 					break;
102 				case 'l':
103 					i++;
104 					/* Load extra modules */
105 					if (strchr(argv[i], ','))  {
106 						extra_modules_count = switch_split(argv[i], ',', extra_modules);
107 					} else {
108 						extra_modules_count = 1;
109 						extra_modules[0] = argv[i];
110 					}
111 					break;
112 				case 'i':
113 					input = argv[++i];
114 					break;
115 				case 'e':
116 					tts_engine = argv[++i];
117 					break;
118 				case 'V':
119 					tts_voice = argv[++i];
120 					break;
121 				case 'r':
122 					rate = atoi(argv[++i]);
123 					break;
124 				case 'v':
125 					verbose = SWITCH_TRUE;
126 					break;
127 				default:
128 					printf("Command line option not recognized: %s\n", argv[i]);
129 					cmd_fail = 1;
130 			}
131 		} else {
132 			break;
133 		}
134 	}
135 
136 	if (argc - i < 1 || cmd_fail) {
137 		goto usage;
138 	}
139 
140 	output = argv[i++];
141 
142 	if (zstr(output)) {
143 		goto usage;
144 	}
145 
146 	if (argc - i > 1) {
147 		text = argv[i++];
148 	}
149 
150 	if (switch_core_init(SCF_MINIMAL, verbose, &err) != SWITCH_STATUS_SUCCESS) {
151 		fprintf(stderr, "Cannot init core [%s]\n", err);
152 		goto end;
153 	}
154 
155 	switch_loadable_module_init(SWITCH_FALSE);
156 	switch_loadable_module_load_module("", "CORE_PCM_MODULE", SWITCH_TRUE, &err);
157 	switch_loadable_module_load_module("", "CORE_SPEEX_MODULE", SWITCH_TRUE, &err);
158 	switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_TRUE, &err);
159 	switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, "mod_console", SWITCH_TRUE, &err);
160 
161 	for (i = 0; i < extra_modules_count; i++) {
162 		if (switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, extra_modules[i], SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
163 			fprintf(stderr, "Cannot init %s [%s]\n", extra_modules[i], err);
164 			goto end;
165 		}
166 	}
167 
168 	if (switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, "mod_sndfile", SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
169 		fprintf(stderr, "Cannot init mod_sndfile [%s]\n", err);
170 		goto end;
171 	}
172 
173 	if (switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, "mod_ssml", SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
174 		fprintf(stderr, "Cannot init mod_ssml [%s]\n", err);
175 		goto end;
176 	}
177 
178 	if (!strcmp(tts_engine, "polly")) {
179 		if (switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, "mod_polly", SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
180 			fprintf(stderr, "Cannot init mod_polly [%s]\n", err);
181 			goto end;
182 		}
183 	}
184 
185 	if (!strcmp(tts_engine, "gcloud")) {
186 		if (switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, "mod_gcloud", SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
187 			fprintf(stderr, "Cannot init mod_polly [%s]\n", err);
188 			goto end;
189 		}
190 	}
191 
192 	if (!strcmp(tts_voice, "default") && !strcmp(tts_engine, "flite")) {
193 		tts_voice = "kal";
194 	}
195 
196 	switch_core_new_memory_pool(&pool);
197 
198 	if (zstr(text) || *text == '-') { // read from stdin
199 		while(read(STDIN_FILENO, txtbuf + len, 1) == 1) {
200 			if(++len == sizeof(txtbuf) - 1) break;
201 		}
202 	} else if (input) {
203 		int fd = open(input, O_RDONLY);
204 
205 		if (fd== -1) {
206 			fprintf(stderr, "Error opening file %s\n", input);
207 			goto end;
208 		}
209 
210 		len = read(fd, txtbuf, sizeof(txtbuf) - 1);
211 		close(fd);
212 	}
213 
214 	if (len > 0) {
215 		text = txtbuf;
216 	}
217 
218 	input = switch_core_sprintf(pool, "tts://%s|%s|%s", tts_engine, tts_voice, text);
219 
220 	// input = "tts://polly|default|Hello";
221 
222 	if (verbose) {
223 		fprintf(stderr, "Speaking %s\n", input);
224 	}
225 
226 	if (switch_core_file_open(&fh_input, input, channels, rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
227 		fprintf(stderr, "Couldn't open %s\n", input);
228 		goto end;
229 	}
230 
231 	if (verbose) {
232 		fprintf(stderr, "Opening file %s\n", output);
233 	}
234 
235 	if (switch_core_file_open(&fh_output, output, channels, rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
236 		fprintf(stderr, "Couldn't open %s\n", output);
237 		goto end;
238 	}
239 
240 	len = sizeof(buf) / 2;
241 
242 	while (switch_core_file_read(&fh_input, buf, &len) == SWITCH_STATUS_SUCCESS) {
243 		if (switch_core_file_write(&fh_output, buf, &len) != SWITCH_STATUS_SUCCESS) {
244 			fprintf(stderr, "Write error\n");
245 			goto end;
246 		}
247 
248 		len = sizeof(buf) / 2;
249 	}
250 
251 	r = 0;
252 
253 end:
254 	if (switch_test_flag(&fh_input, SWITCH_FILE_OPEN)) {
255 		switch_core_file_close(&fh_input);
256 	}
257 
258 	if (switch_test_flag(&fh_output, SWITCH_FILE_OPEN)) {
259 		switch_core_file_close(&fh_output);
260 	}
261 
262 	if (pool) {
263 		switch_core_destroy_memory_pool(&pool);
264 	}
265 
266 	fs_tts_cleanup();
267 
268 	// switch_core_destroy();
269 
270 	return r;
271 usage:
272 	printf("Usage: %s [options] output [text]\n\n", argv[0]);
273 	printf("The output must end in the format, e.g., myfile.wav myfile.mp3\n");
274 	printf("\t\t -c path\t\t Path to the FS configurations.\n");
275 	printf("\t\t -k path\t\t Path to the FS log directory\n");
276 	printf("\t\t -l module[,module]\t Load additional modules (comma-separated)\n");
277 	printf("\t\t -m path\t\t Path to the modules.\n");
278 	printf("\t\t -r rate\t\t sampling rate\n");
279 	printf("\t\t -v\t\t\t verbose\n");
280 	printf("\t\t -e\t\t\t TTS engine\n");
281 	printf("\t\t -V\t\t\t TTS voice\n");
282 	fs_tts_cleanup();
283 	return 1;
284 }
285 
286 /* For Emacs:
287  * Local Variables:
288  * mode:c
289  * indent-tabs-mode:t
290  * tab-width:4
291  * c-basic-offset:4
292  * End:
293  * For VIM:
294  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
295  */
296