Alsa: Use hires timestamps (#560)

ALSA added nanosecond timestamps in library version 0.9.1.  Use these
instead of microsecond timestamps for reporting audio timestamps as well
as underrun/overrun times.

alsa-lib version 0.9.1 was released on Mar 11 2003.  I had fallback code
to use non-hires timestamps, but after 18 years it didn't seem
necessary, and I removed it from this commit.
This commit is contained in:
Trent Piepho 2021-06-02 18:03:42 -07:00 committed by GitHub
commit 9413fa051f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -220,8 +220,10 @@ _PA_DEFINE_FUNC(snd_config_update_free_global);
_PA_DEFINE_FUNC(snd_pcm_status);
_PA_DEFINE_FUNC(snd_pcm_status_sizeof);
_PA_DEFINE_FUNC(snd_pcm_status_get_tstamp);
_PA_DEFINE_FUNC(snd_pcm_status_get_htstamp);
_PA_DEFINE_FUNC(snd_pcm_status_get_state);
_PA_DEFINE_FUNC(snd_pcm_status_get_trigger_tstamp);
_PA_DEFINE_FUNC(snd_pcm_status_get_trigger_htstamp);
_PA_DEFINE_FUNC(snd_pcm_status_get_delay);
#define alsa_snd_pcm_status_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_status)
@ -499,8 +501,10 @@ static int PaAlsa_LoadLibrary()
_PA_LOAD_FUNC(snd_pcm_status);
_PA_LOAD_FUNC(snd_pcm_status_sizeof);
_PA_LOAD_FUNC(snd_pcm_status_get_tstamp);
_PA_LOAD_FUNC(snd_pcm_status_get_htstamp);
_PA_LOAD_FUNC(snd_pcm_status_get_state);
_PA_LOAD_FUNC(snd_pcm_status_get_trigger_tstamp);
_PA_LOAD_FUNC(snd_pcm_status_get_trigger_htstamp);
_PA_LOAD_FUNC(snd_pcm_status_get_delay);
_PA_LOAD_FUNC(snd_card_next);
@ -3156,6 +3160,28 @@ static PaError IsStreamActive( PaStream *s )
return stream->isActive;
}
/** Extract audio/trigger htstamp from status and convert into PaTime (seconds).
*
* trigger is boolean: trigger stampstamp vs audio timestamp. If delay is non-NULL, return delay in
* frames. */
static PaTime StatusToTime( const snd_pcm_status_t *status, int trigger, snd_pcm_uframes_t* delay )
{
snd_htimestamp_t timestamp;
if ( trigger )
{
alsa_snd_pcm_status_get_trigger_htstamp( status, &timestamp );
}
else
{
alsa_snd_pcm_status_get_htstamp( status, &timestamp );
}
if ( delay )
{
*delay = alsa_snd_pcm_status_get_delay( status );
}
return timestamp.tv_sec + ( (PaTime)timestamp.tv_nsec * 1e-9 );
}
static PaTime GetStreamTime( PaStream *s )
{
PaAlsaStream *stream = (PaAlsaStream*)s;
@ -3179,8 +3205,7 @@ static PaTime GetStreamTime( PaStream *s )
alsa_snd_pcm_status( stream->playback.pcm, status );
}
alsa_snd_pcm_status_get_tstamp( status, &timestamp );
return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1e6;
return StatusToTime( status, 0, NULL );
}
static double GetStreamCpuLoad( PaStream* s )
@ -3276,7 +3301,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
if( alsa_snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
{
alsa_snd_pcm_status_get_trigger_tstamp( st, &t );
self->underrun = now * 1000 - ( (PaTime)t.tv_sec * 1000 + (PaTime)t.tv_usec / 1000 );
self->underrun = ( now - StatusToTime( st, 1, NULL ) ) * 1000;
if( !self->playback.canMmap )
{
@ -3295,8 +3320,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
alsa_snd_pcm_status( self->capture.pcm, st );
if( alsa_snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
{
alsa_snd_pcm_status_get_trigger_tstamp( st, &t );
self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
self->overrun = ( now - StatusToTime( st, 1, NULL ) ) * 1000;
if (!self->capture.canMmap)
{
@ -3408,37 +3432,29 @@ static void OnExit( void *data )
static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
{
snd_pcm_status_t *capture_status, *playback_status;
snd_timestamp_t capture_timestamp, playback_timestamp;
snd_pcm_status_t *status;
PaTime capture_time = 0., playback_time = 0.;
alsa_snd_pcm_status_alloca( &capture_status );
alsa_snd_pcm_status_alloca( &playback_status );
alsa_snd_pcm_status_alloca( &status );
if( stream->capture.pcm )
{
snd_pcm_sframes_t capture_delay;
alsa_snd_pcm_status( stream->capture.pcm, capture_status );
alsa_snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
alsa_snd_pcm_status( stream->capture.pcm, status );
capture_time = StatusToTime( status, 0, &capture_delay );
capture_time = capture_timestamp.tv_sec +
( (PaTime)capture_timestamp.tv_usec / 1000000.0 );
timeInfo->currentTime = capture_time;
capture_delay = alsa_snd_pcm_status_get_delay( capture_status );
timeInfo->inputBufferAdcTime = timeInfo->currentTime -
timeInfo->inputBufferAdcTime = capture_time -
(PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
}
if( stream->playback.pcm )
{
snd_pcm_sframes_t playback_delay;
PaTime playback_time;
alsa_snd_pcm_status( stream->playback.pcm, playback_status );
alsa_snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
playback_time = playback_timestamp.tv_sec +
((PaTime)playback_timestamp.tv_usec / 1000000.0);
alsa_snd_pcm_status( stream->playback.pcm, status );
playback_time = StatusToTime( status, 0, &playback_delay );
if( stream->capture.pcm ) /* Full duplex */
{
@ -3450,7 +3466,6 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t
else
timeInfo->currentTime = playback_time;
playback_delay = alsa_snd_pcm_status_get_delay( playback_status );
timeInfo->outputBufferDacTime = timeInfo->currentTime +
(PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
}