Example #1
0
uint32_t AudioStreamOutALSA::latency() const
{
    int err;
    int t;
    snd_pcm_status_t *status;

	snd_pcm_status_alloca(&status);

    if(mHandle->handle == NULL) {
        LOGV("handle is null, error !");
        return 0;
    }
	
	if ((err = snd_pcm_status(mHandle->handle, status)) < 0) {
	 LOGV("stream status error :%s\n", snd_strerror(err));
        return USEC_TO_MSEC (mHandle->latency);
	}

    t = snd_pcm_status_get_delay(status);
    LOGV("snd_pcm_status_get_delay = %d", t);
    LOGV("AudioStreamOutALSA::latency = %d, sampleRate = %d", 
        (t * 1000) / sampleRate(),
        sampleRate());
    return (t * 1000) / sampleRate();
}
Example #2
0
/* stolen from devposs */
static uint32_t gettimer(void)
{
	long tmp=playpos;
	int odelay;

	if (busy++)
	{
		odelay=kernlen;
	} else {
		int err;
#ifdef ALSA_DEBUG
		fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = ");
#endif
		if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0)
		{
#ifdef ALSA_DEBUG
			fprintf(stderr, "failed: %s\n", snd_strerror(-err));
#endif
			fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err));
			odelay=kernlen;
		} else {
#ifdef ALSA_DEBUG
			fprintf(stderr, "ok\n");
			fprintf(stderr, "snd_pcm_status_get_delay(alsa_pcm_status) = ");
#endif

			odelay=snd_pcm_status_get_delay(alsa_pcm_status);
#ifdef ALSA_DEBUG
			fprintf(stderr, "%i\n", odelay);
#endif
			if (odelay<0) /* we ignore buffer-underruns */
				odelay=0;
			else if (odelay==0)
			{
			/* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */
				odelay = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status);
				if (odelay<0)
					odelay=0;
			}

			odelay<<=(bit16+stereo);
			if (odelay>kernlen)
			{
				odelay=kernlen;
			} else if ((odelay<kernlen))
			{
				kernlen=odelay;
				kernpos=(cachepos-kernlen+buflen)%buflen;
			}
		}
	}
	
	tmp-=odelay;
	busy--;
	return imuldiv(tmp, 65536>>(stereo+bit16), plrRate);
}
Example #3
0
/* more or less stolen from devposs */
static int getplaypos(void)
{
	int retval;

	if (busy++)
	{
	} else {
		snd_pcm_sframes_t tmp;	
		int err;
		
#ifdef ALSA_DEBUG
		fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = ");
#endif
		if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0)
		{
#ifdef ALSA_DEBUG
			fprintf(stderr, "failed: %s\n", snd_strerror(-err));
#endif
			fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err));
		} else {
#ifdef ALSA_DEBUG
			fprintf(stderr, "ok\n");
			fprintf(stderr, "ALSA snd_pcm_status_get_delay(alsa_pcm_status = ");
#endif
			tmp=snd_pcm_status_get_delay(alsa_pcm_status);
#ifdef ALSA_DEBUG
			fprintf(stderr, "%ld\n", tmp);
#endif
			tmp<<=(bit16+stereo);
	
			if (tmp<0) /* we ignore buffer-underruns */
				tmp=0;
			else if (tmp==0)
			{
			/* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */
				tmp = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status);
				if (tmp<0)
					tmp=0;
			}
		
			if (tmp>kernlen)
			{
			} else {
				kernlen=tmp;
			}
			kernpos=(cachepos-kernlen+buflen)%buflen;
		}
	}
	retval=kernpos;
	busy--;
	return retval;
}
/*----------------------------------------------------------------------
|    AlsaOutput_GetStatus
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutput_GetStatus(BLT_OutputNode*       _self,
                     BLT_OutputNodeStatus* status)
{
    AlsaOutput*       self = ATX_SELF(AlsaOutput, BLT_OutputNode);
    snd_pcm_status_t* pcm_status;
    snd_pcm_sframes_t delay = 0;
    int               io_result;

    /* default values */
    status->media_time.seconds = 0;
    status->media_time.nanoseconds = 0;
    status->flags = 0;

    /* get the driver status */
    snd_pcm_status_alloca_no_assert(&pcm_status);
    io_result = snd_pcm_status(self->device_handle, pcm_status);
    if (io_result != 0) {
        return BLT_FAILURE;
    }
    delay = snd_pcm_status_get_delay(pcm_status);
    if (delay == 0) {
        /* workaround buggy alsa drivers */
        io_result = snd_pcm_delay(self->device_handle, &delay);
        if (io_result != 0) {
            return BLT_FAILURE;
        }
    }
    
    if (delay > 0 && self->media_type.sample_rate) {
        ATX_UInt64 media_time_samples = (self->next_media_time * 
                                         (ATX_UInt64)self->media_type.sample_rate)/
                                         (ATX_UInt64)1000000000;
        ATX_UInt64 media_time_ns;
        if (delay <= (snd_pcm_sframes_t)media_time_samples) {
            media_time_samples -= delay;
        } else {
            media_time_samples = 0;
        }
        media_time_ns = (media_time_samples*(ATX_UInt64)1000000000)/self->media_type.sample_rate;
        status->media_time = BLT_TimeStamp_FromNanos(media_time_ns);
    } else {
        status->media_time = BLT_TimeStamp_FromNanos(self->next_media_time);
    }
    
    /* return the computed media time */
    ATX_LOG_FINEST_3("delay = %lld samples, input port time = %lld, media time = %lld", (ATX_UInt64)delay, self->next_media_time, BLT_TimeStamp_ToNanos(status->media_time));
    return BLT_SUCCESS;
}
int main(int argc, char *argv[]) {
    const char *dev;
    int r, cap, count = 0;
    snd_pcm_hw_params_t *hwparams;
    snd_pcm_sw_params_t *swparams;
    snd_pcm_status_t *status;
    snd_pcm_t *pcm;
    unsigned rate = 44100;
    unsigned periods = 2;
    snd_pcm_uframes_t boundary, buffer_size = 44100/10; /* 100s */
    int dir = 1;
    struct timespec start, last_timestamp = { 0, 0 };
    uint64_t start_us, last_us = 0;
    snd_pcm_sframes_t last_avail = 0, last_delay = 0;
    struct pollfd *pollfds;
    int n_pollfd;
    int64_t sample_count = 0;
    struct sched_param sp;

    r = -1;
#ifdef _POSIX_PRIORITY_SCHEDULING
    sp.sched_priority = 5;
    r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
#endif
    if (r)
        printf("Could not get RT prio. :(\n");

    snd_pcm_hw_params_alloca(&hwparams);
    snd_pcm_sw_params_alloca(&swparams);
    snd_pcm_status_alloca(&status);

    r = clock_gettime(CLOCK_MONOTONIC, &start);
    assert(r == 0);

    start_us = timespec_us(&start);

    dev = argc > 1 ? argv[1] : "front:AudioPCI";
    cap = argc > 2 ? atoi(argv[2]) : 0;

    if (cap == 0)
      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
    else
      r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_any(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    assert(r == 0);

    r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
    assert(r == 0);

    r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL);
    assert(r == 0);

    r = snd_pcm_hw_params_set_channels(pcm, hwparams, 2);
    assert(r == 0);

    r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir);
    assert(r == 0);

    r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size);
    assert(r == 0);

    r = snd_pcm_hw_params(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_hw_params_current(pcm, hwparams);
    assert(r == 0);

    r = snd_pcm_sw_params_current(pcm, swparams);
    assert(r == 0);

    if (cap == 0)
      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1);
    else
      r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0);
    assert(r == 0);

    r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0);
    assert(r == 0);

    r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
    assert(r == 0);
    r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size);
    assert(r == 0);

    r = snd_pcm_sw_params_get_boundary(swparams, &boundary);
    assert(r == 0);
    r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary);
    assert(r == 0);

    r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE);
    assert(r == 0);

    r = snd_pcm_sw_params(pcm, swparams);
    assert(r == 0);

    r = snd_pcm_prepare(pcm);
    assert(r == 0);

    r = snd_pcm_sw_params_current(pcm, swparams);
    assert(r == 0);

