[PATCH 00/13] loopback: Make module-loopback honor requested latency (v5)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 15.11.2015 22:08, Alexander E. Patrakov wrote:
> 25.02.2015 23:43, Georg Chini wrote:
>> Hello,
>>
>> this is the split version of the module-loopback patch. The patch 
>> optimizes the latency
>> initialization and regulation of the module.
>> Many thanks to Alexander Patrakov for splitting and reviewing the 
>> patch and also for
>> his contribution to the code.
>>
>> Georg Chini (13):
>>    loopback: Fix the obviously-wrong "buffer+=buffer" logic
>>    loopback: Save the timestamps when we snapshot latency
>>    loopback: Improved estimation of latency
>>    loopback: Adjust rates based on latency difference
>>    loopback: Don't track max_request and min_memblockq_length
>>    loopback: Restart the timer right away
>>    loopback: Refactor latency initialization
>>    loopback: Track underruns and cant-peek events
>>    loopback: Track the amount of jitter
>>    loopback: Added a deadband to reduce rate hunting
>>    loopback: Don't change rate abruptly
>>    loopback: Validate the rate parameter
>>    loopback: add parameter buffer_latency_msec
>>
>>   src/modules/module-loopback.c | 578 
>> ++++++++++++++++++++++++++++++++----------
>>   src/pulse/sample.c            |   5 +-
>>   2 files changed, 443 insertions(+), 140 deletions(-)
>>
>
> I have tested this code again. Here is my opinion.

Thank you a lot!

