alsaloop: fixes, added -W/--wake option

- added -W/--wake option to reduce poll time
- another try to fix the avail_min parameter for playback
- fixed initial silence fill

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2010-10-14 11:17:25 +02:00
parent b68986fb4a
commit 513a9c7ad1
5 changed files with 101 additions and 34 deletions

View file

@ -189,6 +189,10 @@ Verbose mode. Use multiple times to increase verbosity.
Verbose xrun profiling. Verbose xrun profiling.
.TP
\fI\-W <timeout>\fP | \fI\-\-wake=<timeout>\fP
Set process wake timeout.
.SH EXAMPLES .SH EXAMPLES

View file

@ -56,6 +56,7 @@ struct loopback_thread *threads;
int threads_count = 0; int threads_count = 0;
pthread_t main_job; pthread_t main_job;
int arg_default_xrun = 0; int arg_default_xrun = 0;
int arg_default_wake = 0;
static void my_exit(struct loopback_thread *thread, int exitcode) static void my_exit(struct loopback_thread *thread, int exitcode)
{ {
@ -193,6 +194,7 @@ void help(void)
"-v,--verbose verbose mode (more -v means more verbose)\n" "-v,--verbose verbose mode (more -v means more verbose)\n"
"-w,--workaround use workaround (serialopen)\n" "-w,--workaround use workaround (serialopen)\n"
"-U,--xrun xrun profiling\n" "-U,--xrun xrun profiling\n"
"-W,--wake process wake timeout in ms\n"
); );
printf("\nRecognized sample formats are:"); printf("\nRecognized sample formats are:");
for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
@ -395,12 +397,13 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
char *arg_ossmixers[MAX_MIXERS]; char *arg_ossmixers[MAX_MIXERS];
int arg_ossmixers_count = 0; int arg_ossmixers_count = 0;
int arg_xrun = arg_default_xrun; int arg_xrun = arg_default_xrun;
int arg_wake = arg_default_wake;
morehelp = 0; morehelp = 0;
while (1) { while (1) {
int c; int c;
if ((c = getopt_long(argc, argv, if ((c = getopt_long(argc, argv,
"hdg:P:C:X:Y:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:U", "hdg:P:C:X:Y:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:UW:",
long_option, NULL)) < 0) long_option, NULL)) < 0)
break; break;
switch (c) { switch (c) {
@ -549,6 +552,11 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
if (cmdline) if (cmdline)
arg_default_xrun = 1; arg_default_xrun = 1;
break; break;
case 'W':
arg_wake = atoi(optarg);
if (cmdline)
arg_default_wake = arg_wake;
break;
} }
} }
@ -587,6 +595,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output,
loop->slave = arg_slave; loop->slave = arg_slave;
loop->thread = arg_thread; loop->thread = arg_thread;
loop->xrun = arg_xrun; loop->xrun = arg_xrun;
loop->wake = arg_wake;
err = add_mixers(loop, arg_mixers, arg_mixers_count); err = add_mixers(loop, arg_mixers, arg_mixers_count);
if (err < 0) { if (err < 0) {
logit(LOG_CRIT, "Unable to add mixer controls.\n"); logit(LOG_CRIT, "Unable to add mixer controls.\n");
@ -691,7 +700,7 @@ static void thread_job1(void *_data)
snd_output_t *output = thread->output; snd_output_t *output = thread->output;
struct pollfd *pfds = NULL; struct pollfd *pfds = NULL;
int pfds_count = 0; int pfds_count = 0;
int i, j, err; int i, j, err, wake = 1000000;
setscheduler(); setscheduler();
@ -709,7 +718,12 @@ static void thread_job1(void *_data)
my_exit(thread, EXIT_FAILURE); my_exit(thread, EXIT_FAILURE);
} }
pfds_count += thread->loopbacks[i]->pollfd_count; pfds_count += thread->loopbacks[i]->pollfd_count;
j = thread->loopbacks[i]->wake;
if (j > 0 && j < wake)
wake = j;
} }
if (wake >= 1000000)
wake = -1;
pfds = calloc(pfds_count, sizeof(struct pollfd)); pfds = calloc(pfds_count, sizeof(struct pollfd));
if (pfds == NULL || pfds_count <= 0) { if (pfds == NULL || pfds_count <= 0) {
logit(LOG_CRIT, "Poll FDs allocation failed.\n"); logit(LOG_CRIT, "Poll FDs allocation failed.\n");
@ -727,7 +741,7 @@ static void thread_job1(void *_data)
} }
if (verbose > 10) if (verbose > 10)
gettimeofday(&tv1, NULL); gettimeofday(&tv1, NULL);
err = poll(pfds, j, -1); err = poll(pfds, j, wake);
if (err < 0) if (err < 0)
err = -errno; err = -errno;
if (verbose > 10) { if (verbose > 10) {

View file

@ -146,6 +146,7 @@ struct loopback {
sync_type_t sync; /* type of sync */ sync_type_t sync; /* type of sync */
slave_type_t slave; slave_type_t slave;
int thread; /* thread number */ int thread; /* thread number */
unsigned int wake;
/* statistics */ /* statistics */
double pitch; double pitch;
double pitch_delta; double pitch_delta;
@ -164,6 +165,9 @@ struct loopback {
snd_timestamp_t xrun_last_check; snd_timestamp_t xrun_last_check;
snd_pcm_sframes_t xrun_last_pdelay; snd_pcm_sframes_t xrun_last_pdelay;
snd_pcm_sframes_t xrun_last_cdelay; snd_pcm_sframes_t xrun_last_cdelay;
snd_pcm_uframes_t xrun_buf_pcount;
snd_pcm_uframes_t xrun_buf_ccount;
unsigned int xrun_out_frames;
long xrun_max_proctime; long xrun_max_proctime;
double xrun_max_missing; double xrun_max_missing;
/* control mixer */ /* control mixer */

View file

@ -83,16 +83,16 @@ static inline snd_pcm_uframes_t get_whole_latency(struct loopback *loop)
} }
static inline unsigned long long static inline unsigned long long
frames_to_time(struct loopback_handle *lhandle, frames_to_time(unsigned int rate,
snd_pcm_uframes_t frames) snd_pcm_uframes_t frames)
{ {
return (frames * 1000000ULL) / lhandle->rate; return (frames * 1000000ULL) / rate;
} }
static inline snd_pcm_uframes_t time_to_frames(struct loopback_handle *lhandle, static inline snd_pcm_uframes_t time_to_frames(unsigned int rate,
unsigned long long time) unsigned long long time)
{ {
return (time * lhandle->rate) / 1000000ULL; return (time * rate) / 1000000ULL;
} }
static int setparams_stream(struct loopback_handle *lhandle, static int setparams_stream(struct loopback_handle *lhandle,
@ -245,12 +245,16 @@ static int setparams_set(struct loopback_handle *lhandle,
snd_output_printf(lhandle->loopback->output, "%s: avail_min1=%li\n", lhandle->id, val); snd_output_printf(lhandle->loopback->output, "%s: avail_min1=%li\n", lhandle->id, val);
} else { } else {
if (lhandle == lhandle->loopback->play) { if (lhandle == lhandle->loopback->play) {
val = bufsize / 2 + bufsize / 4 + bufsize / 8; val = bufsize + bufsize / 2;
if (val > buffer_size / 4) if (val < (period_size * 3) / 4)
val = buffer_size / 4; val = (period_size * 3) / 4;
if (val > (buffer_size * 3) / 4)
val = (buffer_size * 3) / 4;
val = buffer_size - val; val = buffer_size - val;
} else { } else {
val = bufsize / 2; val = bufsize / 2;
if (val < period_size / 2)
val = period_size / 2;
if (val > buffer_size / 4) if (val > buffer_size / 4)
val = buffer_size / 4; val = buffer_size / 4;
} }
@ -372,6 +376,11 @@ static void xrun_profile0(struct loopback *loop)
getcurtimestamp(&loop->xrun_last_update); getcurtimestamp(&loop->xrun_last_update);
loop->xrun_last_pdelay = pdelay; loop->xrun_last_pdelay = pdelay;
loop->xrun_last_cdelay = cdelay; loop->xrun_last_cdelay = cdelay;
loop->xrun_buf_pcount = loop->play->buf_count;
loop->xrun_buf_ccount = loop->capt->buf_count;
#ifdef USE_SAMPLERATE
loop->xrun_out_frames = loop->src_out_frames;
#endif
} }
} }
@ -384,27 +393,45 @@ static inline void xrun_profile(struct loopback *loop)
static void xrun_stats0(struct loopback *loop) static void xrun_stats0(struct loopback *loop)
{ {
snd_timestamp_t t; snd_timestamp_t t;
double expected, last, wake, check, queued = -1, proc, missing; double expected, last, wake, check, queued = -1, proc, missing = -1;
double maxbuf, pfilled, cfilled, cqueued = -1, avail_min;
double sincejob;
expected = ((double)loop->latency / expected = ((double)loop->latency /
(double)loop->play->rate) * 1000; (double)loop->play->rate_req) * 1000;
getcurtimestamp(&t); getcurtimestamp(&t);
last = (double)timediff(t, loop->xrun_last_update) / 1000; last = (double)timediff(t, loop->xrun_last_update) / 1000;
wake = (double)timediff(t, loop->xrun_last_wake) / 1000; wake = (double)timediff(t, loop->xrun_last_wake) / 1000;
check = (double)timediff(t, loop->xrun_last_check) / 1000; check = (double)timediff(t, loop->xrun_last_check) / 1000;
sincejob = (double)timediff(t, loop->tstamp_start) / 1000;
if (loop->xrun_last_pdelay != XRUN_PROFILE_UNKNOWN) if (loop->xrun_last_pdelay != XRUN_PROFILE_UNKNOWN)
queued = (((double)loop->xrun_last_pdelay * queued = ((double)loop->xrun_last_pdelay /
loop->play->pitch) / (double)loop->play->rate) * 1000;
if (loop->xrun_last_cdelay != XRUN_PROFILE_UNKNOWN)
cqueued = ((double)loop->xrun_last_cdelay /
(double)loop->capt->rate) * 1000;
maxbuf = ((double)loop->play->buffer_size /
(double)loop->play->rate) * 1000; (double)loop->play->rate) * 1000;
proc = (double)loop->xrun_max_proctime / 1000; proc = (double)loop->xrun_max_proctime / 1000;
missing = last - queued; pfilled = ((double)(loop->xrun_buf_pcount + loop->xrun_out_frames) /
if (loop->xrun_max_missing < missing) (double)loop->play->rate) * 1000;
cfilled = ((double)loop->xrun_buf_ccount /
(double)loop->capt->rate) * 1000;
avail_min = (((double)loop->play->buffer_size -
(double)loop->play->avail_min ) /
(double)loop->play->rate) * 1000;
avail_min = expected - avail_min;
if (queued >= 0)
missing = last - queued;
if (missing >= 0 && loop->xrun_max_missing < missing)
loop->xrun_max_missing = missing; loop->xrun_max_missing = missing;
loop->xrun_max_proctime = 0; loop->xrun_max_proctime = 0;
getcurtimestamp(&t); getcurtimestamp(&t);
logit(LOG_INFO, " last write before %.4fms, queued %.4fms -> missing %.4fms\n", last, queued, missing); logit(LOG_INFO, " last write before %.4fms, queued %.4fms/%.4fms -> missing %.4fms\n", last, queued, cqueued, missing);
logit(LOG_INFO, " expected %.4fms, processing %.4fms, max missing %.4fms\n", expected, proc, loop->xrun_max_missing); logit(LOG_INFO, " expected %.4fms, processing %.4fms, max missing %.4fms\n", expected, proc, loop->xrun_max_missing);
logit(LOG_INFO, " last wake before %.4fms, last check before %.4fms\n", wake, check); logit(LOG_INFO, " last wake %.4fms, last check %.4fms, avail_min %.4fms\n", wake, check, avail_min);
logit(LOG_INFO, " max buf %.4fms, pfilled %.4fms, cfilled %.4fms\n", maxbuf, pfilled, cfilled);
logit(LOG_INFO, " job started before %.4fms\n", sincejob);
} }
static inline void xrun_stats(struct loopback *loop) static inline void xrun_stats(struct loopback *loop)
@ -483,7 +510,14 @@ static void buf_add_src(struct loopback *loop)
count1 = count; count1 = count;
if (count1 + pos1 > capt->buf_size) if (count1 + pos1 > capt->buf_size)
count1 = capt->buf_size - pos1; count1 = capt->buf_size - pos1;
src_short_to_float_array((short *)(capt->buf + if (capt->format == SND_PCM_FORMAT_S32)
src_int_to_float_array((int *)(capt->buf +
pos1 * capt->frame_size),
loop->src_data.data_in +
pos * capt->channels,
count1 * capt->channels);
else
src_short_to_float_array((short *)(capt->buf +
pos1 * capt->frame_size), pos1 * capt->frame_size),
loop->src_data.data_in + loop->src_data.data_in +
pos * capt->channels, pos * capt->channels,
@ -514,7 +548,14 @@ static void buf_add_src(struct loopback *loop)
count1 = buf_avail(play); count1 = buf_avail(play);
if (count1 == 0) if (count1 == 0)
break; break;
src_float_to_short_array(loop->src_data.data_out + if (capt->format == SND_PCM_FORMAT_S32)
src_float_to_int_array(loop->src_data.data_out +
pos * play->channels,
(int *)(play->buf +
pos1 * play->frame_size),
count1 * play->channels);
else
src_float_to_short_array(loop->src_data.data_out +
pos * play->channels, pos * play->channels,
(short *)(play->buf + (short *)(play->buf +
pos1 * play->frame_size), pos1 * play->frame_size),
@ -691,12 +732,12 @@ static int writeit(struct loopback_handle *lhandle)
fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size, fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size,
r, lhandle->frame_size, lhandle->loopback->pfile); r, lhandle->frame_size, lhandle->loopback->pfile);
#endif #endif
xrun_profile(lhandle->loopback);
res += r; res += r;
lhandle->counter += r; lhandle->counter += r;
lhandle->buf_count -= r; lhandle->buf_count -= r;
lhandle->buf_pos += r; lhandle->buf_pos += r;
lhandle->buf_pos %= lhandle->buf_size; lhandle->buf_pos %= lhandle->buf_size;
xrun_profile(lhandle->loopback);
} }
return res; return res;
} }
@ -919,6 +960,7 @@ static int xrun_sync(struct loopback *loop)
snd_output_printf(loop->output, "%s: sync verify: %li\n", loop->id, delay1); snd_output_printf(loop->output, "%s: sync verify: %li\n", loop->id, delay1);
} }
} }
loop->xrun_max_proctime = 0;
return 0; return 0;
} }
@ -1344,11 +1386,11 @@ int pcmjob_start(struct loopback *loop)
loop->reinit = 0; loop->reinit = 0;
loop->use_samplerate = 0; loop->use_samplerate = 0;
if (loop->latency_req) { if (loop->latency_req) {
loop->latency_reqtime = frames_to_time(loop->play, loop->latency_reqtime = frames_to_time(loop->play->rate_req,
loop->latency_req); loop->latency_req);
loop->latency_req = 0; loop->latency_req = 0;
} }
loop->latency = time_to_frames(loop->play, loop->latency_reqtime); loop->latency = time_to_frames(loop->play->rate_req, loop->latency_reqtime);
if ((err = setparams(loop, loop->latency/2)) < 0) if ((err = setparams(loop, loop->latency/2)) < 0)
goto __error; goto __error;
if (verbose) if (verbose)
@ -1406,9 +1448,11 @@ int pcmjob_start(struct loopback *loop)
goto __error; goto __error;
} }
if (loop->use_samplerate) { if (loop->use_samplerate) {
if (loop->capt->format != SND_PCM_FORMAT_S16 || if ((loop->capt->format != SND_PCM_FORMAT_S16 ||
loop->play->format != SND_PCM_FORMAT_S16) { loop->play->format != SND_PCM_FORMAT_S16) &&
logit(LOG_CRIT, "samplerate conversion supports only %s format (play=%s, capt=%s)\n", snd_pcm_format_name(SND_PCM_FORMAT_S16), snd_pcm_format_name(loop->play->format), snd_pcm_format_name(loop->capt->format)); (loop->capt->format != SND_PCM_FORMAT_S32 ||
loop->play->format != SND_PCM_FORMAT_S32)) {
logit(LOG_CRIT, "samplerate conversion supports only %s or %s formats (play=%s, capt=%s)\n", snd_pcm_format_name(SND_PCM_FORMAT_S16), snd_pcm_format_name(SND_PCM_FORMAT_S32), snd_pcm_format_name(loop->play->format), snd_pcm_format_name(loop->capt->format));
loop->use_samplerate = 0; loop->use_samplerate = 0;
err = -EIO; err = -EIO;
goto __error; goto __error;

View file

@ -34,19 +34,20 @@ EOF
test3() { test3() {
echo "TEST3" echo "TEST3"
LATENCY=180000
cat > $CFGFILE <<EOF cat > $CFGFILE <<EOF
-C hw:1,0,0 -P plug:dmix:0 --tlatency 30000 --thread 0 \ -C hw:1,0,0 -P plug:dmix:0 --tlatency $LATENCY --thread 0 \
--mixer "name='Master Playback Volume'@name='Master Playback Volume'" \ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
--mixer "name='Master Playback Switch'@name='Master Playback Switch'" \ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
--mixer "name='PCM Playback Volume'" \ --mixer "name='PCM Playback Volume'" \
--ossmixer "name=Master@VOLUME" --ossmixer "name=Master@VOLUME"
-C hw:1,0,1 -P plug:dmix:0 --tlatency 30000 --thread 1 -C hw:1,0,1 -P plug:dmix:0 --tlatency $LATENCY --thread 1
-C hw:1,0,2 -P plug:dmix:0 --tlatency 30000 --thread 2 -C hw:1,0,2 -P plug:dmix:0 --tlatency $LATENCY --thread 2
-C hw:1,0,3 -P plug:dmix:0 --tlatency 30000 --thread 3 -C hw:1,0,3 -P plug:dmix:0 --tlatency $LATENCY --thread 3
-C hw:1,0,4 -P plug:dmix:0 --tlatency 30000 --thread 4 -C hw:1,0,4 -P plug:dmix:0 --tlatency $LATENCY --thread 4
-C hw:1,0,5 -P plug:dmix:0 --tlatency 30000 --thread 5 -C hw:1,0,5 -P plug:dmix:0 --tlatency $LATENCY --thread 5
-C hw:1,0,6 -P plug:dmix:0 --tlatency 30000 --thread 6 -C hw:1,0,6 -P plug:dmix:0 --tlatency $LATENCY --thread 6
-C hw:1,0,7 -P plug:dmix:0 --tlatency 30000 --thread 7 -C hw:1,0,7 -P plug:dmix:0 --tlatency $LATENCY --thread 7
EOF EOF
$DBG ./alsaloop --config $CFGFILE $ARGS $DBG ./alsaloop --config $CFGFILE $ARGS
} }