/*     assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */

    n_pollfd = snd_pcm_poll_descriptors_count(pcm);
    assert(n_pollfd > 0);

    pollfds = malloc(sizeof(struct pollfd) * n_pollfd);
    assert(pollfds);

    r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd);
    assert(r == n_pollfd);

    printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size);

    if (cap) {
      r = snd_pcm_start(pcm);
      assert(r == 0);
    }

    for (;;) {
        snd_pcm_sframes_t avail, delay;
        struct timespec now, timestamp;
        unsigned short revents;
        int handled = 0;
        uint64_t now_us, timestamp_us;
        snd_pcm_state_t state;
        unsigned long long pos;

        r = poll(pollfds, n_pollfd, 0);
        assert(r >= 0);

        r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents);
        assert(r == 0);

        if (cap == 0)
          assert((revents & ~POLLOUT) == 0);
        else
          assert((revents & ~POLLIN) == 0);

        avail = snd_pcm_avail(pcm);
        assert(avail >= 0);

        r = snd_pcm_status(pcm, status);
        assert(r == 0);

        /* This assertion fails from time to time. ALSA seems to be broken */
/*         assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */
/*         printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */

        snd_pcm_status_get_htstamp(status, &timestamp);
        delay = snd_pcm_status_get_delay(status);
        state = snd_pcm_status_get_state(status);

        r = clock_gettime(CLOCK_MONOTONIC, &now);
        assert(r == 0);

        assert(!revents || avail > 0);

        if ((!cap && avail) || (cap && (unsigned)avail >= buffer_size)) {
            snd_pcm_sframes_t sframes;
            static const uint16_t psamples[2] = { 0, 0 };
            uint16_t csamples[2];

            if (cap == 0)
              sframes = snd_pcm_writei(pcm, psamples, 1);
            else
              sframes = snd_pcm_readi(pcm, csamples, 1);
            assert(sframes == 1);

            handled = 1;
            sample_count++;
        }

        if (!handled &&
            memcmp(&timestamp, &last_timestamp, sizeof(timestamp)) == 0 &&
            avail == last_avail &&
            delay == last_delay) {
            /* This is boring */
            continue;
        }

        now_us = timespec_us(&now);
        timestamp_us = timespec_us(&timestamp);

        if (cap == 0)
            pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / 44100);
        else
            pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / 44100);

        if (count++ % 50 == 0)
            printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n");

        printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n",
               (unsigned long long) (now_us - last_us),
               (unsigned long long) (now_us - start_us),
               (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),
               pos,
               (unsigned long long) sample_count,
               (signed long) avail,
               (signed long) delay,
               revents,
               handled,
               state);

        if (cap == 0)
          /** When this assert is hit, most likely something bad
           * happened, i.e. the avail jumped suddenly. */
          assert((unsigned) avail <= buffer_size);

        last_avail = avail;
        last_delay = delay;
        last_timestamp = timestamp;
        last_us = now_us;
    }

    return 0;
}
Example #6
0
/* more or less stolen from devposs */
static void flush(void)
{
	int result, n, odelay;
	int err;

	if (busy++)
	{
		busy--;
		return;		
	}
		
#ifdef ALSA_DEBUG
	fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = ");
#endif
	if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0)
	{
#ifdef ALSA_DEBUG
		fprintf(stderr, "failed: %s\n", snd_strerror(-err));
#endif
		fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err));
		busy--;
		return;
	}
