1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1999 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author : Alan W Black (awb@cstr.ed.ac.uk) */
34 /* Date : March 1999 */
35 /*-----------------------------------------------------------------------*/
36 /* */
37 /* Client end of Festival server API in C designed specifically for */
38 /* Galaxy Communicator use though might be of use for other things */
39 /* */
40 /* This is a standalone C client, no other Festival or Speech Tools */
41 /* libraries need be link with this. Thus is very small. */
42 /* */
43 /* Compile with (plus socket libraries if required) */
44 /* cc -o festival_client -DSTANDALONE festival_client.c */
45 /* */
46 /* Run as */
47 /* festival_client -text "hello there" -o hello.snd */
48 /* */
49 /* */
50 /* This is provided as an example, it is quite limited in what it does */
51 /* but is functional compiling without -DSTANDALONE gives you a simple */
52 /* API */
53 /* */
54 /*=======================================================================*/
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/socket.h>
61 #include <netdb.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include "festival_client.h"
65
66 /* For testing endianness */
67 int fapi_endian_loc = 1;
68
69 static char *socket_receive_file_to_buff(int fd,int *size);
70
delete_FT_Wave(FT_Wave * wave)71 void delete_FT_Wave(FT_Wave *wave)
72 {
73 if (wave != 0)
74 {
75 if (wave->samples != 0)
76 free(wave->samples);
77 free(wave);
78 }
79 }
80
save_FT_Wave_snd(FT_Wave * wave,const char * filename)81 int save_FT_Wave_snd(FT_Wave *wave, const char *filename)
82 {
83 FILE *fd;
84 struct {
85 unsigned int magic; /* magic number */
86 unsigned int hdr_size; /* size of this header */
87 int data_size; /* length of data (optional) */
88 unsigned int encoding; /* data encoding format */
89 unsigned int sample_rate; /* samples per second */
90 unsigned int channels; /* number of interleaved channels */
91 } header;
92 short sw_short;
93 int i;
94
95 if ((filename == 0) ||
96 (strcmp(filename,"stdout") == 0) ||
97 (strcmp(filename,"-") == 0))
98 fd = stdout;
99 else if ((fd = fopen(filename,"wb")) == NULL)
100 {
101 fprintf(stderr,"save_FT_Wave: can't open file \"%s\" for writing\n",
102 filename);
103 return -1;
104 }
105
106 header.magic = (unsigned int)0x2e736e64;
107 header.hdr_size = sizeof(header);
108 header.data_size = 2 * wave->num_samples;
109 header.encoding = 3; /* short */
110 header.sample_rate = wave->sample_rate;
111 header.channels = 1;
112 if (FAPI_LITTLE_ENDIAN)
113 { /* snd is always sparc/68000 byte order */
114 header.magic = SWAPINT(header.magic);
115 header.hdr_size = SWAPINT(header.hdr_size);
116 header.data_size = SWAPINT(header.data_size);
117 header.encoding = SWAPINT(header.encoding);
118 header.sample_rate = SWAPINT(header.sample_rate);
119 header.channels = SWAPINT(header.channels);
120 }
121 /* write header */
122 if (fwrite(&header, sizeof(header), 1, fd) != 1)
123 return -1;
124 if (FAPI_BIG_ENDIAN)
125 fwrite(wave->samples,sizeof(short),wave->num_samples,fd);
126 else
127 { /* have to swap */
128 for (i=0; i < wave->num_samples; i++)
129 {
130 sw_short = SWAPSHORT(wave->samples[i]);
131 fwrite(&sw_short,sizeof(short),1,fd);
132 }
133 }
134
135 if (fd != stdout)
136 fclose(fd);
137 return 0;
138 }
139
delete_FT_Info(FT_Info * info)140 void delete_FT_Info(FT_Info *info)
141 {
142 if (info != 0)
143 free(info);
144 }
145
festival_default_info()146 static FT_Info *festival_default_info()
147 {
148 FT_Info *info;
149 info = (FT_Info *)malloc(1 * sizeof(FT_Info));
150
151 info->server_host = FESTIVAL_DEFAULT_SERVER_HOST;
152 info->server_port = FESTIVAL_DEFAULT_SERVER_PORT;
153 info->text_mode = FESTIVAL_DEFAULT_TEXT_MODE;
154
155 info->server_fd = -1;
156
157 return info;
158 }
159
festival_socket_open(const char * host,int port)160 static int festival_socket_open(const char *host, int port)
161 {
162 /* Return an FD to a remote server */
163 struct sockaddr_in serv_addr;
164 struct hostent *serverhost;
165 int fd;
166
167 fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
168
169 if (fd < 0)
170 {
171 fprintf(stderr,"festival_client: can't get socket\n");
172 return -1;
173 }
174 memset(&serv_addr, 0, sizeof(serv_addr));
175 if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1)
176 {
177 /* its a name rather than an ipnum */
178 serverhost = gethostbyname(host);
179 if (serverhost == (struct hostent *)0)
180 {
181 fprintf(stderr,"festival_client: gethostbyname failed\n");
182 return -1;
183 }
184 memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length);
185 }
186 serv_addr.sin_family = AF_INET;
187 serv_addr.sin_port = htons(port);
188
189 if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0)
190 {
191 fprintf(stderr,"festival_client: connect to server failed\n");
192 return -1;
193 }
194
195 return fd;
196 }
197
nist_get_param_int(char * hdr,char * field,int def_val)198 static int nist_get_param_int(char *hdr, char *field, int def_val)
199 {
200 char *p;
201 int val;
202
203 if (((p=strstr(hdr,field)) != NULL) &&
204 (strncmp(" -i ",p+strlen(field),4) == 0))
205 {
206 sscanf(p+strlen(field)+4,"%d",&val);
207 return val;
208 }
209 else
210 return def_val;
211 }
212
nist_require_swap(char * hdr)213 static int nist_require_swap(char *hdr)
214 {
215 char *p;
216 char *field = "sample_byte_format";
217
218 if ((p=strstr(hdr,field)) != NULL)
219 {
220 if (((strncmp(" -s2 01",p+strlen(field),7) == 0) && FAPI_BIG_ENDIAN) ||
221 ((strncmp(" -s2 10",p+strlen(field),7) == 0)
222 && FAPI_LITTLE_ENDIAN))
223 return 1;
224 }
225 return 0; /* if unknown assume native byte order */
226 }
227
client_accept_s_expr(int fd)228 static char *client_accept_s_expr(int fd)
229 {
230 /* Read s-expression from server, as a char * */
231 char *expr;
232 int filesize;
233
234 expr = socket_receive_file_to_buff(fd,&filesize);
235 expr[filesize] = '\0';
236 return expr;
237 }
238
client_accept_waveform(int fd)239 static FT_Wave *client_accept_waveform(int fd)
240 {
241 /* Read waveform from server */
242 char *wavefile;
243 int filesize;
244 int num_samples, sample_rate, i;
245 FT_Wave *wave;
246
247 wavefile = socket_receive_file_to_buff(fd,&filesize);
248 wave = 0;
249
250 /* I know this is NIST file and its an error if it isn't */
251 if (filesize >= 1024)
252 {
253 num_samples = nist_get_param_int(wavefile,"sample_count",0);
254 sample_rate = nist_get_param_int(wavefile,"sample_rate",16000);
255 if ((num_samples*sizeof(short))+1024 == filesize)
256 {
257 wave = (FT_Wave *)malloc(sizeof(FT_Wave));
258 wave->num_samples = num_samples;
259 wave->sample_rate = sample_rate;
260 wave->samples = (short *)malloc(num_samples*sizeof(short));
261 memmove(wave->samples,wavefile+1024,num_samples*sizeof(short));
262 if (nist_require_swap(wavefile))
263 for (i=0; i < num_samples; i++)
264 wave->samples[i] = SWAPSHORT(wave->samples[i]);
265 }
266 }
267
268 if (wavefile != 0) /* just in case we've got an ancient free() */
269 free(wavefile);
270 return wave;
271 }
272
socket_receive_file_to_buff(int fd,int * size)273 static char *socket_receive_file_to_buff(int fd,int *size)
274 {
275 /* Receive file (probably a waveform file) from socket using */
276 /* Festival key stuff technique, but long winded I know, sorry */
277 /* but will receive any file without closeing the stream or */
278 /* using OOB data */
279 static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
280 char *buff;
281 int bufflen;
282 int n,k,i;
283 char c;
284
285 bufflen = 1024;
286 buff = (char *)malloc(bufflen);
287 *size=0;
288
289 for (k=0; file_stuff_key[k] != '\0';)
290 {
291 n = read(fd,&c,1);
292 if (n==0) break; /* hit stream eof before end of file */
293 if ((*size)+k+1 >= bufflen)
294 { /* +1 so you can add a NULL if you want */
295 bufflen += bufflen/4;
296 buff = (char *)realloc(buff,bufflen);
297 }
298 if (file_stuff_key[k] == c)
299 k++;
300 else if ((c == 'X') && (file_stuff_key[k+1] == '\0'))
301 { /* It looked like the key but wasn't */
302 for (i=0; i < k; i++,(*size)++)
303 buff[*size] = file_stuff_key[i];
304 k=0;
305 /* omit the stuffed 'X' */
306 }
307 else
308 {
309 for (i=0; i < k; i++,(*size)++)
310 buff[*size] = file_stuff_key[i];
311 k=0;
312 buff[*size] = c;
313 (*size)++;
314 }
315
316 }
317
318 return buff;
319 }
320
321 /***********************************************************************/
322 /* Public Functions to this API */
323 /***********************************************************************/
324
festivalOpen(FT_Info * info)325 FT_Info *festivalOpen(FT_Info *info)
326 {
327 /* Open socket to server */
328
329 if (info == 0)
330 info = festival_default_info();
331
332 info->server_fd =
333 festival_socket_open(info->server_host, info->server_port);
334 if (info->server_fd == -1)
335 return NULL;
336
337 return info;
338 }
339
festivalStringToWave(FT_Info * info,char * text)340 FT_Wave *festivalStringToWave(FT_Info *info,char *text)
341 {
342 FT_Wave *wave;
343 FILE *fd;
344 char *p;
345 char ack[4];
346 int n;
347
348 if (info == 0)
349 return 0;
350
351 if (info->server_fd == -1)
352 {
353 fprintf(stderr,"festival_client: server connection unopened\n");
354 return 0;
355 }
356 fd = fdopen(dup(info->server_fd),"wb");
357
358 /* Copy text over to server, escaping any quotes */
359 fprintf(fd,"(tts_textall \"\n");
360 for (p=text; p && (*p != '\0'); p++)
361 {
362 if ((*p == '"') || (*p == '\\'))
363 putc('\\',fd);
364 putc(*p,fd);
365 }
366 fprintf(fd,"\" \"%s\")\n",info->text_mode);
367 fclose(fd);
368
369 /* Read back info from server */
370 /* This assumes only one waveform will come back, also LP is unlikely */
371 wave = 0;
372 do {
373 for (n=0; n < 3; )
374 n += read(info->server_fd,ack+n,3-n);
375 ack[3] = '\0';
376 if (strcmp(ack,"WV\n") == 0) /* receive a waveform */
377 wave = client_accept_waveform(info->server_fd);
378 else if (strcmp(ack,"LP\n") == 0) /* receive an s-expr */
379 client_accept_s_expr(info->server_fd);
380 else if (strcmp(ack,"ER\n") == 0) /* server got an error */
381 {
382 fprintf(stderr,"festival_client: server returned error\n");
383 break;
384 }
385 } while (strcmp(ack,"OK\n") != 0);
386
387 return wave;
388 }
389
390
festivalClose(FT_Info * info)391 int festivalClose(FT_Info *info)
392 {
393 if (info == 0)
394 return 0;
395
396 if (info->server_fd != -1)
397 close(info->server_fd);
398
399 return 0;
400 }
401
402 #ifdef STANDALONE
main(int argc,char ** argv)403 int main(int argc, char **argv)
404 {
405 char *server=0;
406 int port=-1;
407 char *text=0;
408 char *output=0;
409 char *mode=0;
410 int i;
411 FT_Info *info;
412 FT_Wave *wave;
413
414 for (i=1; i < argc; i++)
415 {
416 if (strcmp(argv[i],"-server") == 0)
417 server = argv[++i];
418 else if (strcmp(argv[i],"-port") == 0)
419 port = atoi(argv[++i]);
420 else if (strcmp(argv[i],"-text") == 0)
421 text = argv[++i];
422 else if (strcmp(argv[i],"-mode") == 0)
423 mode = argv[++i];
424 else if (strcmp(argv[i],"-o") == 0)
425 output = argv[++i];
426 }
427 if (i > argc)
428 {
429 fprintf(stderr,"missing argument\n");
430 exit(1);
431 }
432
433 info = festival_default_info();
434 if (server != 0)
435 info->server_host = server;
436 if (port != -1)
437 info->server_port = port;
438 if (mode != 0)
439 info->text_mode = mode;
440
441 info = festivalOpen(info);
442 wave = festivalStringToWave(info,text);
443
444 if (wave != 0)
445 save_FT_Wave_snd(wave,output);
446
447 festivalClose(info);
448
449 return 0;
450 }
451 #endif
452