mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
Added time, poll and effect options and code.
This commit is contained in:
parent
78522ea0ea
commit
c580e02348
1 changed files with 200 additions and 39 deletions
239
test/latency.c
239
test/latency.c
|
|
@ -1,3 +1,32 @@
|
|||
/*
|
||||
* Latency test program
|
||||
*
|
||||
* Author: Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* Author of bandpass filtersweep effect:
|
||||
* Maarten de Boer <mdeboer@iua.upf.es>
|
||||
*
|
||||
* This small demo program can be used for measuring latency between
|
||||
* capture and playback. This latency is measured from driver (diff when
|
||||
* playback and capture was started). Scheduler is set to SCHED_RR.
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -6,38 +35,24 @@
|
|||
#include <getopt.h>
|
||||
#include "../include/asoundlib.h"
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
|
||||
char *pdevice = "hw:0,0";
|
||||
char *cdevice = "hw:0,0";
|
||||
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
|
||||
int rate = 22050;
|
||||
int channels = 2;
|
||||
int latency_min = 32; /* in frames */
|
||||
int latency_max = 2048; /* in frames */
|
||||
int latency_min = 32; /* in frames / 2 */
|
||||
int latency_max = 2048; /* in frames / 2 */
|
||||
int loop_sec = 30; /* seconds */
|
||||
int block = 0; /* block mode */
|
||||
int tick_time = 0; /* disabled, otherwise in us */
|
||||
int tick_time_ok = 0;
|
||||
int use_poll = 0;
|
||||
unsigned long loop_limit;
|
||||
|
||||
snd_output_t *output = NULL;
|
||||
|
||||
#if 0
|
||||
static char *xitoa(int aaa)
|
||||
{
|
||||
static char str[12];
|
||||
|
||||
sprintf(str, "%i", aaa);
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This small demo program can be used for measuring latency between
|
||||
* capture and playback. This latency is measured from driver (diff when
|
||||
* playback and capture was started). Scheduler is set to SCHED_RR.
|
||||
*
|
||||
* Used format is 44100Hz, Signed Little Endian 16-bit, Stereo.
|
||||
*/
|
||||
|
||||
int setparams_stream(snd_pcm_t *handle,
|
||||
snd_pcm_hw_params_t *params,
|
||||
const char *id)
|
||||
|
|
@ -105,7 +120,7 @@ int setparams_set(snd_pcm_t *handle,
|
|||
snd_pcm_sw_params_t *swparams,
|
||||
const char *id)
|
||||
{
|
||||
int err;
|
||||
int err, val, sleep_min = 0;
|
||||
|
||||
err = snd_pcm_hw_params(handle, params);
|
||||
if (err < 0) {
|
||||
|
|
@ -122,12 +137,37 @@ int setparams_set(snd_pcm_t *handle,
|
|||
printf("Unable to set start threshold mode for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, !block ? 4 : snd_pcm_hw_params_get_period_size(params, NULL));
|
||||
tick_time_ok = 0;
|
||||
if (tick_time > 0) {
|
||||
int time, ttime;
|
||||
time = snd_pcm_hw_params_get_period_time(params, NULL);
|
||||
ttime = snd_pcm_hw_params_get_tick_time(params, NULL);
|
||||
if (time < ttime) {
|
||||
printf("Skipping to set minimal sleep: period time < tick time\n");
|
||||
} else if (ttime <= 0) {
|
||||
printf("Skipping to set minimal sleep: tick time <= 0 (%i)\n", ttime);
|
||||
} else {
|
||||
sleep_min = tick_time / ttime;
|
||||
if (sleep_min <= 0)
|
||||
sleep_min = 1;
|
||||
err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min);
|
||||
if (err < 0) {
|
||||
printf("Unable to set minimal sleep %i for %s: %s\n", sleep_min, id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
tick_time_ok = sleep_min * ttime;
|
||||
}
|
||||
}
|
||||
val = !block ? 4 : snd_pcm_hw_params_get_period_size(params, NULL);
|
||||
if (tick_time_ok > 0)
|
||||
val = 16;
|
||||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, val);
|
||||
if (err < 0) {
|
||||
printf("Unable to set avail min for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, !block ? 4 : 1);
|
||||
val = !block ? 4 : 1;
|
||||
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, val);
|
||||
if (err < 0) {
|
||||
printf("Unable to set transfer align for %s: %s\n", id, snd_strerror(err));
|
||||
return err;
|
||||
|
|
@ -222,6 +262,23 @@ void showstat(snd_pcm_t *handle, size_t frames)
|
|||
snd_pcm_status_dump(status, output);
|
||||
}
|
||||
|
||||
void showlatency(size_t latency)
|
||||
{
|
||||
double d;
|
||||
latency *= 2;
|
||||
d = (double)latency / (double)rate;
|
||||
printf("Trying latency %li frames, %.3fus, %.6fms (%.4fHz)\n", (long)latency, d * 1000000, d * 1000, (double)1 / d);
|
||||
}
|
||||
|
||||
void showinmax(size_t in_max)
|
||||
{
|
||||
double d;
|
||||
|
||||
printf("Maximum read: %li frames\n", (long)in_max);
|
||||
d = (double)in_max / (double)rate;
|
||||
printf("Maximum read latency: %.3fus, %.6fms (%.4fHz)\n", d * 1000000, d * 1000, (double)1 / d);
|
||||
}
|
||||
|
||||
void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -266,7 +323,7 @@ long timediff(snd_timestamp_t t1, snd_timestamp_t t2)
|
|||
return (t1.tv_sec * 1000000) + l;
|
||||
}
|
||||
|
||||
long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
||||
long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames, size_t *max)
|
||||
{
|
||||
long r;
|
||||
|
||||
|
|
@ -274,8 +331,11 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
|||
do {
|
||||
r = snd_pcm_readi(handle, buf, len);
|
||||
} while (r == -EAGAIN);
|
||||
if (r > 0)
|
||||
if (r > 0) {
|
||||
*frames += r;
|
||||
if (*max < r)
|
||||
*max = r;
|
||||
}
|
||||
// printf("read = %li\n", r);
|
||||
} else {
|
||||
int frame_bytes = (snd_pcm_format_width(format) / 8) * channels;
|
||||
|
|
@ -285,6 +345,8 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
|||
buf += r * frame_bytes;
|
||||
len -= r;
|
||||
*frames += r;
|
||||
if (*max < r)
|
||||
*max = r;
|
||||
}
|
||||
// printf("r = %li, len = %li\n", r, len);
|
||||
} while (r >= 1 && len > 0);
|
||||
|
|
@ -311,6 +373,49 @@ long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FILTERSWEEP_LFO_CENTER 2000.
|
||||
#define FILTERSWEEP_LFO_DEPTH 1800.
|
||||
#define FILTERSWEEP_LFO_FREQ 0.2
|
||||
#define FILTER_BANDWIDTH 50
|
||||
|
||||
/* filter sweep variables */
|
||||
float lfo,dlfo,fs,fc,BW,C,D,a0,a1,a2,b1,b2,*x[3],*y[3];
|
||||
|
||||
void applyeffect(char* buffer,int r)
|
||||
{
|
||||
short* samples = (short*) buffer;
|
||||
int i;
|
||||
for (i=0;i<r;i++)
|
||||
{
|
||||
int chn;
|
||||
|
||||
fc = sin(lfo)*FILTERSWEEP_LFO_DEPTH+FILTERSWEEP_LFO_CENTER;
|
||||
lfo += dlfo;
|
||||
if (lfo>2.*M_PI) lfo -= 2.*M_PI;
|
||||
C = 1./tan(M_PI*BW/fs);
|
||||
D = 2.*cos(2*M_PI*fc/fs);
|
||||
a0 = 1./(1.+C);
|
||||
a1 = 0;
|
||||
a2 = -a0;
|
||||
b1 = -C*D*a0;
|
||||
b2 = (C-1)*a0;
|
||||
|
||||
for (chn=0;chn<channels;chn++)
|
||||
{
|
||||
x[chn][2] = x[chn][1];
|
||||
x[chn][1] = x[chn][0];
|
||||
|
||||
y[chn][2] = y[chn][1];
|
||||
y[chn][1] = y[chn][0];
|
||||
|
||||
x[chn][0] = samples[i*channels+chn];
|
||||
y[chn][0] = a0*x[chn][0] + a1*x[chn][1] + a2*x[chn][2]
|
||||
- b1*y[chn][1] - b2*y[chn][2];
|
||||
samples[i*channels+chn] = y[chn][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void help(void)
|
||||
{
|
||||
|
|
@ -328,6 +433,9 @@ Usage: latency [OPTION]... [FILE]...
|
|||
-r,--rate rate
|
||||
-s,--seconds duration of test in seconds
|
||||
-b,--block block mode
|
||||
-t,--time maximal tick time in us
|
||||
-p,--poll use poll (wait for event - reduces CPU usage)
|
||||
-e,--effect apply an effect (bandpass filtersweep)
|
||||
");
|
||||
printf("Recognized sample formats are:");
|
||||
for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) {
|
||||
|
|
@ -335,7 +443,14 @@ Usage: latency [OPTION]... [FILE]...
|
|||
if (s)
|
||||
printf(" %s", s);
|
||||
}
|
||||
printf("\n");
|
||||
printf("\n\n");
|
||||
printf("\
|
||||
Tip #1 (useable latency with large periods, non-blocking mode, good CPU usage,
|
||||
superb xrun prevention):
|
||||
latency -m 8192 -M 8192 -t 1 -p
|
||||
Tip #2 (superb latency, non-blocking mode, but heavy CPU usage):
|
||||
latency -m 128 -M 128
|
||||
");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
|
@ -353,20 +468,23 @@ int main(int argc, char *argv[])
|
|||
{"rate", 1, NULL, 'r'},
|
||||
{"seconds", 1, NULL, 's'},
|
||||
{"block", 0, NULL, 'b'},
|
||||
{"time", 1, NULL, 't'},
|
||||
{"poll", 0, NULL, 'p'},
|
||||
{"effect", 0, NULL, 'e'},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
snd_pcm_t *phandle, *chandle;
|
||||
char *buffer;
|
||||
int err, latency, morehelp;
|
||||
int size, ok;
|
||||
int ok;
|
||||
snd_timestamp_t p_tstamp, c_tstamp;
|
||||
ssize_t r;
|
||||
size_t frames_in, frames_out;
|
||||
|
||||
size_t frames_in, frames_out, in_max;
|
||||
int effect = 0;
|
||||
morehelp = 0;
|
||||
while (1) {
|
||||
int c;
|
||||
if ((c = getopt_long(argc, argv, "hP:C:m:M:F:f:c:r:s:b", long_option, NULL)) < 0)
|
||||
if ((c = getopt_long(argc, argv, "hP:C:m:M:F:f:c:r:s:bt:pe", long_option, NULL)) < 0)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
|
|
@ -379,13 +497,13 @@ int main(int argc, char *argv[])
|
|||
cdevice = strdup(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
err = atoi(optarg);
|
||||
err = atoi(optarg) / 2;
|
||||
latency_min = err >= 4 ? err : 4;
|
||||
if (latency_max < latency_min)
|
||||
latency_max = latency_min;
|
||||
break;
|
||||
case 'M':
|
||||
err = atoi(optarg);
|
||||
err = atoi(optarg) / 2;
|
||||
latency_max = latency_min > err ? latency_min : err;
|
||||
break;
|
||||
case 'F':
|
||||
|
|
@ -410,6 +528,16 @@ int main(int argc, char *argv[])
|
|||
case 'b':
|
||||
block = 1;
|
||||
break;
|
||||
case 't':
|
||||
tick_time = atoi(optarg);
|
||||
tick_time = tick_time < 0 ? 0 : tick_time;
|
||||
break;
|
||||
case 'p':
|
||||
use_poll = 1;
|
||||
break;
|
||||
case 'e':
|
||||
effect = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -432,7 +560,8 @@ int main(int argc, char *argv[])
|
|||
printf("Playback device is %s\n", pdevice);
|
||||
printf("Capture device is %s\n", cdevice);
|
||||
printf("Parameters are %iHz, %s, %i channels, %s mode\n", rate, snd_pcm_format_name(format), channels, block ? "blocking" : "non-blocking");
|
||||
printf("Loop limit is %li frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min, latency_max);
|
||||
printf("Wanted tick time: %ius, poll mode: %s\n", tick_time, use_poll ? "yes" : "no");
|
||||
printf("Loop limit is %li frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2);
|
||||
|
||||
if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
|
||||
printf("Playback open error: %s\n", snd_strerror(err));
|
||||
|
|
@ -442,12 +571,30 @@ int main(int argc, char *argv[])
|
|||
printf("Record open error: %s\n", snd_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* initialize filter sweep variables */
|
||||
if (effect) {
|
||||
fs = (float) rate;
|
||||
BW = FILTER_BANDWIDTH;
|
||||
|
||||
lfo = 0;
|
||||
dlfo = 2.*M_PI*FILTERSWEEP_LFO_FREQ/fs;
|
||||
|
||||
x[0] = (float*) malloc(channels*sizeof(float));
|
||||
x[1] = (float*) malloc(channels*sizeof(float));
|
||||
x[2] = (float*) malloc(channels*sizeof(float));
|
||||
y[0] = (float*) malloc(channels*sizeof(float));
|
||||
y[1] = (float*) malloc(channels*sizeof(float));
|
||||
y[2] = (float*) malloc(channels*sizeof(float));
|
||||
}
|
||||
|
||||
while (1) {
|
||||
frames_in = frames_out = 0;
|
||||
if (setparams(phandle, chandle, &latency) < 0)
|
||||
break;
|
||||
printf("Trying latency %i frames...\n", latency);
|
||||
showlatency(latency);
|
||||
if (tick_time_ok)
|
||||
printf("Using tick time %ius\n", tick_time_ok);
|
||||
if ((err = snd_pcm_link(chandle, phandle)) < 0) {
|
||||
printf("Streams link error: %s\n", snd_strerror(err));
|
||||
exit(0);
|
||||
|
|
@ -477,18 +624,32 @@ int main(int argc, char *argv[])
|
|||
printf("Capture:\n");
|
||||
showstat(chandle, frames_in);
|
||||
#endif
|
||||
|
||||
ok = 1;
|
||||
size = 0;
|
||||
in_max = 0;
|
||||
while (ok && frames_in < loop_limit) {
|
||||
if ((r = readbuf(chandle, buffer, latency, &frames_in)) < 0)
|
||||
ok = 0;
|
||||
else if (writebuf(phandle, buffer, r, &frames_out) < 0)
|
||||
if (use_poll) {
|
||||
/* use poll to wait for next event */
|
||||
snd_pcm_wait(chandle, 1000);
|
||||
}
|
||||
if ((r = readbuf(chandle, buffer, latency, &frames_in, &in_max)) < 0)
|
||||
ok = 0;
|
||||
else {
|
||||
if (effect)
|
||||
applyeffect(buffer,r);
|
||||
if (writebuf(phandle, buffer, r, &frames_out) < 0)
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
printf("Success\n");
|
||||
else
|
||||
printf("Failure\n");
|
||||
printf("Playback:\n");
|
||||
showstat(phandle, frames_out);
|
||||
printf("Capture:\n");
|
||||
showstat(chandle, frames_in);
|
||||
showinmax(in_max);
|
||||
if (p_tstamp.tv_sec == p_tstamp.tv_sec &&
|
||||
p_tstamp.tv_usec == c_tstamp.tv_usec)
|
||||
printf("Hardware sync\n");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue