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, ¤tTime ) == 0 )
157 {
158 if (past->past_IfLastExitValid)
159 {
160 insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime );
161 pahsc->pahsc_InsideCountSum += insideCount;
162 totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime );
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