📄 pa_linux_alsa.c
字号:
/*
* $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
* Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#undef ALSA_PCM_NEW_HW_PARAMS_API
#undef ALSA_PCM_NEW_SW_PARAMS_API
#include <sys/poll.h>
#include <string.h> /* strlen() */
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/mman.h>
#include <signal.h> /* For sig_atomic_t */
#include "portaudio.h"
#include "pa_util.h"
#include "pa_unix_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_linux_alsa.h"
/* Check return value of ALSA function, and map it to PaError */
#define ENSURE_(expr, code) \
do { \
if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
{ \
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
{ \
PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
} \
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
result = (code); \
goto error; \
} \
} while( 0 );
#define ASSERT_CALL_(expr, success) \
aErr_ = (expr); \
assert( aErr_ == success );
static int aErr_; /* Used with ENSURE_ */
static pthread_t callbackThread_;
typedef enum
{
StreamDirection_In,
StreamDirection_Out
} StreamDirection;
/* Threading utility struct */
typedef struct PaAlsaThreading
{
pthread_t watchdogThread;
pthread_t callbackThread;
int watchdogRunning;
int rtSched;
int rtPrio;
int useWatchdog;
unsigned long throttledSleepTime;
volatile PaTime callbackTime;
volatile PaTime callbackCpuTime;
PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
} PaAlsaThreading;
typedef struct
{
PaSampleFormat hostSampleFormat;
unsigned long framesPerBuffer;
int numUserChannels, numHostChannels;
int userInterleaved, hostInterleaved;
snd_pcm_t *pcm;
snd_pcm_uframes_t bufferSize;
snd_pcm_format_t nativeFormat;
unsigned int nfds;
int ready; /* Marked ready from poll */
void **userBuffers;
snd_pcm_uframes_t offset;
StreamDirection streamDir;
snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
} PaAlsaStreamComponent;
/* Implementation specific stream structure */
typedef struct PaAlsaStream
{
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
PaAlsaThreading threading;
unsigned long framesPerUserBuffer;
int primeBuffers;
int callbackMode; /* bool: are we running in callback mode? */
int pcmsSynced; /* Have we successfully synced pcms */
/* the callback thread uses these to poll the sound device(s), waiting
* for data to be ready/available */
struct pollfd *pfds;
int pollTimeout;
/* Used in communication between threads */
volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
volatile sig_atomic_t callbackAbort; /* Drop frames? */
volatile sig_atomic_t callbackStop; /* Signal a stop */
volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */
pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */
pthread_cond_t startCond; /* Wait untill audio is started in callback thread */
int neverDropInput;
PaTime underrun;
PaTime overrun;
PaAlsaStreamComponent capture, playback;
}
PaAlsaStream;
/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
typedef struct PaAlsaHostApiRepresentation
{
PaUtilHostApiRepresentation commonHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
PaHostApiIndex hostApiIndex;
}
PaAlsaHostApiRepresentation;
typedef struct PaAlsaDeviceInfo
{
PaDeviceInfo commonDeviceInfo;
char *alsaName;
int isPlug;
int minInputChannels;
int minOutputChannels;
}
PaAlsaDeviceInfo;
/* Threading utilities */
static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
{
th->watchdogRunning = 0;
th->rtSched = 0;
th->callbackTime = 0;
th->callbackCpuTime = 0;
th->useWatchdog = 1;
th->throttledSleepTime = 0;
th->cpuLoadMeasurer = clm;
th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
+ sched_get_priority_min( SCHED_FIFO );
}
static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
{
PaError result = paNoError;
void *pret;
if( exitResult )
*exitResult = paNoError;
if( watchdogExitResult )
*watchdogExitResult = paNoError;
if( th->watchdogRunning )
{
pthread_cancel( th->watchdogThread );
ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
if( pret && pret != PTHREAD_CANCELED )
{
if( watchdogExitResult )
*watchdogExitResult = *(PaError *) pret;
free( pret );
}
}
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
/* TODO: Make join time out */
if( !wait )
pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
if( pret && pret != PTHREAD_CANCELED )
{
if( exitResult )
*exitResult = *(PaError *) pret;
free( pret );
}
return result;
}
static void OnWatchdogExit( void *userData )
{
PaAlsaThreading *th = (PaAlsaThreading *) userData;
struct sched_param spm = { 0 };
assert( th );
ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
PA_DEBUG(( "Watchdog exiting\n" ));
}
static PaError BoostPriority( PaAlsaThreading *th )
{
PaError result = paNoError;
struct sched_param spm = { 0 };
spm.sched_priority = th->rtPrio;
assert( th );
if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
{
PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
PA_DEBUG(( "Failed bumping priority\n" ));
result = 0;
}
else
result = 1; /* Success */
error:
return result;
}
static void *WatchdogFunc( void *userData )
{
PaError result = paNoError, *pres = NULL;
int err;
PaAlsaThreading *th = (PaAlsaThreading *) userData;
unsigned intervalMsec = 500;
const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
double cpuLoad, avgCpuLoad = 0.;
int throttled = 0;
assert( th );
pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
/* Boost priority of callback thread */
PA_ENSURE( result = BoostPriority( th ) );
if( !result )
{
pthread_exit( NULL ); /* Boost failed, might as well exit */
}
cpuTimeThen = th->callbackCpuTime;
{
int policy;
struct sched_param spm = { 0 };
pthread_getschedparam( pthread_self(), &policy, &spm );
PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
}
while( 1 )
{
double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
/* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
pthread_testcancel();
Pa_Sleep( intervalMsec );
pthread_testcancel();
if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
{
PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
/* Tell thread to terminate */
err = pthread_kill( th->callbackThread, SIGKILL );
pthread_exit( NULL );
}
PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
/* Check if we should throttle, or unthrottle :P */
cpuTimeNow = th->callbackCpuTime;
cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
cpuTimeThen = cpuTimeNow;
timeNow = PaUtil_GetTime();
timeElapsed = timeNow - timeThen;
timeThen = timeNow;
cpuLoad = cpuTimeElapsed / timeElapsed;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -