Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio clock synchronization issue #451

Closed
mb-arturia opened this issue May 31, 2021 · 12 comments
Closed

Audio clock synchronization issue #451

mb-arturia opened this issue May 31, 2021 · 12 comments

Comments

@mb-arturia
Copy link

mb-arturia commented May 31, 2021

Hi !
I use bluez-alsa on an embedded ARM system and it works really well with a2dp-sink mode and bluealsa-aplay when it can use the real samplerate clock of the DAC, even at low latency (period_time <2ms, 3 periods).

But when I use it with a sampleclock that is not exact, but some 100 Hz lower, there is a bufferization that grows until getting dropped RTP packets (e.g message "Missing RTP packet: 53662 != 53657" after some time, and then every ~2 seconds)

I have this case when I use a dmix pcm device (forcing 48kHz samplerate and low latency), and the alsa samplerate plug to do the resampling from 44100 Hz to 48kHz. In this case the alsa resampling resulting clock is not exactly 44100 HZ: It is rather below 44000 Hz.

My question:
How can the clocks and the data flow be synchronized ?
Is there a min sampleclock precision necessary ?

Thank you in advance
Markus

@borine
Copy link
Collaborator

borine commented May 31, 2021

bluealsa-aplay makes no attempt to adjust for clock drift. If the clock on your soundcard is really so far different from the bluetooth controller clock then either constant underruns or dropped a2dp codec frames are inevitable. What I don't understand is how the dmix plugin and/or the rate plugin can cause such rate errors, since both are driven by the hardware clock of the underlying soundcard. If the soundcard is consuming exactly 480 frames per 10ms from its ring buffer, then the rate plugin will consume exactly 441 frames per 10ms from its buffer. There will of course be some increase in jitter (as seen by the application, but not the soundcard), but that will not affect the rate that frames are consumed over any period greater than 10ms.

Also, given that A2DP latency is at least 100ms (or 40ms for so-called "low latency" codecs), and sometimes greater than 300ms, what is the expected benefit of running the soundcard with such a low period time? If you increase the period time to 20ms or 100ms does that reduce the frequency of dropped bluetooth packets?

@mb-arturia
Copy link
Author

mb-arturia commented May 31, 2021

Thank you for your fast response.
One of the main developers of ALSA explained me that "alsa-lib rate plugin tries to align the rate with the period size (in some level), so 44.1kHz to 48kHz isn't exactly from 44.1kHz".
The error grows with smaller period and buffer sizes. (I use 3 periods of 64 samples (1.33 ms) at 48 kHz). I need the low latency setup for my second application running on the system.
The bluealsa-aplay application resulting format for 44.1 kHz is: period_size=58 and buffer_size=176 samples. This seems to give a sample rate of 58 * 48000 / 64 = 43500 Hz or 176 * 48000 / (3 * 64) = 44000 Hz, I am not sure.

When I chose a setup with a bigger latency, e.g. 3 periods of 640 samples (13.3 ms) at 48kHz, for resampling 44.1kHz I get a 3 periods with period_size=588. (588 * 48000 / 640 = 44100 Hz). This is the exact wanted sample rate.
In this case bluealsa-aplay works perfectly like a charm.

Markus

@borine
Copy link
Collaborator

borine commented May 31, 2021

OK, so it seems the rate plugin copies the hw period_time? Thinking about it now I guess that's inevitable as it is event-driven by hw interrupts via poll(). I also guess the real rate is not critical when reading from a file (although pitch will be too low and playtime too long)

A hw period size of 64 frames @ 48000Hz gives period time 1.3333333 ms
1.333333 ms @ 44100Hz gives target period size 58.79999853 frames

So rate uses the hw period time of 1.333333ms but rounds the number of frames to read in each period down to 58
This results in an effective rate of 43500Hz - woefully slow.

I think the moral is: don't use the rate plugin with low latency setups!

@mb-arturia
Copy link
Author

Unfortunately yes...
But as you said bluealsa-aplay makes no attempt to adjust for clock drift, this means that I have to look for a custom solution to do a better resampling...
Nevertheless thank you for your quick help

@borine
Copy link
Collaborator

borine commented May 31, 2021

Here's a simple(-ish) .asoundrc that might be good enough for resampling bluetooth audio:

pcm.mydmix {
        type dmix
        ipc_key 1024
        ipc_key_add_uid true
        slave {
                pcm "hw:0,0"
                rate 48000
                period_size 64
                periods 3
        }
}

pcm.proxy_resample {
        type file
        file "|aplay -r 48000 -c 2 -f s16_le -D mydmix -"
        format "raw"
        slave.pcm null
}

pcm.myplugdmix {
        type plug
        slave {
                pcm proxy_resample
                rate 48000
                format s16_le
                channels 2
        }
}

Might need a bit of refinement, but hopefully you will at least get audio for more that a few seconds from

bluealsa-aplay --pcm=myplugdmix

@mb-arturia
Copy link
Author

Thank you for the idea with the pipe aplay.
It works quite good with this and the resample problem is solved.
I just have some xruns in aplay at start during some hundreds of milliseconds, even with a SCHED_FIFO rt priority, but it works.
If I find the refinement for this I will post it here :-)

@mb-arturia
Copy link
Author

Would it make sense to support only samplerate 48000 in bluealsa, or would it break compatibility with clients ?
I saw that on my Windows I can only select samplerate 44100, whilst on MacOS only samplerate 48000 is available.

@borine
Copy link
Collaborator

borine commented Jun 1, 2021

The bluetooth spec mandates only the SBC codec for A2DP, and requires that a sink supports both 48kHz and 44.1kHz sample rates, but a source need only support one of them. So for SBC at least, you may find that some devices will not work if your sink does not support both rates. I don't know about other codecs though.

@arkq
Copy link
Owner

arkq commented Jun 1, 2021

would it make sense to support only samplerate 48000 in bluealsa, or would it break compatibility with clients?

You can create an option similar to the --a2dp-force-audio-cd, but be aware of what @borine has mentioned. For standard codecs, spec requires 48kHz and 44.1kHz to be available in sink, so source can select which one it wants to use. In most cases it select higher sampling rate, which might produce better sound (?) experience - that's why there is an option --a2dp-force-audio-cd (which will not work at all if sink does not support 44.1kHz). For proprietary codecs it's hard to tell, but I would assume that they follow standard spec for 48kHz and 44.1kHz samplings.

@borine
Copy link
Collaborator

borine commented Aug 23, 2021

There has been a significant change in the ALSA rate plugin recently that has improved the operation of the ring buffer and may possibly have also reduced, or even eliminated, the rounding error. The HEAD of master branch in alsa-lib is currently broken, but if you build alsa-lib from, say, commit alsa-project/alsa-lib@1a1f0fb you will hopefully see much better accuracy from the rate plugin with small period sizes. The actual commit that seems to have made the difference is alsa-project/alsa-lib@5089358

@borine
Copy link
Collaborator

borine commented Sep 11, 2021

On further testing, I've found that the recent ALSA rate plugin changes have not made any difference to the rounding error in the period size calculation, so I think that for such low period time requirements the only hope is that PR #459 is completed such that bluealsa-aplay can itself compensate for the rate plugin error. That is not ideal as it will inevitably degrade the audio quality, but at least better synchronization should become achievable.

For others reading this who do not need such very low latency, I've written a tip on how to avoid the rounding error by carefully selecting the period time, and submitted this as a change to the bluealsa-aplay manual page, PR #492

@borine
Copy link
Collaborator

borine commented Sep 17, 2021

With thanks to @mb-arturia for the tip on rate plugin rounding errors, the latest update to the bluealsa-aplay manual now documents how to avoid rate conversion timing errors by carefully choosing the bluealsa-aplay pcm period time and the dmix period time. That does not directly help in the specific case here where other system constraints force the use of a period time that does not comply with the guidance in the manual page, however as the problem lies within the ALSA plugin code I do not see that as a BlueALSA issue. There is a possibilty that PR #459 may offer a solution, so please follow progress on that PR if you need to use such small period times with bluealsa-aplay.

@borine borine closed this as completed Sep 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants