?? pa_unix.c
字號:
*/
#define SCHEDULER_POLICY SCHED_RR
#define WATCHDOG_MAX_SECONDS (3)
#define WATCHDOG_INTERVAL_USEC (1000000)
static int PaHost_CanaryProc( PaHostSoundControl *pahsc )
{
int result = 0;
#ifdef GNUSTEP
GSRegisterCurrentThread(); /* SB20010904 */
#endif
while( pahsc->pahsc_CanaryRun) {
usleep( WATCHDOG_INTERVAL_USEC );
gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
}
DBUG(("PaHost_CanaryProc: exiting.\n"));
#ifdef GNUSTEP
GSUnregisterCurrentThread(); /* SB20010904 */
#endif
return result;
}
/*******************************************************************************************
* Monitor audio thread and lower its it if it hogs the CPU.
* To prevent getting killed, the audio thread must update a
* variable with a timer value.
* If the value is not recent enough, then the
* thread will get killed.
*/
static PaError PaHost_WatchDogProc( PaHostSoundControl *pahsc )
{
struct sched_param schp = { 0 };
int maxPri;
#ifdef GNUSTEP
GSRegisterCurrentThread(); /* SB20010904 */
#endif
/* Run at a priority level above audio thread so we can still run if it hangs. */
/* Rise more than 1 because of rumored off-by-one scheduler bugs. */
schp.sched_priority = pahsc->pahsc_AudioPriority + 4;
maxPri = sched_get_priority_max(SCHEDULER_POLICY);
if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri;
if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
{
ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n"));
goto killAudio;
}
/* Compare watchdog time with audio and canary thread times. */
/* Sleep for a while or until thread cancelled. */
while( pahsc->pahsc_WatchDogRun )
{
int delta;
struct timeval currentTime;
usleep( WATCHDOG_INTERVAL_USEC );
gettimeofday( ¤tTime, NULL );
/* If audio thread is not advancing, then it must be hung so kill it. */
delta = currentTime.tv_sec - pahsc->pahsc_EntryTime.tv_sec;
DBUG(("PaHost_WatchDogProc: audio delta = %d\n", delta ));
if( delta > WATCHDOG_MAX_SECONDS )
{
goto killAudio;
}
/* If canary died, then lower audio priority and halt canary. */
delta = currentTime.tv_sec - pahsc->pahsc_CanaryTime.tv_sec;
if( delta > WATCHDOG_MAX_SECONDS )
{
ERR_RPT(("PaHost_WatchDogProc: canary died!\n"));
goto lowerAudio;
}
}
DBUG(("PaHost_WatchDogProc: exiting.\n"));
#ifdef GNUSTEP
GSUnregisterCurrentThread(); /* SB20010904 */
#endif
return 0;
lowerAudio:
{
struct sched_param schat = { 0 };
if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0)
{
ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno ));
/* Fall through into killing audio thread. */
}
else
{
ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n"));
goto cleanup;
}
}
killAudio:
ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n"));
pthread_kill( pahsc->pahsc_AudioThread, SIGKILL );
cleanup:
pahsc->pahsc_CanaryRun = 0;
DBUG(("PaHost_WatchDogProc: cancel Canary\n"));
pthread_cancel( pahsc->pahsc_CanaryThread );
DBUG(("PaHost_WatchDogProc: join Canary\n"));
pthread_join( pahsc->pahsc_CanaryThread, NULL );
DBUG(("PaHost_WatchDogProc: forget Canary\n"));
pahsc->pahsc_IsCanaryThreadValid = 0;
#ifdef GNUSTEP
GSUnregisterCurrentThread(); /* SB20010904 */
#endif
return 0;
}
/*******************************************************************************************/
static void PaHost_StopWatchDog( PaHostSoundControl *pahsc )
{
/* Cancel WatchDog thread if there is one. */
if( pahsc->pahsc_IsWatchDogThreadValid )
{
pahsc->pahsc_WatchDogRun = 0;
DBUG(("PaHost_StopWatchDog: cancel WatchDog\n"));
pthread_cancel( pahsc->pahsc_WatchDogThread );
pthread_join( pahsc->pahsc_WatchDogThread, NULL );
pahsc->pahsc_IsWatchDogThreadValid = 0;
}
/* Cancel Canary thread if there is one. */
if( pahsc->pahsc_IsCanaryThreadValid )
{
pahsc->pahsc_CanaryRun = 0;
DBUG(("PaHost_StopWatchDog: cancel Canary\n"));
pthread_cancel( pahsc->pahsc_CanaryThread );
DBUG(("PaHost_StopWatchDog: join Canary\n"));
pthread_join( pahsc->pahsc_CanaryThread, NULL );
pahsc->pahsc_IsCanaryThreadValid = 0;
}
}
/*******************************************************************************************/
static PaError PaHost_StartWatchDog( PaHostSoundControl *pahsc )
{
int hres;
PaError result = 0;
/* The watch dog watches for these timer updates */
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
gettimeofday( &pahsc->pahsc_CanaryTime, NULL );
/* Launch a canary thread to detect priority abuse. */
pahsc->pahsc_CanaryRun = 1;
hres = pthread_create(&(pahsc->pahsc_CanaryThread),
NULL /*pthread_attr_t * attr*/,
(pthread_function_t)PaHost_CanaryProc, pahsc);
if( hres != 0 )
{
pahsc->pahsc_IsCanaryThreadValid = 0;
result = paHostError;
sPaHostError = hres;
goto error;
}
pahsc->pahsc_IsCanaryThreadValid = 1;
/* Launch a watchdog thread to prevent runaway audio thread. */
pahsc->pahsc_WatchDogRun = 1;
hres = pthread_create(&(pahsc->pahsc_WatchDogThread),
NULL /*pthread_attr_t * attr*/,
(pthread_function_t)PaHost_WatchDogProc, pahsc);
if( hres != 0 )
{
pahsc->pahsc_IsWatchDogThreadValid = 0;
result = paHostError;
sPaHostError = hres;
goto error;
}
pahsc->pahsc_IsWatchDogThreadValid = 1;
return result;
error:
PaHost_StopWatchDog( pahsc );
return result;
}
/*******************************************************************************************
* Bump priority of audio thread if running with superuser priveledges.
* if priority bumped then launch a watchdog.
*/
static PaError PaHost_BoostPriority( internalPortAudioStream *past )
{
PaHostSoundControl *pahsc;
PaError result = paNoError;
struct sched_param schp = { 0 };
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return paInternalError;
pahsc->pahsc_AudioThreadPID = getpid();
DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID ));
/* Choose a priority in the middle of the range. */
pahsc->pahsc_AudioPriority = (sched_get_priority_max(SCHEDULER_POLICY) -
sched_get_priority_min(SCHEDULER_POLICY)) / 2;
schp.sched_priority = pahsc->pahsc_AudioPriority;
if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
{
DBUG(("PortAudio: only superuser can use real-time priority.\n"));
}
else
{
DBUG(("PortAudio: audio callback priority set to level %d!\n", schp.sched_priority));
/* We are running at high priority so we should have a watchdog in case audio goes wild. */
result = PaHost_StartWatchDog( pahsc );
}
return result;
}
/*******************************************************************************************/
static PaError Pa_AudioThreadProc( internalPortAudioStream *past )
{
PaError result;
PaHostSoundControl *pahsc;
ssize_t bytes_read, bytes_written;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return paInternalError;
#ifdef GNUSTEP
GSRegisterCurrentThread(); /* SB20010904 */
#endif
result = PaHost_BoostPriority( past );
if( result < 0 ) goto error;
past->past_IsActive = 1;
DBUG(("entering thread.\n"));
while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
{
/* Read data from device */
if(pahsc->pahsc_NativeInputBuffer)
{
unsigned int totalread = 0;
DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer));
do
{
bytes_read = read(pahsc->pahsc_InputHandle,
(char *)pahsc->pahsc_NativeInputBuffer + totalread,
pahsc->pahsc_BytesPerInputBuffer - totalread);
if (bytes_read < 0)
{
ERR_RPT(("PortAudio: read interrupted!\n"));
break;
}
totalread += bytes_read;
} while( totalread < pahsc->pahsc_BytesPerInputBuffer);
}
/* Convert 16 bit native data to user data and call user routine. */
DBUG(("converting...\n"));
Pa_StartUsageCalculation( past );
result = Pa_CallConvertInt16( past,
pahsc->pahsc_NativeInputBuffer,
pahsc->pahsc_NativeOutputBuffer );
Pa_EndUsageCalculation( past );
if( result != 0)
{
DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
result));
break;
}
/* Write data to device. */
if( pahsc->pahsc_NativeOutputBuffer )
{
unsigned int totalwritten = 0;
do
{
bytes_written = write(pahsc->pahsc_OutputHandle,
(void *)pahsc->pahsc_NativeOutputBuffer,
pahsc->pahsc_BytesPerOutputBuffer);
if( bytes_written < 0 )
{
ERR_RPT(("PortAudio: write interrupted!"));
break;
}
totalwritten += bytes_written;
} while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer);
}
Pa_UpdateStreamTime(pahsc);
}
DBUG(("Pa_AudioThreadProc: left audio loop.\n"));
past->past_IsActive = 0;
PaHost_StopWatchDog( pahsc );
error:
DBUG(("leaving audio thread.\n"));
#ifdef GNUSTEP
GSUnregisterCurrentThread(); /* SB20010904 */
#endif
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 cshrc file.
*/
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
{
int minBuffers;
int minLatencyMsec = MIN_LATENCY_MSEC;
char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
if( minLatencyText != NULL )
{
PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
minLatencyMsec = atoi( minLatencyText );
if( minLatencyMsec < 1 ) minLatencyMsec = 1;
else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
}
minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
if( minBuffers < 2 ) minBuffers = 2;
return minBuffers;
}
/*******************************************************************/
PaError PaHost_OpenStream( internalPortAudioStream *past )
{
PaError result = paNoError;
PaHostSoundControl *pahsc;
unsigned int minNumBuffers;
internalPortAudioDevice *pad;
DBUG(("PaHost_OpenStream() called.\n" ));
/* Allocate and initialize host data. */
pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
if( pahsc == NULL )
{
result = paInsufficientMemory;
goto error;
}
memset( pahsc, 0, sizeof(PaHostSoundControl) );
past->past_DeviceData = (void *) pahsc;
pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
pahsc->pahsc_IsAudioThreadValid = 0;
pahsc->pahsc_IsWatchDogThreadValid = 0;
/* Allocate native buffers. */
pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
past->past_NumInputChannels * sizeof(short);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -