#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";
int rate = 22050;
int channels = 2;
int buffer_size = 0;
int period_size = 0;
int latency_min = 32;
int latency_max = 2048;
int loop_sec = 30;
int block = 0;
int use_poll = 0;
int resample = 1;
unsigned long loop_limit;
const char *id)
{
int err;
unsigned int rrate;
if (err < 0) {
printf(
"Broken configuration for %s PCM: no configurations available: %s\n",
snd_strerror(err),
id);
return err;
}
if (err < 0) {
printf(
"Resample setup failed for %s (val %i): %s\n",
id, resample,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Access type not available for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Sample format not available for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Channels count (%i) not available for %s: %s\n", channels,
id,
snd_strerror(err));
return err;
}
rrate = rate;
if (err < 0) {
printf(
"Rate %iHz not available for %s: %s\n", rate,
id,
snd_strerror(err));
return err;
}
if ((int)rrate != rate) {
printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
return -EINVAL;
}
return 0;
}
const char *id)
{
int err;
periodsize = bufsize * 2;
if (err < 0) {
printf(
"Unable to set buffer size %li for %s: %s\n", bufsize * 2,
id,
snd_strerror(err));
return err;
}
if (period_size > 0)
periodsize = period_size;
else
periodsize /= 2;
if (err < 0) {
printf(
"Unable to set period size %li for %s: %s\n", periodsize,
id,
snd_strerror(err));
return err;
}
return 0;
}
const char *id)
{
int err;
if (err < 0) {
printf(
"Unable to set hw params for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Unable to determine current swparams for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Unable to set start threshold mode for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (!block)
val = 4;
else
if (err < 0) {
printf(
"Unable to set avail min for %s: %s\n",
id,
snd_strerror(err));
return err;
}
if (err < 0) {
printf(
"Unable to set sw params for %s: %s\n",
id,
snd_strerror(err));
return err;
}
return 0;
}
{
int err, last_bufsize = *bufsize;
unsigned int p_time, c_time;
unsigned int val;
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);
}
if (buffer_size > 0) {
*bufsize = buffer_size;
goto __set_it;
}
__again:
if (buffer_size > 0)
return -1;
if (last_bufsize == *bufsize)
*bufsize += 4;
last_bufsize = *bufsize;
if (*bufsize > latency_max)
return -1;
__set_it:
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);
}
if (p_psize > (unsigned int)*bufsize)
*bufsize = p_psize;
if (c_psize > (unsigned int)*bufsize)
*bufsize = c_psize;
if (p_time != c_time)
goto __again;
if (p_psize * 2 < p_size) {
if (val > 2) {
printf("playback device does not support 2 periods per buffer\n");
exit(0);
}
goto __again;
}
if (c_psize * 2 < c_size) {
if (val > 2 ) {
printf("capture device does not support 2 periods per buffer\n");
exit(0);
}
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);
}
exit(0);
}
fflush(stdout);
return 0;
}
void showstat(
snd_pcm_t *handle,
size_t frames)
{
int err;
exit(0);
}
printf("*** frames = %li ***\n", (long)frames);
}
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);
}
{
int 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);
}
{
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 = 1000000 + 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 {
} while (r == -EAGAIN);
if (r > 0) {
*frames += r;
if ((long)*max < r)
*max = r;
}
} else {
do {
if (r > 0) {
buf += r * frame_bytes;
*frames += r;
if ((long)*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;
if (r == -EAGAIN)
continue;
if (r < 0)
return r;
buf += r * frame_bytes;
*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"
"-B,--buffer buffer size in frames\n"
"-E,--period period size in frames\n"
"-s,--seconds duration of test in seconds\n"
"-b,--block block mode\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; ++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'},
{"buffer", 1, NULL, 'B'},
{"period", 1, NULL, 'E'},
{"seconds", 1, NULL, 's'},
{"block", 0, NULL, 'b'},
{"poll", 0, NULL, 'p'},
{"effect", 0, NULL, 'e'},
{NULL, 0, NULL, 0},
};
char *buffer;
int err, latency, morehelp;
int ok;
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:B:E:s:bpen", 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':
printf("Unknown format, setting to default S16_LE\n");
}
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 'B':
err = atoi(optarg);
buffer_size = err >= 32 && err < 200000 ? err : 0;
break;
case 'E':
err = atoi(optarg);
period_size = err >= 32 && err < 200000 ? err : 0;
break;
case 's':
err = atoi(optarg);
loop_sec = err >= 1 && err <= 100000 ? err : 30;
break;
case 'b':
block = 1;
break;
case 'p':
use_poll = 1;
break;
case 'e':
effect = 1;
break;
case 'n':
resample = 0;
break;
}
}
if (morehelp) {
help();
return 0;
}
if (err < 0) {
return 0;
}
loop_limit = loop_sec * rate;
latency = latency_min - 4;
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("Poll mode: %s\n", use_poll ? "yes" : "no");
printf("Loop limit is %lu frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2);
return 0;
}
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);
exit(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;
}
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) {
}
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 == c_tstamp.tv_sec &&
p_tstamp.tv_usec == c_tstamp.tv_usec)
printf("Hardware sync\n");
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;
}
}
return 0;
}
const char * snd_strerror(int errnum)
Returns the message for an error code.
Definition: error.c:51
struct timeval snd_timestamp_t
Definition: global.h:152
struct _snd_output snd_output_t
Internal structure for an output object.
Definition: output.h:56
int snd_output_stdio_attach(snd_output_t **outputp, FILE *fp, int _close)
Creates a new output object using an existing stdio FILE pointer.
Definition: output.c:188
const char * snd_pcm_format_name(const snd_pcm_format_t format)
get name of PCM sample format
Definition: pcm.c:2121
snd_pcm_format_t snd_pcm_format_value(const char *name)
get PCM sample format from name
Definition: pcm.c:2145
int snd_pcm_dump(snd_pcm_t *pcm, snd_output_t *out)
Dump PCM info.
Definition: pcm.c:2373
int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out)
Dump status.
Definition: pcm.c:2352
#define snd_pcm_hw_params_alloca(ptr)
allocate an invalid snd_pcm_hw_params_t using standard alloca
Definition: pcm.h:764
int snd_pcm_hw_params_get_periods_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
Extract minimum periods count from a configuration space.
Definition: pcm.c:5393
int snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
Restrict a configuration space to have buffer size nearest to a target.
Definition: pcm.c:5899
int snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
Restrict a configuration space to have rate nearest to a target.
Definition: pcm.c:4779
int snd_pcm_hw_params_set_channels(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)
Restrict a configuration space to contain only one channels count.
Definition: pcm.c:4544
int snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)
Restrict a configuration space to contain only one access type.
Definition: pcm.c:4229
int snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)
Restrict a configuration space to contain only one format.
Definition: pcm.c:4326
int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Fill params with a full configuration space for a PCM.
Definition: pcm.c:3833
int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
Extract period time from a configuration space.
Definition: pcm.c:4950
int snd_pcm_hw_params_set_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)
Restrict a configuration space to contain only real hardware rates.
Definition: pcm.c:4830
int snd_pcm_hw_params_set_period_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)
Restrict a configuration space to have period size nearest to a target.
Definition: pcm.c:5295
void snd_pcm_hw_params_copy(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src)
copy one snd_pcm_hw_params_t to another
Definition: pcm.c:4184
int snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
Extract buffer size from a configuration space.
Definition: pcm.c:5761
int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)
Extract period size from a configuration space.
Definition: pcm.c:5138
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int samples)
Silence a PCM samples buffer.
Definition: pcm_misc.c:607
int snd_pcm_format_width(snd_pcm_format_t format)
Return nominal bits per a PCM sample.
Definition: pcm_misc.c:210
int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)
Set start threshold inside a software configuration container.
Definition: pcm.c:6598
int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)
Set avail min inside a software configuration container.
Definition: pcm.c:6490
#define snd_pcm_sw_params_alloca(ptr)
allocate an invalid snd_pcm_sw_params_t using standard alloca
Definition: pcm.h:905
#define snd_pcm_status_alloca(ptr)
allocate an invalid snd_pcm_status_t using standard alloca
Definition: pcm.h:1028
void snd_pcm_status_get_trigger_tstamp(const snd_pcm_status_t *obj, snd_timestamp_t *ptr)
Get trigger timestamp from a PCM status container.
Definition: pcm.c:6839
int snd_pcm_close(snd_pcm_t *pcm)
close PCM handle
Definition: pcm.c:766
int snd_pcm_unlink(snd_pcm_t *pcm)
Remove a PCM from a linked group.
Definition: pcm.c:1719
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Install one PCM hardware configuration chosen from a configuration space and snd_pcm_prepare it.
Definition: pcm.c:940
int snd_pcm_hw_free(snd_pcm_t *pcm)
Remove PCM hardware configuration and free associated resources.
Definition: pcm.c:955
int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
Obtain status (runtime) information for PCM handle.
Definition: pcm.c:1041
int snd_pcm_drain(snd_pcm_t *pcm)
Stop a PCM preserving pending frames.
Definition: pcm.c:1345
snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
Read interleaved frames from a PCM.
Definition: pcm.c:1634
int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
Return current software configuration for a PCM.
Definition: pcm.c:6178
struct _snd_pcm_hw_params snd_pcm_hw_params_t
Definition: pcm.h:64
#define SND_PCM_NONBLOCK
Definition: pcm.h:393
int snd_pcm_start(snd_pcm_t *pcm)
Start a PCM.
Definition: pcm.c:1277
int snd_pcm_drop(snd_pcm_t *pcm)
Stop a PCM dropping pending frames.
Definition: pcm.c:1308
int snd_pcm_prepare(snd_pcm_t *pcm)
Prepare PCM for use.
Definition: pcm.c:1222
struct _snd_pcm_sw_params snd_pcm_sw_params_t
Definition: pcm.h:67
int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
Opens a PCM.
Definition: pcm.c:2688
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
Install PCM software configuration defined by params.
Definition: pcm.c:988
int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock)
set nonblock mode
Definition: pcm.c:802
struct _snd_pcm_status snd_pcm_status_t
Definition: pcm.h:69
struct _snd_pcm snd_pcm_t
Definition: pcm.h:408
snd_pcm_format_t
Definition: pcm.h:125
unsigned long snd_pcm_uframes_t
Definition: pcm.h:388
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
Write interleaved frames to a PCM.
Definition: pcm.c:1556
int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
Link two PCMs.
Definition: pcm.c:1701
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
Wait for a PCM to become ready.
Definition: pcm.c:2846
@ SND_PCM_ACCESS_RW_INTERLEAVED
Definition: pcm.h:118
@ SND_PCM_FORMAT_S16_LE
Definition: pcm.h:133
@ SND_PCM_FORMAT_UNKNOWN
Definition: pcm.h:127
@ SND_PCM_STREAM_PLAYBACK
Definition: pcm.h:103
@ SND_PCM_STREAM_CAPTURE
Definition: pcm.h:105
unsigned int len
Definition: seq_event.h:0