⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pa_linux_alsa.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
        self->hostInterleaved = !self->userInterleaved;
    }

    ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );

    ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
    ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
    /* reject if there's no sample rate within 1% of the one requested */
    if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
    {
        PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));                 
        PA_ENSURE( paInvalidSampleRate );
    }

    ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );

    /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
    dir = 0;
    ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
    dir = 0;
    ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
    
    /* Find an acceptable number of periods */
    numPeriods = (latency * sr) / self->framesPerBuffer + 1;
    dir = 0;
    ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
    /* Minimum of periods should already be 2 */
    PA_UNLESS( numPeriods >= 2, paInternalError );

    /* Set the parameters! */
    ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
    ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );

    /* Latency in seconds, one period is not counted as latency */
    latency = (numPeriods - 1) * self->framesPerBuffer / sr;

    /* Now software parameters... */
    ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );

    ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
    ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );

    /* Silence buffer in the case of underrun */
    if( !primeBuffers ) /* XXX: Make sense? */
    {
        snd_pcm_uframes_t boundary;
        ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
    }
        
    ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
    ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
    ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );

    /* Set the parameters! */
    ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );

    *sampleRate = sr;
    *returnedLatency = latency;

end:
    return result;

error:
    goto end;   /* No particular action */
}

static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
        const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
        PaStreamFlags streamFlags, void *userData )
{
    PaError result = paNoError;
    assert( self );

    memset( self, 0, sizeof (PaAlsaStream) );

    if( NULL != callback )
    {
        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
                                               &alsaApi->callbackStreamInterface,
                                               callback, userData );
        self->callbackMode = 1;
    }
    else
    {
        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
                                               &alsaApi->blockingStreamInterface,
                                               NULL, userData );
    }

    self->framesPerUserBuffer = framesPerUserBuffer;
    self->neverDropInput = streamFlags & paNeverDropInput;
    /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
    /*
    if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
        self->primeBuffers = 1;
        */
    memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
    memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
    if( inParams )
        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
    if( outParams )
        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );

    assert( self->capture.nfds || self->playback.nfds );

    PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
                    self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );

    PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
    InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
    ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
    ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
    ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );

error:
    return result;
}

/** Free resources associated with stream, and eventually stream itself.
 *
 * Frees allocated memory, and terminates individual StreamComponents.
 */
static void PaAlsaStream_Terminate( PaAlsaStream *self )
{
    assert( self );

    if( self->capture.pcm )
    {
        PaAlsaStreamComponent_Terminate( &self->capture );
    }
    if( self->playback.pcm )
    {
        PaAlsaStreamComponent_Terminate( &self->playback );
    }

    PaUtil_FreeMemory( self->pfds );
    ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
    ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
    ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );

    PaUtil_FreeMemory( self );
}

/** Calculate polling timeout
 *
 * @param frames Time to wait
 * @return Polling timeout in milliseconds
 */
static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
{
    assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
    /* Period in msecs, rounded up */
    return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
}

/** Set up ALSA stream parameters.
 *
 */
static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
        *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
        unsigned long *maxHostBufferSize )
{
    PaError result = paNoError;
    double realSr = sampleRate;

    if( self->capture.pcm )
        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
                    self->callbackMode, &realSr, inputLatency ) );
    if( self->playback.pcm )
        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
                    self->callbackMode, &realSr, outputLatency ) );

    /* Should be exact now */
    self->streamRepresentation.streamInfo.sampleRate = realSr;

    /* this will cause the two streams to automatically start/stop/prepare in sync.
     * We only need to execute these operations on one of the pair.
     * A: We don't want to do this on a blocking stream.
     */
    if( self->callbackMode && self->capture.pcm && self->playback.pcm )
    {
        int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
        if( err >= 0 )
            self->pcmsSynced = 1;
        else
            PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
    }

    /* Frames per host buffer for the stream is set as a compromise between the two directions */
    framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
            self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
    self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer );    /* Period in msecs, rounded up */

    *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
            self->playback.pcm ? self->playback.bufferSize : 0 );

    /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
    self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);

    if( self->callbackMode )
    {
        /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
         * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
         * of host buffer frames with what the user specified */
        if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
        {
            /* self->alignFrames = 1; */

            /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
             * on block adaption */
        /*
            if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
                        self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
                self->useBlockAdaption = 1;
            else
                self->alignFrames = 1;
        */
        }
    }

error:
    return result;
}

/* We need to determine how many frames per host buffer to use.  Our
 * goals are to provide the best possible performance, but also to
 * most closely honor the requested latency settings.  Therefore this
 * decision is based on:
 *
 *   - the period sizes that playback and/or capture support.  The
 *     host buffer size has to be one of these.
 *   - the number of periods that playback and/or capture support.
 *
 * We want to make period_size*(num_periods-1) to be as close as possible
 * to latency*rate for both playback and capture.
 *
 * This is one of those blocks of code that will just take a lot of
 * refinement to be any good.
 *
 * In the full-duplex case it is possible that the routine was unable
 * to find a number of frames per buffer acceptable to both devices
 * TODO: Implement an algorithm to find the value closest to acceptance
 * by both devices, to minimize difference between period sizes?
 */
static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
        const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
{
    PaError result = paNoError;
    unsigned long framesPerBuffer = 0;
    int numHostInputChannels = 0, numHostOutputChannels = 0;

    /* XXX: Clean this up */
    if( stream->capture.pcm )
    {
        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
        numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
    }
    if( stream->playback.pcm )
    {
        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
        numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
    }

    if( stream->capture.pcm && stream->playback.pcm )
    {
        snd_pcm_uframes_t desiredLatency, e;
        snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
                          optimalPeriodSize, periodSize;
        int dir = 0;
        unsigned int minPeriods = 2;

        snd_pcm_t *pcm;
        snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;

        snd_pcm_hw_params_alloca( &hwParamsPlayback );
        snd_pcm_hw_params_alloca( &hwParamsCapture );

        /* Come up with a common desired latency */
        pcm = stream->playback.pcm;
        snd_pcm_hw_params_any( pcm, hwParamsPlayback );
        ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
                paBadIODeviceCombination );

        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );

        pcm = stream->capture.pcm;
        ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
        ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
                paBadIODeviceCombination );

        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );

        minPeriodSize = PA_MAX( minPlayback, minCapture );
        maxPeriodSize = PA_MIN( maxPlayback, maxCapture );

        desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
                * sampleRate);
        /* Clamp desiredLatency */
        {
            snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
            maxBufferSize = PA_MIN( maxBufferSize, tmp );

            desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
        }

        /* Find the closest power of 2 */
        e = ilogb( minPeriodSize );
        if( minPeriodSize & (minPeriodSize - 1) )
            e += 1;
        periodSize = (snd_pcm_uframes_t) pow( 2, e );

        while( periodSize <= maxPeriodSize )
        {
            if( snd_pcm_hw_params_test_peri

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -