Minimizing Latency With pacat And Tvtime

Update: Since writing this I've decided that a slightly different approach might be better, but what I've described here is probably good enough. I think that when in play mode pacat should fully read stdin every read regardless of what is requested by pulseaudio. It should then buffer just enough so that it's reasonably confident that the current and future requests from pulseaudio can be satisfied. I'll look into "module-loopback" and see if such an approach makes sense for it.

I use tvtime on my Fedora system to watch TV with my TV tuner card. Since tvtime does not handle forwarding audio between audio cards the forwarding is left as an exercise to the user. Unfortunately forwarding the audio in a way the does not introduce latency, which makes A/V sync worse, is difficult. One approach is to use two instances of pacat as described on this page. Although I use the technique described on that page with this script I've made a change to pacat that is helpful that I'll describe in this post.

The pacat solution works well until, for some reason, the play pacat (the instance of pacat on the right side of the pipe) hangs temporarily or otherwise gets behind. This can be seen by control-Zing the pacat wrapper script for a few seconds and then resuming it. Doing so causes a backlog of unread audio frames and the play pacat never catches up. This seems to happen for other reasons, such as when the system gets too busy.

I've modified pacat so that it takes a "--drain" switch. When the switch is specified and pacat is in play mode if the number of consecutive full reads (reads where the requested number of bytes is returned) exceeds some threshold then pacat reads additional bytes in order to get rid of the backlogged audio frames. The idea is that each read request being fully satisfied tends to mean that there is a backlog of data. The patch is here. I've attempted to submit an earlier version of the patch to the PulseAudio project, which you can read about here. I may try again sometime.

In addition to the pacat change I've found that, if for some reason, there is an underrun the latency is increased via increase_watermark() in alsa-source.c and alsa-sink.c. Although that logic may well be helpful in some cases I've found it increases the latency too much and for too long for watching TV via tvtime. I've modified pulseaudio so that it does not increase the latency beyond 50 msecs. The patch is here. It's experimental in the sense that there is probably a better way of solving the problem, but it works well enough for me.

An alternative to pacat the PulseAudio's "module-loopback". However, even with a low "latency_msec" there is still occationally excessive latency that I have not had a chance to track down.