1 /*
2  * $Id: pa_sgi.c,v 1.1 2006/04/30 16:23:59 jcr13 Exp $
3  * PortAudio Portable Real-Time Audio Library. Copyright (c) 1999-2001 Phil Burk.
4  * Latest Version at: http://www.portaudio.com
5  *
6  * Silicon Graphics (SGI) IRIX implementation by Pieter Suurmond.
7  * This implementation uses sproc()-spawning, not the POSIX-threads.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files
11  * (the "Software"), to deal in the Software without restriction,
12  * including without limitation the rights to use, copy, modify, merge,
13  * publish, distribute, sublicense, and/or sell copies of the Software,
14  * and to permit persons to whom the Software is furnished to do so,
15  * subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * Any person wishing to distribute modifications to the Software is
21  * requested to send the modifications to the original developer so that
22  * they can be incorporated into the canonical version.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
28  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31  *
32 MODIFICATIONS:
33   8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
34   8/17/2001 - v15, first unstable alpha release for IRIX, sent to Phil & Ross.
35   9/23/2001 - Many fixes and changes: POLLIN for input, not POLLOUT!
36   7/04/2002 - Implemented multiple user buffers per host buffer to allow clients that
37               request smaller buffersizes.
38   3/13/2003 - Fixed clicks in full-duplex (wire) mode. Fixed some uninitialised vars, got rid of
39               all GCC-warnings (-Wall). Tested with MIPS compiler and GCC 3.0.4. on IRIX 6.5 (AL v7).
40 TODO:
41   - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
42     and maybe also the other natively supported formats? (might increase performance)
43   - Implement fancy callback block adapter as described in the PDF by Stephane Letz in the ASIO dir.
44 
45 REFERENCES:
46   - IRIX 6.2 man pages regarding SGI AL library.
47   - IRIS Digital Media Programming Guide (online books and man-pages come
48     with IRIX 6.2 and may not be publically available on the internet).
49 */
50 
51 #include <stdio.h>              /* Standard libraries. */
52 #include <stdlib.h>
53 
54 #include "../pa_common/portaudio.h" /* (Makefile fails to find in subdirs, -I doesn't work?). */
55 #include "../pa_common/pa_host.h"
56 #include "../pa_common/pa_trace.h"
57 
58 #include <errno.h>              /* Needed for int oserror(void);. */
59 #include <sys/time.h>
60 #include <sys/types.h>
61 #include <sys/prctl.h>
62 #include <sys/schedctl.h>       /* For schedctl(NDPRI, NDPHIMIN). */
63 #include <fcntl.h>              /* fcntl.h needed.                */
64 #include <unistd.h>             /* For streams, ioctl(), etc.     */
65 #include <ulocks.h>
66 #include <poll.h>
67 #include <dmedia/audio.h>      /* System specific (IRIX 6.2-6.5). */
68 
69 /*----------------- MACROS --------------------*/
70 #define PRINT(x)    { printf x; fflush(stdout); }
71 #define ERR_RPT(x)  PRINT(x)
72 #define DBUG(x)     /* PRINT(x) */
73 #define DBUGX(x)    /* PRINT(x) */
74 
75 #define MAX_CHARS_DEVNAME           (16)
76 #define MAX_SAMPLE_RATES            (8)         /* Known from SGI AL there are 7.                      */
77                                                 /* Constants used in 'Pa_GetMinNumBuffers()' below:    */
78 #define MIN_LATENCY_MSEC            (200)       /* Used if 'getenv("PA_MIN_LATENCY_MSEC")' fails.      */
79 #define PA_LATENCY_ENV_NAME         ("PA_MIN_LATENCY_MSEC")        /* Same names as in file pa_unix.h. */
80 
81 /*------------------------------- IRIX AL specific device info: --------------------------------------*/
82 typedef struct                    internalPortAudioDevice
83 {
84   PaDeviceID                      pad_DeviceID;                         /* THIS "ID" IS NEW HERE.         */
85   long                            pad_ALdevice;                         /* SGI-number!                    */
86   double                          pad_SampleRates[MAX_SAMPLE_RATES];    /* For pointing to from pad_Info  */
87   char                            pad_DeviceName[MAX_CHARS_DEVNAME+1];  /* +1 for \0, one more than OSS.  */
88   PaDeviceInfo                    pad_Info;                             /* pad_Info (v15) contains:       */
89   struct internalPortAudioDevice* pad_Next;                             /* Singly linked list (NULL=end). */
90 } internalPortAudioDevice;
91 
92 /*----------------- Structure containing all SGI IRIX specific data: ---------------------------------------*/
93 typedef struct      PaHostSoundControl
94 {
95   ALconfig          pahsc_ALconfigIN,                   /* IRIX-audio-library-datatype. Configuration       */
96                     pahsc_ALconfigOUT;                  /* stucts separate for input and output ports.      */
97   ALport            pahsc_ALportIN,                     /* IRIX-audio-library-datatype. ALports can only be */
98                     pahsc_ALportOUT;                    /* unidirectional, so we sometimes need 2 of them.  */
99   int               pahsc_threadPID;                    /* Sproc()-result, written by PaHost_StartEngine(). */
100 
101   unsigned int      pahsc_UserBuffersPerHostBuffer,
102                     pahsc_SamplesPerInputHostBuffer,    /* Channels per frame are accounted for. */
103                     pahsc_SamplesPerOutputHostBuffer,
104                     pahsc_BytesPerInputHostBuffer,      /* Size per sample are accounted for. */
105                     pahsc_BytesPerOutputHostBuffer;
106   short            *pahsc_InputHostBuffer,              /* Allocated here, in this file, if necessary.      */
107                    *pahsc_OutputHostBuffer;
108 
109   struct itimerval  pahsc_EntryTime,                    /* For measuring CPU utilization (same as linux).   */
110                     pahsc_LastExitTime;
111   long              pahsc_InsideCountSum,
112                     pahsc_TotalCountSum;
113 } PaHostSoundControl;
114 
115 /*-------------------------------------------------------- Shared Data -------------------------------*/
116 static internalPortAudioDevice* sDeviceList = NULL;     /* FIXME - put Mutex around this shared data. */
117 static int                      sPaHostError = 0;       /* Maybe more than one process writing errs!? */
118 usema_t                         *SendSema,              /* These variables are shared between the     */
119                                 *RcvSema;               /* audio handling process and main process.   */
120 /*--------------------------*/
Pa_GetHostError(void)121 long Pa_GetHostError(void)
122 {
123     return (long)sPaHostError;
124 }
125 
126 /*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
127 /*                              (copied from source pa_linux_oss/pa_linux_oss.c)   */
Pa_StartUsageCalculation(internalPortAudioStream * past)128 static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
129 {
130     PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
131     if( pahsc == NULL ) return;
132     /* Query system timer for usage analysis and to prevent overuse of CPU. */
133     getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
134 }
135 
SubtractTime_AminusB(struct itimerval * timeA,struct itimerval * timeB)136 static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
137 {
138     long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
139     long usecs = secs * 1000000;
140     usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
141     return usecs;
142 }
143 
Pa_EndUsageCalculation(internalPortAudioStream * past)144 static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
145 {
146     struct itimerval currentTime;
147     long  insideCount;
148     long  totalCount;       /* Measure CPU utilization during this callback. */
149 
150 #define LOWPASS_COEFFICIENT_0   (0.95)
151 #define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
152 
153     PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
154     if (pahsc == NULL)
155         return;
156     if (getitimer( ITIMER_REAL, &currentTime ) == 0 )
157         {
158         if (past->past_IfLastExitValid)
159             {
160             insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
161             pahsc->pahsc_InsideCountSum += insideCount;
162             totalCount =  SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
163             pahsc->pahsc_TotalCountSum += totalCount;
164         /*  DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
165             /* Low pass filter the result because sometimes we get called several times in a row. */
166             /* That can cause the TotalCount to be very low which can cause the usage to appear   */
167             /* unnaturally high. So we must filter numerator and denominator separately!!!        */
168             if (pahsc->pahsc_InsideCountSum > 0)
169                 {
170                 past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
171                                                  (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
172                 past->past_AverageTotalCount  = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
173                                                  (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
174                 past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
175                 pahsc->pahsc_InsideCountSum = 0;
176                 pahsc->pahsc_TotalCountSum = 0;
177                 }
178             }
179         past->past_IfLastExitValid = 1;
180         }
181     pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
182     pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
183     setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
184     past->past_IfLastExitValid = 1;
185 }   /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
186 
187 
188 /*--------------------------------------------------------------------------------------*/
translateSGIerror(void)189 PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
190 {                               /* call to report via ERR_RPT(), yields a PaError-num.  */
191     const char* a = "SGI AL ";  /* (Not absolutely sure errno came from THIS thread!    */
192     switch(oserror())           /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
193         {
194         case AL_BAD_OUT_OF_MEM:
195              ERR_RPT(("%sout of memory.\n", a));
196              return paInsufficientMemory;                   /* Known PaError.   */
197         case AL_BAD_CONFIG:
198              ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
199              return paHostError;                            /* Generic PaError. */
200         case AL_BAD_CHANNELS:
201              ERR_RPT(("%schannels not 1,2 or 4.\n", a));
202              return paHostError;                            /* Generic PaError. */
203         case AL_BAD_NO_PORTS:
204              ERR_RPT(("%sout of audio ports.\n", a));
205              return paHostError;                            /* Generic PaError. */
206         case AL_BAD_DEVICE:
207              ERR_RPT(("%swrong device number.\n", a));
208              return paHostError;                            /* Generic PaError. */
209         case AL_BAD_DEVICE_ACCESS:
210              ERR_RPT(("%swrong device access.\n", a));
211              return paHostError;                            /* Generic PaError. */
212         case AL_BAD_DIRECTION:
213              ERR_RPT(("%sinvalid direction.\n", a));
214              return paHostError;                            /* Generic PaError. */
215         case AL_BAD_SAMPFMT:
216              ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
217              return paHostError;                            /* Generic PaError. */
218         case AL_BAD_FLOATMAX:
219              ERR_RPT(("%smax float value is zero.\n", a));
220              return paHostError;                            /* Generic PaError. */
221         case AL_BAD_WIDTH:
222              ERR_RPT(("%sunsupported samplewidth.\n", a));
223              return paHostError;                            /* Generic PaError. */
224         case AL_BAD_QSIZE:
225              ERR_RPT(("%sinvalid queue size.\n", a));
226              return paHostError;                            /* Generic PaError. */
227         case AL_BAD_PVBUFFER:
228              ERR_RPT(("%sPVbuffer null.\n", a));
229              return paHostError;                            /* Generic PaError. */
230         case AL_BAD_BUFFERLENGTH_NEG:
231              ERR_RPT(("%snegative bufferlength.\n", a));
232              return paHostError;                            /* Generic PaError. */
233         case AL_BAD_BUFFERLENGTH_ODD:
234              ERR_RPT(("%sodd bufferlength.\n", a));
235              return paHostError;                            /* Generic PaError. */
236         case AL_BAD_PARAM:
237              ERR_RPT(("%sparameter not valid for device.\n", a));
238              return paHostError;                            /* Generic PaError. */
239         default:
240              ERR_RPT(("%sunknown error.\n", a));
241              return paHostError;                            /* Generic PaError. */
242         }
243 }
244 
245 /*------------------------------------------------------------------------------------------*/
246 /* Tries to set various rates and formats and fill in the device info structure.            */
Pa_sgiQueryDevice(long ALdev,PaDeviceID id,char * name,internalPortAudioDevice * pad)247 static PaError Pa_sgiQueryDevice(long                     ALdev,  /* (AL_DEFAULT_DEVICE)    */
248                                  PaDeviceID               id,     /* (DefaultI|ODeviceID()) */
249                                  char*                    name,   /* (for example "SGI AL") */
250                                  internalPortAudioDevice* pad)    /* Result written to pad. */
251 {
252     long    min, max;                           /* To catch hardware characteristics.       */
253     ALseterrorhandler(0);                       /* 0 = turn off the default error handler.  */
254     /*--------------------------------------------------------------------------------------*/
255     pad->pad_ALdevice = ALdev;                              /* Set the AL device number.    */
256     pad->pad_DeviceID = id;                                 /* Set the PA device number.    */
257     if (strlen(name) > MAX_CHARS_DEVNAME)                   /* MAX_CHARS defined above.     */
258         {
259         ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
260         return paHostError;
261         }
262     strcpy(pad->pad_DeviceName, name);                      /* Write name-string.           */
263     pad->pad_Info.name = pad->pad_DeviceName;               /* Set pointer,..hmmm.          */
264     /*--------------------------------- natively supported sample formats: -----------------*/
265     pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
266                                                  /* Then also choose other CallConvertXX()! */
267     /*--------------------------------- number of available i/o channels: ------------------*/
268     if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
269         return translateSGIerror();
270     pad->pad_Info.maxInputChannels = max;
271     DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
272     if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
273         return translateSGIerror();
274     pad->pad_Info.maxOutputChannels = max;
275     DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
276     /*--------------------------------- supported samplerates: ----------------------*/
277     pad->pad_Info.numSampleRates = 7;
278     pad->pad_Info.sampleRates = pad->pad_SampleRates;
279     pad->pad_SampleRates[0] = (double)AL_RATE_8000;     /* long -> double. */
280     pad->pad_SampleRates[1] = (double)AL_RATE_11025;
281     pad->pad_SampleRates[2] = (double)AL_RATE_16000;
282     pad->pad_SampleRates[3] = (double)AL_RATE_22050;
283     pad->pad_SampleRates[4] = (double)AL_RATE_32000;
284     pad->pad_SampleRates[5] = (double)AL_RATE_44100;
285     pad->pad_SampleRates[6] = (double)AL_RATE_48000;
286     if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max))  /* Ask INPUT rate-max.       */
287         return translateSGIerror();                     /* double -> long.           */
288     if (max != (long)(0.5 + pad->pad_SampleRates[6]))   /* FP-compare not recommndd. */
289         goto weird;
290     if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max.      */
291         return translateSGIerror();
292     if (max != (long)(0.5 + pad->pad_SampleRates[6]))
293         {
294 weird:  ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
295         return paHostError;             /* Or make it a warning and just carry on... */
296         }
297     /*-------------------------------------------------------------------------------*/
298     return paNoError;
299 }
300 
301 
302 /*--------------------------------------------------------------------------------*/
Pa_CountDevices()303 int Pa_CountDevices()       /* Name of this function suggests it only counts and  */
304 {                           /* is NOT destructive, it however resets whole PA !   */
305     int                        numDevices = 0;        /* Let 's not do that here. */
306     internalPortAudioDevice*   currentDevice = sDeviceList;   /* COPY GLOBAL VAR. */
307 #if 0                       /* Remains from linux_oss v15: Pa_Initialize(), on    */
308     if (!currentDevice)     /* its turn, calls PaHost_Init() via file pa_lib.c.   */
309         Pa_Initialize();    /* Isn't that a bit too 'rude'?        Don't be too   */
310 #endif                      /* friendly to clients that forgot to initialize PA.  */
311     while (currentDevice)   /* Slower but more elegant than the sNumDevices-way:  */
312         {
313         numDevices++;
314         currentDevice = currentDevice->pad_Next;
315         }
316     return numDevices;
317 }
318 
319 /*-------------------------------------------------------------------------------*/
Pa_GetInternalDevice(PaDeviceID id)320 static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
321 {
322     int                         numDevices = 0;
323     internalPortAudioDevice     *res = (internalPortAudioDevice*)NULL;
324     internalPortAudioDevice     *pad = sDeviceList;         /* COPY GLOBAL VAR.  */
325     while (pad)                         /* pad may be NULL, that's ok, return 0. */
326         {  /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.)      */
327         if (pad->pad_DeviceID == id)    /* This the device we were looking for?  */
328             res = pad;                  /* But keep on(!) counting so we don't   */
329         numDevices++;                   /* have to call Pa_CountDevices() later. */
330         pad = pad->pad_Next;            /* Advance to the next device or NULL.   */
331         }                               /* No assumptions about order of ID's in */
332     if (!res)                           /* the list.                             */
333         ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
334     if ((id < 0) || (id >= numDevices))
335         {
336         ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
337 #if 1                                             /* Be strict, even when found, */
338         res = (internalPortAudioDevice*)NULL;     /* do not accept illegal ID's. */
339 #endif
340         }
341     return res;
342 }
343 
344 /*----------------------------------------------------------------------*/
Pa_GetDeviceInfo(PaDeviceID id)345 const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
346 {
347     PaDeviceInfo*             res = (PaDeviceInfo*)NULL;
348     internalPortAudioDevice*  pad = Pa_GetInternalDevice(id);  /* Call. */
349     if (pad)
350         res = &pad->pad_Info;   /* Not finding the specified ID is not  */
351     if (!res)                   /* the same as &pad->pad_Info == NULL.  */
352         ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
353     return res;                 /* So (maybe) a second/third ERR_RPT(). */
354 }
355 
356 /*------------------------------------------------*/
Pa_GetDefaultInputDeviceID(void)357 PaDeviceID Pa_GetDefaultInputDeviceID(void)
358 {
359     return 0;   /* 0 is the default device ID. */
360 }
361 /*------------------------------------------------*/
Pa_GetDefaultOutputDeviceID(void)362 PaDeviceID Pa_GetDefaultOutputDeviceID(void)
363 {
364     return 0;
365 }
366 
367 /*-------------------------------------------------------------------------------------------------*/
368 /* Build linked a list with all the available audio devices on this SGI machine (only 1 for now).  */
PaHost_Init(void)369 PaError PaHost_Init(void)                              /* Called by Pa_Initialize() from pa_lib.c. */
370 {
371     internalPortAudioDevice*    pad;
372     PaError                     r = paNoError;
373     int                         audioLibFileID;             /* To test for the presence of audio.  */
374 
375     if (sDeviceList)                                        /* Allow re-init, only warn, no error. */
376         {
377         ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
378         return r;
379         }
380     /*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/
381     audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY);   /* Try to open Indigo style audio  */
382     if (audioLibFileID < 0)                                     /* IO port. On failure, machine    */
383         {                                                       /* has no audio ability.           */
384         ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
385         return paHostError;
386         }
387     close(audioLibFileID);                              /* Allocate fast mem to hold device info.  */
388     pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
389     if (pad == NULL)
390         return paInsufficientMemory;
391     memset(pad, 0, sizeof(internalPortAudioDevice));    /* "pad->pad_Next = NULL" is more elegant. */
392     r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE,            /* Set AL device num (AL_DEFAULT_DEVICE).  */
393                           Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
394                           "AL default",                 /* A suitable name.                        */
395                           pad);                         /* Write args and queried info into pad.   */
396     if (r != paNoError)
397         {
398         ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
399         PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));   /* sDeviceList still NULL ! */
400         }
401     else
402         sDeviceList = pad;            /* First element in linked list. pad->pad_Next already NULL. */
403     /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/
404     /*---------------------------------------------------------------------------------------------*/
405     return r;
406 }
407 
408 /*--------------------------------------------------------------------------------------------*/
409 #define MIN(a,b)    ((a)<(b)?(a):(b))   /* MIN()-function is used below.                      */
410 #define kPollSEMA   0                   /* To index the pollfd-array, reads nicer than just   */
411 #define kPollOUT    1                   /* numbers.                                           */
412 #define kPollIN     2
Pa_SgiAudioProcess(void * v)413 void Pa_SgiAudioProcess(void *v)        /* This function is sproc-ed by PaHost_StartEngine()  */
414 {                                       /* as a separate thread. (Argument must be void*).    */
415     short                   evtLoop;    /* Reset by parent indirectly, or at local errors.    */
416     PaError                 result;
417     struct pollfd           PollFD[3];  /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
418     internalPortAudioStream *past = (internalPortAudioStream*)v;   /* Copy void-ptr-argument. */
419     PaHostSoundControl      *pahsc;
420     short                   n, inputEvent, outputEvent, ioEvent, semaEvent = 0;
421     short                   *inBuffer, *outBuffer;      /* Only 16 bit for now, may change... */
422     unsigned int            samplesPerInputUserBuffer, samplesPerOutputUserBuffer;
423 
424     DBUG(("Entering sproc-thread.\n"));
425     if (!past)
426         {
427         sPaHostError = paInternalError;     /* Or paBadStreamPtr ? */
428         ERR_RPT(("argument NULL!\n"));
429         goto noPast;
430         }
431     pahsc = (PaHostSoundControl*)past->past_DeviceData;
432     if (!pahsc)
433         {
434         sPaHostError = paInternalError;     /* The only way is to signal error to shared area?!   */
435         ERR_RPT(("past_DeviceData NULL!\n"));
436         goto noPahsc;                       /* Sproc-ed threads MAY NOT RETURN paInternalError.   */
437         }
438     /*----------------------------- open AL-ports here, after sproc(): -----------------------*/
439     if (past->past_NumInputChannels > 0)                                  /* Open input port. */
440         {
441         pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", pahsc->pahsc_ALconfigIN);
442         if (!pahsc->pahsc_ALportIN)
443             {
444             ERR_RPT(("Failed to open AL input port.\n"));
445             sPaHostError = paInternalError;
446             goto skip;
447             }
448         DBUG(("Opened %d input channel(s).\n", past->past_NumInputChannels));
449         samplesPerInputUserBuffer = pahsc->pahsc_SamplesPerInputHostBuffer /
450                                     pahsc->pahsc_UserBuffersPerHostBuffer;
451         }
452     else
453         samplesPerInputUserBuffer = 0; /* Added 2003. */
454     if (past->past_NumOutputChannels > 0)                               /* Open output port. */
455         {
456         pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", pahsc->pahsc_ALconfigOUT);
457         if (!pahsc->pahsc_ALportOUT)
458             {
459             ERR_RPT(("Failed to open AL output port.\n"));
460             sPaHostError = paInternalError;                 /* Assume pahsc_ALconfigs are the */
461             goto skip;                                      /* same for IN and OUT in case    */
462             }                                               /* both ports are opened (bidir). */
463         DBUG(("Opened %d output channel(s).\n", past->past_NumOutputChannels));
464         samplesPerOutputUserBuffer = pahsc->pahsc_SamplesPerOutputHostBuffer /
465                                      pahsc->pahsc_UserBuffersPerHostBuffer;
466         DBUG(("samplesPerOutputUserBuffer = %d\n", samplesPerOutputUserBuffer));
467         }
468     else
469         samplesPerOutputUserBuffer = 0; /* Added 2003. */
470     /*-----------------------------------------------------------------------*/
471     past->past_IsActive = 1;            /* Wasn't this already done by the calling parent?!   */
472     PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN);    /* ALgetfd returns -1 on failures */
473     PollFD[kPollIN].events = POLLIN;                        /* such as ALport not there.      */
474     PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
475     PollFD[kPollOUT].events = POLLOUT;                      /* .events = POLLOUT is OK.       */
476     schedctl(NDPRI, NDPHIMIN);              /* Sets non-degrading priority for this process.  */
477     PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777);  /* To communicate with parent.    */
478     PollFD[kPollSEMA].events = POLLIN;                      /* .events = POLLIN is OK.        */
479     uspsema(SendSema);              /* Blocks until ... MUST be here, this uspsema(). */
480     evtLoop = ((past->past_StopNow | past->past_StopSoon) == 0);
481     while (evtLoop)
482         {
483         /*----------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ---------*/
484         if (pahsc->pahsc_InputHostBuffer)           /* Then pahsc_ALportIN should also be there.  */
485             {
486             /* For input port, fill point is number of locations in the sample queue that must be */
487             /* filled in order to trigger a return from select(). (or poll())                     */
488             /* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
489             if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputHostBuffer))
490                 {                                    /* Multiple amount as transferred per time.  */
491                 ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
492                 sPaHostError = paInternalError;         /* (Using exit(-1) would be a bit rude.)  */
493                 goto skip;
494                 }
495             }
496         /* 'else' added march 2003: set only one of both fillpoints: input or output. When    */
497         /* setting both fillpoints (as in earlier version) clicks occur at full duplex-mode.  */
498         else if (pahsc->pahsc_OutputHostBuffer)     /* Then pahsc_ALportOUT should also be there. */
499             {
500             /* For output port, fill point is number of locations that must be free in order to   */
501             /* wake up from select(). (or poll())                                                 */
502             if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputHostBuffer))
503                 {
504                 ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
505                 sPaHostError = paInternalError;
506                 goto skip;
507                 }
508             }                       /* poll() with timeout=-1 makes it block until a requested    */
509         poll(PollFD, 3, -1);        /* event occurs or until call is interrupted. If fd-value in  */
510                                     /* array <0, events is ignored and revents is set to 0.       */
511         /*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
512         semaEvent = PollFD[kPollSEMA].revents & POLLIN;
513         if (semaEvent)
514             {
515             if (past->past_StopSoon)
516                 evtLoop = 0;
517             if (past->past_StopNow)
518                 goto skip;
519             }
520         /*------------------------------------- FILLED-EVENT FROM INPUT BUFFER: --------------------------*/
521         inputEvent = PollFD[kPollIN].revents & POLLIN;
522         if (inputEvent)         /* Don't need to check (pahsc->pahsc_InputHostBuffer):  */
523             {                   /* if buffer was not there, ALport not there, no events!  */
524             if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_InputHostBuffer,
525                             pahsc->pahsc_SamplesPerInputHostBuffer))
526                 {                           /* Here again: number of samples instead of number of frames. */
527                 ERR_RPT(("ALreadsamps() failed.\n"));
528                 sPaHostError = paInternalError;
529                 goto skip;
530                 }
531             }
532         outputEvent = PollFD[kPollOUT].revents & POLLOUT;
533         ioEvent = (inputEvent | outputEvent);   /* Binary or is ok. */
534         /*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
535         if (ioEvent)                            /* Always true? Or can some other system-event awaken the */
536             {                                   /* poll? Sure it wasn't just a "sema"- (i.e. user)-event? */
537             Pa_StartUsageCalculation(past);                         /* Convert 16 bit native data to      */
538                                                                     /* user data and call user routine.   */
539             inBuffer  = pahsc->pahsc_InputHostBuffer;               /* Short pointers for now, care!      */
540             outBuffer = pahsc->pahsc_OutputHostBuffer;
541             n = pahsc->pahsc_UserBuffersPerHostBuffer;              /* 'n' may never start at NULL ! */
542             do  {
543                 result = Pa_CallConvertInt16(past, inBuffer, outBuffer);
544                 if (result)    /* This is apparently NOT an error! Just letting the userCallBack stop us. */
545                     { DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result)); goto skip; }
546                 inBuffer  += samplesPerInputUserBuffer;             /* Num channels is accounted for. */
547                 outBuffer += samplesPerOutputUserBuffer;
548                 } while (--n);
549             Pa_EndUsageCalculation(past);
550             }
551         /*------------------------------------ FREE-EVENT FROM OUTPUT BUFFER: ---------------------------*/
552         if (pahsc->pahsc_OutputHostBuffer && ioEvent)   /* Don't wait for outputEvent solely (that may cause clicks). */
553             {                                           /* Just assume it's time to write, outputEvent may not yet be there. */
554             if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_OutputHostBuffer,
555                              pahsc->pahsc_SamplesPerOutputHostBuffer))
556                 {
557                 ERR_RPT(("ALwritesamps() failed.\n"));  /* Better use SEMAS for messaging back to parent! */
558                 sPaHostError = paInternalError;
559                 goto skip;
560                 }
561             }
562         }
563 skip:
564     /*------------------------------- close AL-ports ----------------------------*/
565     if (pahsc->pahsc_ALportIN)
566         {
567         if (ALcloseport(pahsc->pahsc_ALportIN))
568             translateSGIerror();   /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
569         else                                /* But go on anyway... to release other stuff... */
570             pahsc->pahsc_ALportIN = (ALport)0;
571         }
572     if (pahsc->pahsc_ALportOUT)
573         {
574         if (ALcloseport(pahsc->pahsc_ALportOUT))
575             translateSGIerror();
576         else
577             pahsc->pahsc_ALportOUT = (ALport)0;
578         }
579 noPahsc:
580     past->past_IsActive = 0;
581     if (semaEvent)
582         {
583         uspsema(SendSema);  /* StopEngine() was still waiting for this acknowledgement. */
584         usvsema(RcvSema);   /* (semaEvent initialized with 0.)          */
585         }
586 noPast:
587     DBUG(("Leaving sproc-thread.\n"));
588 }
589 
590 
591 /*--------------------------------------------------------------------------------------*/
PaHost_OpenStream(internalPortAudioStream * past)592 PaError PaHost_OpenStream(internalPortAudioStream *past)
593 {
594     PaError                 result = paNoError;
595     PaHostSoundControl      *pahsc;
596     unsigned int            minNumBuffers;
597     internalPortAudioDevice *padIN, *padOUT;        /* For looking up native AL-numbers. */
598     long                    pvbuf[8], sr, alq;      /* To get/set hardware configs.      */
599 
600     DBUG(("PaHost_OpenStream() called.\n"));        /* Alloc FASTMEM and init host data. */
601     if (!past)
602         {
603         ERR_RPT(("Streampointer NULL!\n"));
604         result = paBadStreamPtr; goto done;
605         }
606     pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
607     if (pahsc == NULL)
608         {
609         ERR_RPT(("FAST Memory allocation failed.\n"));  /* Pass trough some ERR_RPT-exit- */
610         result = paInsufficientMemory; goto done;       /* code (nothing will be freed).  */
611         }
612     memset(pahsc, 0, sizeof(PaHostSoundControl));
613     pahsc->pahsc_threadPID = -1;                    /* Should pahsc_threadPID be inited to */
614     past->past_DeviceData = (void*)pahsc;           /* -1 instead of 0 ??                  */
615     /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
616     ALseterrorhandler(0);                           /* 0 = turn off the default error handler.   */
617     pvbuf[0] = AL_INPUT_RATE;
618     pvbuf[2] = AL_INPUT_COUNT;
619     pvbuf[4] = AL_OUTPUT_RATE;              /* TO FIX: rates may be logically, not always in Hz! */
620     pvbuf[6] = AL_OUTPUT_COUNT;
621     sr = (long)(past->past_SampleRate + 0.5);   /* Common for both input and output :-) */
622     /*-----------------------------------------------------------------------------*/
623     /* OVERWRITE 'past_NumUserBuffers'-field in the struct supplied by the caller. */
624     /* This field may be set to zero by a client application to ask for minimum    */
625     /* latency. It is used below, to set both input- and output-AL-queuesizes.     */
626     minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer,
627                                         past->past_SampleRate);   /* Take biggest. */
628     past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
629                                  minNumBuffers : past->past_NumUserBuffers;
630     DBUG(("past->past_NumUserBuffers=%d\n", past->past_NumUserBuffers));
631     /*----------------------------------------------------------------------------------*/
632     pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers >> 1;
633     DBUG(("pahsc_UserBuffersPerHostBuffer=%d\n",pahsc->pahsc_UserBuffersPerHostBuffer));
634     /*  1 is minimum because Pa_GetMinNumBuffers() returns >= 2.
635         Callback will be called 'pahsc_UserBuffersPerHostBuffer' times (with 'past_FramesPerUserBuffer')
636         per host transfer. */
637     /*---------------------------------------------------- SET INPUT CONFIGURATION: ---------------------*/
638     if (past->past_NumInputChannels > 0)                                /* We need to lookup the corre-  */
639         {                                                               /* sponding native AL-number(s). */
640         /*--------------------------------------------------- Allocate native buffers: --------------*/
641         pahsc->pahsc_SamplesPerInputHostBuffer = pahsc->pahsc_UserBuffersPerHostBuffer *
642                                                  past->past_FramesPerUserBuffer *       /* Needed by the */
643                                                  past->past_NumInputChannels;           /* audio-thread. */
644         DBUG(("pahsc_SamplesPerInputHostBuffer=%d\n", pahsc->pahsc_SamplesPerInputHostBuffer));
645         pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_SamplesPerInputHostBuffer * sizeof(short);
646         pahsc->pahsc_InputHostBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer);
647         if (!pahsc->pahsc_InputHostBuffer)
648             {
649             ERR_RPT(("Fast memory allocation failed (in).\n"));
650             result = paInsufficientMemory;
651             goto done;
652             }
653         padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
654         if (!padIN)
655             {
656             ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
657             result = paHostError;
658             goto done;
659             }
660         if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
661             goto sgiError;                                  /* the same AL-device, the AL-library might  */
662         if (pvbuf[1] != sr)                                 /* contain more than AL_DEFAULT_DEVICE in    */
663             {  /* Rate different from current harware-rate?    the future. Therefore 2 seperate queries. */
664             if (pvbuf[3] > 0)     /* Means, there's other clients using AL-input-ports */
665                 {
666                 ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
667 another process is currently using input at %ld Hz.\n", sr, pvbuf[1]));
668                 result = paHostError;
669                 goto done;
670                 }
671             pvbuf[1] = sr;                  /* Then set input-rate. */
672             if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
673                 goto sgiError;        /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
674             }                         /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */
675         pahsc->pahsc_ALconfigIN = ALnewconfig();                       /* Released at PaHost_CloseStream().  */
676         if (pahsc->pahsc_ALconfigIN == (ALconfig)0)
677             goto sgiError;
678         if (ALsetsampfmt(pahsc->pahsc_ALconfigIN, AL_SAMPFMT_TWOSCOMP))/* Choose paInt16 as native i/o-format.      */
679             goto sgiError;
680         if (ALsetwidth (pahsc->pahsc_ALconfigIN, AL_SAMPLE_16))        /* Only meaningful when sample format for    */
681             goto sgiError;                                      /* config is set to two's complement format. */
682         /************************ Future versions might (dynamically) switch to 32-bit floats? *******
683         if (ALsetsampfmt(pahsc_ALconfigIN, AL_SAMPFMT_FLOAT))    (Then also call another CallConvert-func.)
684             goto sgiError;
685         if (ALsetfloatmax (pahsc_ALconfigIN, 1.0))       Only meaningful when sample format for config
686             goto sgiError;                               is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
687         /*--------- Set internal AL queuesize (in samples, not in frames!) -------------------------------*/
688         alq = (long)past->past_NumUserBuffers * past->past_FramesPerUserBuffer * past->past_NumInputChannels;
689         DBUG(("AL input queuesize = %ld samples.\n", alq));
690         if (ALsetqueuesize(pahsc->pahsc_ALconfigIN, alq))
691             goto sgiError;
692         if (ALsetchannels (pahsc->pahsc_ALconfigIN, (long)(past->past_NumInputChannels)))
693             goto sgiError;                              /* Returns 0 on success, -1 on failure. */
694         }
695     else
696         pahsc->pahsc_InputHostBuffer = (short*)NULL;    /* Added 2003! Is checked in callback-routine. */
697     /*---------------------------------------------------- SET OUTPUT CONFIGURATION: ------------------------*/
698     if (past->past_NumOutputChannels > 0)               /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */
699         {                                               /* We use padOUT/IN later on, or at least 1 of both. */
700         pahsc->pahsc_SamplesPerOutputHostBuffer = pahsc->pahsc_UserBuffersPerHostBuffer *
701                                                   past->past_FramesPerUserBuffer *          /* Needed by the */
702                                                   past->past_NumOutputChannels;             /* audio-thread. */
703         DBUG(("pahsc_SamplesPerOutputHostBuffer=%d\n", pahsc->pahsc_SamplesPerOutputHostBuffer));
704         pahsc->pahsc_BytesPerOutputHostBuffer   = pahsc->pahsc_SamplesPerOutputHostBuffer * sizeof(short);
705         pahsc->pahsc_OutputHostBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer);
706         if (!pahsc->pahsc_OutputHostBuffer)
707             {
708             ERR_RPT(("Fast memory allocation failed (out).\n"));
709             result = paInsufficientMemory;
710             goto done;
711             }
712         padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
713         if (!padOUT)
714             {
715             ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
716             result = paHostError;
717             goto done;
718             }
719         if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
720             goto sgiError;
721         if (pvbuf[5] != sr)
722             {   /* Output needed and rate different from current harware-rate. */
723             if (pvbuf[7] > 0)     /* Means, there's other clients using AL-output-ports */
724                 {
725                 ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
726 another process is currently using output at %ld Hz.\n", sr, pvbuf[5]));
727                 result = paHostError;
728                 goto done;                              /* Will free again the inputbuffer */
729                 }                                       /* that was just created above.    */
730             pvbuf[5] = sr;                  /* Then set output-rate. */
731             if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
732                 goto sgiError;
733             }
734         pahsc->pahsc_ALconfigOUT = ALnewconfig();                           /* Released at PaHost_CloseStream(). */
735         if (pahsc->pahsc_ALconfigOUT == (ALconfig)0)
736             goto sgiError;
737         if (ALsetsampfmt(pahsc->pahsc_ALconfigOUT, AL_SAMPFMT_TWOSCOMP))    /* Choose paInt16 as native i/o-format. */
738             goto sgiError;
739         if (ALsetwidth (pahsc->pahsc_ALconfigOUT, AL_SAMPLE_16))        /* Only meaningful when sample format for    */
740             goto sgiError;                                              /* config is set to two's complement format. */
741                                                 /** Future versions might (dynamically) switch to 32-bit floats. **/
742         alq = (long)past->past_NumUserBuffers * past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
743         DBUG(("AL output queuesize = %ld samples.\n", alq));
744         if (ALsetqueuesize(pahsc->pahsc_ALconfigOUT, alq))
745             goto sgiError;
746         if (ALsetchannels (pahsc->pahsc_ALconfigOUT, (long)(past->past_NumOutputChannels)))
747             goto sgiError;
748         }
749     else
750         pahsc->pahsc_OutputHostBuffer = (short*)NULL;
751     /*----------------------------------------------- TEST DEVICE ID's: --------------------*/
752     if ((past->past_OutputDeviceID != past->past_InputDeviceID) &&          /* Who SETS these devive-numbers? */
753         (past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
754         {
755         ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
756         result = paHostError;
757         goto done;
758         }
759     goto done;  /* (no errors occured) */
760 sgiError:
761     result = translateSGIerror();   /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
762 done:
763     if (result != paNoError)
764         PaHost_CloseStream(past);               /* Frees memory (only if really allocated!).  */
765     return result;
766 }
767 
768 /*-----------------------------------------------------*/
PaHost_StartOutput(internalPortAudioStream * past)769 PaError PaHost_StartOutput(internalPortAudioStream *past)
770 {
771     return paNoError;   /* Hmm, not implemented yet? */
772 }
PaHost_StartInput(internalPortAudioStream * past)773 PaError PaHost_StartInput(internalPortAudioStream *past)
774 {
775     return paNoError;
776 }
777 
778 /*------------------------------------------------------------------------------*/
PaHost_StartEngine(internalPortAudioStream * past)779 PaError PaHost_StartEngine(internalPortAudioStream *past)
780 {
781     PaHostSoundControl  *pahsc;
782     usptr_t             *arena;
783     if (!past)          /* Test argument. */
784         {
785         ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
786         return paBadStreamPtr;
787         }
788     pahsc = (PaHostSoundControl*)past->past_DeviceData;
789     if (!pahsc)
790         {
791         ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
792         return paHostError;
793         }
794     past->past_StopSoon = 0;            /* Assume SGI ALport is already opened! */
795     past->past_StopNow  = 0;            /* Why don't we check pahsc for NULL?   */
796     past->past_IsActive = 1;
797 
798     /* Although the pthread_create() function, as well as <pthread.h>, may be   */
799     /* available in IRIX, use sproc() on SGI to create audio-background-thread. */
800     /* (Linux/oss uses pthread_create() instead of __clone() because:           */
801     /*  - pthread_create also works for other UNIX systems like Solaris,        */
802     /*  - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().)  */
803 
804     usconfig(CONF_ARENATYPE, US_SHAREDONLY);    /* (From SGI-AL-examples, file  */
805     arena = usinit(tmpnam(0));                  /*  motifexample.c, function    */
806     SendSema = usnewpollsema(arena, 0);         /*  InitializeAudioProcess().)  */
807     RcvSema = usnewsema(arena, 1);              /* 1= common mutual exclusion semaphore, where 1 and only 1 process
808                                                    will be permitted through a semaphore at a time.  Values > 1
809                                                    imply that up to val resources may be simultaneously used, but requests
810                                                    for more than val resources cause the calling process to block until a
811                                                    resource comes free (by a process holding a resource performing a
812                                                    usvsema().                IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
813     prctl(PR_SETEXITSIG, 0);                    /* No not (void*)9, but 0, which doesn't kill the parent! */
814                                                 /* PR_SETEXITSIG controls whether all members of a share group will be
815                                                    signaled if any one of them leaves the share group (either via exit()
816                                                    or exec()).  If 2nd arg, interpreted as an int is 0, then normal IRIX
817                                                    process termination rules apply, namely that the parent is sent a
818                                                    SIGCLD upon death of child, but no indication of death of parent is
819                                                    given.  If the second argument is a valid signal number then if any
820                                                    member of a share group leaves the share group, a signal is
821                                                    sent to ALL surviving members of the share group.  */
822     /* SPAWN AUDIO-CHILD: */
823     pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
824                                    PR_SALL,            /* new process, or -1.   */
825                                    (void*)past);       /* Pass past as optional */  /* IS THIS SAFE, will past never */
826     if (pahsc->pahsc_threadPID == -1)                  /* third void-ptr-arg.   */  /* be moved around in memory???? */
827         {
828         ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
829         sPaHostError = oserror();   /* Pass native error-number to shared area. */
830         return paHostError;         /* But return the generic error-number.     */
831         }
832     return paNoError;               /* Hmmm, errno may come from other threads in same group! */
833 }                                   /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
834 
835 /*------------------------------------------------------------------------------*/
PaHost_StopEngine(internalPortAudioStream * past,int abort)836 PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
837 {
838     PaError             result = paNoError;
839     PaHostSoundControl  *pahsc;
840 
841     DBUG(("PaHost_StopEngine() called.\n"));
842     if (!past)
843         return paBadStreamPtr;
844     pahsc = (PaHostSoundControl*)past->past_DeviceData;
845                                                     /* Prevent from doing this twice!! */
846     if ((!pahsc) ||                                 /* Some tests call this CLOSE twice!! */
847         (!past->past_IsActive) ||
848         past->past_StopSoon || past->past_StopNow)
849         return result;              /* paNoError (already stopped, no err?).       */
850     past->past_StopSoon = 1;        /* Tell background thread to stop generating   */
851     if (abort)                      /* more and to let current data play out. If   */
852         past->past_StopNow = 1;     /* aborting, tell backgrnd thread to stop NOW! */
853                                     /*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
854     usvsema(SendSema);              /* Increments count associated with SendSema.  */
855                                     /* Wait for the response.                      */
856     uspsema(RcvSema);               /* Decrements count of previously allocated    */
857                                     /* semaphore specified by RcvSema.             */
858     while (past->past_IsActive)     /* REALLY WAIT. */
859         {
860         /* DBUG(("wait 1 ms for audio-thread to stop.\n")); */
861         Pa_Sleep(1);
862         }
863 
864 #if 0   /* We don't need to KILL(), just COMMUNICATE and be patient...             */
865     if (pahsc->pahsc_threadPID != -1)   /* Did we really init it to -1 somewhere?  */
866         {
867         DBUG(("PaHost_StopEngine() is about to kill(SIGKILL) audio-thread.\n"));
868         if (kill(pahsc->pahsc_threadPID, SIGKILL))  /* Or SIGTERM or SIGQUIT(core)  */
869             {                                       /* Returns -1 in case of error. */
870             result = paHostError;
871             sPaHostError = oserror();   /* Hmmm, other threads may also write here! */
872             ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
873             }
874         else
875             pahsc->pahsc_threadPID = -1;    /* Notify that we've killed this thread. */
876         }
877 #endif
878     past->past_IsActive = 0;    /* Even when kill() failed and pahsc_threadPID still there??? */
879     return result;
880 }
881 
882 /*---------------------------------------------------------------*/
PaHost_StopOutput(internalPortAudioStream * past,int abort)883 PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
884 {
885     return paNoError;                   /* Not implemented yet? */
886 }
PaHost_StopInput(internalPortAudioStream * past,int abort)887 PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
888 {
889     return paNoError;
890 }
891 
892 /*******************************************************************/
PaHost_CloseStream(internalPortAudioStream * past)893 PaError PaHost_CloseStream(internalPortAudioStream *past)
894 {
895     PaHostSoundControl  *pahsc;
896     PaError             result = paNoError;
897 
898     DBUG(("PaHost_CloseStream() called.\n"));
899     if (!past)
900         return paBadStreamPtr;
901     pahsc = (PaHostSoundControl *) past->past_DeviceData;
902     if (!pahsc)             /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
903         return result;      /* This test prevents from freeing NULL-pointers. */
904 
905     if (pahsc->pahsc_ALconfigIN)
906         {                                   /* Release configuration structs, only if allocated. */
907         ALfreeconfig(pahsc->pahsc_ALconfigIN);
908         pahsc->pahsc_ALconfigIN = NULL;
909         }
910     if (pahsc->pahsc_ALconfigOUT)
911         {
912         ALfreeconfig(pahsc->pahsc_ALconfigOUT); /* (Al-ports were already closed by audioProcess). */
913         pahsc->pahsc_ALconfigOUT = NULL;
914         }
915     if (pahsc->pahsc_InputHostBuffer)
916         {
917         PaHost_FreeFastMemory(pahsc->pahsc_InputHostBuffer, pahsc->pahsc_BytesPerInputHostBuffer);
918         pahsc->pahsc_InputHostBuffer = NULL;
919         }
920     if (pahsc->pahsc_OutputHostBuffer)
921         {
922         PaHost_FreeFastMemory(pahsc->pahsc_OutputHostBuffer, pahsc->pahsc_BytesPerOutputHostBuffer);
923         pahsc->pahsc_OutputHostBuffer = NULL;
924         }
925     PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));
926     past->past_DeviceData = NULL;    /* PaHost_OpenStream() allocated FAST MEM. */
927     return result;
928 }
929 
930 
931 /*------------------------------------------------------------------------*/
932 /* Determine minimum number of buffers required for (SGI) host based on   */
933 /* minimum latency. Latency can be optionally set by user by setting an   */
934 /* environment variable. For example, to set my latency to 200 msec, I've */
935 /* put this line in my '.cshrc' file:    setenv PA_MIN_LATENCY_MSEC 200   */
936 /* It always calls the 'PRINT' macro.                                     */
937 /* The minimum number that is returned is 2.                              */
938 /* This number is directly proportional to the AL-queue sizes to set up.  */
939 /* It is one more than the number of user buffers per host buffer - in    */
940 /* case minimum is returned, or, twice the user buffers per host buffer.  */
941 /*------------------------------------------------------------------------*/
Pa_GetMinNumBuffers(int framesPerUserBuffer,double framesPerSecond)942 int Pa_GetMinNumBuffers(int framesPerUserBuffer, double framesPerSecond)
943 {
944     int     minBuffers, minLatencyMsec;
945     char    *minLatencyText;
946     double  actualLatency;
947 
948     minLatencyText = getenv(PA_LATENCY_ENV_NAME); /* Defined at top of file. */
949     if (minLatencyText)
950         {
951         minLatencyMsec = atoi(minLatencyText);
952         if (minLatencyMsec < 10)
953             {             /* 10 is the minimum. */
954             minLatencyMsec = 10;
955             PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' below minimum of %d milliseconds.\n",
956                 minLatencyMsec));
957             }
958         else if (minLatencyMsec > 4000)
959             {             /* 4000 is the maximum. */
960             minLatencyMsec = 4000;
961             PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' above maximum of %d milliseconds.\n",
962                 minLatencyMsec));
963             }
964         else
965             PRINT (("Using environment variable 'PA_MIN_LATENCY_MSEC' (set to %d milliseconds).\n",
966                 minLatencyMsec));
967         }
968     else
969         {
970         minLatencyMsec = MIN_LATENCY_MSEC;   /* Defined at top of this file. */
971         PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' not found.\nUsing default of %d milliseconds\n",
972             minLatencyMsec));
973         }
974     minBuffers = (int)((minLatencyMsec * framesPerSecond) /
975                        (1000.0 * framesPerUserBuffer));
976     if (minBuffers < 2)
977         minBuffers = 2;
978     actualLatency = 1000.0 * minBuffers * framesPerUserBuffer / framesPerSecond;
979     PRINT (("Actual AL latency set to %.2f milliseconds\n", actualLatency));
980     return minBuffers;
981 }
982 
983 /*---------------------------------------------------------------------*/
PaHost_Term(void)984 PaError PaHost_Term(void)   /* Frees all of the linked audio-devices.  */
985 {                           /* Called by Pa_Terminate() from pa_lib.c. */
986     internalPortAudioDevice *pad = sDeviceList,
987                             *nxt;
988     while (pad)
989         {
990         DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
991         nxt = pad->pad_Next;
992         PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
993         pad = nxt;              /* PaHost_Init allocated this fast mem.*/
994         }
995     sDeviceList = (internalPortAudioDevice*)NULL;
996     return 0;
997 }
998 
999 /***********************************************************************/
Pa_Sleep(long msec)1000 void Pa_Sleep( long msec )  /* Sleep requested number of milliseconds. */
1001 {
1002 #if 0
1003     struct timeval timeout;
1004     timeout.tv_sec = msec / 1000;
1005     timeout.tv_usec = (msec % 1000) * 1000;
1006     select(0, NULL, NULL, NULL, &timeout);
1007 #else
1008     long usecs = msec * 1000;
1009     usleep( usecs );
1010 #endif
1011 }
1012 
1013 /*---------------------------------------------------------------------------------------*/
1014 /* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
1015 /* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
1016 /* a call to PaHost_FreeFastMemory().                                                    */
PaHost_AllocateFastMemory(long numBytes)1017 void *PaHost_AllocateFastMemory(long numBytes)
1018 {
1019     void *addr = malloc(numBytes);  /* mpin() reads into memory all pages over the given */
1020     if (addr)                       /* range and locks the pages into memory. A counter  */
1021         {                           /* is incremented each time the page is locked. The  */
1022         if (mpin(addr, numBytes))   /* superuser can lock as many pages as it wishes,    */
1023             {                       /* others are limited to the configurable PLOCK_MA.  */
1024             ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
1025 #if 1
1026             free(addr);             /* You MAY cut out these 2 lines to be less strict,  */
1027             addr = NULL;            /* you then only get the warning but PA goes on...   */
1028 #endif                              /* Only problem then may be corresponding munpin()   */
1029             }                       /* call at PaHost_FreeFastMemory(), below.           */
1030         memset(addr, 0, numBytes);  /* Locks established with mlock are not inherited by */
1031         }                           /* a child process after a fork. Furthermore, IRIX-  */
1032     return addr;                    /* man-pages warn against mixing both mpin and mlock */
1033 }                                   /* in 1 piece of code, so stick to mpin()/munpin() ! */
1034 
1035 
1036 /*---------------------------------------------------------------------------------------*/
1037 /* Free memory that could be accessed in real-time. This call MUST be balanced with a    */
1038 /* call to PaHost_AllocateFastMemory().                                                  */
PaHost_FreeFastMemory(void * addr,long numBytes)1039 void PaHost_FreeFastMemory(void *addr, long numBytes)
1040 {
1041     if (addr)
1042         {
1043         if (munpin(addr, numBytes))     /* Will munpin() fail when it was never mpinned? */
1044             ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
1045         free(addr);                     /* But go on, try to release it, just warn...    */
1046         }
1047 }
1048 
1049 /*----------------------------------------------------------*/
PaHost_StreamActive(internalPortAudioStream * past)1050 PaError PaHost_StreamActive( internalPortAudioStream   *past )
1051 {
1052     PaHostSoundControl *pahsc;
1053     if (past == NULL)
1054         return paBadStreamPtr;
1055     pahsc = (PaHostSoundControl *) past->past_DeviceData;
1056     if (pahsc == NULL)
1057         return paInternalError;
1058     return (PaError)(past->past_IsActive != 0);
1059 }
1060 
1061 /*-------------------------------------------------------------------*/
Pa_StreamTime(PortAudioStream * stream)1062 PaTimestamp Pa_StreamTime( PortAudioStream *stream )
1063 {
1064     internalPortAudioStream *past = (internalPortAudioStream *) stream;
1065 /* FIXME - return actual frames played, not frames generated.
1066 ** Need to query the output device somehow.
1067 */
1068     return past->past_FrameCount;
1069 }
1070