/test/latency.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#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;
int latency_max = 2048;
int loop_sec = 30;
int block = 0;
int tick_time = 0;
int tick_time_ok = 0;
int use_poll = 0;
unsigned long loop_limit;
snd_output_t *output = NULL;
int setparams_stream(
snd_pcm_t *handle,
snd_pcm_hw_params_t *params,
const char *
id)
{
int err;
unsigned int rrate;
err =
snd_pcm_hw_params_any(handle, params);
if (err < 0) {
printf(
"Broken configuration for %s PCM: no configurations available: %s\n",
snd_strerror(err),
id);
return err;
}
err =
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
printf(
"Access type not available for %s: %s\n",
id,
snd_strerror(err));
return err;
}
err =
snd_pcm_hw_params_set_format(handle, params, format);
if (err < 0) {
printf(
"Sample format not available for %s: %s\n",
id,
snd_strerror(err));
return err;
}
err =
snd_pcm_hw_params_set_channels(handle, params, channels);
if (err < 0) {
printf(
"Channels count (%i) not available for %s: %s\n", channels,
id,
snd_strerror(err));
return err;
}
rrate = rate;
err =
snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
if (err < 0) {
printf(
"Rate %iHz not available for %s: %s\n", rate,
id,
snd_strerror(err));
return err;
}
if (rrate != rate) {
printf(
"Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
return -EINVAL;
}
return 0;
}
int setparams_bufsize(snd_pcm_t *handle,
snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *tparams,
snd_pcm_uframes_t bufsize,
const char *
id)
{
int err;
snd_pcm_uframes_t periodsize;
snd_pcm_hw_params_copy(params, tparams);
periodsize = bufsize * 2;
err =
snd_pcm_hw_params_set_buffer_size_near(handle, params, &periodsize);
if (err < 0) {
printf(
"Unable to set buffer size %li for %s: %s\n", bufsize * 2,
id,
snd_strerror(err));
return err;
}
periodsize /= 2;
err =
snd_pcm_hw_params_set_period_size_near(handle, params, &periodsize, 0);
if (err < 0) {
printf(
"Unable to set period size %li for %s: %s\n", periodsize,
id,
snd_strerror(err));
return err;
}
return 0;
}
int setparams_set(snd_pcm_t *handle,
snd_pcm_hw_params_t *params,
snd_pcm_sw_params_t *swparams,
const char *
id)
{
int err;
snd_pcm_uframes_t val;
unsigned int sleep_min = 0;
err =
snd_pcm_hw_params(handle, params);
if (err < 0) {
printf(
"Unable to set hw params for %s: %s\n",
id,
snd_strerror(err));
return err;
}
err =
snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
printf(
"Unable to determine current swparams for %s: %s\n",
id,
snd_strerror(err));
return err;
}
err =
snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
if (err < 0) {
printf(
"Unable to set start threshold mode for %s: %s\n",
id,
snd_strerror(err));
return err;
}
tick_time_ok = 0;
if (tick_time > 0) {
int time, ttime;
snd_pcm_hw_params_get_period_time(params, &time, NULL);
snd_pcm_hw_params_get_tick_time(params, &ttime, 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;
}
}
if (!block)
val = 4;
else
snd_pcm_hw_params_get_period_size(params, &val, 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;
}
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;
}
err =
snd_pcm_sw_params(handle, swparams);
if (err < 0) {
printf(
"Unable to set sw params for %s: %s\n",
id,
snd_strerror(err));
return err;
}
return 0;
}
int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
int *bufsize)
{
int err, last_bufsize = *bufsize;
snd_pcm_hw_params_t *pt_params, *ct_params;
snd_pcm_hw_params_t *p_params, *c_params;
snd_pcm_sw_params_t *p_swparams, *c_swparams;
snd_pcm_uframes_t size, p_size, c_size, p_psize, c_psize;
unsigned int p_time, c_time;
snd_pcm_hw_params_alloca(&p_params);
snd_pcm_hw_params_alloca(&c_params);
snd_pcm_hw_params_alloca(&pt_params);
snd_pcm_hw_params_alloca(&ct_params);
snd_pcm_sw_params_alloca(&p_swparams);
snd_pcm_sw_params_alloca(&c_swparams);
if ((err = setparams_stream(phandle, pt_params,
"playback")) < 0) {
printf(
"Unable to set parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
if ((err = setparams_stream(chandle, ct_params,
"capture")) < 0) {
printf(
"Unable to set parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
__again:
if (last_bufsize == *bufsize)
*bufsize += 4;
last_bufsize = *bufsize;
if (*bufsize > latency_max)
return -1;
if ((err = setparams_bufsize(phandle, p_params, pt_params, *bufsize,
"playback")) < 0) {
printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
if ((err = setparams_bufsize(chandle, c_params, ct_params, *bufsize,
"capture")) < 0) {
printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
snd_pcm_hw_params_get_period_size(p_params, &size, NULL);
if (size > *bufsize)
*bufsize = size;
snd_pcm_hw_params_get_period_size(c_params, &size, NULL);
if (size > *bufsize)
*bufsize = size;
snd_pcm_hw_params_get_period_time(p_params, &p_time, NULL);
snd_pcm_hw_params_get_period_time(c_params, &c_time, NULL);
if (p_time != c_time)
goto __again;
snd_pcm_hw_params_get_period_size(p_params, &p_psize, NULL);
snd_pcm_hw_params_get_buffer_size(p_params, &p_size);
if (p_psize * 2 < p_size)
goto __again;
snd_pcm_hw_params_get_period_size(c_params, &c_psize, NULL);
snd_pcm_hw_params_get_buffer_size(c_params, &c_size);
if (c_psize * 2 < c_size)
goto __again;
if ((err = setparams_set(phandle, p_params, p_swparams,
"playback")) < 0) {
printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
if ((err = setparams_set(chandle, c_params, c_swparams,
"capture")) < 0) {
printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
exit(0);
}
if ((err =
snd_pcm_prepare(phandle)) < 0) {
printf(
"Prepare error: %s\n",
snd_strerror(err));
exit(0);
}
snd_pcm_dump(phandle, output);
snd_pcm_dump(chandle, output);
fflush(stdout);
return 0;
}
void showstat(snd_pcm_t *handle, size_t frames)
{
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);
}
printf(
"*** frames = %li ***\n", (
long)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;
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_tstamp(status, timestamp);
}
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(snd_timestamp_t t1, snd_timestamp_t 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;
}
long readbuf(snd_pcm_t *handle,
char *buf,
long len, size_t *frames, size_t *max)
{
long r;
if (!block) {
do {
r =
snd_pcm_readi(handle, buf, len);
}
while (r == -EAGAIN);
if (r > 0) {
*frames += r;
if (*max < r)
*max = r;
}
}
else {
int frame_bytes = (
snd_pcm_format_width(format) / 8) * channels;
do {
r =
snd_pcm_readi(handle, buf, len);
if (r > 0) {
buf += r * frame_bytes;
len -= r;
*frames += r;
if (*max < r)
*max = r;
}
}
while (r >= 1 && len > 0);
}
return r;
}
long writebuf(snd_pcm_t *handle,
char *buf,
long len, size_t *frames)
{
long r;
while (len > 0) {
r =
snd_pcm_writei(handle, buf, len);
if (r == -EAGAIN)
continue;
if (r < 0)
return r;
buf += r * 4;
len -= r;
*frames += r;
}
return 0;
}
#define FILTERSWEEP_LFO_CENTER 2000.
#define FILTERSWEEP_LFO_DEPTH 1800.
#define FILTERSWEEP_LFO_FREQ 0.2
#define FILTER_BANDWIDTH 50
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)
{
int k;
printf(
"Usage: latency [OPTION]... [FILE]...\n"
"-h,--help help\n"
"-P,--pdevice playback device\n"
"-C,--cdevice capture device\n"
"-m,--min minimum latency in frames\n"
"-M,--max maximum latency in frames\n"
"-F,--frames frames to transfer\n"
"-f,--format sample format\n"
"-c,--channels channels\n"
"-r,--rate rate\n"
"-s,--seconds duration of test in seconds\n"
"-b,--block block mode\n"
"-t,--time maximal tick time in us\n"
"-p,--poll use poll (wait for event - reduces CPU usage)\n"
"-e,--effect apply an effect (bandpass filter sweep)\n"
);
printf(
"Recognized sample formats are:");
for (k = 0; k < SND_PCM_FORMAT_LAST; ++(
unsigned long) k) {
const char *s =
snd_pcm_format_name(k);
if (s)
printf(
" %s", s);
}
printf(
"\n\n");
printf(
"Tip #1 (usable latency with large periods, non-blocking mode, good CPU usage,\n"
" superb xrun prevention):\n"
" latency -m 8192 -M 8192 -t 1 -p\n"
"Tip #2 (superb latency, non-blocking mode, but heavy CPU usage):\n"
" latency -m 128 -M 128\n"
);
}
int main(
int argc,
char *argv[])
{
struct option long_option[] =
{
{
"help", 0, NULL,
'h'},
{
"pdevice", 1, NULL,
'P'},
{
"cdevice", 1, NULL,
'C'},
{
"min", 1, NULL,
'm'},
{
"max", 1, NULL,
'M'},
{
"frames", 1, NULL,
'F'},
{
"format", 1, NULL,
'f'},
{
"channels", 1, NULL,
'c'},
{
"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 ok;
snd_timestamp_t p_tstamp, c_tstamp;
ssize_t r;
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:bt:pe", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
morehelp++;
break;
case 'P':
pdevice = strdup(optarg);
break;
case 'C':
cdevice = strdup(optarg);
break;
case 'm':
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) / 2;
latency_max = latency_min > err ? latency_min : err;
break;
case 'f':
format =
snd_pcm_format_value(optarg);
if (format ==
SND_PCM_FORMAT_UNKNOWN) {
printf(
"Unknown format, setting to default S16_LE\n");
format =
SND_PCM_FORMAT_S16_LE;
}
break;
case 'c':
err = atoi(optarg);
channels = err >= 1 && err < 1024 ? err : 1;
break;
case 'r':
err = atoi(optarg);
rate = err >= 4000 && err < 200000 ? err : 44100;
break;
case 's':
err = atoi(optarg);
loop_sec = err >= 1 && err <= 100000 ? err : 30;
break;
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;
}
}
if (morehelp) {
help();
return 0;
}
err =
snd_output_stdio_attach(&output, stdout, 0);
if (err < 0) {
printf(
"Output failed: %s\n",
snd_strerror(err));
return 0;
}
loop_limit = loop_sec * rate;
latency = latency_min - 4;
buffer = malloc((latency_max *
snd_pcm_format_width(format) / 8) * 2);
setscheduler();
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(
"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));
return 0;
}
if ((err =
snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
printf(
"Record open error: %s\n",
snd_strerror(err));
return 0;
}
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;
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);
}
if (
snd_pcm_format_set_silence(format, buffer, latency*channels) < 0) {
fprintf(stderr,
"silence error\n");
break;
}
if (writebuf(phandle, buffer, latency, &frames_out) < 0) {
fprintf(stderr,
"write error\n");
break;
}
if (writebuf(phandle, buffer, latency, &frames_out) < 0) {
fprintf(stderr,
"write error\n");
break;
}
if ((err =
snd_pcm_start(chandle)) < 0) {
printf(
"Go error: %s\n",
snd_strerror(err));
exit(0);
}
gettimestamp(phandle, &p_tstamp);
gettimestamp(chandle, &c_tstamp);
#if 0
printf(
"Playback:\n");
showstat(phandle, frames_out);
printf(
"Capture:\n");
showstat(chandle, frames_in);
#endif
ok = 1;
in_max = 0;
while (ok && frames_in < loop_limit) {
if (use_poll) {
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");
snd_pcm_drop(chandle);
snd_pcm_nonblock(phandle, 0);
snd_pcm_drain(phandle);
snd_pcm_nonblock(phandle, !block ? 1 : 0);
if (ok) {
#if 1
printf(
"Playback time = %li.%i, Record time = %li.%i, diff = %li\n",
p_tstamp.tv_sec,
(
int)p_tstamp.tv_usec,
c_tstamp.tv_sec,
(
int)c_tstamp.tv_usec,
timediff(p_tstamp, c_tstamp));
#endif
break;
}
snd_pcm_unlink(chandle);
snd_pcm_hw_free(phandle);
snd_pcm_hw_free(chandle);
}
snd_pcm_close(phandle);
snd_pcm_close(chandle);
return 0;
}
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
#include <stdio.h>
00031
#include <stdlib.h>
00032
#include <string.h>
00033
#include <sched.h>
00034
#include <errno.h>
00035
#include <getopt.h>
00036
#include "../include/asoundlib.h"
00037
#include <sys/time.h>
00038
#include <math.h>
00039
00040
char *pdevice =
"hw:0,0";
00041
char *cdevice =
"hw:0,0";
00042
snd_pcm_format_t format =
SND_PCM_FORMAT_S16_LE;
00043
int rate = 22050;
00044
int channels = 2;
00045
int latency_min = 32;
00046
int latency_max = 2048;
00047
int loop_sec = 30;
00048
int block = 0;
00049
int tick_time = 0;
00050
int tick_time_ok = 0;
00051
int use_poll = 0;
00052
unsigned long loop_limit;
00053
00054
snd_output_t *output = NULL;
00055
00056
int setparams_stream(snd_pcm_t *handle,
00057 snd_pcm_hw_params_t *params,
00058
const char *
id)
00059 {
00060
int err;
00061
unsigned int rrate;
00062
00063 err =
snd_pcm_hw_params_any(handle, params);
00064
if (err < 0) {
00065 printf(
"Broken configuration for %s PCM: no configurations available: %s\n",
snd_strerror(err),
id);
00066
return err;
00067 }
00068 err =
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
00069
if (err < 0) {
00070 printf(
"Access type not available for %s: %s\n",
id,
snd_strerror(err));
00071
return err;
00072 }
00073 err =
snd_pcm_hw_params_set_format(handle, params, format);
00074
if (err < 0) {
00075 printf(
"Sample format not available for %s: %s\n",
id,
snd_strerror(err));
00076
return err;
00077 }
00078 err =
snd_pcm_hw_params_set_channels(handle, params, channels);
00079
if (err < 0) {
00080 printf(
"Channels count (%i) not available for %s: %s\n", channels,
id,
snd_strerror(err));
00081
return err;
00082 }
00083 rrate = rate;
00084 err =
snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
00085
if (err < 0) {
00086 printf(
"Rate %iHz not available for %s: %s\n", rate,
id,
snd_strerror(err));
00087
return err;
00088 }
00089
if (rrate != rate) {
00090 printf(
"Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
00091
return -EINVAL;
00092 }
00093
return 0;
00094 }
00095
00096
int setparams_bufsize(snd_pcm_t *handle,
00097 snd_pcm_hw_params_t *params,
00098 snd_pcm_hw_params_t *tparams,
00099 snd_pcm_uframes_t bufsize,
00100
const char *
id)
00101 {
00102
int err;
00103
snd_pcm_uframes_t periodsize;
00104
00105
snd_pcm_hw_params_copy(params, tparams);
00106 periodsize = bufsize * 2;
00107 err =
snd_pcm_hw_params_set_buffer_size_near(handle, params, &periodsize);
00108
if (err < 0) {
00109 printf(
"Unable to set buffer size %li for %s: %s\n", bufsize * 2,
id,
snd_strerror(err));
00110
return err;
00111 }
00112 periodsize /= 2;
00113 err =
snd_pcm_hw_params_set_period_size_near(handle, params, &periodsize, 0);
00114
if (err < 0) {
00115 printf(
"Unable to set period size %li for %s: %s\n", periodsize,
id,
snd_strerror(err));
00116
return err;
00117 }
00118
return 0;
00119 }
00120
00121
int setparams_set(snd_pcm_t *handle,
00122 snd_pcm_hw_params_t *params,
00123 snd_pcm_sw_params_t *swparams,
00124
const char *
id)
00125 {
00126
int err;
00127
snd_pcm_uframes_t val;
00128
unsigned int sleep_min = 0;
00129
00130 err =
snd_pcm_hw_params(handle, params);
00131
if (err < 0) {
00132 printf(
"Unable to set hw params for %s: %s\n",
id,
snd_strerror(err));
00133
return err;
00134 }
00135 err =
snd_pcm_sw_params_current(handle, swparams);
00136
if (err < 0) {
00137 printf(
"Unable to determine current swparams for %s: %s\n",
id,
snd_strerror(err));
00138
return err;
00139 }
00140 err =
snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
00141
if (err < 0) {
00142 printf(
"Unable to set start threshold mode for %s: %s\n",
id,
snd_strerror(err));
00143
return err;
00144 }
00145 tick_time_ok = 0;
00146
if (tick_time > 0) {
00147
int time, ttime;
00148
snd_pcm_hw_params_get_period_time(params, &time, NULL);
00149
snd_pcm_hw_params_get_tick_time(params, &ttime, NULL);
00150
if (time < ttime) {
00151 printf(
"Skipping to set minimal sleep: period time < tick time\n");
00152 }
else if (ttime <= 0) {
00153 printf(
"Skipping to set minimal sleep: tick time <= 0 (%i)\n", ttime);
00154 }
else {
00155 sleep_min = tick_time / ttime;
00156
if (sleep_min <= 0)
00157 sleep_min = 1;
00158 err =
snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min);
00159
if (err < 0) {
00160 printf(
"Unable to set minimal sleep %i for %s: %s\n", sleep_min,
id,
snd_strerror(err));
00161
return err;
00162 }
00163 tick_time_ok = sleep_min * ttime;
00164 }
00165 }
00166
if (!block)
00167 val = 4;
00168
else
00169
snd_pcm_hw_params_get_period_size(params, &val, NULL);
00170
if (tick_time_ok > 0)
00171 val = 16;
00172 err =
snd_pcm_sw_params_set_avail_min(handle, swparams, val);
00173
if (err < 0) {
00174 printf(
"Unable to set avail min for %s: %s\n",
id,
snd_strerror(err));
00175
return err;
00176 }
00177 val = !block ? 4 : 1;
00178 err =
snd_pcm_sw_params_set_xfer_align(handle, swparams, val);
00179
if (err < 0) {
00180 printf(
"Unable to set transfer align for %s: %s\n",
id,
snd_strerror(err));
00181
return err;
00182 }
00183 err =
snd_pcm_sw_params(handle, swparams);
00184
if (err < 0) {
00185 printf(
"Unable to set sw params for %s: %s\n",
id,
snd_strerror(err));
00186
return err;
00187 }
00188
return 0;
00189 }
00190
00191
int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
int *bufsize)
00192 {
00193
int err, last_bufsize = *bufsize;
00194
snd_pcm_hw_params_t *pt_params, *ct_params;
00195
snd_pcm_hw_params_t *p_params, *c_params;
00196
snd_pcm_sw_params_t *p_swparams, *c_swparams;
00197
snd_pcm_uframes_t size, p_size, c_size, p_psize, c_psize;
00198
unsigned int p_time, c_time;
00199
00200
snd_pcm_hw_params_alloca(&p_params);
00201
snd_pcm_hw_params_alloca(&c_params);
00202
snd_pcm_hw_params_alloca(&pt_params);
00203
snd_pcm_hw_params_alloca(&ct_params);
00204
snd_pcm_sw_params_alloca(&p_swparams);
00205
snd_pcm_sw_params_alloca(&c_swparams);
00206
if ((err = setparams_stream(phandle, pt_params,
"playback")) < 0) {
00207 printf(
"Unable to set parameters for playback stream: %s\n",
snd_strerror(err));
00208 exit(0);
00209 }
00210
if ((err = setparams_stream(chandle, ct_params,
"capture")) < 0) {
00211 printf(
"Unable to set parameters for playback stream: %s\n",
snd_strerror(err));
00212 exit(0);
00213 }
00214
00215 __again:
00216
if (last_bufsize == *bufsize)
00217 *bufsize += 4;
00218 last_bufsize = *bufsize;
00219
if (*bufsize > latency_max)
00220
return -1;
00221
if ((err = setparams_bufsize(phandle, p_params, pt_params, *bufsize,
"playback")) < 0) {
00222 printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
00223 exit(0);
00224 }
00225
if ((err = setparams_bufsize(chandle, c_params, ct_params, *bufsize,
"capture")) < 0) {
00226 printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
00227 exit(0);
00228 }
00229
00230
snd_pcm_hw_params_get_period_size(p_params, &size, NULL);
00231
if (size > *bufsize)
00232 *bufsize = size;
00233
snd_pcm_hw_params_get_period_size(c_params, &size, NULL);
00234
if (size > *bufsize)
00235 *bufsize = size;
00236
snd_pcm_hw_params_get_period_time(p_params, &p_time, NULL);
00237
snd_pcm_hw_params_get_period_time(c_params, &c_time, NULL);
00238
if (p_time != c_time)
00239
goto __again;
00240
00241
snd_pcm_hw_params_get_period_size(p_params, &p_psize, NULL);
00242
snd_pcm_hw_params_get_buffer_size(p_params, &p_size);
00243
if (p_psize * 2 < p_size)
00244
goto __again;
00245
snd_pcm_hw_params_get_period_size(c_params, &c_psize, NULL);
00246
snd_pcm_hw_params_get_buffer_size(c_params, &c_size);
00247
if (c_psize * 2 < c_size)
00248
goto __again;
00249
00250
if ((err = setparams_set(phandle, p_params, p_swparams,
"playback")) < 0) {
00251 printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
00252 exit(0);
00253 }
00254
if ((err = setparams_set(chandle, c_params, c_swparams,
"capture")) < 0) {
00255 printf(
"Unable to set sw parameters for playback stream: %s\n",
snd_strerror(err));
00256 exit(0);
00257 }
00258
00259
if ((err =
snd_pcm_prepare(phandle)) < 0) {
00260 printf(
"Prepare error: %s\n",
snd_strerror(err));
00261 exit(0);
00262 }
00263
00264
snd_pcm_dump(phandle, output);
00265
snd_pcm_dump(chandle, output);
00266 fflush(stdout);
00267
return 0;
00268 }
00269
00270
void showstat(snd_pcm_t *handle, size_t frames)
00271 {
00272
int err;
00273
snd_pcm_status_t *status;
00274
00275
snd_pcm_status_alloca(&status);
00276
if ((err =
snd_pcm_status(handle, status)) < 0) {
00277 printf(
"Stream status error: %s\n",
snd_strerror(err));
00278 exit(0);
00279 }
00280 printf(
"*** frames = %li ***\n", (
long)frames);
00281
snd_pcm_status_dump(status, output);
00282 }
00283
00284
void showlatency(size_t latency)
00285 {
00286
double d;
00287 latency *= 2;
00288 d = (
double)latency / (
double)rate;
00289 printf(
"Trying latency %li frames, %.3fus, %.6fms (%.4fHz)\n", (
long)latency, d * 1000000, d * 1000, (
double)1 / d);
00290 }
00291
00292
void showinmax(size_t in_max)
00293 {
00294
double d;
00295
00296 printf(
"Maximum read: %li frames\n", (
long)in_max);
00297 d = (
double)in_max / (
double)rate;
00298 printf(
"Maximum read latency: %.3fus, %.6fms (%.4fHz)\n", d * 1000000, d * 1000, (
double)1 / d);
00299 }
00300
00301
void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp)
00302 {
00303
int err;
00304
snd_pcm_status_t *status;
00305
00306
snd_pcm_status_alloca(&status);
00307
if ((err =
snd_pcm_status(handle, status)) < 0) {
00308 printf(
"Stream status error: %s\n",
snd_strerror(err));
00309 exit(0);
00310 }
00311
snd_pcm_status_get_trigger_tstamp(status, timestamp);
00312 }
00313
00314
void setscheduler(
void)
00315 {
00316
struct sched_param sched_param;
00317
00318
if (sched_getparam(0, &sched_param) < 0) {
00319 printf(
"Scheduler getparam failed...\n");
00320
return;
00321 }
00322 sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
00323
if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
00324 printf(
"Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority);
00325 fflush(stdout);
00326
return;
00327 }
00328 printf(
"!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority);
00329 }
00330
00331
long timediff(snd_timestamp_t t1, snd_timestamp_t t2)
00332 {
00333
signed long l;
00334
00335 t1.tv_sec -= t2.tv_sec;
00336 l = (
signed long) t1.tv_usec - (
signed long) t2.tv_usec;
00337
if (l < 0) {
00338 t1.tv_sec--;
00339 l = -l;
00340 l %= 1000000;
00341 }
00342
return (t1.tv_sec * 1000000) + l;
00343 }
00344
00345
long readbuf(snd_pcm_t *handle,
char *buf,
long len, size_t *frames, size_t *max)
00346 {
00347
long r;
00348
00349
if (!block) {
00350
do {
00351 r =
snd_pcm_readi(handle, buf, len);
00352 }
while (r == -EAGAIN);
00353
if (r > 0) {
00354 *frames += r;
00355
if (*max < r)
00356 *max = r;
00357 }
00358
00359 }
else {
00360
int frame_bytes = (
snd_pcm_format_width(format) / 8) * channels;
00361
do {
00362 r =
snd_pcm_readi(handle, buf, len);
00363
if (r > 0) {
00364 buf += r * frame_bytes;
00365 len -= r;
00366 *frames += r;
00367
if (*max < r)
00368 *max = r;
00369 }
00370
00371 }
while (r >= 1 && len > 0);
00372 }
00373
00374
return r;
00375 }
00376
00377
long writebuf(snd_pcm_t *handle,
char *buf,
long len, size_t *frames)
00378 {
00379
long r;
00380
00381
while (len > 0) {
00382 r =
snd_pcm_writei(handle, buf, len);
00383
if (r == -EAGAIN)
00384
continue;
00385
00386
if (r < 0)
00387
return r;
00388
00389 buf += r * 4;
00390 len -= r;
00391 *frames += r;
00392 }
00393
return 0;
00394 }
00395
00396
#define FILTERSWEEP_LFO_CENTER 2000.
00397
#define FILTERSWEEP_LFO_DEPTH 1800.
00398
#define FILTERSWEEP_LFO_FREQ 0.2
00399
#define FILTER_BANDWIDTH 50
00400
00401
00402
float lfo,dlfo,fs,fc,BW,C,D,a0,a1,a2,b1,b2,*x[3],*y[3];
00403
00404
void applyeffect(
char* buffer,
int r)
00405 {
00406
short* samples = (
short*) buffer;
00407
int i;
00408
for (i=0;i<r;i++)
00409 {
00410
int chn;
00411
00412 fc = sin(lfo)*FILTERSWEEP_LFO_DEPTH+FILTERSWEEP_LFO_CENTER;
00413 lfo += dlfo;
00414
if (lfo>2.*M_PI) lfo -= 2.*M_PI;
00415 C = 1./tan(M_PI*BW/fs);
00416 D = 2.*cos(2*M_PI*fc/fs);
00417 a0 = 1./(1.+C);
00418 a1 = 0;
00419 a2 = -a0;
00420 b1 = -C*D*a0;
00421 b2 = (C-1)*a0;
00422
00423
for (chn=0;chn<channels;chn++)
00424 {
00425 x[chn][2] = x[chn][1];
00426 x[chn][1] = x[chn][0];
00427
00428 y[chn][2] = y[chn][1];
00429 y[chn][1] = y[chn][0];
00430
00431 x[chn][0] = samples[i*channels+chn];
00432 y[chn][0] = a0*x[chn][0] + a1*x[chn][1] + a2*x[chn][2]
00433 - b1*y[chn][1] - b2*y[chn][2];
00434 samples[i*channels+chn] = y[chn][0];
00435 }
00436 }
00437 }
00438
00439
void help(
void)
00440 {
00441
int k;
00442 printf(
00443
"Usage: latency [OPTION]... [FILE]...\n"
00444
"-h,--help help\n"
00445
"-P,--pdevice playback device\n"
00446
"-C,--cdevice capture device\n"
00447
"-m,--min minimum latency in frames\n"
00448
"-M,--max maximum latency in frames\n"
00449
"-F,--frames frames to transfer\n"
00450
"-f,--format sample format\n"
00451
"-c,--channels channels\n"
00452
"-r,--rate rate\n"
00453
"-s,--seconds duration of test in seconds\n"
00454
"-b,--block block mode\n"
00455
"-t,--time maximal tick time in us\n"
00456
"-p,--poll use poll (wait for event - reduces CPU usage)\n"
00457
"-e,--effect apply an effect (bandpass filter sweep)\n"
00458 );
00459 printf(
"Recognized sample formats are:");
00460
for (k = 0; k < SND_PCM_FORMAT_LAST; ++(
unsigned long) k) {
00461
const char *s =
snd_pcm_format_name(k);
00462
if (s)
00463 printf(
" %s", s);
00464 }
00465 printf(
"\n\n");
00466 printf(
00467
"Tip #1 (usable latency with large periods, non-blocking mode, good CPU usage,\n"
00468
" superb xrun prevention):\n"
00469
" latency -m 8192 -M 8192 -t 1 -p\n"
00470
"Tip #2 (superb latency, non-blocking mode, but heavy CPU usage):\n"
00471
" latency -m 128 -M 128\n"
00472 );
00473 }
00474
00475
int main(
int argc,
char *argv[])
00476 {
00477
struct option long_option[] =
00478 {
00479 {
"help", 0, NULL,
'h'},
00480 {
"pdevice", 1, NULL,
'P'},
00481 {
"cdevice", 1, NULL,
'C'},
00482 {
"min", 1, NULL,
'm'},
00483 {
"max", 1, NULL,
'M'},
00484 {
"frames", 1, NULL,
'F'},
00485 {
"format", 1, NULL,
'f'},
00486 {
"channels", 1, NULL,
'c'},
00487 {
"rate", 1, NULL,
'r'},
00488 {
"seconds", 1, NULL,
's'},
00489 {
"block", 0, NULL,
'b'},
00490 {
"time", 1, NULL,
't'},
00491 {
"poll", 0, NULL,
'p'},
00492 {
"effect", 0, NULL,
'e'},
00493 {NULL, 0, NULL, 0},
00494 };
00495
snd_pcm_t *phandle, *chandle;
00496
char *buffer;
00497
int err, latency, morehelp;
00498
int ok;
00499
snd_timestamp_t p_tstamp, c_tstamp;
00500 ssize_t r;
00501 size_t frames_in, frames_out, in_max;
00502
int effect = 0;
00503 morehelp = 0;
00504
while (1) {
00505
int c;
00506
if ((c = getopt_long(argc, argv,
"hP:C:m:M:F:f:c:r:s:bt:pe", long_option, NULL)) < 0)
00507
break;
00508
switch (c) {
00509
case 'h':
00510 morehelp++;
00511
break;
00512
case 'P':
00513 pdevice = strdup(optarg);
00514
break;
00515
case 'C':
00516 cdevice = strdup(optarg);
00517
break;
00518
case 'm':
00519 err = atoi(optarg) / 2;
00520 latency_min = err >= 4 ? err : 4;
00521
if (latency_max < latency_min)
00522 latency_max = latency_min;
00523
break;
00524
case 'M':
00525 err = atoi(optarg) / 2;
00526 latency_max = latency_min > err ? latency_min : err;
00527
break;
00528
case 'f':
00529 format =
snd_pcm_format_value(optarg);
00530
if (format ==
SND_PCM_FORMAT_UNKNOWN) {
00531 printf(
"Unknown format, setting to default S16_LE\n");
00532 format =
SND_PCM_FORMAT_S16_LE;
00533 }
00534
break;
00535
case 'c':
00536 err = atoi(optarg);
00537 channels = err >= 1 && err < 1024 ? err : 1;
00538
break;
00539
case 'r':
00540 err = atoi(optarg);
00541 rate = err >= 4000 && err < 200000 ? err : 44100;
00542
break;
00543
case 's':
00544 err = atoi(optarg);
00545 loop_sec = err >= 1 && err <= 100000 ? err : 30;
00546
break;
00547
case 'b':
00548 block = 1;
00549
break;
00550
case 't':
00551 tick_time = atoi(optarg);
00552 tick_time = tick_time < 0 ? 0 : tick_time;
00553
break;
00554
case 'p':
00555 use_poll = 1;
00556
break;
00557
case 'e':
00558 effect = 1;
00559
break;
00560 }
00561 }
00562
00563
if (morehelp) {
00564 help();
00565
return 0;
00566 }
00567 err =
snd_output_stdio_attach(&output, stdout, 0);
00568
if (err < 0) {
00569 printf(
"Output failed: %s\n",
snd_strerror(err));
00570
return 0;
00571 }
00572
00573 loop_limit = loop_sec * rate;
00574 latency = latency_min - 4;
00575 buffer = malloc((latency_max *
snd_pcm_format_width(format) / 8) * 2);
00576
00577 setscheduler();
00578
00579 printf(
"Playback device is %s\n", pdevice);
00580 printf(
"Capture device is %s\n", cdevice);
00581 printf(
"Parameters are %iHz, %s, %i channels, %s mode\n", rate,
snd_pcm_format_name(format), channels, block ?
"blocking" :
"non-blocking");
00582 printf(
"Wanted tick time: %ius, poll mode: %s\n", tick_time, use_poll ?
"yes" :
"no");
00583 printf(
"Loop limit is %li frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2);
00584
00585
if ((err =
snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
00586 printf(
"Playback open error: %s\n",
snd_strerror(err));
00587
return 0;
00588 }
00589
if ((err =
snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE, block ? 0 : SND_PCM_NONBLOCK)) < 0) {
00590 printf(
"Record open error: %s\n",
snd_strerror(err));
00591
return 0;
00592 }
00593
00594
00595
if (effect) {
00596 fs = (
float) rate;
00597 BW = FILTER_BANDWIDTH;
00598
00599 lfo = 0;
00600 dlfo = 2.*M_PI*FILTERSWEEP_LFO_FREQ/fs;
00601
00602 x[0] = (
float*) malloc(channels*
sizeof(
float));
00603 x[1] = (
float*) malloc(channels*
sizeof(
float));
00604 x[2] = (
float*) malloc(channels*
sizeof(
float));
00605 y[0] = (
float*) malloc(channels*
sizeof(
float));
00606 y[1] = (
float*) malloc(channels*
sizeof(
float));
00607 y[2] = (
float*) malloc(channels*
sizeof(
float));
00608 }
00609
00610
while (1) {
00611 frames_in = frames_out = 0;
00612
if (setparams(phandle, chandle, &latency) < 0)
00613
break;
00614 showlatency(latency);
00615
if (tick_time_ok)
00616 printf(
"Using tick time %ius\n", tick_time_ok);
00617
if ((err =
snd_pcm_link(chandle, phandle)) < 0) {
00618 printf(
"Streams link error: %s\n",
snd_strerror(err));
00619 exit(0);
00620 }
00621
if (
snd_pcm_format_set_silence(format, buffer, latency*channels) < 0) {
00622 fprintf(stderr,
"silence error\n");
00623
break;
00624 }
00625
if (writebuf(phandle, buffer, latency, &frames_out) < 0) {
00626 fprintf(stderr,
"write error\n");
00627
break;
00628 }
00629
if (writebuf(phandle, buffer, latency, &frames_out) < 0) {
00630 fprintf(stderr,
"write error\n");
00631
break;
00632 }
00633
00634
if ((err =
snd_pcm_start(chandle)) < 0) {
00635 printf(
"Go error: %s\n",
snd_strerror(err));
00636 exit(0);
00637 }
00638 gettimestamp(phandle, &p_tstamp);
00639 gettimestamp(chandle, &c_tstamp);
00640
#if 0
00641
printf(
"Playback:\n");
00642 showstat(phandle, frames_out);
00643 printf(
"Capture:\n");
00644 showstat(chandle, frames_in);
00645
#endif
00646
00647 ok = 1;
00648 in_max = 0;
00649
while (ok && frames_in < loop_limit) {
00650
if (use_poll) {
00651
00652
snd_pcm_wait(chandle, 1000);
00653 }
00654
if ((r = readbuf(chandle, buffer, latency, &frames_in, &in_max)) < 0)
00655 ok = 0;
00656
else {
00657
if (effect)
00658 applyeffect(buffer,r);
00659
if (writebuf(phandle, buffer, r, &frames_out) < 0)
00660 ok = 0;
00661 }
00662 }
00663
if (ok)
00664 printf(
"Success\n");
00665
else
00666 printf(
"Failure\n");
00667 printf(
"Playback:\n");
00668 showstat(phandle, frames_out);
00669 printf(
"Capture:\n");
00670 showstat(chandle, frames_in);
00671 showinmax(in_max);
00672
if (p_tstamp.tv_sec == p_tstamp.tv_sec &&
00673 p_tstamp.tv_usec == c_tstamp.tv_usec)
00674 printf(
"Hardware sync\n");
00675
snd_pcm_drop(chandle);
00676
snd_pcm_nonblock(phandle, 0);
00677
snd_pcm_drain(phandle);
00678
snd_pcm_nonblock(phandle, !block ? 1 : 0);
00679
if (ok) {
00680
#if 1
00681
printf(
"Playback time = %li.%i, Record time = %li.%i, diff = %li\n",
00682 p_tstamp.tv_sec,
00683 (
int)p_tstamp.tv_usec,
00684 c_tstamp.tv_sec,
00685 (
int)c_tstamp.tv_usec,
00686 timediff(p_tstamp, c_tstamp));
00687
#endif
00688
break;
00689 }
00690
snd_pcm_unlink(chandle);
00691
snd_pcm_hw_free(phandle);
00692
snd_pcm_hw_free(chandle);
00693 }
00694
snd_pcm_close(phandle);
00695
snd_pcm_close(chandle);
00696
return 0;
00697 }
Generated on Fri Feb 25 15:18:28 2005 for ALSA project - the C library reference by
1.3.7