Initial import to Gerrit.
[profile/ivi/festival.git] / src / arch / festival / audspio.cc
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
47 void audsp_play_wave(EST_Wave *w) { cerr << "no spooler available\n"; }
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
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
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
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
173 static void pipe_close(int *fds)
174 {
175     // Close down the pipes
176     close(fds[0]);
177     close(fds[1]);
178 }
179
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
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
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