[alsa-devel] [PATCH] test: add audio_time

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Sat Sep 22 03:05:18 CEST 2012


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 at 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;
+}
-- 
1.7.9.5



More information about the Alsa-devel mailing list