#ifdef ALSA_DEBUG
	fprintf(stderr, "ok\n");
	fprintf(stderr, "ALSA snd_pcm_status_get_delay(alsa_pcm_status) = ");
#endif
	odelay=snd_pcm_status_get_delay(alsa_pcm_status);
#ifdef ALSA_DEBUG
	fprintf(stderr, "%i\n", odelay);
#endif
	odelay<<=(bit16+stereo);	
	if (odelay<0) /* we ignore buffer-underruns */
		odelay=0;
	else if (odelay==0)
	{
	/* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */
		odelay = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status);
		if (odelay<0)
			odelay=0;
	}

	if (odelay>kernlen)
	{
		odelay=kernlen;
	} else if ((odelay<kernlen))
	{
		kernlen=odelay;
		kernpos=(cachepos-kernlen+buflen)%buflen;
	}
	
	if (!cachelen)
	{
		busy--;
		return;
	}
	
	if (bufpos<=cachepos)
		n=buflen-cachepos;
	else
		n=bufpos-cachepos;

	/* TODO, check kernel-size
	if (n>info.bytes)
		n=info.bytes;
	*/
	
	if (n<=0)
	{
		busy--;
		return;
	}

#ifdef ALSA_DEBUG
	fprintf(stderr, "ALSA snd_pcm_writei(alsa_pcm, buffer, %i) = ", n>>(bit16+stereo));
#endif
	result=snd_pcm_writei(alsa_pcm, playbuf+cachepos, n>>(bit16+stereo));
	if (result<0)
	{
#ifdef ALSA_DEBUG
		fprintf(stderr, "failed: %s\n", snd_strerror(-result));
#endif
		if (result==-EPIPE)
		{
			fprintf(stderr, "ALSA: Machine is too slow, calling snd_pcm_prepare()\n");
			fprintf(stderr, "ALSA snd_pcm_prepare(alsa_pcm)");
			snd_pcm_prepare(alsa_pcm); /* TODO, can this fail? */
		}
		busy--;
		return;
#ifdef ALSA_DEBUG
	} else {
		fprintf(stderr, "ok\n");
#endif
	}
	result<<=(bit16+stereo);
	cachepos=(cachepos+result+buflen)%buflen;
	playpos+=result;
	cachelen-=result;
	kernlen+=result;

	busy--;
}
Example #7
0
void *CallbackThread( void *userData )
{
    PaAlsaStream *stream = (PaAlsaStream*)userData;
    pthread_cleanup_push( &Stop, stream );   // Execute Stop on exit

    if( stream->pcm_playback )
        snd_pcm_start( stream->pcm_playback );
    else if( stream->pcm_capture )
        snd_pcm_start( stream->pcm_capture );

    while(1)
    {
        int frames_avail;
        int frames_got;

        PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
        int callbackResult;
        int framesProcessed;

        pthread_testcancel();
        {
            /* calculate time info */
            snd_timestamp_t capture_timestamp;
            snd_timestamp_t playback_timestamp;
            snd_pcm_status_t *capture_status;
            snd_pcm_status_t *playback_status;
            snd_pcm_status_alloca( &capture_status );
            snd_pcm_status_alloca( &playback_status );

            if( stream->pcm_capture )
            {
                snd_pcm_status( stream->pcm_capture, capture_status );
                snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
            }
            if( stream->pcm_playback )
            {
                snd_pcm_status( stream->pcm_playback, playback_status );
                snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
            }

            /* Hmm, we potentially have both a playback and a capture timestamp.
             * Hopefully they are the same... */
            if( stream->pcm_capture && stream->pcm_playback )
            {
                float capture_time = capture_timestamp.tv_sec +
                                     ((float)capture_timestamp.tv_usec/1000000);
                float playback_time= playback_timestamp.tv_sec +
                                     ((float)playback_timestamp.tv_usec/1000000);
                if( fabsf(capture_time-playback_time) > 0.01 )
                    PA_DEBUG(("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time)));
                timeInfo.currentTime = capture_time;
            }
            else if( stream->pcm_playback )
            {
                timeInfo.currentTime = playback_timestamp.tv_sec +
                                       ((float)playback_timestamp.tv_usec/1000000);
            }
            else
            {
                timeInfo.currentTime = capture_timestamp.tv_sec +
                                       ((float)capture_timestamp.tv_usec/1000000);
            }

            if( stream->pcm_capture )
            {
                snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status );
                timeInfo.inputBufferAdcTime = timeInfo.currentTime -
                    (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
            }

            if( stream->pcm_playback )
            {
                snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status );
                timeInfo.outputBufferDacTime = timeInfo.currentTime +
                    (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
            }
        }


        /*
            IMPLEMENT ME:
                - handle buffer slips
        */

        /*
            depending on whether the host buffers are interleaved, non-interleaved
            or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(),
            PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here.
        */

        framesProcessed = frames_avail = wait( stream );

        while( frames_avail > 0 )
        {
            //PA_DEBUG(( "%d frames available\n", frames_avail ));

            /* Now we know the soundcard is ready to produce/receive at least
             * one period.  We just need to get the buffers for the client
             * to read/write. */
            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
                    0 /* @todo pass underflow/overflow flags when necessary */ );

            frames_got = setup_buffers( stream, frames_avail );


            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );

            callbackResult = paContinue;

            /* this calls the callback */

            framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
                                                          &callbackResult );

            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );

            /* inform ALSA how many frames we wrote */

            if( stream->pcm_capture )
                snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_got );

            if( stream->pcm_playback )
                snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_got );

            if( callbackResult != paContinue )
                break;

            frames_avail -= frames_got;
        }


        /*
            If you need to byte swap outputBuffer, you can do it here using
            routines in pa_byteswappers.h
        */

        if( callbackResult != paContinue )
        {
            stream->callback_finished = 1;
            stream->callbackAbort = (callbackResult == paAbort);

            pthread_exit( NULL );
        }
    }

    /* This code is unreachable, but important to include regardless because it
     * is possibly a macro with a closing brace to match the opening brace in
     * pthread_cleanup_push() above.  The documentation states that they must
     * always occur in pairs. */

    pthread_cleanup_pop( 1 );
}
Example #8
0
/*****************************************************************************
 * ALSAFill: function used to fill the ALSA buffer as much as possible
 *****************************************************************************/
static void ALSAFill( aout_instance_t * p_aout )
{
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
    aout_buffer_t * p_buffer;
    snd_pcm_status_t * p_status = p_sys->p_status;
    int i_snd_rc;
    mtime_t next_date;

    /* Fill in the buffer until space or audio output buffer shortage */

    /* Get the status */
    i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
    if( i_snd_rc < 0 )
    {
        msg_Err( p_aout, "cannot get device status" );
        goto error;
    }

    /* Handle buffer underruns and get the status again */
    if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
    {
        /* Prepare the device */
        i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );

        if( i_snd_rc )
        {
            msg_Err( p_aout, "cannot recover from buffer underrun" );
            goto error;
        }

        msg_Dbg( p_aout, "recovered from buffer underrun" );

        /* Get the new status */
        i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
        if( i_snd_rc < 0 )
        {
            msg_Err( p_aout, "cannot get device status after recovery" );
            goto error;
        }

        /* Underrun, try to recover as quickly as possible */
        next_date = mdate();
    }
    else
    {
        /* Here the device should be in RUNNING state, p_status is valid. */
        snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
        if( delay == 0 ) /* workaround buggy alsa drivers */
            if( snd_pcm_delay( p_sys->p_snd_pcm, &delay ) < 0 )
                delay = 0; /* FIXME: use a positive minimal delay */
        int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay );
        next_date = mdate() + ( (mtime_t)i_bytes * 1000000
                / p_aout->output.output.i_bytes_per_frame
                / p_aout->output.output.i_rate
                * p_aout->output.output.i_frame_length );

#ifdef ALSA_DEBUG
        snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
        if( state != SND_PCM_STATE_RUNNING )
            msg_Err( p_aout, "pcm status (%d) != RUNNING", state );

        msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes );

        msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
        msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
        msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );

        msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) );
#endif
    }

    p_buffer = aout_OutputNextBuffer( p_aout, next_date,
           (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );

    /* Audio output buffer shortage -> stop the fill process and wait */
    if( p_buffer == NULL )
        goto error;

    for (;;)
    {
        i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer,
                                   p_buffer->i_nb_samples );
        if( i_snd_rc != -ESTRPIPE )
            break;

        /* a suspend event occurred
         * (stream is suspended and waiting for an application recovery) */
        msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );

        while( vlc_object_alive (p_aout) && vlc_object_alive (p_aout->p_libvlc) &&
               ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN )
        {
            msleep( 1000000 );
        }

        if( i_snd_rc < 0 )
            /* Device does not supprot resuming, restart it */
            i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );

    }

    if( i_snd_rc < 0 )
        msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );

    aout_BufferFree( p_buffer );
    return;

error:
    if( i_snd_rc < 0 )
        msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
    msleep( p_sys->i_period_time >> 1 );
}
Example #9
0
/*****************************************************************************
 * ALSAFill: function used to fill the ALSA buffer as much as possible
 *****************************************************************************/
static void ALSAFill( aout_instance_t * p_aout )
{
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
    snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
    snd_pcm_status_t * p_status;
    int i_snd_rc;
    mtime_t next_date;

    int canc = vlc_savecancel();
    /* Fill in the buffer until space or audio output buffer shortage */

    /* Get the status */
    snd_pcm_status_alloca(&p_status);
    i_snd_rc = snd_pcm_status( p_pcm, p_status );
    if( i_snd_rc < 0 )
    {
        msg_Err( p_aout, "cannot get device status" );
        goto error;
    }

    /* Handle buffer underruns and get the status again */
    if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
    {
        /* Prepare the device */
        i_snd_rc = snd_pcm_prepare( p_pcm );
        if( i_snd_rc )
        {
            msg_Err( p_aout, "cannot recover from buffer underrun" );
            goto error;
        }

        msg_Dbg( p_aout, "recovered from buffer underrun" );

        /* Get the new status */
        i_snd_rc = snd_pcm_status( p_pcm, p_status );
        if( i_snd_rc < 0 )
        {
            msg_Err( p_aout, "cannot get device status after recovery" );
            goto error;
        }

        /* Underrun, try to recover as quickly as possible */
        next_date = mdate();
    }
    else
    {
        /* Here the device should be in RUNNING state, p_status is valid. */
        snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
        if( delay == 0 ) /* workaround buggy alsa drivers */
            if( snd_pcm_delay( p_pcm, &delay ) < 0 )
                delay = 0; /* FIXME: use a positive minimal delay */

        size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
        mtime_t delay_us = CLOCK_FREQ * i_bytes
                / p_aout->output.output.i_bytes_per_frame
                / p_aout->output.output.i_rate
                * p_aout->output.output.i_frame_length;

#ifdef ALSA_DEBUG
        snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
        if( state != SND_PCM_STATE_RUNNING )
            msg_Err( p_aout, "pcm status (%d) != RUNNING", state );

        msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );

        msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
        msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
        msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
        msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
#endif
        next_date = mdate() + delay_us;
    }

    block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
           (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );

    /* Audio output buffer shortage -> stop the fill process and wait */
    if( p_buffer == NULL )
        goto error;

    for (;;)
    {
        int n = snd_pcm_poll_descriptors_count(p_pcm);
        struct pollfd ufd[n];
        unsigned short revents;

        snd_pcm_poll_descriptors(p_pcm, ufd, n);
        do
        {
            vlc_restorecancel(canc);
            poll(ufd, n, -1);
            canc = vlc_savecancel();
            snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
        }
        while(!revents);

        if(revents & POLLOUT)
        {
            i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
                                       p_buffer->i_nb_samples );
            if( i_snd_rc != -ESTRPIPE )
                break;
        }

        /* a suspend event occurred
         * (stream is suspended and waiting for an application recovery) */
        msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );

        while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
        {
            vlc_restorecancel(canc);
            msleep(CLOCK_FREQ); /* device still suspended, wait... */
            canc = vlc_savecancel();
        }

        if( i_snd_rc < 0 )
            /* Device does not support resuming, restart it */
            i_snd_rc = snd_pcm_prepare( p_pcm );

    }

    if( i_snd_rc < 0 )
        msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );

    vlc_restorecancel(canc);
    block_Release( p_buffer );
    return;

error:
    if( i_snd_rc < 0 )
        msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );

    vlc_restorecancel(canc);
    msleep(p_sys->i_period_time / 2);
}