diff options
-rw-r--r-- | include/pcm.h | 2 | ||||
-rw-r--r-- | include/sound/asound.h | 7 | ||||
-rw-r--r-- | src/pcm/pcm.c | 31 | ||||
-rw-r--r-- | test/audio_time.c | 68 |
4 files changed, 96 insertions, 12 deletions
diff --git a/include/pcm.h b/include/pcm.h index 290593b9..1ce91e7d 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -631,6 +631,7 @@ int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, unsigned int *rate_num, unsigned int *rate_den); @@ -939,6 +940,7 @@ void snd_pcm_status_get_trigger_tstamp(const snd_pcm_status_t *obj, snd_timestam void snd_pcm_status_get_trigger_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); void snd_pcm_status_get_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr); void snd_pcm_status_get_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); +void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr); snd_pcm_sframes_t snd_pcm_status_get_delay(const snd_pcm_status_t *obj); snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj); snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj); diff --git a/include/sound/asound.h b/include/sound/asound.h index e24d144a..16d03e82 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -160,7 +160,7 @@ enum { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 11) typedef unsigned long sndrv_pcm_uframes_t; typedef long sndrv_pcm_sframes_t; @@ -285,6 +285,7 @@ enum sndrv_pcm_subformat { #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ +#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ enum sndrv_pcm_state { SNDRV_PCM_STATE_OPEN = 0, /* stream is open */ @@ -426,7 +427,8 @@ struct sndrv_pcm_status { sndrv_pcm_uframes_t avail_max; /* max frames available on hw since last status */ sndrv_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ int suspended_state; /* suspended stream state */ - unsigned char reserved[60]; /* must be filled with zero */ + struct timespec audio_tstamp; /* from sample counter or wall clock */ + unsigned char reserved[60-sizeof(struct timespec)]; /* must be filled with zero */ }; struct sndrv_pcm_mmap_status { @@ -435,6 +437,7 @@ struct sndrv_pcm_mmap_status { sndrv_pcm_uframes_t hw_ptr; /* RO: hw ptr (0...boundary-1) */ struct timespec tstamp; /* Timestamp */ int suspended_state; /* RO: suspended stream state */ + struct timespec audio_tstamp; /* from sample counter or wall clock */ }; struct sndrv_pcm_mmap_control { diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 65c7646d..5880057e 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -3124,6 +3124,26 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param } /** + * \brief Check if hardware supports audio wallclock timestamps + * \param params Configuration space + * \retval 0 Hardware doesn't support audio wallclock timestamps + * \retval 1 Hardware supports audio wallclock timestamps + * + * This function should only be called when the configuration space + * contains a single configuration. Call #snd_pcm_hw_params to choose + * a single configuration from the configuration space. + */ +int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params) +{ + assert(params); + if (CHECK_SANITY(params->info == ~0U)) { + SNDMSG("invalid PCM info field"); + return 0; /* FIXME: should be a negative error? */ + } + return !!(params->info & SNDRV_PCM_INFO_HAS_WALL_CLOCK); +} + +/** * \brief Get rate exact info from a configuration space * \param params Configuration space * \param rate_num Pointer to returned rate numerator @@ -6214,6 +6234,17 @@ void snd_pcm_status_get_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *p use_default_symbol_version(__snd_pcm_status_get_htstamp, snd_pcm_status_get_htstamp, ALSA_0.9.0rc8); /** + * \brief Get "now" hi-res audio timestamp from a PCM status container + * \param obj pointer to #snd_pcm_status_t + * \param ptr Pointer to returned timestamp + */ +void snd_pcm_status_get_audio_htstamp(const snd_pcm_status_t *obj, snd_htimestamp_t *ptr) +{ + assert(obj && ptr); + *ptr = obj->audio_tstamp; +} + +/** * \brief Get delay from a PCM status container (see #snd_pcm_delay) * \return Delay in frames * diff --git a/test/audio_time.c b/test/audio_time.c index a910783b..03817c7c 100644 --- a/test/audio_time.c +++ b/test/audio_time.c @@ -33,6 +33,7 @@ long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2) void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, snd_htimestamp_t *trigger_timestamp, + snd_htimestamp_t *audio_timestamp, snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay) { int err; @@ -45,6 +46,7 @@ void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, } snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp); snd_pcm_status_get_htstamp(status, timestamp); + snd_pcm_status_get_audio_htstamp(status, audio_timestamp); *avail = snd_pcm_status_get_avail(status); *delay = snd_pcm_status_get_delay(status); } @@ -53,6 +55,7 @@ void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, #define PCM_LINK /* sync start for playback and capture */ #define TRACK_CAPTURE /* dump capture timing info */ #define TRACK_PLAYBACK /* dump playback timing info */ +#define TRACK_SAMPLE_COUNTS /* show difference between sample counters and audiotimestamps returned by driver */ #define PLAYBACK_BUFFERS 4 @@ -65,9 +68,13 @@ int main(void) snd_pcm_sframes_t frames; snd_htimestamp_t tstamp_c, tstamp_p; snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p; + snd_htimestamp_t audio_tstamp_c, audio_tstamp_p; unsigned char buffer_p[PERIOD*4*4]; unsigned char buffer_c[PERIOD*4*4]; + snd_pcm_hw_params_t *hwparams_p; + snd_pcm_hw_params_t *hwparams_c; + snd_pcm_sw_params_t *swparams_p; snd_pcm_sw_params_t *swparams_c; @@ -94,6 +101,18 @@ int main(void) goto _exit; } + snd_pcm_hw_params_alloca(&hwparams_p); + /* get the current hwparams */ + err = snd_pcm_hw_params_current(handle_p, hwparams_p); + if (err < 0) { + printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err)); + goto _exit; + } + if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_p)) + printf("Playback relies on audio wallclock timestamps\n"); + else + printf("Playback relies on audio sample counter timestamps\n"); + snd_pcm_sw_params_alloca(&swparams_p); /* get the current swparams */ err = snd_pcm_sw_params_current(handle_p, swparams_p); @@ -131,6 +150,18 @@ int main(void) goto _exit; } + snd_pcm_hw_params_alloca(&hwparams_c); + /* get the current hwparams */ + err = snd_pcm_hw_params_current(handle_c, hwparams_c); + if (err < 0) { + printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err)); + goto _exit; + } + if (snd_pcm_hw_params_supports_audio_wallclock_ts(hwparams_c)) + printf("Capture relies on audio wallclock timestamps\n"); + else + printf("Capture relies on audio sample counter timestamps\n"); + snd_pcm_sw_params_alloca(&swparams_c); /* get the current swparams */ err = snd_pcm_sw_params_current(handle_c, swparams_c); @@ -202,26 +233,43 @@ int main(void) frame_count_p += frames; #if defined(TRACK_PLAYBACK) - gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &avail_p, &delay_p); + gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &audio_tstamp_p, &avail_p, &delay_p); +#if defined(TRACK_SAMPLE_COUNTS) curr_count_p = frame_count_p - delay_p; /* written minus queued */ - printf("playback: systime: %lli nsec, sample time %lli nsec \tsystime delta %lli \n", - timediff(tstamp_p,trigger_tstamp_p), - (long long)round(((float)curr_count_p * 1000000000.0 / 48000.0)), - timediff(tstamp_p, trigger_tstamp_p) - (long long)round((double)curr_count_p * 1000000000.0 / 48000.0) + printf("playback: curr_count %lli driver count %lli, delta %lli\n", + (long long)curr_count_p * 1000000000LL / 48000 , + timestamp2ns(audio_tstamp_p), + (long long)curr_count_p * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_p) + ); +#endif + + printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", + timediff(tstamp_p, trigger_tstamp_p), + timestamp2ns(audio_tstamp_p), + timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p) ); #endif #if defined(TRACK_CAPTURE) - gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &avail_c, &delay_c); + gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &audio_tstamp_c, &avail_c, &delay_c); +#if defined(TRACK_SAMPLE_COUNTS) curr_count_c = frame_count_c + delay_c; /* read plus queued */ - printf("\t capture: systime: %lli nsec, sample time %lli nsec \tsystime delta %lli \n", - timediff(tstamp_c,trigger_tstamp_c), - (long long)round(((float)curr_count_c * 1000000000.0 / 48000.0)), - timediff(tstamp_c, trigger_tstamp_c) - (long long)round((double)curr_count_c * 1000000000.0 / 48000.0) + + printf("capture: curr_count %lli driver count %lli, delta %lli\n", + (long long)curr_count_c * 1000000000LL / 48000 , + timestamp2ns(audio_tstamp_c), + (long long)curr_count_c * 1000000000LL / 48000 - timestamp2ns(audio_tstamp_c) + ); +#endif + + printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli\n", + timediff(tstamp_c, trigger_tstamp_c), + timestamp2ns(audio_tstamp_c), + timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c) ); #endif |