📄 pa_linux_alsa.c
字号:
avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
/*
if( throttled )
PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
*/
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
{
static int policy;
static struct sched_param spm = { 0 };
static const struct sched_param defaultSpm = { 0 };
PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
pthread_getschedparam( th->callbackThread, &policy, &spm );
if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
{
throttled = 1;
}
else
PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
/* Give other processes a go, before raising priority again */
PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
Pa_Sleep( th->throttledSleepTime );
/* Reset callback priority */
if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
{
PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
}
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
intervalMsec = 50;
else
intervalMsec = 100;
/*
lowpassCoeff = .97;
lowpassCoeff1 = .99999 - lowpassCoeff;
*/
}
else if( throttled && avgCpuLoad < .8 )
{
intervalMsec = 500;
throttled = 0;
/*
lowpassCoeff = .9;
lowpassCoeff1 = .99999 - lowpassCoeff;
*/
}
}
pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
error:
/* Shouldn't get here in the normal case */
/* Pass on error code */
pres = malloc( sizeof (PaError) );
*pres = result;
pthread_exit( pres );
}
static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
{
PaError result = paNoError;
pthread_attr_t attr;
int started = 0;
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
if( th->rtSched )
{
if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
{
int savedErrno = errno; /* In case errno gets overwritten */
assert( savedErrno != EINVAL ); /* Most likely a programmer error */
PA_UNLESS( (savedErrno == EPERM), paInternalError );
PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
}
else
PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
}
#endif
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
/* Priority relative to other processes */
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
started = 1;
if( th->rtSched )
{
if( th->useWatchdog )
{
int err;
struct sched_param wdSpm = { 0 };
/* Launch watchdog, watchdog sets callback thread priority */
int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
wdSpm.sched_priority = prio;
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
{
PA_UNLESS( err == EPERM, paInternalError );
/* Permission error, go on without realtime privileges */
PA_DEBUG(( "Failed bumping priority\n" ));
}
else
{
int policy;
th->watchdogRunning = 1;
ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
/* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
if( wdSpm.sched_priority != prio )
{
PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
PA_ENSURE( paInternalError );
}
}
}
else
PA_ENSURE( BoostPriority( th ) );
}
end:
return result;
error:
if( started )
KillCallbackThread( th, 0, NULL, NULL );
goto end;
}
static void CallbackUpdate( PaAlsaThreading *th )
{
th->callbackTime = PaUtil_GetTime();
th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
}
/* prototypes for functions declared in this file */
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *callback,
void *userData );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
static PaError AbortStream( PaStream *stream );
static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static double GetStreamCpuLoad( PaStream* stream );
static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
/* Callback prototypes */
static void *CallbackThreadFunc( void *userData );
/* Blocking prototypes */
static signed long GetStreamReadAvailable( PaStream* s );
static signed long GetStreamWriteAvailable( PaStream* s );
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
{
return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
}
PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
PaAlsaHostApiRepresentation *alsaHostApi = NULL;
PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
alsaHostApi->hostApiIndex = hostApiIndex;
*hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paALSA;
(*hostApi)->info.name = "ALSA";
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
PA_ENSURE( BuildDeviceList( alsaHostApi ) );
PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
CloseStream, StartStream,
StopStream, AbortStream,
IsStreamStopped, IsStreamActive,
GetStreamTime, GetStreamCpuLoad,
PaUtil_DummyRead, PaUtil_DummyWrite,
PaUtil_DummyGetReadAvailable,
PaUtil_DummyGetWriteAvailable );
PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
CloseStream, StartStream,
StopStream, AbortStream,
IsStreamStopped, IsStreamActive,
GetStreamTime, PaUtil_DummyGetCpuLoad,
ReadStream, WriteStream,
GetStreamReadAvailable,
GetStreamWriteAvailable );
return result;
error:
if( alsaHostApi )
{
if( alsaHostApi->allocations )
{
PaUtil_FreeAllAllocations( alsaHostApi->allocations );
PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
}
PaUtil_FreeMemory( alsaHostApi );
}
return result;
}
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
assert( hostApi );
if( alsaHostApi->allocations )
{
PaUtil_FreeAllAllocations( alsaHostApi->allocations );
PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
}
PaUtil_FreeMemory( alsaHostApi );
snd_config_update_free_global();
}
/*! Determine max channels and default latencies.
*
* This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
* traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
* and a suitable result returned. The device is closed before returning.
*/
static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
double *defaultHighLatency, double *defaultSampleRate, int isPlug )
{
PaError result = paNoError;
snd_pcm_hw_params_t *hwParams;
snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
unsigned int minChans, maxChans;
double defaultSr = *defaultSampleRate;
assert( pcm );
ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
snd_pcm_hw_params_alloca( &hwParams );
snd_pcm_hw_params_any( pcm, hwParams );
if( defaultSr >= 0 )
{
/* Could be that the device opened in one mode supports samplerates that the other mode wont have,
* so try again .. */
if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
{
defaultSr = -1.;
PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
}
}
if( defaultSr < 0. ) /* Default sample rate not set */
{
unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
}
ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
assert( maxChans <= INT_MAX );
assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
resulting in zeroed values */
/* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
if( isPlug && maxChans > 128 )
{
maxChans = 128;
PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
}
/* TWEAKME:
*
* Giving values for default min and max latency is not
* straightforward. Here are our objectives:
*
* * for low latency, we want to give the lowest value
* that will work reliably. This varies based on the
* sound card, kernel, CPU, etc. I think it is better
* to give sub-optimal latency than to give a number
* too low and cause dropouts. My conservative
* estimate at this point is to base it on 4096-sample
* latency at 44.1 kHz, which gives a latency of 23ms.
* * for high latency we want to give a large enough
* value that dropouts are basically impossible. This
* doesn't really require as much tweaking, since
* providing too large a number will just cause us to
* select the nearest setting that will work at stream
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -