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