1 
2 /*
3 #    Sfront, a SAOL to C translator
4 #    This file: aucontrol control driver for sfront
5 #
6 # Copyright (c) 1999-2006, Regents of the University of California
7 # All rights reserved.
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are
11 # met:
12 #
13 #  Redistributions of source code must retain the above copyright
14 #  notice, this list of conditions and the following disclaimer.
15 #
16 #  Redistributions in binary form must reproduce the above copyright
17 #  notice, this list of conditions and the following disclaimer in the
18 #  documentation and/or other materials provided with the distribution.
19 #
20 #  Neither the name of the University of California, Berkeley nor the
21 #  names of its contributors may be used to endorse or promote products
22 #  derived from this software without specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #
36 #    Maintainer: John Lazzaro, lazzaro@cs.berkeley.edu
37 */
38 
39 /****************************************************************/
40 /****************************************************************/
41 /*               audiounit control driver for sfront            */
42 /****************************************************************/
43 
44 /*~~~~~~~~~~~~~~~~~*/
45 /* include headers */
46 /*_________________*/
47 
48 /* for socket system */
49 
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <unistd.h>
53 #include <sys/uio.h>
54 #include <fcntl.h>
55 
56 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
57 /*        MIDI event constants       */
58 /*  Must match audiounit.c versions  */
59 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
60 
61 /* retry limit for socket writing */
62 
63 #define CSYSI_AUCONTROL_RETRY_MAX  256
64 
65 /* bitfield constants for MIDIevent flags variable */
66 
67 #define CSYSI_AUCONTROL_MIDIFLAGS_WAITING 0x01u  /* queuing flag bit */
68 
69 /* bitfield constants for SASLevent flags variable */
70 
71 #define CSYSI_AUCONTROL_SASLFLAGS_WAITING 0x01u  /* queuing flag bit */
72 
73 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
74 /*   typedef for MIDI events   */
75 /*  Fields & order must match  */
76 /*  asysn_audiounit_MIDIevent  */
77 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
78 
79 typedef struct csysi_aucontrol_MIDIevent {
80   unsigned char cmd;
81   unsigned char d0;
82   unsigned char d1;
83   unsigned char flags;
84   int kcycleidx;
85 } csysi_aucontrol_MIDIevent;
86 
87 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
88 /*   typedef for SASL events   */
89 /*  Fields & order must match  */
90 /*  asysn_audiounit_SASLevent  */
91 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
92 
93 typedef struct csysi_aucontrol_SASLevent {
94   int index;
95   Float32 value;
96   unsigned char flags;
97   int kcycleidx;
98 } csysi_aucontrol_SASLevent;
99 
100 /*~~~~~~~~~~~~~~~~~*/
101 /* aucontrol state */
102 /*~~~~~~~~~~~~~~~~~*/
103 
104 typedef struct csysi_aucontrol_state {
105   int mpipe;
106   csysi_aucontrol_MIDIevent nextMIDIevent;
107   int spipe;
108   csysi_aucontrol_SASLevent nextSASLevent;
109 } csysi_aucontrol_state;
110 
111 /*~~~~~~~~~~~~~~~~~~~~~~~~~*/
112 /* helper function externs */
113 /*_________________________*/
114 
115 extern int csysi_aucontrol_midievent_read(csysi_aucontrol_state * mystate);
116 extern int csysi_aucontrol_saslevent_read(csysi_aucontrol_state * mystate);
117 
118 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
119 /*     high-level functions: called by sfront engine            */
120 /*______________________________________________________________*/
121 
122 /****************************************************************/
123 /*             initialization routine for control               */
124 /****************************************************************/
125 
126 int csys_setup(ENGINE_PTR_DECLARE)
127 
128 {
129   csysi_aucontrol_state * mystate;
130   int msize;
131   char message[256];
132 
133   msize = sizeof(csysi_aucontrol_state);
134   if (!(mystate = calloc(1, msize)))
135     return CSYS_ERROR;
136   asysn_audiounit_memstatus(mystate, msize, MADV_WILLNEED);
137 
138   EV(csys_state) = (void *) mystate;
139 
140   if ((EV(csys_argc) == 5) &&
141       !strcmp(EV(csys_argv)[1], "-asys_audiounit_mpipe") &&
142       !strcmp(EV(csys_argv)[3], "-asys_audiounit_spipe"))
143     {
144       mystate->mpipe = atoi(EV(csys_argv[2]));
145       mystate->spipe = atoi(EV(csys_argv[4]));
146     }
147 
148   sprintf(message, "Opening aucontrol driver for %s\n\n",
149 	  (EV(csys_argc) >= 1) ? EV(csys_argv)[0] : "(unknown)");
150 
151   ASYS_AUDIOUNIT_WIRETAP_PUTSTRING(message);
152 
153   return CSYS_DONE;
154 }
155 
156 /****************************************************************/
157 /*             polling routine for new data                     */
158 /****************************************************************/
159 
160 int csys_newdata(ENGINE_PTR_DECLARE)
161 
162 {
163   csysi_aucontrol_state * mystate = (csysi_aucontrol_state *) EV(csys_state);
164   int has_midi, has_sasl;
165 
166 #if defined(CSYS_CDRIVER_AUCONTROLM)
167   has_midi = (mystate->nextMIDIevent.flags & CSYSI_AUCONTROL_MIDIFLAGS_WAITING) ||
168     (csysi_aucontrol_midievent_read(mystate) == CSYS_MIDIEVENTS);
169 #else
170   has_midi = 0;
171 #endif
172 
173   has_sasl = (mystate->nextSASLevent.flags & CSYSI_AUCONTROL_SASLFLAGS_WAITING) ||
174     (csysi_aucontrol_saslevent_read(mystate) == CSYS_SASLEVENTS);
175 
176   if (!(has_midi || has_sasl))    /* quick exit in common case */
177     return CSYS_NONE;
178 
179   has_midi = has_midi && (mystate->nextMIDIevent.kcycleidx <= EV(kcycleidx));
180   has_sasl = has_sasl && (mystate->nextSASLevent.kcycleidx <= EV(kcycleidx));
181 
182   if (!(has_midi || has_sasl))
183     return CSYS_NONE;
184 
185   if (has_midi && has_sasl)
186     return CSYS_EVENTS;
187 
188 #if defined(CSYS_CDRIVER_AUCONTROLM)
189   if (has_midi)
190     return CSYS_MIDIEVENTS;
191 #endif
192 
193   if (has_sasl)
194     return CSYS_SASLEVENTS;
195 }
196 
197 
198 #if defined(CSYS_CDRIVER_AUCONTROLM)
199 
200 /****************************************************************/
201 /*                 processes a MIDI event                       */
202 /****************************************************************/
203 
204 int csys_midievent(ENGINE_PTR_DECLARE_COMMA unsigned char * cmd,
205 		   unsigned char * ndata, unsigned char * vdata,
206 		   unsigned short * extchan, float * fval)
207 
208 {
209   csysi_aucontrol_state * mystate = (csysi_aucontrol_state *) EV(csys_state);
210 
211   if ((*cmd = mystate->nextMIDIevent.cmd) < CSYS_MIDI_SYSTEM)
212     {
213       *extchan = (*cmd) & 0x0Fu;
214       *ndata = mystate->nextMIDIevent.d0;
215       *vdata = mystate->nextMIDIevent.d1;
216     }
217   else
218     *cmd = CSYS_MIDI_NOOP;  /* filter MIDI System commands */
219 
220   if (csysi_aucontrol_midievent_read(mystate) == CSYS_NONE)
221     {
222       mystate->nextMIDIevent.flags &= ~(CSYSI_AUCONTROL_MIDIFLAGS_WAITING);
223       return CSYS_NONE;
224     }
225 
226   if (mystate->nextMIDIevent.kcycleidx <= EV(kcycleidx))
227     return CSYS_MIDIEVENTS;
228 
229   return CSYS_NONE;
230 }
231 
232 #endif
233 
234 /****************************************************************/
235 /*                 processes a SASL event                       */
236 /****************************************************************/
237 
238 int csys_saslevent(ENGINE_PTR_DECLARE_COMMA unsigned char * cmd,
239 		   unsigned char * priority, unsigned short * id,
240 		   unsigned short * label, float * fval,
241 		   unsigned int * pnum, float ** p)
242 
243 {
244   csysi_aucontrol_state * mystate = (csysi_aucontrol_state *) EV(csys_state);
245 
246   *cmd = CSYS_SASL_CONTROL;
247   *priority = 1;
248   *id = CSYS_SASL_NOINSTR;
249   *label = CSYS_NOLABEL;
250   *pnum = mystate->nextSASLevent.index;
251   *fval = mystate->nextSASLevent.value;
252 
253   if (csysi_aucontrol_saslevent_read(mystate) == CSYS_NONE)
254     {
255       mystate->nextSASLevent.flags &= ~(CSYSI_AUCONTROL_SASLFLAGS_WAITING);
256       return CSYS_NONE;
257     }
258 
259   if (mystate->nextSASLevent.kcycleidx <= EV(kcycleidx))
260     return CSYS_SASLEVENTS;
261 
262   return CSYS_NONE;
263 }
264 
265 
266 /****************************************************************/
267 /*                  closing routine for control                 */
268 /****************************************************************/
269 
270 void csys_shutdown(ENGINE_PTR_DECLARE)
271 
272 {
273   csysi_aucontrol_state * mystate = (csysi_aucontrol_state *) EV(csys_state);
274   int msize;
275   char message[256];
276 
277   /* flush unprocessed events from MIDI and SASL pipes */
278 
279 #if defined(CSYS_CDRIVER_AUCONTROLM)
280   do { } while (csysi_aucontrol_midievent_read(mystate) != CSYS_NONE);
281 #endif
282   do { } while (csysi_aucontrol_saslevent_read(mystate) != CSYS_NONE);
283 
284   /* log driver close, and free mystate */
285 
286   sprintf(message, "Closing aucontrol driver for %s\n\n",
287 	  (EV(csys_argc) >= 1) ? EV(csys_argv)[0] : "(unknown)");
288 
289   ASYS_AUDIOUNIT_WIRETAP_PUTSTRING(message);
290 
291   msize = sizeof(csysi_aucontrol_state);
292   asysn_audiounit_memstatus(mystate, msize, MADV_FREE);
293   free(mystate);
294 
295   return;
296 }
297 
298 
299 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
300 /*                     helper functions                         */
301 /*______________________________________________________________*/
302 
303 #if defined(CSYS_CDRIVER_AUCONTROLM)
304 
305 /****************************************************************/
306 /*              read a MIDI event from the socket               */
307 /****************************************************************/
308 
309 int csysi_aucontrol_midievent_read(csysi_aucontrol_state * mystate)
310 
311 {
312   int retry = 0;
313   int len;
314 
315   if (mystate->mpipe == 0)
316       return CSYS_NONE;   /* no mpipe, so no MIDIevents */
317 
318   do {
319 
320     if (((len = read(mystate->mpipe, &(mystate->nextMIDIevent),
321 		     sizeof(csysi_aucontrol_MIDIevent))) < 0) && (errno == EAGAIN))
322       return CSYS_NONE;        /* no MIDIevents in mpipe */
323 
324     if (len == sizeof(csysi_aucontrol_MIDIevent))
325       return CSYS_MIDIEVENTS;  /* a MIDIevent was read  */
326 
327     if ((len >= 0) || (errno != EINTR) || (++retry > CSYSI_AUCONTROL_RETRY_MAX))
328       {
329 	if (len == 0)
330 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
331 	    ("\tError in csys_newdata: read() returned a zero-length MIDIevent\n");
332 
333 	if (len > 0)
334 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
335 	    ("\tError in csys_newdata: read() returned an incomplete MIDIevent\n");
336 
337 	if ((len < 0) && (errno != EINTR))
338 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
339 	    ("\tError in csys_newdata: errno other than EINTR or EAGAIN (MIDI)\n");
340 
341 	if ((len < 0) && (retry > ASYS_AUDIOUNIT_RETRY_MAX))
342 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
343 	    ("\tError in csys_newdata: Maximum retry for EINTR exceeded (MIDI)\n");
344 
345 	return CSYS_NONE;     /* to do: clear flag, consider error reporting */
346       }
347 
348   } while(1);   /* loop to try again when (errno == EINTR) */
349 
350   return CSYS_NONE;
351 }
352 
353 #endif
354 
355 /****************************************************************/
356 /*              read a SASL event from the socket               */
357 /****************************************************************/
358 
359 int csysi_aucontrol_saslevent_read(csysi_aucontrol_state * mystate)
360 
361 {
362   int retry = 0;
363   int len;
364 
365   if (mystate->spipe == 0)
366       return CSYS_NONE;   /* no spipe, so no SASLevents */
367 
368   do {
369 
370     if (((len = read(mystate->spipe, &(mystate->nextSASLevent),
371 		     sizeof(csysi_aucontrol_SASLevent))) < 0) && (errno == EAGAIN))
372       return CSYS_NONE;        /* no SASLevents in spipe */
373 
374     if (len == sizeof(csysi_aucontrol_SASLevent))
375       return CSYS_SASLEVENTS;  /* a SASLevent was read  */
376 
377     if ((len >= 0) || (errno != EINTR) || (++retry > CSYSI_AUCONTROL_RETRY_MAX))
378       {
379 	if (len == 0)
380 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
381 	    ("\tError in csys_newdata: read() returned a zero-length SASLevent\n");
382 
383 	if (len > 0)
384 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
385 	    ("\tError in csys_newdata: read() returned an incomplete SASLevent\n");
386 
387 	if ((len < 0) && (errno != EINTR))
388 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
389 	    ("\tError in csys_newdata: errno other than EINTR or EAGAIN (SASL)\n");
390 
391 	if ((len < 0) && (retry > ASYS_AUDIOUNIT_RETRY_MAX))
392 	  ASYS_AUDIOUNIT_WIRETAP_PUTSTRING
393 	    ("\tError in csys_newdata: Maximum retry for EINTR exceeded (SASL)\n");
394 
395 	return CSYS_NONE;     /* to do: clear flag, consider error reporting */
396       }
397 
398   } while(1);   /* loop to try again when (errno == EINTR) */
399 
400   return CSYS_NONE;
401 }
402