[pulseaudio-discuss] [PATCH v4] Make module loopback honor requested latency

Georg Chini georg at chini.tk
Sat Feb 7 13:14:47 PST 2015

On 07.02.2015 20:50, Alexander E. Patrakov wrote:
> 06.02.2015 14:56, Georg Chini wrote:
>> One more thing: There is a systematic error in the adjust_time I could
>> not work around without
>> introducing too much overhead. The latency snapshot varies widely in the
>> execution time, I
>> measured values between 50 us and more than 60 ms. So if the extreme
>> values follow each other
>> you will have one adjust time that is around 60 ms too long and another
>> one which is 60 ms too
>> short. Maybe this also contributes significantly to the (in)stability of
>> regulation.
> Well, I have looked into this issue.
> Basically, you have a callback, time_callback, which is called every 
> u->adjust_time microseconds, according to a timer. All it does is to 
> send asynchronous messages to the sink input and the source output 
> using pa_asyncmsgq_send(), and they fill in various portions of the 
> latency snapshot by querying the relevant memblockq and the 
> sink/source itself, as well as snapshotting the total length of data 
> received or sent.
> A potential problem is that pa_source_get_latency_within_thread() and 
> pa_sink_get_latency_within_thread() themselves, in the case of an alsa 
> source, go through a smoother (see source_get_latency() in 
> src/modules/alsa/alsa-source.c), which _also_ tries to do sample rate 
> estimation for you! Try to avoid that, even though this means code 
> duplication.

Sorry, but I do not think the smoother is the problem here. I do get 
quite reliable latency results.
The problem is really (if there is a problem at all) the execution time 
of the code. These are not
asynchronously called functions, they wait until they are finished. And 
that exactly is the problem,
sometimes the queue is quite full and so it takes a lot of time until 
the pa_asyncmsgq_send() returns.
The smoother was one of the first things I suspected to be responsible 
for false reports, but I could
not verify it. Since I measure the time for the second call to 
pa_asyncmsgq_send() the numbers
reported look ok.
I think there really is some "latency jitter" that cannot be avoided - 
interrupts that cannot be handled
immediately, USB bus in use when the sound card wants to deliver data 
and you can probably come
up with a lot more situations where it is possible that data cannot be 
delivered on time. You'll never get
perfect results when you measure something and so you cannot apply a 
perfect regulator even
if it would be nice in theory.

> Unfortunately, this is easy to recommend, but I can't really see how 
> this can be done.
> The smoother is updated _after_ a successful write to the alsa device 
> (via traditional UNIX write or via mmap), while the pop callbacks are 
> executed just before that. So, they are called at the moment when the 
> influence from a bad rate estimation via the smoother is the greatest.
> Now the suggestions.
> I think that, ideally, for such use cases, it is important to have 
> timestamped latency snapshots for both sinks and sources in PulseAudio 
> core. This would mean introducing a new message that gets the latest 
> reliable latency snapshot (i.e., timestamp according to the wall 
> clock, send/receive counter, input/output buffer size, and the latency 
> itself), without any interpolation. If the sink does not implement 
> this, just fallback (in the generic sink code) to getting the current 
> latency. Also, because such snapshots for the sink and the source will 
> not happen at the same moment, you have to deal with it.

You can actually try and get both snapshots at the same time. I did this 
and was quite astonished to
find that the results were less reliable this way. I could not figure 
out why. (You can call
get_latency_in_thread() for source and sink from both snapshot functions 
without crashing pulse,
at least when you make sure they are ONLY called from the timer 
function. Something else seems to
call one of the snapshots for whatever reason).

> Also, I have a very heretic thought. Namely, that the smoother in the 
> alsa sink and source may actually be a bad idea and is better removed. 
> I have not tested this. But it is used only in two places: for 
> reporting latency (where it confuses your module) and for calculating 
> the amount of time to sleep 

As I said, I think the latency deviations I see are real and not 
artifacts, so there is no confusion.

> in the case of timer-based scheduling (where even module-alsa-sink 
> does not trust the result, i.e. discards it if it is greater than the 
> non-transformed time interval). And, if I recollect correctly, there 
> were complaints about it being fooled by batch cards, and they were 
> cited as one of the reasons not to enable timer-based scheduling on 
> batch cards. So - maybe, for the purposes of timer based-scheduling we 
> should just assume the worst case, i.e. the card that is, say, 0.75% 
> faster than nominal, and use the nominal rate together with the latest 
> snapshot time in {source,sink}_get_latency()? Basically, the fear is 
> that the smoother makes a greater mistake in the estimated rate than 
> just assuming the nominal one. Maybe you can try this suggestion?

For timer based scheduling the regulator works perfect, you would not 
even need a stop criterion,
so why bother?

> For Tanu's patch status page: please leave the status of this patch as 
> unreviewed. The general idea of the patch does not look brilliant, but 
> it's the best known-working idea that we currently have on the topic, 
> and I have not reviewed all the fine details.

Well from a practical point of view it does a pretty good job although 
the idea may not be brilliant.
I'm willing to implement your better idea when you come up with it.
  Did you ever test it? And compare it to what the current 
module-loopback does?

More information about the pulseaudio-discuss mailing list