aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio_v18/pa_sgi
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2004-02-02 11:28:02 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2004-02-02 11:28:02 +0000
commitae6b5d89ea93b95c2990895077cf5e8f0bba9ad9 (patch)
tree1e7570f11ac688e94342968e90301c4684e61193 /pd/portaudio_v18/pa_sgi
parentf26399eba6ee6ce9eb7bae9a4b60a90dc2ebca94 (diff)
This commit was generated by cvs2svn to compensate for changes in r1301,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=1302
Diffstat (limited to 'pd/portaudio_v18/pa_sgi')
-rw-r--r--pd/portaudio_v18/pa_sgi/Makefile63
-rw-r--r--pd/portaudio_v18/pa_sgi/pa_sgi.c1069
-rw-r--r--pd/portaudio_v18/pa_sgi/pthread-Makefile52
-rw-r--r--pd/portaudio_v18/pa_sgi/pthread-pa_sgi.c908
4 files changed, 2092 insertions, 0 deletions
diff --git a/pd/portaudio_v18/pa_sgi/Makefile b/pd/portaudio_v18/pa_sgi/Makefile
new file mode 100644
index 00000000..0cecda2d
--- /dev/null
+++ b/pd/portaudio_v18/pa_sgi/Makefile
@@ -0,0 +1,63 @@
+# Makefile for pa_sgi. PortAudio for Silicon Graphics IRIX 6.2-6.5.
+# Pieter suurmond, march 15, 2003. (pa-V18-patch).
+# Tested under IRIX 6.5 with both GCC and MIPS compilers.
+# Based on SGI-specific sproc()-method to spawn children, not on POSIX-threads.
+
+# Choose compiler in combination with options:
+
+CC = cc
+CFLAGS = -O2
+
+# Possible options with MIPSpro compiler are: -32, -o32, -n32, -64,
+# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
+# For GCC, use -Wall. And use for example -O2 or -O3 for better optimization:
+
+#CC = gcc
+#CFLAGS = -O2 -Wall
+
+# Instead of "-lpthread", as with linux, just the audio- and math-library for SGI:
+
+LIBS = -laudio -lm
+
+# So sourcefiles can find included headerfiles in pa_common:
+CDEFINES = -I../pa_common
+
+PASRC = ../pa_common/pa_lib.c pa_sgi.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests performed on SGI Indy with R5000 @ 180MHz running IRIX 6.5:
+# Used GCC compiler version 3.0.4. and MIPS.
+
+TESTC = $(PASRC) ../pa_tests/patest_record.c ## OK GCC march 2003 (MIPS sees errors in patest_record.c, refuses compilation).
+#TESTC = $(PASRC) ../pa_tests/patest_latency.c ## OK GCC march 2003. (MIPS doesn't like //)
+#TESTC = $(PASRC) ../pa_tests/patest_longsine.c ## OK GCC march 2003.
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c ## OK GCC Click free now. march 2003.
+#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c ## OK GCC march 2003.
+
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c # Never knew my Indy had 16 input- and output-channels!
+#TESTC = $(PASRC) ../pa_tests/patest_saw.c ## - Pa_sleep doesn't work anymore?!
+#TESTC = $(PASRC) ../pa_tests/patest_sine.c # - Pa_sleep doesn't work anymore?!
+#TESTC = $(PASRC) ../pa_tests/patest_many.c ## - Pa_sleep doesn't work anymore?!
+#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c ## Silence for 2 seconds doesn't work, sine sounds ok.
+
+#TESTC = $(PASRC) ../pa_tests/patest_sine8.c ## Silence for 2 seconds doesn't work, sine sounds ok.
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK GCC and MIPS-CC march 2003.
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c ## OK GCC march 2003
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c #
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c # MIPS doesn't like patest_stop.c Worked before but now
+ # error AL_BAD_QSIZE on IRIX 6.5 (with GCC).
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c #
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c # I don't hear the 6th beep, not really in sync @500ms lat.
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # A lot of error messages but no coredump.
+#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Segmentation fault (core dumped)!
+
+TESTH = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+ $(CC) $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
diff --git a/pd/portaudio_v18/pa_sgi/pa_sgi.c b/pd/portaudio_v18/pa_sgi/pa_sgi.c
new file mode 100644
index 00000000..fa2978b5
--- /dev/null
+++ b/pd/portaudio_v18/pa_sgi/pa_sgi.c
@@ -0,0 +1,1069 @@
+/*
+ * $Id: pa_sgi.c,v 1.2.4.2 2003/03/13 00:56:47 pieter Exp $
+ * PortAudio Portable Real-Time Audio Library. Copyright (c) 1999-2001 Phil Burk.
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Silicon Graphics (SGI) IRIX implementation by Pieter Suurmond.
+ * This implementation uses sproc()-spawning, not the POSIX-threads.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+MODIFICATIONS:
+ 8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
+ 8/17/2001 - v15, first unstable alpha release for IRIX, sent to Phil & Ross.
+ 9/23/2001 - Many fixes and changes: POLLIN for input, not POLLOUT!
+ 7/04/2002 - Implemented multiple user buffers per host buffer to allow clients that
+ request smaller buffersizes.
+ 3/13/2003 - Fixed clicks in full-duplex (wire) mode. Fixed some uninitialised vars, got rid of
+ all GCC-warnings (-Wall). Tested with MIPS compiler and GCC 3.0.4. on IRIX 6.5 (AL v7).
+TODO:
+ - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
+ and maybe also the other natively supported formats? (might increase performance)
+ - Implement fancy callback block adapter as described in the PDF by Stephane Letz in the ASIO dir.
+
+REFERENCES:
+ - IRIX 6.2 man pages regarding SGI AL library.
+ - IRIS Digital Media Programming Guide (online books and man-pages come
+ with IRIX 6.2 and may not be publically available on the internet).
+*/
+
+#include <stdio.h> /* Standard libraries. */
+#include <stdlib.h>
+
+#include "../pa_common/portaudio.h" /* (Makefile fails to find in subdirs, -I doesn't work?). */
+#include "../pa_common/pa_host.h"
+#include "../pa_common/pa_trace.h"
+
+#include <errno.h> /* Needed for int oserror(void);. */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/schedctl.h> /* For schedctl(NDPRI, NDPHIMIN). */
+#include <fcntl.h> /* fcntl.h needed. */
+#include <unistd.h> /* For streams, ioctl(), etc. */
+#include <ulocks.h>
+#include <poll.h>
+#include <dmedia/audio.h> /* System specific (IRIX 6.2-6.5). */
+
+/*----------------- MACROS --------------------*/
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define MAX_CHARS_DEVNAME (16)
+#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7. */
+ /* Constants used in 'Pa_GetMinNumBuffers()' below: */
+#define MIN_LATENCY_MSEC (200) /* Used if 'getenv("PA_MIN_LATENCY_MSEC")' fails. */
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") /* Same names as in file pa_unix.h. */
+
+/*------------------------------- IRIX AL specific device info: --------------------------------------*/
+typedef struct internalPortAudioDevice
+{
+ PaDeviceID pad_DeviceID; /* THIS "ID" IS NEW HERE. */
+ long pad_ALdevice; /* SGI-number! */
+ double pad_SampleRates[MAX_SAMPLE_RATES]; /* For pointing to from pad_Info */
+ char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
+ PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
+ struct internalPortAudioDevice* pad_Next; /* Singly linked list (NULL=end). */
+} internalPortAudioDevice;
+
+/*----------------- Structure containing all SGI IRIX specific data: ---------------------------------------*/
+typedef struct PaHostSoundControl
+{
+ ALconfig pahsc_ALconfigIN, /* IRIX-audio-library-datatype. Configuration */
+ pahsc_ALconfigOUT; /* stucts separate for input and output ports. */
+ ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
+ pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
+ int pahsc_threadPID; /* Sproc()-result, written by PaHost_StartEngine(). */
+
+ unsigned int pahsc_UserBuffersPerHostBuffer,
+ pahsc_SamplesPerInputHostBuffer, /* Channels per frame are accounted for. */
+ pahsc_SamplesPerOutputHostBuffer,
+ pahsc_BytesPerInputHostBuffer, /* Size per sample are accounted for. */
+ pahsc_BytesPerOutputHostBuffer;
+ short *pahsc_InputHostBuffer, /* Allocated here, in this file, if necessary. */
+ *pahsc_OutputHostBuffer;
+
+ struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
+ pahsc_LastExitTime;
+ long pahsc_InsideCountSum,
+ pahsc_TotalCountSum;
+} PaHostSoundControl;
+
+/*-------------------------------------------------------- Shared Data -------------------------------*/
+static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
+static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
+usema_t *SendSema, /* These variables are shared between the */
+ *RcvSema; /* audio handling process and main process. */
+/*--------------------------*/
+long Pa_GetHostError(void)
+{
+ return (long)sPaHostError;
+}
+
+/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
+/* (copied from source pa_linux_oss/pa_linux_oss.c) */
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
+}
+
+static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
+{
+ long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
+ return usecs;
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval currentTime;
+ long insideCount;
+ long totalCount; /* Measure CPU utilization during this callback. */
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return;
+ if (getitimer( ITIMER_REAL, &currentTime ) == 0 )
+ {
+ if (past->past_IfLastExitValid)
+ {
+ insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
+ pahsc->pahsc_InsideCountSum += insideCount;
+ totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
+ pahsc->pahsc_TotalCountSum += totalCount;
+ /* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
+ /* Low pass filter the result because sometimes we get called several times in a row. */
+ /* That can cause the TotalCount to be very low which can cause the usage to appear */
+ /* unnaturally high. So we must filter numerator and denominator separately!!! */
+ if (pahsc->pahsc_InsideCountSum > 0)
+ {
+ past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
+ past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ pahsc->pahsc_InsideCountSum = 0;
+ pahsc->pahsc_TotalCountSum = 0;
+ }
+ }
+ past->past_IfLastExitValid = 1;
+ }
+ pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
+ pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
+ setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
+ past->past_IfLastExitValid = 1;
+} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
+{ /* call to report via ERR_RPT(), yields a PaError-num. */
+ const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
+ switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
+ {
+ case AL_BAD_OUT_OF_MEM:
+ ERR_RPT(("%sout of memory.\n", a));
+ return paInsufficientMemory; /* Known PaError. */
+ case AL_BAD_CONFIG:
+ ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_CHANNELS:
+ ERR_RPT(("%schannels not 1,2 or 4.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_NO_PORTS:
+ ERR_RPT(("%sout of audio ports.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE:
+ ERR_RPT(("%swrong device number.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE_ACCESS:
+ ERR_RPT(("%swrong device access.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DIRECTION:
+ ERR_RPT(("%sinvalid direction.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_SAMPFMT:
+ ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_FLOATMAX:
+ ERR_RPT(("%smax float value is zero.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_WIDTH:
+ ERR_RPT(("%sunsupported samplewidth.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_QSIZE:
+ ERR_RPT(("%sinvalid queue size.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PVBUFFER:
+ ERR_RPT(("%sPVbuffer null.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_NEG:
+ ERR_RPT(("%snegative bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_ODD:
+ ERR_RPT(("%sodd bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PARAM:
+ ERR_RPT(("%sparameter not valid for device.\n", a));
+ return paHostError; /* Generic PaError. */
+ default:
+ ERR_RPT(("%sunknown error.\n", a));
+ return paHostError; /* Generic PaError. */
+ }
+}
+
+/*------------------------------------------------------------------------------------------*/
+/* Tries to set various rates and formats and fill in the device info structure. */
+static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
+ PaDeviceID id, /* (DefaultI|ODeviceID()) */
+ char* name, /* (for example "SGI AL") */
+ internalPortAudioDevice* pad) /* Result written to pad. */
+{
+ long min, max; /* To catch hardware characteristics. */
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ /*--------------------------------------------------------------------------------------*/
+ pad->pad_ALdevice = ALdev; /* Set the AL device number. */
+ pad->pad_DeviceID = id; /* Set the PA device number. */
+ if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
+ {
+ ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
+ return paHostError;
+ }
+ strcpy(pad->pad_DeviceName, name); /* Write name-string. */
+ pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
+ /*--------------------------------- natively supported sample formats: -----------------*/
+ pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
+ /* Then also choose other CallConvertXX()! */
+ /*--------------------------------- number of available i/o channels: ------------------*/
+ if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxInputChannels = max;
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
+ if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxOutputChannels = max;
+ DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
+ /*--------------------------------- supported samplerates: ----------------------*/
+ pad->pad_Info.numSampleRates = 7;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates;
+ pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
+ pad->pad_SampleRates[1] = (double)AL_RATE_11025;
+ pad->pad_SampleRates[2] = (double)AL_RATE_16000;
+ pad->pad_SampleRates[3] = (double)AL_RATE_22050;
+ pad->pad_SampleRates[4] = (double)AL_RATE_32000;
+ pad->pad_SampleRates[5] = (double)AL_RATE_44100;
+ pad->pad_SampleRates[6] = (double)AL_RATE_48000;
+ if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
+ return translateSGIerror(); /* double -> long. */
+ if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
+ goto weird;
+ if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
+ return translateSGIerror();
+ if (max != (long)(0.5 + pad->pad_SampleRates[6]))
+ {
+weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
+ return paHostError; /* Or make it a warning and just carry on... */
+ }
+ /*-------------------------------------------------------------------------------*/
+ return paNoError;
+}
+
+
+/*--------------------------------------------------------------------------------*/
+int Pa_CountDevices() /* Name of this function suggests it only counts and */
+{ /* is NOT destructive, it however resets whole PA ! */
+ int numDevices = 0; /* Let 's not do that here. */
+ internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
+#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
+ if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
+ Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
+#endif /* friendly to clients that forgot to initialize PA. */
+ while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
+ {
+ numDevices++;
+ currentDevice = currentDevice->pad_Next;
+ }
+ return numDevices;
+}
+
+/*-------------------------------------------------------------------------------*/
+static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
+{
+ int numDevices = 0;
+ internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
+ internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
+ while (pad) /* pad may be NULL, that's ok, return 0. */
+ { /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
+ if (pad->pad_DeviceID == id) /* This the device we were looking for? */
+ res = pad; /* But keep on(!) counting so we don't */
+ numDevices++; /* have to call Pa_CountDevices() later. */
+ pad = pad->pad_Next; /* Advance to the next device or NULL. */
+ } /* No assumptions about order of ID's in */
+ if (!res) /* the list. */
+ ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
+ if ((id < 0) || (id >= numDevices))
+ {
+ ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
+#if 1 /* Be strict, even when found, */
+ res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
+#endif
+ }
+ return res;
+}
+
+/*----------------------------------------------------------------------*/
+const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
+{
+ PaDeviceInfo* res = (PaDeviceInfo*)NULL;
+ internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
+ if (pad)
+ res = &pad->pad_Info; /* Not finding the specified ID is not */
+ if (!res) /* the same as &pad->pad_Info == NULL. */
+ ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
+ return res; /* So (maybe) a second/third ERR_RPT(). */
+}
+
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultInputDeviceID(void)
+{
+ return 0; /* 0 is the default device ID. */
+}
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultOutputDeviceID(void)
+{
+ return 0;
+}
+
+/*-------------------------------------------------------------------------------------------------*/
+/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */
+PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
+{
+ internalPortAudioDevice* pad;
+ PaError r = paNoError;
+ int audioLibFileID; /* To test for the presence of audio. */
+
+ if (sDeviceList) /* Allow re-init, only warn, no error. */
+ {
+ ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
+ return r;
+ }
+ /*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/
+ audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
+ if (audioLibFileID < 0) /* IO port. On failure, machine */
+ { /* has no audio ability. */
+ ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
+ return paHostError;
+ }
+ close(audioLibFileID); /* Allocate fast mem to hold device info. */
+ pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
+ if (pad == NULL)
+ return paInsufficientMemory;
+ memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
+ r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
+ Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
+ "AL default", /* A suitable name. */
+ pad); /* Write args and queried info into pad. */
+ if (r != paNoError)
+ {
+ ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
+ }
+ else
+ sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */
+ /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/
+ /*---------------------------------------------------------------------------------------------*/
+ return r;
+}
+
+/*--------------------------------------------------------------------------------------------*/
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* MIN()-function is used below. */
+#define kPollSEMA 0 /* To index the pollfd-array, reads nicer than just */
+#define kPollOUT 1 /* numbers. */
+#define kPollIN 2
+void Pa_SgiAudioProcess(void *v) /* This function is sproc-ed by PaHost_StartEngine() */
+{ /* as a separate thread. (Argument must be void*). */
+ short evtLoop; /* Reset by parent indirectly, or at local errors. */
+ PaError result;
+ struct pollfd PollFD[3]; /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
+ internalPortAudioStream *past = (internalPortAudioStream*)v; /* Copy void-ptr-argument. */
+ PaHostSoundControl *pahsc;
+ short n, inputEvent, outputEvent, ioEvent, semaEvent = 0;
+ short *inBuffer, *outBuffer; /* Only 16 bit for now, may change... */
+ unsigned int samplesPerInputUserBuffer, samplesPerOutputUserBuffer;
+
+ DBUG(("Entering sproc-thread.\n"));
+ if (!past)
+ {
+ sPaHostError = paInternalError; /* Or paBadStreamPtr ? */
+ ERR_RPT(("argument NULL!\n"));
+ goto noPast;
+ }
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (!pahsc)
+ {
+ sPaHostError = paInternalError; /* The only way is to signal error to shared area?! */
+ ERR_RPT(("past_DeviceData NULL!\n"));
+ goto noPahsc; /* Sproc-ed threads MAY NOT RETURN paInternalError. */
+ }
+ /*----------------------------- open AL-ports here, after sproc(): -----------------------*/
+ if (past->past_NumInputChannels > 0) /* Open input port. */
+ {
+ pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", pahsc->pahsc_ALconfigIN);
+ if (!pahsc->pahsc_ALportIN)
+ {
+ ERR_RPT(("Failed to open AL input port.\n"));
+ sPaHostError = paInternalError;
+ goto skip;
+ }
+ DBUG(("Opened %d input channel(s).\n", past->past_NumInputChannels));
+ samplesPerInputUserBuffer = pahsc->pahsc_SamplesPerInputHostBuffer /
+ pahsc->pahsc_UserBuffersPerHostBuffer;
+ }
+ else
+ samplesPerInputUserBuffer = 0; /* Added 2003. */
+ if (past->past_NumOutputChannels > 0) /* Open output port. */
+ {
+ pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", pahsc->pahsc_ALconfigOUT);
+ if (!pahsc->pahsc_ALportOUT)
+ {
+ ERR_RPT(("Failed to open AL output port.\n"));
+ sPaHostError = paInternalError; /* Assume pahsc_ALconfigs are the */
+ goto skip; /* same for IN and OUT in case */
+ } /* both ports are opened (bidir). */
+ DBUG(("Opened %d output channel(s).\n", past->past_NumOutputChannels));
+ samplesPerOutputUserBuffer = pahsc->pahsc_SamplesPerOutputHostBuffer /
+ pahsc->pahsc_UserBuffersPerHostBuffer;
+ DBUG(("samplesPerOutputUserBuffer = %d\n", samplesPerOutputUserBuffer));
+ }
+ else
+ samplesPerOutputUserBuffer = 0; /* Added 2003. */
+ /*-----------------------------------------------------------------------*/
+ past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
+ PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN); /* ALgetfd returns -1 on failures */
+ PollFD[kPollIN].events = POLLIN; /* such as ALport not there. */
+ PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
+ PollFD[kPollOUT].events = POLLOUT; /* .events = POLLOUT is OK. */
+ schedctl(NDPRI, NDPHIMIN); /* Sets non-degrading priority for this process. */
+ PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777); /* To communicate with parent. */
+ PollFD[kPollSEMA].events = POLLIN; /* .events = POLLIN is OK. */
+ uspsema(SendSema); /* Blocks until ... MUST be here, this uspsema(). */
+ evtLoop = ((past->past_StopNow | past->past_StopSoon) == 0);
+ while (evtLoop)
+ {
+ /*----------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ---------*/
+ if (pahsc->pahsc_InputHostBuffer) /* Then pahsc_ALportIN should also be there. */
+ {
+ /* For input port, fill point is number of locations in the sample queue that must be */
+ /* filled in order to trigger a return from select(). (or poll()) */
+ /* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
+ if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputHostBuffer))
+ { /* Multiple amount as transferred per time. */
+ ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
+ sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
+ goto skip;
+ }
+ }
+ /* 'else' added march 2003: set only one of both fillpoints: input or output. When */
+ /* setting both fillpoints (as in earlier version) clicks occur at full duplex-mode. */
+ else if (pahsc->pahsc_OutputHostBuffer) /* Then pahsc_ALportOUT should also be there. */
+ {
+ /* For output port, fill point is number of locations that must be free in order to */
+ /* wake up from select(). (or poll()) */
+ if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputHostBuffer))
+ {
+ ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
+ sPaHostError = paInternalError;
+ goto skip;
+ }
+ } /* poll() with timeout=-1 makes it block until a requested */
+ poll(PollFD, 3, -1); /* event occurs or until call is interrupted. If fd-value in */
+ /* array <0, events is ignored and revents is set to 0. */
+ /*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
+ semaEvent = PollFD[kPollSEMA].revents & POLLIN;
+ if (semaEvent)
+ {
+ if (past->past_StopSoon)
+ evtLoop = 0;
+ if (past->past_StopNow)
+ goto skip;
+ }
+ /*------------------------------------- FILLED-EVENT FROM INPUT BUFFER: --------------------------*/
+ inputEvent = PollFD[kPollIN].revents & POLLIN;
+ if (inputEvent) /* Don't need to check (pahsc->pahsc_InputHostBuffer): */
+ { /* if buffer was not there, ALport not there, no events! */
+ if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_InputHostBuffer,
+ pahsc->pahsc_SamplesPerInputHostBuffer))
+ { /* Here again: number of samples instead of number of frames. */
+ ERR_RPT(("ALreadsamps() failed.\n"));
+ sPaHostError = paInternalError;
+ goto skip;
+ }
+ }
+ outputEvent = PollFD[kPollOUT].revents & POLLOUT;
+ ioEvent = (inputEvent | outputEvent); /* Binary or is ok. */
+ /*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
+ if (ioEvent) /* Always true? Or can some other system-event awaken the */
+ { /* poll? Sure it wasn't just a "sema"- (i.e. user)-event? */
+ Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
+ /* user data and call user routine. */
+ inBuffer = pahsc->pahsc_InputHostBuffer; /* Short pointers for now, care! */
+ outBuffer = pahsc->pahsc_OutputHostBuffer;
+ n = pahsc->pahsc_UserBuffersPerHostBuffer; /* 'n' may never start at NULL ! */
+ do {
+ result = Pa_CallConvertInt16(past, inBuffer, outBuffer);
+ if (result) /* This is apparently NOT an error! Just letting the userCallBack stop us. */
+ { DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result)); goto skip; }
+ inBuffer += samplesPerInputUserBuffer; /* Num channels is accounted for. */
+ outBuffer += samplesPerOutputUserBuffer;
+ } while (--n);
+ Pa_EndUsageCalculation(past);
+ }
+ /*------------------------------------ FREE-EVENT FROM OUTPUT BUFFER: ---------------------------*/
+ if (pahsc->pahsc_OutputHostBuffer && ioEvent) /* Don't wait for outputEvent solely (that may cause clicks). */
+ { /* Just assume it's time to write, outputEvent may not yet be there. */
+ if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_OutputHostBuffer,
+ pahsc->pahsc_SamplesPerOutputHostBuffer))
+ {
+ ERR_RPT(("ALwritesamps() failed.\n")); /* Better use SEMAS for messaging back to parent! */
+ sPaHostError = paInternalError;
+ goto skip;
+ }
+ }
+ }
+skip:
+ /*------------------------------- close AL-ports ----------------------------*/
+ if (pahsc->pahsc_ALportIN)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportIN))
+ translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+ else /* But go on anyway... to release other stuff... */
+ pahsc->pahsc_ALportIN = (ALport)0;
+ }
+ if (pahsc->pahsc_ALportOUT)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportOUT))
+ translateSGIerror();
+ else
+ pahsc->pahsc_ALportOUT = (ALport)0;
+ }
+noPahsc:
+ past->past_IsActive = 0;
+ if (semaEvent)
+ {
+ uspsema(SendSema); /* StopEngine() was still waiting for this acknowledgement. */
+ usvsema(RcvSema); /* (semaEvent initialized with 0.) */
+ }
+noPast:
+ DBUG(("Leaving sproc-thread.\n"));
+}
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError PaHost_OpenStream(internalPortAudioStream *past)
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
+ long pvbuf[8], sr, alq; /* To get/set hardware configs. */
+
+ DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
+ if (!past)
+ {
+ ERR_RPT(("Streampointer NULL!\n"));
+ result = paBadStreamPtr; goto done;
+ }
+ pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+ if (pahsc == NULL)
+ {
+ ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
+ result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
+ }
+ memset(pahsc, 0, sizeof(PaHostSoundControl));
+ pahsc->pahsc_threadPID = -1; /* Should pahsc_threadPID be inited to */
+ past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */
+ /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ pvbuf[0] = AL_INPUT_RATE;
+ pvbuf[2] = AL_INPUT_COUNT;
+ pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
+ pvbuf[6] = AL_OUTPUT_COUNT;
+ sr = (long)(past->past_SampleRate + 0.5); /* Common for both input and output :-) */
+ /*-----------------------------------------------------------------------------*/
+ /* OVERWRITE 'past_NumUserBuffers'-field in the struct supplied by the caller. */
+ /* This field may be set to zero by a client application to ask for minimum */
+ /* latency. It is used below, to set both input- and output-AL-queuesizes. */
+ minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer,
+ past->past_SampleRate); /* Take biggest. */
+ past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
+ minNumBuffers : past->past_NumUserBuffers;
+ DBUG(("past->past_NumUserBuffers=%d\n", past->past_NumUserBuffers));
+ /*----------------------------------------------------------------------------------*/
+ pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers >> 1;
+ DBUG(("pahsc_UserBuffersPerHostBuffer=%d\n",pahsc->pahsc_UserBuffersPerHostBuffer));
+ /* 1 is minimum because Pa_GetMinNumBuffers() returns >= 2.
+ Callback will be called 'pahsc_UserBuffersPerHostBuffer' times (with 'past_FramesPerUserBuffer')
+ per host transfer. */
+ /*---------------------------------------------------- SET INPUT CONFIGURATION: ---------------------*/
+ if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
+ { /* sponding native AL-number(s). */
+ /*--------------------------------------------------- Allocate native buffers: --------------*/
+ pahsc->pahsc_SamplesPerInputHostBuffer = pahsc->pahsc_UserBuffersPerHostBuffer *
+ past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumInputChannels; /* audio-thread. */
+ DBUG(("pahsc_SamplesPerInputHostBuffer=%d\n", pahsc->pahsc_SamplesPerInputHostBuffer));
+ pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_SamplesPerInputHostBuffer * sizeof(short);
+ pahsc->pahsc_InputHostBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer);
+ if (!pahsc->pahsc_InputHostBuffer)
+ {
+ ERR_RPT(("Fast memory allocation failed (in).\n"));
+ result = paInsufficientMemory;
+ goto done;
+ }
+ padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
+ if (!padIN)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
+ result = paHostError;
+ goto done;
+ }
+ if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
+ goto sgiError; /* the same AL-device, the AL-library might */
+ if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
+ { /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
+ if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
+another process is currently using input at %ld Hz.\n", sr, pvbuf[1]));
+ result = paHostError;
+ goto done;
+ }
+ pvbuf[1] = sr; /* Then set input-rate. */
+ if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
+ goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
+ } /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */
+ pahsc->pahsc_ALconfigIN = ALnewconfig(); /* Released at PaHost_CloseStream(). */
+ if (pahsc->pahsc_ALconfigIN == (ALconfig)0)
+ goto sgiError;
+ if (ALsetsampfmt(pahsc->pahsc_ALconfigIN, AL_SAMPFMT_TWOSCOMP))/* Choose paInt16 as native i/o-format. */
+ goto sgiError;
+ if (ALsetwidth (pahsc->pahsc_ALconfigIN, AL_SAMPLE_16)) /* Only meaningful when sample format for */
+ goto sgiError; /* config is set to two's complement format. */
+ /************************ Future versions might (dynamically) switch to 32-bit floats? *******
+ if (ALsetsampfmt(pahsc_ALconfigIN, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
+ goto sgiError;
+ if (ALsetfloatmax (pahsc_ALconfigIN, 1.0)) Only meaningful when sample format for config
+ goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
+ /*--------- Set internal AL queuesize (in samples, not in frames!) -------------------------------*/
+ alq = (long)past->past_NumUserBuffers * past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ DBUG(("AL input queuesize = %ld samples.\n", alq));
+ if (ALsetqueuesize(pahsc->pahsc_ALconfigIN, alq))
+ goto sgiError;
+ if (ALsetchannels (pahsc->pahsc_ALconfigIN, (long)(past->past_NumInputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ }
+ else
+ pahsc->pahsc_InputHostBuffer = (short*)NULL; /* Added 2003! Is checked in callback-routine. */
+ /*---------------------------------------------------- SET OUTPUT CONFIGURATION: ------------------------*/
+ if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */
+ { /* We use padOUT/IN later on, or at least 1 of both. */
+ pahsc->pahsc_SamplesPerOutputHostBuffer = pahsc->pahsc_UserBuffersPerHostBuffer *
+ past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumOutputChannels; /* audio-thread. */
+ DBUG(("pahsc_SamplesPerOutputHostBuffer=%d\n", pahsc->pahsc_SamplesPerOutputHostBuffer));
+ pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_SamplesPerOutputHostBuffer * sizeof(short);
+ pahsc->pahsc_OutputHostBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer);
+ if (!pahsc->pahsc_OutputHostBuffer)
+ {
+ ERR_RPT(("Fast memory allocation failed (out).\n"));
+ result = paInsufficientMemory;
+ goto done;
+ }
+ padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
+ if (!padOUT)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
+ result = paHostError;
+ goto done;
+ }
+ if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
+ goto sgiError;
+ if (pvbuf[5] != sr)
+ { /* Output needed and rate different from current harware-rate. */
+ if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
+another process is currently using output at %ld Hz.\n", sr, pvbuf[5]));
+ result = paHostError;
+ goto done; /* Will free again the inputbuffer */
+ } /* that was just created above. */
+ pvbuf[5] = sr; /* Then set output-rate. */
+ if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
+ goto sgiError;
+ }
+ pahsc->pahsc_ALconfigOUT = ALnewconfig(); /* Released at PaHost_CloseStream(). */
+ if (pahsc->pahsc_ALconfigOUT == (ALconfig)0)
+ goto sgiError;
+ if (ALsetsampfmt(pahsc->pahsc_ALconfigOUT, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
+ goto sgiError;
+ if (ALsetwidth (pahsc->pahsc_ALconfigOUT, AL_SAMPLE_16)) /* Only meaningful when sample format for */
+ goto sgiError; /* config is set to two's complement format. */
+ /** Future versions might (dynamically) switch to 32-bit floats. **/
+ alq = (long)past->past_NumUserBuffers * past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ DBUG(("AL output queuesize = %ld samples.\n", alq));
+ if (ALsetqueuesize(pahsc->pahsc_ALconfigOUT, alq))
+ goto sgiError;
+ if (ALsetchannels (pahsc->pahsc_ALconfigOUT, (long)(past->past_NumOutputChannels)))
+ goto sgiError;
+ }
+ else
+ pahsc->pahsc_OutputHostBuffer = (short*)NULL;
+ /*----------------------------------------------- TEST DEVICE ID's: --------------------*/
+ if ((past->past_OutputDeviceID != past->past_InputDeviceID) && /* Who SETS these devive-numbers? */
+ (past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
+ {
+ ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
+ result = paHostError;
+ goto done;
+ }
+ goto done; /* (no errors occured) */
+sgiError:
+ result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+done:
+ if (result != paNoError)
+ PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
+ return result;
+}
+
+/*-----------------------------------------------------*/
+PaError PaHost_StartOutput(internalPortAudioStream *past)
+{
+ return paNoError; /* Hmm, not implemented yet? */
+}
+PaError PaHost_StartInput(internalPortAudioStream *past)
+{
+ return paNoError;
+}
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StartEngine(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ usptr_t *arena;
+ if (!past) /* Test argument. */
+ {
+ ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
+ return paBadStreamPtr;
+ }
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (!pahsc)
+ {
+ ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
+ return paHostError;
+ }
+ past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
+ past->past_StopNow = 0; /* Why don't we check pahsc for NULL? */
+ past->past_IsActive = 1;
+
+ /* Although the pthread_create() function, as well as <pthread.h>, may be */
+ /* available in IRIX, use sproc() on SGI to create audio-background-thread. */
+ /* (Linux/oss uses pthread_create() instead of __clone() because: */
+ /* - pthread_create also works for other UNIX systems like Solaris, */
+ /* - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().) */
+
+ usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* (From SGI-AL-examples, file */
+ arena = usinit(tmpnam(0)); /* motifexample.c, function */
+ SendSema = usnewpollsema(arena, 0); /* InitializeAudioProcess().) */
+ RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process
+ will be permitted through a semaphore at a time. Values > 1
+ imply that up to val resources may be simultaneously used, but requests
+ for more than val resources cause the calling process to block until a
+ resource comes free (by a process holding a resource performing a
+ usvsema(). IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
+ prctl(PR_SETEXITSIG, 0); /* No not (void*)9, but 0, which doesn't kill the parent! */
+ /* PR_SETEXITSIG controls whether all members of a share group will be
+ signaled if any one of them leaves the share group (either via exit()
+ or exec()). If 2nd arg, interpreted as an int is 0, then normal IRIX
+ process termination rules apply, namely that the parent is sent a
+ SIGCLD upon death of child, but no indication of death of parent is
+ given. If the second argument is a valid signal number then if any
+ member of a share group leaves the share group, a signal is
+ sent to ALL surviving members of the share group. */
+ /* SPAWN AUDIO-CHILD: */
+ pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
+ PR_SALL, /* new process, or -1. */
+ (void*)past); /* Pass past as optional */ /* IS THIS SAFE, will past never */
+ if (pahsc->pahsc_threadPID == -1) /* third void-ptr-arg. */ /* be moved around in memory???? */
+ {
+ ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
+ sPaHostError = oserror(); /* Pass native error-number to shared area. */
+ return paHostError; /* But return the generic error-number. */
+ }
+ return paNoError; /* Hmmm, errno may come from other threads in same group! */
+} /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+
+ DBUG(("PaHost_StopEngine() called.\n"));
+ if (!past)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ /* Prevent from doing this twice!! */
+ if ((!pahsc) || /* Some tests call this CLOSE twice!! */
+ (!past->past_IsActive) ||
+ past->past_StopSoon || past->past_StopNow)
+ return result; /* paNoError (already stopped, no err?). */
+ past->past_StopSoon = 1; /* Tell background thread to stop generating */
+ if (abort) /* more and to let current data play out. If */
+ past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
+ /*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
+ usvsema(SendSema); /* Increments count associated with SendSema. */
+ /* Wait for the response. */
+ uspsema(RcvSema); /* Decrements count of previously allocated */
+ /* semaphore specified by RcvSema. */
+ while (past->past_IsActive) /* REALLY WAIT. */
+ {
+ /* DBUG(("wait 1 ms for audio-thread to stop.\n")); */
+ Pa_Sleep(1);
+ }
+
+#if 0 /* We don't need to KILL(), just COMMUNICATE and be patient... */
+ if (pahsc->pahsc_threadPID != -1) /* Did we really init it to -1 somewhere? */
+ {
+ DBUG(("PaHost_StopEngine() is about to kill(SIGKILL) audio-thread.\n"));
+ if (kill(pahsc->pahsc_threadPID, SIGKILL)) /* Or SIGTERM or SIGQUIT(core) */
+ { /* Returns -1 in case of error. */
+ result = paHostError;
+ sPaHostError = oserror(); /* Hmmm, other threads may also write here! */
+ ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
+ }
+ else
+ pahsc->pahsc_threadPID = -1; /* Notify that we've killed this thread. */
+ }
+#endif
+ past->past_IsActive = 0; /* Even when kill() failed and pahsc_threadPID still there??? */
+ return result;
+}
+
+/*---------------------------------------------------------------*/
+PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
+{
+ return paNoError; /* Not implemented yet? */
+}
+PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+
+ DBUG(("PaHost_CloseStream() called.\n"));
+ if (!past)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (!pahsc) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
+ return result; /* This test prevents from freeing NULL-pointers. */
+
+ if (pahsc->pahsc_ALconfigIN)
+ { /* Release configuration structs, only if allocated. */
+ ALfreeconfig(pahsc->pahsc_ALconfigIN);
+ pahsc->pahsc_ALconfigIN = NULL;
+ }
+ if (pahsc->pahsc_ALconfigOUT)
+ {
+ ALfreeconfig(pahsc->pahsc_ALconfigOUT); /* (Al-ports were already closed by audioProcess). */
+ pahsc->pahsc_ALconfigOUT = NULL;
+ }
+ if (pahsc->pahsc_InputHostBuffer)
+ {
+ PaHost_FreeFastMemory(pahsc->pahsc_InputHostBuffer, pahsc->pahsc_BytesPerInputHostBuffer);
+ pahsc->pahsc_InputHostBuffer = NULL;
+ }
+ if (pahsc->pahsc_OutputHostBuffer)
+ {
+ PaHost_FreeFastMemory(pahsc->pahsc_OutputHostBuffer, pahsc->pahsc_BytesPerOutputHostBuffer);
+ pahsc->pahsc_OutputHostBuffer = NULL;
+ }
+ PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));
+ past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST MEM. */
+ return result;
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Determine minimum number of buffers required for (SGI) host based on */
+/* minimum latency. Latency can be optionally set by user by setting an */
+/* environment variable. For example, to set my latency to 200 msec, I've */
+/* put this line in my '.cshrc' file: setenv PA_MIN_LATENCY_MSEC 200 */
+/* It always calls the 'PRINT' macro. */
+/* The minimum number that is returned is 2. */
+/* This number is directly proportional to the AL-queue sizes to set up. */
+/* It is one more than the number of user buffers per host buffer - in */
+/* case minimum is returned, or, twice the user buffers per host buffer. */
+/*------------------------------------------------------------------------*/
+int Pa_GetMinNumBuffers(int framesPerUserBuffer, double framesPerSecond)
+{
+ int minBuffers, minLatencyMsec;
+ char *minLatencyText;
+ double actualLatency;
+
+ minLatencyText = getenv(PA_LATENCY_ENV_NAME); /* Defined at top of file. */
+ if (minLatencyText)
+ {
+ minLatencyMsec = atoi(minLatencyText);
+ if (minLatencyMsec < 10)
+ { /* 10 is the minimum. */
+ minLatencyMsec = 10;
+ PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' below minimum of %d milliseconds.\n",
+ minLatencyMsec));
+ }
+ else if (minLatencyMsec > 4000)
+ { /* 4000 is the maximum. */
+ minLatencyMsec = 4000;
+ PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' above maximum of %d milliseconds.\n",
+ minLatencyMsec));
+ }
+ else
+ PRINT (("Using environment variable 'PA_MIN_LATENCY_MSEC' (set to %d milliseconds).\n",
+ minLatencyMsec));
+ }
+ else
+ {
+ minLatencyMsec = MIN_LATENCY_MSEC; /* Defined at top of this file. */
+ PRINT (("Environment variable 'PA_MIN_LATENCY_MSEC' not found.\nUsing default of %d milliseconds\n",
+ minLatencyMsec));
+ }
+ minBuffers = (int)((minLatencyMsec * framesPerSecond) /
+ (1000.0 * framesPerUserBuffer));
+ if (minBuffers < 2)
+ minBuffers = 2;
+ actualLatency = 1000.0 * minBuffers * framesPerUserBuffer / framesPerSecond;
+ PRINT (("Actual AL latency set to %.2f milliseconds\n", actualLatency));
+ return minBuffers;
+}
+
+/*---------------------------------------------------------------------*/
+PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */
+{ /* Called by Pa_Terminate() from pa_lib.c. */
+ internalPortAudioDevice *pad = sDeviceList,
+ *nxt;
+ while (pad)
+ {
+ DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
+ nxt = pad->pad_Next;
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
+ pad = nxt; /* PaHost_Init allocated this fast mem.*/
+ }
+ sDeviceList = (internalPortAudioDevice*)NULL;
+ return 0;
+}
+
+/***********************************************************************/
+void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select(0, NULL, NULL, NULL, &timeout);
+#else
+ long usecs = msec * 1000;
+ usleep( usecs );
+#endif
+}
+
+/*---------------------------------------------------------------------------------------*/
+/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
+/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
+/* a call to PaHost_FreeFastMemory(). */
+void *PaHost_AllocateFastMemory(long numBytes)
+{
+ void *addr = malloc(numBytes); /* mpin() reads into memory all pages over the given */
+ if (addr) /* range and locks the pages into memory. A counter */
+ { /* is incremented each time the page is locked. The */
+ if (mpin(addr, numBytes)) /* superuser can lock as many pages as it wishes, */
+ { /* others are limited to the configurable PLOCK_MA. */
+ ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
+#if 1
+ free(addr); /* You MAY cut out these 2 lines to be less strict, */
+ addr = NULL; /* you then only get the warning but PA goes on... */
+#endif /* Only problem then may be corresponding munpin() */
+ } /* call at PaHost_FreeFastMemory(), below. */
+ memset(addr, 0, numBytes); /* Locks established with mlock are not inherited by */
+ } /* a child process after a fork. Furthermore, IRIX- */
+ return addr; /* man-pages warn against mixing both mpin and mlock */
+} /* in 1 piece of code, so stick to mpin()/munpin() ! */
+
+
+/*---------------------------------------------------------------------------------------*/
+/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
+/* call to PaHost_AllocateFastMemory(). */
+void PaHost_FreeFastMemory(void *addr, long numBytes)
+{
+ if (addr)
+ {
+ if (munpin(addr, numBytes)) /* Will munpin() fail when it was never mpinned? */
+ ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
+ free(addr); /* But go on, try to release it, just warn... */
+ }
+}
+
+/*----------------------------------------------------------*/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if (past == NULL)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return paInternalError;
+ return (PaError)(past->past_IsActive != 0);
+}
+
+/*-------------------------------------------------------------------*/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+/* FIXME - return actual frames played, not frames generated.
+** Need to query the output device somehow.
+*/
+ return past->past_FrameCount;
+}
diff --git a/pd/portaudio_v18/pa_sgi/pthread-Makefile b/pd/portaudio_v18/pa_sgi/pthread-Makefile
new file mode 100644
index 00000000..527677a8
--- /dev/null
+++ b/pd/portaudio_v18/pa_sgi/pthread-Makefile
@@ -0,0 +1,52 @@
+# Make PortAudio for Silicon Graphics IRIX (6.2)
+# Pieter suurmond, september 22, 2001. (v15 pa_sgi sub-version #0.18)
+
+# pthread, math (as with linux) and SGI audio library:
+# SGI-books say -lpthread should be the last on the line.
+LIBS = -lm -laudio -lpthread
+
+CDEFINES = -I../pa_common
+
+# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
+# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
+# And use for example -O2 or -O3 for better optimization:
+CFLAGS = -O2
+PASRC = ../pa_common/pa_lib.c pa_sgi.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2).
+#TESTC = $(PASRC) ../pa_tests/patest_record.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK but needs more than 4 buffers to do without glitches.
+TESTC = $(PASRC) ../pa_tests/patest_saw.c # Seems OK (does it gracefully exit?).
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore.
+
+# Tests that do not yet work.
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default
+#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default
+#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # THIS FUZZ CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-(
+ # PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH.
+ # OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON???
+ # ANYWAY, I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS!
+TESTH = $(PAINC)
+
+all: patest
+
+# "cc" for the MIPSpro compiler, may be changed to "gcc":
+patest: $(TESTC) $(TESTH) Makefile
+ cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
diff --git a/pd/portaudio_v18/pa_sgi/pthread-pa_sgi.c b/pd/portaudio_v18/pa_sgi/pthread-pa_sgi.c
new file mode 100644
index 00000000..e9ab273c
--- /dev/null
+++ b/pd/portaudio_v18/pa_sgi/pthread-pa_sgi.c
@@ -0,0 +1,908 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * SGI IRIX implementation by Pieter Suurmond, september 22, 2001 (#0.18).
+ *
+ * Copyright (c) 1999-2001 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+Modfication History:
+ 8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
+ 8/17/2001 - alpha release with IRIX sproc()-method, may sometimes let IRIX6.2 crash at closing audiostream.
+ 9/22/2001 - #0.18 pthread starts to work a bit:
+ BUT UNDER IRIX6.2, I DON'T GET IT TO WORK REALLY CORRECTLY,
+ this POSIX-attempt,
+ DON'T USE THIS FILE FOR RELIABLE OPERATION, IT IS HERE JUST
+ FOR DOCUMENTATION/ARCHIVE... OR FOR ANYONE WHO WANTS TO FIX......
+TODO:
+ - Test under IRIX 6.5.
+ - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
+ and maybe also the other natively supported formats? (might increase performance)
+ - Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet,
+ seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?)
+ - The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
+REFERENCES:
+ - IRIX 6.2 man pages regarding SGI AL library.
+ - IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and
+ may not be publically available on the internet).
+*/
+
+#include <stdio.h> /* Standard libraries. */
+#include <stdlib.h>
+
+#include "../pa_common/portaudio.h" /* Portaudio headers. */
+#include "../pa_common/pa_host.h"
+#include "../pa_common/pa_trace.h"
+
+/*
+#include <malloc.h>
+#include <memory.h>
+#include <sys/prctl.h> Not needed
+#include <sys/types.h>
+#include <sys/schedctl.h>
+#include <signal.h>
+#include <sys/ioctl.h> Needed?
+#include <sys/time.h>
+#include <sched.h> sched_param struct and related functions
+ used in setting thread priorities.
+#include <limits.h> Some POSIX constants such as _POSIX_THREAD_THREADS_MAX
+*/
+
+#include <pthread.h> /* Pthreads are supported by IRIX 6.2 after */
+ /* patches 1361, 1367, and 1429 are applied. */
+
+#include <fcntl.h> /* fcntl.h needed for "O_RDONLY". */
+#include <unistd.h> /* For usleep() and constants used when calling sysconf() */
+ /* to query POSIX limits (see the sysconf(3) ref. page. */
+
+#include <dmedia/audio.h> /* SGI-specific audio library. */
+
+
+/*--------------------------------------------*/
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) PRINT(x)
+#define DBUGX(x) /* PRINT(x) */
+
+#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
+#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */
+
+typedef struct internalPortAudioDevice /* IRIX specific device info: */
+{
+ PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */
+ long pad_ALdevice; /* SGI-number! */
+ double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
+ char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
+ PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
+ /* int structVersion; */
+ /* const char* name; */
+ /* int maxInputChannels, maxOutputChannels; */
+ /* int numSampleRates; Num rates, or -1 if range supprtd. */
+ /* const double* sampleRates; Array of supported sample rates, */
+ /* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */
+ struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */
+} internalPortAudioDevice;
+
+typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */
+{
+ ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
+ pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
+ pthread_t pahsc_ThreadPID;
+ short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */
+ *pahsc_NativeOutputBuffer;
+ unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */
+ pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */
+ unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */
+ pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */
+ struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
+ pahsc_LastExitTime;
+ long pahsc_InsideCountSum,
+ pahsc_TotalCountSum;
+} PaHostSoundControl;
+
+/*----------------------------- Shared Data ------------------------------------------------------*/
+static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
+static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
+
+long Pa_GetHostError(void)
+{
+ return (long)sPaHostError;
+}
+
+/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
+/* (copied from source pa_linux_oss/pa_linux_oss.c) */
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval itimer;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+ getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
+}
+
+static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
+{
+ long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
+ return usecs;
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval currentTime;
+ long insideCount;
+ long totalCount; /* Measure CPU utilization during this callback. */
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return;
+ if (getitimer( ITIMER_REAL, &currentTime ) == 0 )
+ {
+ if (past->past_IfLastExitValid)
+ {
+ insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
+ pahsc->pahsc_InsideCountSum += insideCount;
+ totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
+ pahsc->pahsc_TotalCountSum += totalCount;
+ /* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
+ /* Low pass filter the result because sometimes we get called several times in a row. */
+ /* That can cause the TotalCount to be very low which can cause the usage to appear */
+ /* unnaturally high. So we must filter numerator and denominator separately!!! */
+ if (pahsc->pahsc_InsideCountSum > 0)
+ {
+ past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
+ past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ pahsc->pahsc_InsideCountSum = 0;
+ pahsc->pahsc_TotalCountSum = 0;
+ }
+ }
+ past->past_IfLastExitValid = 1;
+ }
+ pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
+ pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
+ setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
+ past->past_IfLastExitValid = 1;
+} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
+{ /* call to report via ERR_RPT(), yields a PaError-num. */
+ const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
+ switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
+ {
+ case AL_BAD_OUT_OF_MEM:
+ ERR_RPT(("%sout of memory.\n", a));
+ return paInsufficientMemory; /* Known PaError. */
+ case AL_BAD_CONFIG:
+ ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_CHANNELS:
+ ERR_RPT(("%schannels not 1,2 or 4.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_NO_PORTS:
+ ERR_RPT(("%sout of audio ports.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE:
+ ERR_RPT(("%swrong device number.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE_ACCESS:
+ ERR_RPT(("%swrong device access.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DIRECTION:
+ ERR_RPT(("%sinvalid direction.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_SAMPFMT:
+ ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_FLOATMAX:
+ ERR_RPT(("%smax float value is zero.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_WIDTH:
+ ERR_RPT(("%sunsupported samplewidth.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_QSIZE:
+ ERR_RPT(("%sinvalid queue size.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PVBUFFER:
+ ERR_RPT(("%sPVbuffer null.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_NEG:
+ ERR_RPT(("%snegative bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_ODD:
+ ERR_RPT(("%sodd bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PARAM:
+ ERR_RPT(("%sparameter not valid for device.\n", a));
+ return paHostError; /* Generic PaError. */
+ default:
+ ERR_RPT(("%sunknown error.\n", a));
+ return paHostError; /* Generic PaError. */
+ }
+}
+
+/*------------------------------------------------------------------------------------------*/
+/* Tries to set various rates and formats and fill in the device info structure. */
+static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
+ PaDeviceID id, /* (DefaultI|ODeviceID()) */
+ char* name, /* (for example "SGI AL") */
+ internalPortAudioDevice* pad) /* Result written to pad. */
+{
+ int format;
+ long min, max; /* To catch hardware characteristics. */
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ /*--------------------------------------------------------------------------------------*/
+ pad->pad_ALdevice = ALdev; /* Set the AL device number. */
+ pad->pad_DeviceID = id; /* Set the PA device number. */
+ if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
+ {
+ ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
+ return paHostError;
+ }
+ strcpy(pad->pad_DeviceName, name); /* Write name-string. */
+ pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
+ /*--------------------------------- natively supported sample formats: -----------------*/
+ pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
+ /* Then also choose other CallConvertXX()! */
+ /*--------------------------------- number of available i/o channels: ------------------*/
+ if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxInputChannels = max;
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
+ if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxOutputChannels = max;
+ DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
+ /*--------------------------------- supported samplerates: ----------------------*/
+ pad->pad_Info.numSampleRates = 7;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates;
+ pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
+ pad->pad_SampleRates[1] = (double)AL_RATE_11025;
+ pad->pad_SampleRates[2] = (double)AL_RATE_16000;
+ pad->pad_SampleRates[3] = (double)AL_RATE_22050;
+ pad->pad_SampleRates[4] = (double)AL_RATE_32000;
+ pad->pad_SampleRates[5] = (double)AL_RATE_44100;
+ pad->pad_SampleRates[6] = (double)AL_RATE_48000;
+ if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
+ return translateSGIerror(); /* double -> long. */
+ if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
+ goto weird;
+ if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
+ return translateSGIerror();
+ if (max != (long)(0.5 + pad->pad_SampleRates[6]))
+ {
+weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
+ return paHostError; /* Or make it a warning and just carry on... */
+ }
+ /*-------------------------------------------------------------------------------*/
+ return paNoError;
+}
+
+
+/*--------------------------------------------------------------------------------*/
+int Pa_CountDevices() /* Name of this function suggests it only counts and */
+{ /* is NOT destructive, it however resets whole PA ! */
+ int numDevices = 0; /* Let 's not do that here. */
+ internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
+#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
+ if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
+ Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
+#endif /* friendly to clients that forgot to initialize PA. */
+ while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
+ {
+ numDevices++;
+ currentDevice = currentDevice->pad_Next;
+ }
+ return numDevices;
+}
+
+/*-------------------------------------------------------------------------------*/
+static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
+{
+ int numDevices = 0;
+ internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
+ internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
+ while (pad) /* pad may be NULL, that's ok, return 0. */
+ { /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
+ if (pad->pad_DeviceID == id) /* This the device we were looking for? */
+ res = pad; /* But keep on(!) counting so we don't */
+ numDevices++; /* have to call Pa_CountDevices() later. */
+ pad = pad->pad_Next; /* Advance to the next device or NULL. */
+ } /* No assumptions about order of ID's in */
+ if (!res) /* the list. */
+ ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
+ if ((id < 0) || (id >= numDevices))
+ {
+ ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
+#if 1 /* Be strict, even when found, */
+ res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
+#endif
+ }
+ return res;
+}
+
+/*----------------------------------------------------------------------*/
+const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
+{
+ PaDeviceInfo* res = (PaDeviceInfo*)NULL;
+ internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
+ if (pad)
+ res = &pad->pad_Info; /* Not finding the specified ID is not */
+ if (!res) /* the same as &pad->pad_Info == NULL. */
+ ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
+ return res; /* So (maybe) a second/third ERR_RPT(). */
+}
+
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultInputDeviceID(void)
+{
+ return 0; /* 0 is the default device ID. */
+}
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultOutputDeviceID(void)
+{
+ return 0;
+}
+
+/*-------------------------------------------------------------------------------------------------*/
+/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */
+PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
+{
+ internalPortAudioDevice* pad;
+ PaError r = paNoError;
+ int audioLibFileID; /* To test for the presence of audio. */
+
+ if (sDeviceList) /* Allow re-init, only warn, no error. */
+ {
+ ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
+ return r;
+ }
+ /*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/
+ audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
+ if (audioLibFileID < 0) /* IO port. On failure, machine */
+ { /* has no audio ability. */
+ ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
+ return paHostError;
+ }
+ close(audioLibFileID); /* Allocate fast mem to hold device info. */
+ pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
+ if (pad == NULL)
+ return paInsufficientMemory;
+ memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
+ r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
+ Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
+ "AL default", /* A suitable name. */
+ pad); /* Write args and queried info into pad. */
+ if (r != paNoError)
+ {
+ ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
+ }
+ else
+ sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */
+ /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/
+ /*---------------------------------------------------------------------------------------------*/
+ return r;
+}
+
+/*---------------------------------------------------------------------------------------------------*/
+static PaError Pa_SgiAudioProcess(internalPortAudioStream *past) /* Spawned by PaHost_StartEngine(). */
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+
+ if (!past)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (!pahsc)
+ return paInternalError;
+ past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
+ DBUG(("entering thread.\n"));
+
+ while (!past->past_StopSoon) /* OR-ing StopSoon and StopNow here gives problems! */
+ {
+ /*---------------------------------------- INPUT: ------------------------------------*/
+ if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */
+ {
+ while (ALgetfilled(pahsc->pahsc_ALportIN) < pahsc->pahsc_SamplesPerInputBuffer)
+ {
+ /* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */
+ if (past->past_StopNow) /* Don't let ALreadsamps() block */
+ goto done;
+ }
+ if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_SamplesPerInputBuffer)) /* Number of samples instead */
+ { /* of number of frames. */
+ ERR_RPT(("ALreadsamps() failed.\n"));
+ result = paInternalError;
+ goto done;
+ }
+ }
+ /*---------------------------------------------------- USER CALLBACK ROUTINE: ----------*/
+ /* DBUG(("Calling Pa_CallConvertInt16()...\n")); */
+ Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
+ result = Pa_CallConvertInt16(past, /* user data and call user routine. */
+ pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_NativeOutputBuffer);
+ Pa_EndUsageCalculation(past);
+ if (result)
+ {
+ DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
+ goto done; /* This is apparently NOT an error! */
+ } /* Just letting the userCallBack stop us. */
+ /*---------------------------------------- OUTPUT: ------------------------------------*/
+ if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */
+ {
+ while (ALgetfillable(pahsc->pahsc_ALportOUT) < pahsc->pahsc_SamplesPerOutputBuffer)
+ {
+ /* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */
+ if (past->past_StopNow) /* Don't let ALwritesamps() block */
+ goto done;
+ }
+ if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
+ pahsc->pahsc_SamplesPerOutputBuffer))
+ {
+ ERR_RPT(("ALwritesamps() failed.\n"));
+ result = paInternalError;
+ goto done;
+ }
+ }
+ /*-------------------------------------------------------------------------------------*/
+ }
+done:
+ /* pahsc->pahsc_ThreadPID = -1; Hu? doesn't help!! (added by Pieter) */
+ past->past_IsActive = 0;
+ DBUG(("leaving thread.\n"));
+ return result;
+}
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError PaHost_OpenStream(internalPortAudioStream *past)
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
+ ALconfig sgiALconfig = NULL; /* IRIX-datatype. */
+ long pvbuf[8]; /* To get/set hardware configs. */
+ long sr, ALqsize;
+ DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
+ if (!past)
+ {
+ ERR_RPT(("Streampointer NULL!\n"));
+ result = paBadStreamPtr; goto done;
+ }
+ pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+ if (pahsc == NULL)
+ {
+ ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
+ result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
+ }
+ memset(pahsc, 0, sizeof(PaHostSoundControl));
+/* pahsc->pahsc_threadPID = -1; Should pahsc_threadPID be inited to */
+ past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */
+ /*--------------------------------------------------- Allocate native buffers: --------*/
+ pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumInputChannels; /* audio-thread. */
+ pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
+ if (past->past_NumInputChannels > 0) /* Assumes short = 16 bits! */
+ {
+ pahsc->pahsc_NativeInputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputBuffer);
+ if( pahsc->pahsc_NativeInputBuffer == NULL )
+ {
+ ERR_RPT(("Fast memory allocation for input-buffer failed.\n"));
+ result = paInsufficientMemory; goto done;
+ }
+ }
+ pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumOutputChannels; /* audio-thread. */
+ pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
+ if (past->past_NumOutputChannels > 0) /* Assumes short = 16 bits! */
+ {
+ pahsc->pahsc_NativeOutputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputBuffer);
+ if (pahsc->pahsc_NativeOutputBuffer == NULL)
+ {
+ ERR_RPT(("Fast memory allocation for output-buffer failed.\n"));
+ result = paInsufficientMemory; goto done;
+ }
+ }
+ /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ pvbuf[0] = AL_INPUT_RATE;
+ pvbuf[2] = AL_INPUT_COUNT;
+ pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
+ pvbuf[6] = AL_OUTPUT_COUNT;
+ sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */
+ if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
+ { /* sponding native AL-number(s). */
+ padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
+ if (!padIN)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
+ result = paHostError; goto done;
+ }
+ if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
+ goto sgiError; /* the same AL-device, the AL-library might */
+ if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
+ { /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
+ if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
+another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
+ result = paHostError; goto done;
+ }
+ pvbuf[1] = sr; /* Then set input-rate. */
+ if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
+ goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
+ } /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */
+ }
+ if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */
+ { /* We use padOUT/IN later on, or at least 1 of both. */
+ padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
+ if (!padOUT)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
+ result = paHostError; goto done;
+ }
+ if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
+ goto sgiError;
+ if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr))
+ { /* Output needed and rate different from current harware-rate. */
+ if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
+another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
+ result = paHostError; goto done; /* Will free again the inputbuffer */
+ } /* that was just created above. */
+ pvbuf[5] = sr; /* Then set output-rate. */
+ if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
+ goto sgiError;
+ }
+ }
+ /*------------------------------------------ Construct an audio-port-configuration ----------*/
+ sgiALconfig = ALnewconfig(); /* Change the SGI-AL-default-settings. */
+ if (sgiALconfig == (ALconfig)0) /* sgiALconfig is released here after use! */
+ goto sgiError; /* See that sgiALconfig is NOT released! */
+ if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
+ goto sgiError;
+ if (ALsetwidth (sgiALconfig, AL_SAMPLE_16)) /* Only meaningful when sample format for */
+ goto sgiError; /* config is set to two's complement format. */
+ /************************ Future versions might (dynamically) switch to 32-bit floats? *******
+ if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
+ goto sgiError;
+ if (ALsetfloatmax (sgiALconfig, 1.0)) Only meaningful when sample format for config
+ goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
+ /*---------- ?? --------------------*/
+ /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+ minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
+ past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
+ minNumBuffers : past->past_NumUserBuffers;
+ /*------------------------------------------------ Set internal AL queuesize (in samples) ----*/
+ if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer)
+ ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer;
+ else /* Take the largest of the two amounts. */
+ ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer;
+ ALqsize *= 4; /* 4 times as large as amount per transfer! */
+ if (ALsetqueuesize(sgiALconfig, ALqsize)) /* Or should we use past_NumUserBuffers here? */
+ goto sgiError; /* Using 2 times may give glitches... */
+ /* Have to work on ALsetqueuesize() above. */
+
+ /* Do ALsetchannels() later, apart per input and/or output. */
+ /*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/
+ if (past->past_OutputDeviceID == past->past_InputDeviceID) /* Who SETS these devive-numbers? */
+ {
+ if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
+ {
+ DBUG(("PaHost_OpenStream: opening both input and output channels.\n"));
+ /*------------------------- open output port: ----------------------------------*/
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+ if (pahsc->pahsc_ALportOUT == (ALport)0)
+ goto sgiError;
+ /*------------------------- open input port: -----------------------------------*/
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+ if (pahsc->pahsc_ALportIN == (ALport)0)
+ goto sgiError; /* For some reason the "patest_wire.c"-test crashes! */
+ } /* Probably due to too small buffersizes?.... */
+ else
+ {
+ ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
+ result = paHostError;
+ goto done;
+ }
+ }
+ else /* (OutputDeviceID != InputDeviceID) */
+ {
+ if (past->past_NumOutputChannels > 0) /* WRITE-ONLY: */
+ {
+ /*------------------------- open output port: ----------------------------------*/
+ DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n",
+ past->past_NumOutputChannels, padOUT->pad_DeviceName));
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+ if (pahsc->pahsc_ALportOUT == (ALport)0)
+ goto sgiError;
+ }
+ if (past->past_NumInputChannels > 0) /* READ-ONLY: */
+ {
+ /*------------------------- open input port: -----------------------------------*/
+ DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n",
+ past->past_NumInputChannels, padIN->pad_DeviceName));
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+ if (pahsc->pahsc_ALportIN == (ALport)0)
+ goto sgiError;
+ }
+ }
+ DBUG(("PaHost_OpenStream() succeeded.\n"));
+ goto done; /* (no errors occured) */
+sgiError:
+ result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+done:
+ if (sgiALconfig)
+ ALfreeconfig(sgiALconfig); /* We don't need that struct anymore. */
+ if (result != paNoError)
+ PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
+ return result;
+}
+
+/*-----------------------------------------------------*/
+PaError PaHost_StartOutput(internalPortAudioStream *past)
+{
+ return paNoError; /* Hmm, not implemented yet? */
+}
+PaError PaHost_StartInput(internalPortAudioStream *past)
+{
+ return paNoError;
+}
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StartEngine(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ int hres;
+ PaError result = paNoError;
+
+ if (!past) /* Test argument. */
+ {
+ ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
+ return paBadStreamPtr;
+ }
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (!pahsc)
+ {
+ ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData == NULL!\n"));
+ return paHostError;
+ }
+ past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+ DBUG(("PaHost_StartEngine() called.\n"));
+ /* Use pthread_create() instead of __clone() because: */
+ /* - pthread_create also works for other UNIX systems like Solaris, */
+ /* - Java HotSpot VM crashes in pthread_setcanceltype() when using __clone(). */
+ hres = pthread_create(&(pahsc->pahsc_ThreadPID), /* SPAWN AUDIO-CHILD. */
+ NULL, /* pthread_attr_t * attr */
+ (void*)Pa_SgiAudioProcess,
+ past);
+ if (hres)
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
+ }
+ return result;
+}
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
+{
+ int hres;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+
+ DBUG(("PaHost_StopEngine() called.\n"));
+ if (!past)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (pahsc == NULL)
+ return result; /* paNoError (already stopped, no err?). */
+ past->past_StopSoon = 1; /* Tell background thread to stop generating */
+ if (abort) /* more and to let current data play out. If */
+ past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
+ if (pahsc->pahsc_ThreadPID != -1) /* Join thread to recover memory resources. */
+ {
+ DBUG(("pthread_join() called.\n"));
+ hres = pthread_join(pahsc->pahsc_ThreadPID, NULL);
+ if (hres)
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ ERR_RPT(("PaHost_StopEngine() failed pthread_join().\n"));
+ }
+ pahsc->pahsc_ThreadPID = -1;
+ }
+ past->past_IsActive = 0;
+ return result;
+}
+
+/*---------------------------------------------------------------*/
+PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
+{
+ return paNoError; /* Not implemented yet? */
+}
+PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+
+ DBUG(("PaHost_CloseStream() called.\n"));
+ if (past == NULL)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
+ return result; /* This test prevents from freeing NULL-pointers. */
+
+ if (pahsc->pahsc_ALportIN)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportIN))
+ result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+ else /* But go on anyway... to release other stuff... */
+ pahsc->pahsc_ALportIN = (ALport)0;
+ }
+ if (pahsc->pahsc_ALportOUT)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportOUT))
+ result = translateSGIerror();
+ else
+ pahsc->pahsc_ALportOUT = (ALport)0;
+ }
+ if (pahsc->pahsc_NativeInputBuffer)
+ {
+ PaHost_FreeFastMemory(pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_BytesPerInputBuffer);
+ pahsc->pahsc_NativeInputBuffer = NULL;
+ }
+ if (pahsc->pahsc_NativeOutputBuffer)
+ {
+ PaHost_FreeFastMemory(pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer);
+ pahsc->pahsc_NativeOutputBuffer = NULL;
+ }
+ PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));
+ past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST. */
+ return result;
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+** set PA_MIN_LATENCY_MSEC=200
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be
+** determined based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+ return 2;
+}
+/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
+/* Do we HAVE to do it this way under IRIX???.... */
+/*--------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------*/
+PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */
+{ /* Called by Pa_Terminate() from pa_lib.c. */
+ internalPortAudioDevice *pad = sDeviceList,
+ *nxt;
+ while (pad)
+ {
+ DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
+ nxt = pad->pad_Next;
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
+ pad = nxt; /* PaHost_Init allocated this FAST MEM.*/
+ }
+ sDeviceList = (internalPortAudioDevice*)NULL;
+ return 0; /* Got rid of sNumDevices=0; */
+}
+
+/***********************************************************************/
+void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select(0, NULL, NULL, NULL, &timeout);
+#else
+ long usecs = msec * 1000;
+ usleep( usecs );
+#endif
+}
+
+/*---------------------------------------------------------------------------------------*/
+/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
+/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
+/* a call to PaHost_FreeFastMemory(). */
+void *PaHost_AllocateFastMemory(long numBytes)
+{
+ void *addr = malloc(numBytes);
+ if (addr)
+ memset(addr, 0, numBytes);
+ return addr;
+}
+
+/*---------------------------------------------------------------------------------------*/
+/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
+/* call to PaHost_AllocateFastMemory(). */
+void PaHost_FreeFastMemory(void *addr, long numBytes)
+{
+ if (addr)
+ free(addr);
+}
+
+/*----------------------------------------------------------*/
+PaError PaHost_StreamActive (internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ if (past == NULL)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return paInternalError;
+ return (PaError)(past->past_IsActive != 0);
+}
+
+/*-------------------------------------------------------------------*/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+/* FIXME - return actual frames played, not frames generated.
+** Need to query the output device somehow.
+*/
+ return past->past_FrameCount;
+}