From: Trent Piepho <35062987+xyzzy42@users.noreply.github.com> Date: Thu, 3 Jun 2021 01:03:42 +0000 (-0700) Subject: Alsa: Use hires timestamps (#560) X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=9413fa051f0f9b830c117cb3dc4f6d675e330a99;p=portaudio 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. --- diff --git a/src/hostapi/alsa/pa_linux_alsa.c b/src/hostapi/alsa/pa_linux_alsa.c index 80b4697..a6d2aec 100644 --- a/src/hostapi/alsa/pa_linux_alsa.c +++ b/src/hostapi/alsa/pa_linux_alsa.c @@ -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, ×tamp ); + } + else + { + alsa_snd_pcm_status_get_htstamp( status, ×tamp ); + } + 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, ×tamp ); - 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; }