Simple test to create playback and capture streams, and check elapsed time vs. sample counts reported by driver. This should be helpful for driver developers and anyone interested in system/audio time drift.
tested only on HDAudio
TODO: - make period configurable - better output messages - support for wall clock when it's in the mainline
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- test/audio_time.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 test/audio_time.c
diff --git a/test/audio_time.c b/test/audio_time.c new file mode 100644 index 0000000..a910783 --- /dev/null +++ b/test/audio_time.c @@ -0,0 +1,237 @@ +/* + * This program only tracks the difference between system time + * and audio time, as reported in snd_pcm_status(). It should be + * helpful to verify the information reported by drivers. + */ + +#include "../include/asoundlib.h" +#include <math.h> + +static char *device = "hw:0,0"; + +snd_output_t *output = NULL; + +long long timestamp2ns(snd_htimestamp_t t) +{ + long long nsec; + + nsec = t.tv_sec * 1000000000; + nsec += t.tv_nsec; + + return nsec; +} + +long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2) +{ + long long nsec1, nsec2; + + nsec1 = timestamp2ns(t1); + nsec2 = timestamp2ns(t2); + + return nsec1 - nsec2; +} + +void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, + snd_htimestamp_t *trigger_timestamp, + snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay) +{ + int err; + snd_pcm_status_t *status; + + snd_pcm_status_alloca(&status); + if ((err = snd_pcm_status(handle, status)) < 0) { + printf("Stream status error: %s\n", snd_strerror(err)); + exit(0); + } + snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp); + snd_pcm_status_get_htstamp(status, timestamp); + *avail = snd_pcm_status_get_avail(status); + *delay = snd_pcm_status_get_delay(status); +} + +#define PERIOD 6000 +#define PCM_LINK /* sync start for playback and capture */ +#define TRACK_CAPTURE /* dump capture timing info */ +#define TRACK_PLAYBACK /* dump playback timing info */ +#define PLAYBACK_BUFFERS 4 + + +int main(void) +{ + int err; + unsigned int i; + snd_pcm_t *handle_p = NULL; + snd_pcm_t *handle_c = NULL; + snd_pcm_sframes_t frames; + snd_htimestamp_t tstamp_c, tstamp_p; + snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p; + unsigned char buffer_p[PERIOD*4*4]; + unsigned char buffer_c[PERIOD*4*4]; + + snd_pcm_sw_params_t *swparams_p; + snd_pcm_sw_params_t *swparams_c; + + snd_pcm_uframes_t curr_count_c; + snd_pcm_uframes_t frame_count_c = 0; + snd_pcm_uframes_t curr_count_p; + snd_pcm_uframes_t frame_count_p = 0; + + snd_pcm_sframes_t delay_p, delay_c; + snd_pcm_uframes_t avail_p, avail_c; + + if ((err = snd_pcm_open(&handle_p, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + printf("Playback open error: %s\n", snd_strerror(err)); + goto _exit; + } + if ((err = snd_pcm_set_params(handle_p, + SND_PCM_FORMAT_S16, + SND_PCM_ACCESS_RW_INTERLEAVED, + 2, + 48000, + 0, + 500000)) < 0) { /* 0.5sec */ + printf("Playback open error: %s\n", snd_strerror(err)); + goto _exit; + } + + snd_pcm_sw_params_alloca(&swparams_p); + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle_p, swparams_p); + if (err < 0) { + printf("Unable to determine current swparams_p: %s\n", snd_strerror(err)); + goto _exit; + } + + /* enable tstamp */ + err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE); + if (err < 0) { + printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); + goto _exit; + } + + /* write the sw parameters */ + err = snd_pcm_sw_params(handle_p, swparams_p); + if (err < 0) { + printf("Unable to set swparams_p : %s\n", snd_strerror(err)); + goto _exit; + } + + if ((err = snd_pcm_open(&handle_c, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { + printf("Capture open error: %s\n", snd_strerror(err)); + goto _exit; + } + if ((err = snd_pcm_set_params(handle_c, + SND_PCM_FORMAT_S16, + SND_PCM_ACCESS_RW_INTERLEAVED, + 2, + 48000, + 0, + 500000)) < 0) { /* 0.5sec */ + printf("Capture open error: %s\n", snd_strerror(err)); + goto _exit; + } + + snd_pcm_sw_params_alloca(&swparams_c); + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle_c, swparams_c); + if (err < 0) { + printf("Unable to determine current swparams_c: %s\n", snd_strerror(err)); + goto _exit; + } + + /* enable tstamp */ + err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE); + if (err < 0) { + printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); + goto _exit; + } + + /* write the sw parameters */ + err = snd_pcm_sw_params(handle_c, swparams_c); + if (err < 0) { + printf("Unable to set swparams_c : %s\n", snd_strerror(err)); + goto _exit; + } + +#ifdef PCM_LINK + if ((err = snd_pcm_link(handle_c, handle_p)) < 0) { + printf("Streams link error: %s\n", snd_strerror(err)); + exit(0); + } +#endif + + i = PLAYBACK_BUFFERS; + while (i--) { + frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); + if (frames < 0) { + printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + goto _exit; + } + frame_count_p += frames; + } + + if (PLAYBACK_BUFFERS != 4) + snd_pcm_start(handle_p); + +#ifndef PCM_LINK + /* need to start capture explicitly */ + snd_pcm_start(handle_c); +#endif + + while (1) { + + frames = snd_pcm_wait(handle_c, -1); + if (frames < 0) { + printf("snd_pcm_wait failed: %s\n", snd_strerror(frames)); + goto _exit; + } + + frames = snd_pcm_readi(handle_c, buffer_c, PERIOD); + if (frames < 0) { + printf("snd_pcm_readi failed: %s\n", snd_strerror(frames)); + goto _exit; + } + frame_count_c += frames; + + frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); + if (frames < 0) { + printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + goto _exit; + } + + frame_count_p += frames; + +#if defined(TRACK_PLAYBACK) + gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &avail_p, &delay_p); + + 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) + ); +#endif + +#if defined(TRACK_CAPTURE) + gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &avail_c, &delay_c); + + 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) + ); +#endif + + } + +_exit: + if (handle_p) + snd_pcm_close(handle_p); + if (handle_c) + snd_pcm_close(handle_c); + + return 0; +}