From 3bb1db2e17ef82cac9877b7d7835137aa926ce8c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 27 Nov 1998 00:13:09 +0000 Subject: [PATCH] Added latency example... --- test/Makefile.am | 3 +- test/latency.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 test/latency.c diff --git a/test/Makefile.am b/test/Makefile.am index d3e5d6e7..dcfa1204 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,10 +1,11 @@ -check_PROGRAMS=control mixer switches pause pcm +check_PROGRAMS=control mixer switches pause pcm latency control_LDADD=../src/libasound.la mixer_LDADD=../src/libasound.la switches_LDADD=../src/libasound.la pause_LDADD=../src/libasound.la pcm_LDADD=../src/libasound.la +latency_LDADD=../src/libasound.la INCLUDES=-I$(top_srcdir)/include diff --git a/test/latency.c b/test/latency.c new file mode 100644 index 00000000..de0853f1 --- /dev/null +++ b/test/latency.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include "../include/asoundlib.h" + +static char *xitoa(int aaa) +{ + static char str[12]; + + sprintf(str, "%i", aaa); + return str; +} + +/* + * This small demo program can be used for measuring latency between + * record and playback. This latency is measured from driver (diff when + * playback and record was started). Scheduler is set to SCHED_RR. + * + * Used format is 44100Hz, Signed Little Endian 16-bit, Stereo. + * + * Program begins with 128-byte fragment (about 726us) and step is 32-byte + * until playback without underruns is reached. This program starts playback + * after two fragments are recorded (teoretical latency is 1452us by 128-byte + * fragment). + */ + +void setformat(void *phandle, void *rhandle) +{ + int err; + snd_pcm_format_t format; + + bzero(&format, sizeof(format)); + format.format = SND_PCM_SFMT_S16_LE; + format.channels = 2; + format.rate = 44100; + if ((err = snd_pcm_playback_format(phandle, &format)) < 0) { + printf("Playback format error: %s\n", snd_strerror(err)); + exit(0); + } + if ((err = snd_pcm_record_format(rhandle, &format)) < 0) { + printf("Record format error: %s\n", snd_strerror(err)); + exit(0); + } + if ((err = snd_pcm_playback_time(phandle, 1)) < 0) { + printf("Playback time error: %s\n", snd_strerror(err)); + exit(0); + } + if ((err = snd_pcm_record_time(rhandle, 1)) < 0) { + printf("Record time error: %s\n", snd_strerror(err)); + exit(0); + } +} + +int setparams(void *phandle, void *rhandle, int *fragmentsize) +{ + int err, step = 4; + snd_pcm_playback_info_t pinfo; + snd_pcm_record_info_t rinfo; + snd_pcm_playback_params_t pparams; + snd_pcm_record_params_t rparams; + + if ((err = snd_pcm_playback_info(phandle, &pinfo)) < 0) { + printf("Playback info error: %s\n", snd_strerror(err)); + exit(0); + } + if ((err = snd_pcm_record_info(rhandle, &rinfo)) < 0) { + printf("Record info error: %s\n", snd_strerror(err)); + exit(0); + } + if (step < pinfo.min_fragment_size) + step = pinfo.min_fragment_size; + if (step < rinfo.min_fragment_size) + step = rinfo.min_fragment_size; + while (step < 4096 && + (step & pinfo.fragment_align) != 0 && + (step & rinfo.fragment_align) != 0) step++; + if (*fragmentsize) { + *fragmentsize += step; + } else { + if (step < 128) + *fragmentsize = 128; + else + *fragmentsize = step; + } + while (*fragmentsize < 4096) { + bzero(&pparams, sizeof(pparams)); + pparams.fragment_size = *fragmentsize; + pparams.fragments_max = -1; /* maximum */ + pparams.fragments_room = 1; + if ((err = snd_pcm_playback_params(phandle, &pparams)) < 0) { + *fragmentsize += step; + continue; + } + bzero(&rparams, sizeof(rparams)); + rparams.fragment_size = *fragmentsize; + rparams.fragments_min = 1; /* wakeup if at least one fragment is ready */ + if ((err = snd_pcm_record_params(rhandle, &rparams)) < 0) { + *fragmentsize += step; + continue; + } + break; + } + if (*fragmentsize < 4096) { + printf("Trying fragment size %i...\n", *fragmentsize); + fflush(stdout); + return 0; + } + return -1; +} + +int playbackunderrun(void *phandle) +{ + int err; + snd_pcm_playback_status_t pstatus; + + if ((err = snd_pcm_playback_status(phandle, &pstatus)) < 0) { + printf("Playback status error: %s\n", snd_strerror(err)); + exit(0); + } + return pstatus.underrun; +} + +void recordfragment(void *rhandle, char *buffer, int index, int fragmentsize) +{ + int err; + + buffer += index * fragmentsize; + if ((err = snd_pcm_read(rhandle, buffer, fragmentsize)) != fragmentsize) { + printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); + exit(0); + } +} + +void playfragment(void *phandle, char *buffer, int index, int fragmentsize) +{ + int err; + + buffer += index * fragmentsize; + if ((err = snd_pcm_write(phandle, buffer, fragmentsize)) != fragmentsize) { + printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); + exit(0); + } +} + +void setscheduler(void) +{ + struct sched_param sched_param; + + if (sched_getparam(0, &sched_param)<0) { + printf("Scheduler getparam failed...\n"); + return; + } + sched_param.sched_priority = sched_get_priority_max(SCHED_RR); + if (!sched_setscheduler(0, SCHED_RR, &sched_param)) { + printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority); + fflush(stdout); + return; + } + printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority); +} + +long timediff(struct timeval t1, struct timeval t2) +{ + signed long l; + + t1.tv_sec -= t2.tv_sec; + l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if (l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return (t1.tv_sec * 1000000) + l; +} + +void main(void) +{ + void *phandle, *rhandle; + char buffer[4096 * 2]; /* max two fragments by 4096 bytes */ + int pcard = 0, pdevice = 0; + int rcard = 0, rdevice = 0; + int err, fragmentsize = 0; + int ridx, pidx, size, ok; + snd_pcm_playback_status_t pstatus; + snd_pcm_record_status_t rstatus; + + setscheduler(); + if ((err = snd_pcm_open(&phandle, pcard, pdevice, SND_PCM_OPEN_PLAYBACK)) < 0) { + printf("Playback open error: %s\n", snd_strerror(err)); + return; + } + if ((err = snd_pcm_open(&rhandle, rcard, rdevice, SND_PCM_OPEN_RECORD)) < 0) { + printf( "Record open error: %s\n", snd_strerror(err)); + return; + } + setformat(phandle, rhandle); + while (1) { + if (setparams(phandle, rhandle, &fragmentsize)<0) + break; + ok = 1; + ridx = pidx = size = 0; + recordfragment(rhandle, buffer, ridx++, fragmentsize); + recordfragment(rhandle, buffer, ridx++, fragmentsize); + if ((err = snd_pcm_record_status(rhandle, &rstatus)) < 0) { + printf("Record status error: %s\n", snd_strerror(err)); + exit(0); + } + ridx %= 2; + playfragment(phandle, buffer, 0, 2 * fragmentsize); + size += 2 * fragmentsize; + pidx += 2; + pidx %= 2; + while (ok && size < 3 * 176400) { /* 30 seconds */ + recordfragment(rhandle, buffer, ridx++, fragmentsize); + ridx %= 2; + playfragment(phandle, buffer, pidx++, fragmentsize); + size += fragmentsize; + pidx %= 2; + if (playbackunderrun(phandle)>0) + ok = 0; + } + if ((err = snd_pcm_playback_status(phandle, &pstatus)) < 0) { + printf("Playback status error: %s\n", snd_strerror(err)); + exit(0); + } + snd_pcm_flush_record(rhandle); + snd_pcm_flush_playback(phandle); + if (ok && !playbackunderrun(phandle)>0) { + printf("Playback OK!!!\n"); + printf("Playback time = %li.%i, Record time = %li.%i, diff = %li\n", + pstatus.stime.tv_sec, + pstatus.stime.tv_usec, + rstatus.stime.tv_sec, + rstatus.stime.tv_usec, + timediff(pstatus.stime, rstatus.stime)); + break; + } + } + snd_pcm_close(phandle); + snd_pcm_close(rhandle); +} -- 2.47.1