📄 pa_sgi.c
字号:
/*
* $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, ¤tTime ) == 0 )
{
if (past->past_IfLastExitValid)
{
insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime );
pahsc->pahsc_InsideCountSum += insideCount;
totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime );
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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -