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