>
>
> It would be logical enough to consider the state of the original 
> loopback module, and compare the state of affairs after 04/13 and 
> after 13/13 to it. And compare to other implementations of adaptive 
> resamplers as well.
>
> To test the thing, I used my laptop (that has an internal sound card) 
> and my desktop (that has an internal PCH soundcard, also I connected 
> my Rotel RA-1570 amplifier via USB).
>
> On the laptop, I created a 5-minute wav file containing a sine wave 
> with 1 kHz frequency. I connected the laptop headphone output to the 
> desktop PCH line input, and used the loopback module to transfer audio 
> from PCH to the Rotel amplifier. In all cases, the nominal sample rate 
> 44100 Hz was used. In all cases, the sound sent to the Rotel amplifier 
> was recorded into a wav file, and the resulting file was analysed with 
> a Python script.
>
> I have tried two techniques for instrumenting the loopback module.
>
> First, I thought that I can record the sound sent to the Rotel sound 
> card by recording from PulseAudio monitor source. However, this turned 
> out to be a bad idea: the existing loopback module behaved differently 
> (according to its log) when its sink was monitored. Namely, it 
> immediately converged to 44100 Hz - something that it could do only 
> after two minutes without such monitoring.
>
> So, I created this snippet in .asoundrc:
>
> pcm.savefile {
>     type file
>     format "wav"
>     slave.pcm "hw:PCUSB"
>     file "/tmp/save.wav"
> }
>
> and loaded module-alsa-sink so that PulseAudio saved its output to a 
> file.
>
> The most important result is that the old module, both when monitored 
> and when not monitored, does not respect the requested latency in this 
> test case. It settles down to something between 300 and 400 ms, even 
> though 200 ms was requested. There is no such bug with the new 
> implementation.
>
> Now let's talk about the script. It is attached. A mono wav file 
> containing some silence, recording of a resampled 1 kHz sine wave, and 
> some silence again, should be passed as an argument. The script will 
> try to estimate the phase of the sine wave (y = A * sin(omega * t + 
> phi), the script tries to estimate phi) at various points in time, and 
> plot the result. The plot will generally contain some linear trend 
> (due to unsynchronized clocks) and some wobble around that. Then it 
> will plot again, with this linear trend eliminated (i.e. will estimate 
> the phase again using the correct signal frequency in terms of the 
> sink clock). Both results are saved as png files, twice (first the 
> full version, then only the second half, to hide the startup process).
>
> I chose this metric because it was used by Fons Adriaensen when 
> discussing the merits of zita-ajbridge:
>
> http://kokkinizita.linuxaudio.org/linuxaudio/zita-ajbridge-doc/quickguide.html 
>
>
> Both my plots and the plots on his page show the unwrapped phase in 
> degrees. Phase, in our case, is related to the delay. 360 degrees = 1 
> millisecond = 34 cm of air. In other words, 1 degree = 2.77 us = 0.94 
> mm of air.
>
>
> The first result (https://imgur.com/a/twBEk ) is actually without any 
> loopback module. Just a recording from the line input. We can clearly 
> see that there is some clock skew between the laptop and desktop 
> soundcards, and that the clock ratio is even not constant. OK - this 
> is a reference how good any loopback module can be.
>
> The second result (https://imgur.com/a/eVahQ ) is with the old module. 
> You see that, with it, the rate oscillates wildly and then snaps to 
> 44100 Hz. However, the final latency is not correct, and it 
> accumulates due to clock mismatch between the two sound cards in the 
> desktop. I have not waited enough to see what happens next, but the 
> result is clearly invalid.

I wonder why the phase is not continuously growing in the final plot. I 
would expect this
because the controller snaps to the base rate.

>
> The next result (https://imgur.com/a/Bi2Yz ) is with the new module, 
> as of PATCH 04/13. There is some "rate hunting", and it results to 
> some noise in phase. It is generally contained between -150 and 200 
> degrees.
>
> Next, let's see (https://imgur.com/a/yQGWL ) what the deadband 
> achieves. Result: it helps against rate hunting, but not much against 
> phase oscillations. This is because new_rate is snapped to base_rate, 
> which would only be correct if the cards actually shared the common 
> clock. So here is what happens: the module runs for some time with 1:1 
> resampling (i.e. with the rate strictly equal to 44100 Hz), and the 
> latency difference slowly drifts due to the clock mismatch. When the 
> difference becomes bigger than the error of its measurement, the 
> module corrects it, and then runs at 44100 Hz for some time again.
>

Could you use 2 s as adjust time? 10 seconds is definitely too slow to 
achieve good results.
I would expect a similar result but with much smaller amplitude of the 
phase oscillation.

>
> However, I'd argue that this phase metric can be improved without the 
> deadband and beyond what this deadband-based implementation provides.
>

What is the problem with the dead band? There are physical limitations 
to what makes
sense to correct and the dead band just takes care of those limits.

>
> First, see (https://imgur.com/a/P5Y0A ) what happens, at PATCH 04/13, 
> if we just decrease adjust_time to 1 second. The rate oscillations 
> become bigger, but phase oscillations stay of the same order as with 
> the default value of adjust_time (10 seconds). So not good.
>
> Then, let's change the rate controller so that it does not attempt to 
> correct all of the latency difference in one step. The value of 
> adjust_time is still 1s, but let's aim to correct the difference after 
> 10 steps by a factor of 2.71. For simplicity (and for testing purposes 
> only), let's destroy the non-linear part of the controller. Here is 
> the code:
>
> static uint32_t rate_controller(
>                 uint32_t base_rate,
>                 pa_usec_t adjust_time,
>                 int32_t latency_difference_usec) {
>
>     uint32_t new_rate;
>
>     new_rate = base_rate * (1.0 + 0.1 * 
> (double)latency_difference_usec / adjust_time);
>
>     return new_rate;
> }
>
> As you can see (https://imgur.com/a/eZT8L ), the phase oscillations 
> are now much more limited!
>
> I have received a private email from Georg where he proposes to use 
> the deadband, but snap to something other than base_rate, e.g. to a 
> result of some slow controller. For me, this is equivalent to using 
> this slow controller, i.e. a good idea.
>

I'll send you a patch privately later today. I kept the controller and 
implemented a more or less
independent controller which takes care of the clock skew / latency 
drift. I'd like to discuss
this approach first with you before I send another patch to the list.

>
> Finally, let's compare Georg's code to known competitors.
>
> alsaloop (part of alsa-utils): https://imgur.com/lvZLoli . Could not 
> settle after 300 seconds, so I am not uploading any processed plots.
>
> alsa_in (part of jack1): https://imgur.com/a/9PnW3 . Rather big 
> oscillations, definitely worse than what's proposed here.
>
> zita-ajbridge: could not run it, because it deadlocks at the start. 
> Could be the same as 
> https://bugs.launchpad.net/ubuntu/+source/zita-ajbridge/+bug/1368716
>
>
> Also, let's consider the limitations of what we can achieve with 
> different controllers.
>
> First, power-saving proponents will not let the latency be sampled 
> more often than once in 100 ms by default. Thus, we can only adjust 
> rates once in 100 ms. It is a PulseAudio limitation that rate is an 
> integer. If the correct rate is 44100.5 Hz, then a controller can only 
> set it to either 44100 or 44101 Hz. In any case, that's 1/88200 
> seconds per second of clock skew. Per our 100 ms period of sampling 
> the latency difference, that's 1.13 us. With a 1 kHz test signal (as 
> used in all examples above), that's about 0.4 degrees of phase. So, 
> "rate is an integer" is not an important limitation - the 
> theoretically achievable resolution is only slightly worse than the 
> natural clock instability of consumer soundcards.
>
>
> TL;DR: I'm in favor of merging up to PATCH 05/13, and, if someone else 
> reviews patches 06 and 07, would recommend merging up to PATCH 08/13. 
> I would recommend waiting two weeks before merging further patches, as 
> the deadband stuff is likely to get undone and redone if the rate 
> controller gets replaced. And I am going to experiment further with 
> different rate controllers on the next weekends.
>



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux