1 /*************************************************************************/
2 /*                                                                       */
3 /*                Centre for Speech Technology Research                  */
4 /*                     University of Edinburgh, UK                       */
5 /*                       Copyright (c) 1996,1997                         */
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                                    */
34 /*             Date   :  September 1996                                  */
35 /*-----------------------------------------------------------------------*/
36 /*                                                                       */
37 /* An audio file spooler, like lpd.  Reads in commands about files to    */
38 /* to play, and queues them until any previous requests are finished.    */
39 /* This allows the synthesizer to get on with synthesizing the next      */
40 /* utterance.                                                            */
41 /*                                                                       */
42 /* Actually this doesn't use anything in Festival, only the speech_tools */
43 /*=======================================================================*/
44 #include <cstdlib>
45 #include <cstdio>
46 #include <cstring>
47 #include <csignal>
48 
49 using namespace std;
50 
51 #include "EST.h"
52 #include "EST_unix.h"
53 
54 #ifdef NO_SPOOLER
55 
main(int argc,char ** argv)56 int main(int argc, char **argv)
57 {
58 
59   printf("Audio spooler not supported\n");
60   return 0;
61 }
62 
63 #else
64 
65 class Command {
66   private:
67     EST_String p_file;
68     int p_rate;
69   public:
Command(const EST_String & f,int rate)70     Command(const EST_String &f, int rate) { p_file=f; p_rate=rate; }
rate(void) const71     int rate(void) const { return p_rate; }
file(void) const72     const EST_String &file(void) const { return p_file; }
73 };
74 
75 class CQueue_Item {
76   public:
77     Command *c;
78     CQueue_Item *next;
CQueue_Item(Command * com)79     CQueue_Item(Command *com) { c=com; next=0; }
~CQueue_Item()80     ~CQueue_Item() { delete c; if (next != 0) delete next; }
81 };
82 
83 class CQueue {
84   private:
85     CQueue_Item *head;
86     CQueue_Item *tail;
87   public:
CQueue()88     CQueue() { head = tail = 0; }
~CQueue()89     ~CQueue() { delete head; }
90     void push(Command *c);
91     Command *pop(void);
92     void display(void) const;
93     int length(void) const;
94     void clear(void);
95 };
96 
97 static void auspl_main(int argc, char **argv);
98 static void check_new_input(void);
99 static char *read_a_line(void);
100 static void process_command(char *line);
101 static void check_new_output(void);
102 static int execute_command(Command *c);
103 static void load_play_file(Command *c);
104 static int sp_terminate(void);
105 static void tidy_up(void);
106 
push(Command * c)107 void CQueue::push(Command *c)
108 {
109     // Put this item on tail
110     CQueue_Item *n = new CQueue_Item(c);
111 
112     if (head == 0)
113     {   // first one
114 	head = n;
115 	tail = n;
116     }
117     else
118     {
119 	tail->next = n;
120 	tail = n;
121     }
122 }
123 
pop(void)124 Command *CQueue::pop(void)
125 {
126     // Pop top from the queue
127 
128     if (head == 0)
129 	return 0;
130     else
131     {
132 	Command *c = head->c;
133 	CQueue_Item *h;
134 	h = head;
135 	h->c = 0;
136 	head = head->next;
137 	h->next = 0;
138 	delete h;
139 	return c;
140     }
141 }
142 
display(void) const143 void CQueue::display(void) const
144 {
145     CQueue_Item *t;
146     int i;
147 
148     cerr << "Command_queue: " << length() << endl;
149     for (i=0,t=head; t != 0; t=t->next,i++)
150 	cerr << " " << i << ": " << t->c->file() << endl;
151 }
152 
length(void) const153 int CQueue::length(void) const
154 {
155     // length of queue
156     CQueue_Item *t;
157     int i;
158 
159     for (i=0,t=head; t != 0; t=t->next)
160 	i++;
161 
162     return i;
163 }
164 
clear(void)165 void CQueue::clear(void)
166 {
167     // Remove all memebers in the queue
168     CQueue_Item *t;
169 
170     // Somebody has to do it ...
171     for (t=head; t != 0; t=t->next)
172 	unlink(t->c->file());
173 
174     delete head;
175     head = 0;
176     tail = 0;
177 }
178 
179 static int no_more_input = FALSE;
180 static CQueue command_queue;
181 static int child_pid = 0;
182 static EST_String current_file;
183 static EST_Option play_wave_options;
184 static int maxqueue = 5;
185 static int pending_close = FALSE;
186 static int kids = 0;
187 
main(int argc,char ** argv)188 int main(int argc, char **argv)
189 {
190 
191     auspl_main(argc,argv);
192 
193     return 0;
194 }
195 
auspl_main(int argc,char ** argv)196 static void auspl_main(int argc, char **argv)
197 {
198     EST_Option al;
199     EST_StrList files;
200 
201     parse_command_line(argc, argv,
202 	 EST_String("Usage: audio spooler \n")+
203 	 "auspl  <options> <file0> <file1> ...\n"+
204 	 "--method <string>    audio play method\n"+
205 	 "--command <string>   Unix command to play file, used when\n"+
206 	 "              method is audio_command\n"+
207 	 "--maxqueue <int> {5} Maximum number of files in queue\n",
208 			files, al);
209 
210     if (al.present("--method"))
211 	play_wave_options.add_item("-p",al.val("--method"));
212     if (al.present("--command"))
213 	play_wave_options.add_item("-command",al.val("--command"));
214     play_wave_options.add_item("-quality","HIGH");
215 
216     if (al.present("--maxqueue"))
217 	maxqueue = al.ival("--maxqueue");
218 
219     while (!sp_terminate())
220     {
221 	check_new_input();
222 	check_new_output();
223     }
224 
225     tidy_up();
226 }
227 
sp_terminate(void)228 static int sp_terminate(void)
229 {
230     // I'm never very sure of all the conditions necessary to terminate
231 
232     if (no_more_input && (command_queue.length() == 0))
233 	return TRUE;
234     else
235 	return FALSE;
236 }
237 
tidy_up(void)238 static void tidy_up(void)
239 {
240     // should wait for any remaining children if I've been
241     // requested to.
242     int pid;
243     int statusp;
244 
245     if (pending_close == TRUE)
246     {
247 	while (kids > 0)
248 	{
249 	    pid = waitpid(0,&statusp,0);
250 	    kids--;
251 	}
252 	fprintf(stdout,"OK\n");   // give an acknowledgement
253 	fflush(stdout);
254     }
255 
256     return;
257 }
258 
check_new_input(void)259 static void check_new_input(void)
260 {
261     // Do a select on stdin to find out if there is any new
262     // commands to process
263     fd_set inset;
264     fd_set outset;
265     fd_set exset;
266     struct timeval t;
267     int sv;
268 
269     t.tv_sec = 0;
270     t.tv_usec = 1000;  // 0.1 seconds
271 
272     FD_ZERO(&inset);
273     FD_ZERO(&outset);
274     FD_ZERO(&exset);
275 
276     if ((command_queue.length() >= maxqueue) ||
277 	no_more_input)
278     {
279 	// wait a bit for the queue to go down a bit
280 	// not we're selecting on no fds at all, just for the delay
281 	sv = select(0,&inset,&outset,&exset,&t);
282 	return;
283     }
284 
285     FD_SET(0,&inset);
286 
287     sv = select(1,&inset,&outset,&exset,&t);
288 
289     if (sv == 1)
290 	process_command(read_a_line());
291     else if (sv == -1)
292 	no_more_input = TRUE;
293 }
294 
getc_unbuffered(int fd)295 static int getc_unbuffered(int fd)
296 {
297     // An attempted to get rid of the buffering
298     char c;
299     int n;
300 
301     n = read(fd,&c,1);
302 
303     if (n == 0)
304 	return EOF;
305     else
306 	return c;
307 }
308 
read_a_line(void)309 static char *read_a_line(void)
310 {
311     // read upto \n on stdin -- wonder if I should read instead
312     int maxsize = 1024;
313     char *line = walloc(char,maxsize+2);
314     int i,c;
315 
316     for (i=0;
317 	 (((c=getc_unbuffered(0)) != '\n') &&
318 	  (c != EOF));
319 	 i++)
320     {
321 	if (i == maxsize)
322 	{
323 	    char *nline = walloc(char,maxsize*2);
324 	    memcpy(nline,line,maxsize);
325 	    maxsize = maxsize*2;
326 	    wfree(line);
327 	    line = nline;
328 	}
329 	line[i] = c;
330     }
331 
332     line[i] = '\n';
333     line[i+1] = '\0';
334     if (c == EOF)
335 	no_more_input = TRUE;
336 
337     if (strncmp(line,"close",5) != 0)
338     {
339 	fprintf(stdout,"OK\n");   // give an acknowledgement
340 	fflush(stdout);
341     }
342 
343     return line;
344 }
345 
process_command(char * line)346 static void process_command(char *line)
347 {
348     // Process command, some are immediate
349     EST_TokenStream ts;
350     ts.open_string(line);
351     EST_String comm = ts.get().string();
352 
353     if ((comm == "quit") || (comm == ""))
354     {
355 	no_more_input = TRUE;
356     }
357     else if (comm == "play")
358     {
359 	EST_String file = ts.get().string();
360 	int rate = atoi(ts.get().string());
361 	Command *c = new Command(file,rate);
362 	command_queue.push(c);
363     }
364     else if (comm == "method")
365     {
366 	play_wave_options.add_item("-p",ts.get().string());
367     }
368     else if (comm == "command")
369     {
370 	play_wave_options.add_item("-command",ts.get_upto_eoln().string());
371     }
372     else if (comm == "rate")
373     {
374 	play_wave_options.add_item("-rate",ts.get().string());
375     }
376     else if (comm == "otype")
377     {
378 	play_wave_options.add_item("-otype",ts.get().string());
379     }
380     else if (comm == "device")
381     {
382 	play_wave_options.add_item("-audiodevice",ts.get().string());
383     }
384     else if (comm == "close")
385     {
386 	pending_close = TRUE;
387 	no_more_input = TRUE;
388     }
389     else if (comm == "shutup")
390     {
391 	// clear queue and kill and child currently playing
392 	command_queue.clear();
393 	if (child_pid != 0)
394 	{
395 	    kill(child_pid,SIGKILL);
396 	    unlink(current_file);
397 	}
398     }
399     else if (comm == "query")
400 	command_queue.display();
401     else if (comm != "")
402     {
403 	cerr << "audsp: unknown command \"" << comm << "\"\n";
404     }
405 
406     ts.close();
407     wfree(line);
408 }
409 
check_new_output(void)410 static void check_new_output(void)
411 {
412     // If we are not waiting on any children lauch next command
413     int pid;
414     int statusp;
415 
416     if (kids > 0)
417     {
418 	pid = waitpid(0,&statusp,WNOHANG);
419 	if (pid != 0)
420 	{
421 	    kids--;
422 	    child_pid = 0;
423 	}
424     }
425     else if (command_queue.length() != 0)
426     {
427 	Command *c = command_queue.pop();
428 	if (execute_command(c) == 0)
429 	    kids++;
430 	delete c;
431     }
432 
433     // else do nothing
434 }
435 
execute_command(Command * c)436 static int execute_command(Command *c)
437 {
438     // Execute the command as a child process
439     int pid;
440 
441     current_file = c->file();
442 
443     if ((pid=fork()) == 0)
444     {   // child process
445 	load_play_file(c);
446 	_exit(0);  // don't close any files on exit
447 	return 0;  // can't get here
448     }
449     else if (pid > 0)
450     {   // parent process
451 	child_pid = pid;
452 	return 0;
453     }
454     else
455     {
456 	cerr << "auspd: fork failed, \"" << c->file() << "\"\n";
457 	return -1;
458     }
459 }
460 
load_play_file(Command * c)461 static void load_play_file(Command *c)
462 {
463     // Load in wave file and play it
464 
465     EST_Wave w;
466 
467     w.load(c->file());
468     play_wave(w,play_wave_options);
469     unlink(c->file());   // delete it afterwards
470 }
471 
472 #endif
473