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 /* Interface with the audio spooler                                      */
38 /*                                                                       */
39 /*=======================================================================*/
40 #include <cstdio>
41 #include "EST_unix.h"
42 #include <cstdlib>
43 #include "festival.h"
44 #include "festivalP.h"
45 
46 #ifdef NO_SPOOLER
audsp_play_wave(EST_Wave * w)47 void audsp_play_wave(EST_Wave *w) { cerr << "no spooler available\n"; }
l_audio_mode(LISP mode)48 LISP l_audio_mode(LISP mode) { return NIL; }
49 #else
50 
51 #include <sys/types.h>
52 #include <sys/wait.h>
53 
54 static int start_sub_process(int *fds,int argc,char **argv);
55 static char **enargen(const char *command,int *argc);
56 static void audsp_send(const char *c);
57 static int *pipe_open(const char *command);
58 static void pipe_close(int *fds);
59 
60 static int *audfds;
61 static int audsp_num=0;
62 static int audsp_pid = 0;
63 
audsp_play_wave(EST_Wave * w)64 void audsp_play_wave(EST_Wave *w)
65 {
66     EST_String tpref = make_tmp_filename();
67     char *tmpfilename = walloc(char,tpref.length()+20);
68     sprintf(tmpfilename,"%s_aud_%05d",(const char *)tpref,audsp_num++);
69     w->save(tmpfilename,"nist");
70     audsp_send(EST_String("play ")+tmpfilename+EST_String(" ")+
71 	       itoString(w->sample_rate()));
72     wfree(tmpfilename);
73 }
74 
audsp_send(const char * c)75 static void audsp_send(const char *c)
76 {
77     char reply[4];
78     int  pid;
79     int statusp;
80 
81     pid = waitpid((pid_t)audsp_pid,&statusp,WNOHANG);
82     if (pid != 0)
83     {
84 	cerr << "Audio spooler has died unexpectedly" << endl;
85 	audsp_mode = FALSE;
86 	festival_error();
87     }
88 
89     write(audfds[0],c,strlen(c));
90     write(audfds[0],"\n",1);
91     read(audfds[1],reply,3);  /* confirmation */
92 }
93 
l_audio_mode(LISP mode)94 LISP l_audio_mode(LISP mode)
95 {
96     // Switch audio mode
97     LISP audio=NIL;
98     LISP command=NIL;
99 
100     if (mode == NIL)
101     {
102 	cerr << "audio_mode: nil is not a valid mode\n";
103 	festival_error();
104     }
105     else if (streq("async",get_c_string(mode)))
106     {   // Asynchronous mode using the audio spooler.
107 	if (audsp_mode == FALSE)
108 	{
109 	    audio = ft_get_param("Audio_Method");
110 	    command = ft_get_param("Audio_Command");
111 	    audfds = pipe_open("audsp");
112 	    if (audio != NIL)
113 		audsp_send(EST_String("method ")+get_c_string(audio));
114 	    if (command != NIL)
115 	    {
116 		// command needs to be a single line so delete an newlines
117 		EST_String flattened = get_c_string(command);
118 		flattened.gsub("\\\n"," ");
119 		flattened.gsub("\n"," ");
120 		audsp_send(EST_String("command ")+flattened);
121 	    }
122 	    if ((audio = ft_get_param("Audio_Required_Rate")) != NIL)
123 		audsp_send(EST_String("rate ")+get_c_string(audio));
124 	    if ((audio = ft_get_param("Audio_Required_Format")) != NIL)
125 		audsp_send(EST_String("otype ")+get_c_string(audio));
126 	    if ((audio = ft_get_param("Audio_Device")) != NIL)
127 		audsp_send(EST_String("device ")+get_c_string(audio));
128 	    audsp_mode = TRUE;
129 	}
130     }
131     else if (streq("sync",get_c_string(mode)))
132     {
133 	// Synchronous mode
134 	if (audsp_mode)
135 	    pipe_close(audfds);
136 	audsp_mode = FALSE;
137     }
138     else if (streq("shutup",get_c_string(mode)))
139     {
140 	if (audsp_mode)
141 	    audsp_send("shutup");
142 	else
143 	{
144 	    cerr << "audio_mode: not in async mode, can't shutup\n";
145 	    festival_error();
146 	}
147     }
148     else if (streq("close",get_c_string(mode)))
149     {   // return only when queue is empty
150 	if (audsp_mode)
151 	    audsp_send("close");
152     }
153     else if (streq("query",get_c_string(mode)))
154     {
155 	if (audsp_mode)
156 	    audsp_send("query");
157 	else
158 	{
159 	    cerr << "audio_mode: not in async mode, can't query\n";
160 	    festival_error();
161 	}
162     }
163     else
164     {
165 	cerr << "audio_mode: unknown mode \"" << get_c_string(mode) <<
166 	    "\"\n";
167 	festival_error();
168     }
169 
170     return mode;
171 }
172 
pipe_close(int * fds)173 static void pipe_close(int *fds)
174 {
175     // Close down the pipes
176     close(fds[0]);
177     close(fds[1]);
178 }
179 
pipe_open(const char * command)180 static int *pipe_open(const char *command)
181 {
182     // Starts a subprocess with its stdin and stdout bounad to pipes
183     // the ends of which are returned in an array
184     int argc;
185     char **argv;
186     int *fds;
187 
188     argv = enargen(command,&argc);
189     fds = walloc(int,2);
190 
191     if (start_sub_process(fds,argc,argv) != 0)
192     {
193 	cerr << "pipe_open: failed to start subprocess: \n" << endl;
194 	cerr << "pipe_open: \"" << command << "\"\n";
195 	festival_error();
196     }
197 
198     return fds;
199 }
200 
start_sub_process(int * fds,int argc,char ** argv)201 static int start_sub_process(int *fds, int argc, char **argv)
202 {
203     // start sub_process with stdin and stdout bound to pipes whose ends
204     // are in fds[0] and fds[1]
205     int pid;
206     int in[2];
207     int out[2];
208     (void)argc;
209 
210     if ((pipe(in) != 0) ||
211 	(pipe(out) != 0))
212     {
213 	cerr << "pipe_open: failed to open pipes\n";
214 	festival_error();
215     }
216 
217     switch (pid=fork())
218     {
219       case 0:              /* child */
220 	close(in[1]);          /* close the end child isn't using */
221 	dup2(in[0],0);         /* reassign stdin to the pipe */
222 	close(out[0]);
223 	dup2(out[1],1);        /* reassign stdout to the pipe */
224 	execvp(argv[0],argv);
225 	cerr << "pipe_open: failed to start " << argv[0] << endl;
226 	exit(-1);      /* should only get here on failure */
227       case -1:
228 	cerr << "pipe_open: fork failed\n";
229 	festival_error();
230       default:             /* parent */
231 	close(in[0]);          /* Close unused sides of the pipes */
232 	close(out[1]);
233 	fds[0] = in[1];
234 	fds[1] = out[0];
235     }
236 
237     audsp_pid = pid;
238     return 0;
239 }
240 
enargen(const char * command,int * argc)241 static char **enargen(const char *command,int *argc)
242 {
243     EST_TokenStream ts;
244     char **argv;
245     int i;
246 
247     ts.open_string(command);
248     for (i=0; ts.get() != ""; i++);
249     ts.close();
250     *argc = i;
251 
252     argv = walloc(char *,i+1);
253     ts.open_string(command);
254     for (i=0; i < *argc; i++)
255 	argv[i] = wstrdup(ts.get().string());
256     argv[i] = 0;
257 
258     return argv;
259 }
260 
261 #endif
262