-
Notifications
You must be signed in to change notification settings - Fork 190
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 playback garbled with mSBC codec #268
Comments
Yes, I was not able to "force" mSBC to work since the beginning of this feature. I suspect that is has something to do with a type of a chip or a type of connection, because I'm able to play mSBC audio only on a chip connected via UART. In all other case I can only receive mSBC audio. Recently I've discovered this: https://marc.info/?l=linux-bluetooth&m=157404481613978&w=2 maybe it is related.... |
I looked at the patch, thanks. But, my Broadcom device is UART, not USB. Are you able to hear any mSBC audio output when played to UART? I'm currently hearing mSBC playback, even though it's garbled. I think it must be very close to working. When I am playing a song, I can definitely make it out, it just has a lot of distortion. I played around with switching endianness and signed/unsigned, but the original S16_LE is definitely correct here. I also tried with MTU's of 48 and 64, but it made no difference. |
So you can hear audio. That's OK. Maybe UART baudrate is too low. Because via my USB chip, there is no sound and when tested with other device I can see missing a lot of mSBC packets (so it is impossible to decode it, hence, silence). With UART chip everything is OK. I haven't tested long playback but some test sound (10-20s) works without a glitch. And I'm using MTU 24. |
I tried MTU 24 and it sounded the same. Could my baudrate be too low for mSBC, even though I can play A2DP SBC audio just fine? Interesting that yours plays correctly. |
I was surprised too :) when I've checked it. If you have two devices try bluealsa-bluealsa with debug enabled. If you will see a lot of "mSBC decoding errors", then it means that some packets were dropped. EDIT: |
I don't have my Pi 3 today, so I'm only using bluealsa as the AG. Not seeing any mSBC errors in the log, although I see where they'd be emitted in the source. I'll report back once I can try out bluealsa-bluealsa. |
@arkq Gave it a try bluealsa to bluealsa. It plays better, but it plays slightly too fast, and starts to get garbled, then pauses for a second and seems to recover slightly, then the process repeats. EDIT: Tried without any patches just to be sure, and it sounded the same. |
@killerkalamari re playing slightly too fast then stalling etc : I've seen that before with RPi; I was testing a2dp not sco at the time, but the solution turned out to be fixing the UART setup. It may be worth just double checking. Are you using For reference, I'm using Raspbian Lite: my hciattach command line is: and my kernel command line is:
The high-speed UART for BT is ttyAMA0, so be sure you do not have |
I think I've hit the same issue. @killerkalamari could you check whether playback with mpg123 produces "better" audio experience? Because what I've observed is that mSBC frames are not delivered to the remote device (tested with two bluealsa: AG and HF). On the HF side I've got dropped whole SBC frames (with mSBC 2 bytes header), which is really strange. With |
One alsa setup difference between mpg123 and aplay is that mpg123 uses a default hw buffer size of 200ms, whereas aplay uses 500ms. Does Also mpg123 requests period time of 1/3 buffer time, whereas aplay requests 1/4 buffer time, so maybe try something like: |
@borine wow, when specifying buffer time the playback experience is same as with mpg123. So that seems to be remedium, however, I still will have to look for the root cause of this issue.... My first guess was the problem with timing during writing data to transparent link. I don't know how it's implemented in kernel, but I guess that the problem might be with underruns on the sco socket, so there is no enough data to be sent during specified timeslot. Or worse, I have to write data on specified timeslot, otherwise it will be discarded. @borine do you have some knowledge in this area? |
My reading of the kernel code is that transparent sco sockets are handled in the same way as cvsd sockets. So I would first look at differences in the application code. To me, the sco mtu of 24 looks suspicious. Unfortunately I'm not able to test this myself atm, but hopefully next week I will have time to set up 2 RPis and see what results I get. If your adapter is USB, I would try an mtu of 48; if it's UART I would try the value got from the kernel. If that makes no difference, then it is time to study the kernel code more carefully. |
@borine Thanks a lot for the workaround! On my device, --buffer-time=100000 sounds even better than 200000. And I think 10000 sounds even better than 100000, but that could be placebo. |
Actually its the period time that is the critical factor. Try a period time of 10000 us - it does not matter what buffer size you use then, |
@borine When I replaced portaudio with plain ALSA what I found that worked for me was that first I called Probably directly related to that, I noticed that if I drop buffer time to even 200000, I start getting XRUNs. I'll experiment with leaving buffer time at 500000 and see if I can fix mSBC with period time alone. |
So the root cause of this issue is the "bursty" nature of the way that libasound passes data to the bluealsa plugin. When I set a period time of 10 ms, libasound translates this to a "period_size" of 320 bytes in the bluealsa plugin. The blusealsa plugin then writes to the pcm fifo in "chunks" of 320 bytes which gives a reasonably smooth flow of data to the daemon. However, when I set a period time of 125 ms (the default used by aplay), the bluealsa plugin is given a period size of 4000 bytes by libasound, When data is written to the fifo in chunks of 4000 bytes, the daemon is faced with a burst of data followed by a drought until the next cycle. The current sco thread code cannot handle this, and ends up missing the tough deadlines for the sco packet writes. The usual solution to "smooth out" bursty data flows is to provide an extra layer of buffering, combined with a delay to the start of output from the buffer until a suitable "threshold" of input is stored in the buffer. This would inevitably increase latency which is very bad for most HFP use cases! I will try to implement a change to the bluealsa plugin that ignores the period size given by libasound and instead writes smaller chunks for sco streams. I don't know if that will have any undesirable side effects, but I'll report back here when I've made some progress. |
The alsa buffer needs to be a minimum of 2 periods - one for the application to be writing to, one for the sound card to be reading from (or vice-versa for a capture device). In practice, at least 3 periods are usually needed to avoid xruns so that there is always one on "standby" so that the timing of i/o is not absolutely critical. When I said:
I was referring to aplay parameters. When programming to the libasound API you should always set an explicit buffer size and period size. For playback streams you should probably also set a start threshold in the sw_params so that the stream will start automatically after the buffer has been "primed" with sufficient data to avoid underruns. The choices you make here are a compromise between the latency / processing requirements of your application and the constraints of the sound card (ie bluealsa in this case). Bluealsa sets a minimum period size of 10 ms and a minimum buffer size of 200 ms. That is why I am recommending a period time of 10 ms for SCO streams - it is the smallest value permissible. The buffer size just needs to hold enough periods so that your application can prevent it running empty. That depends entirely on your application. If not told otherwise, aplay will set the buffer to 4 x period size, which is sufficient for simple playback of wav files. Your application may require a larger buffer if if has more time-consuming decoding to do. The question here is whether the application should be responsible for knowing that SCO streams require very a small period size, or whether bluealsa should take care of it internally. I prefer that bluealsa should deal with it - the necessary change in the plugin code is trivial and so far I have seen no side effects. If it is decided that the application is responsible, then I think bluealsa should at least set an upper bound to the permitted period size in its constraints. Even with the smaller period size, I still get occasional underruns (roughly one every 200 seconds or so), so there may still be some other change required. Also I have not yet tested two-way streams to simulate a HFP dialogue, so there is still lots more work to do. |
Hmm, thanks for such an analysis. I'm also in the middle of investigating this issue. What I've checked is the data transfer from one device to other. And it seems that when data is not transferred properly (e.g. missed time-slot for writing data), the receiving side sees all zeros... I will try to visualize data flow from the writing and reading perspective in the function of time. Maybe is will help... Regarding the burst of data. The SCO IO thread has time synchronization which should prevent writing more data to the socket than it should be written at a given time point. So, I'm not quite convinced that SCO data is written in bursts to socket (but maybe I'm wrong, the visualization will show). |
The data bursts are on the pcm fifo from the plugin. Writing to the SCO socket looks fine, so long as it happens within the required time slots. If the thread spends too long waiting for the next burst of data on the fifo, or perhaps also too much time spent encoding too many frames in the same iiteration, then the sco write will be too late for the baseband "slot" and the controller is forced to write a frame with empty content just to satisfy the SCO link requirement. I'm convinced that the choice is between small chunks on the fifo, or a bigger input buffer in the sco thread. So far I have tried only reducing the chunk size and this dramatically improves the performance of msbc streams; but unfortunately is not sufficient to eliminate all underruns on the remote device. |
With this simple patch to the plugin I get perfect playback with msbc using arecord/aplay on the remote end using MSBC; CVSD also works perfectly:
However, bluealsa-aplay gives very occasional underruns (typically 4 times playing a 10 minute sound file) when using MSBC. I'm going to concentrate on the bluealsa-aplay code now to understand the difference from arecord when using MSBC. I think the above patch to the plugin fixes the underlying issue with the main bluealsa code, and I'm hoping some small tweak to bluealsa-aplay will fix that also. |
@borine Tested against my TaoTronics mSBC device, with |
hmm, "speeded up" usually means audio frames are being dropped. So, this is still only part of the problem. More work required 😞 |
@killerkalamari, @borine please test new master, I think I've "fixed" this issue :) The problem was with the sampling rate synchronization, which causes data to be written to the SCO link too fast. For now I've changed encoding part, but it might be required to fix decoding as well in order to smooth out data flow even more. |
@arkq msbc playback is now much improved using 2 RPis, I'm seeing no underruns (hurrah!). The new code now also shows missing frames and I'm seeing approximately 0.08% of frames are missing; so I think you are right about the decoding needing more work. I think when an incoming packet arrives the daemon needs to read it as soon as possible - otherwise there is a risk the next packet will overwrite it in the controller's buffer and data is lost. |
I've been watching this thread attentively. Even with master at 9eb717f, I get garbled mSBC audio with a Zebra HS3100 headset. I've had to disable mSBC for now. The output looks like this:
|
libsbc returns error code ENOENT (No such file or directory) when the sbc syncword (1st 3 bytes) is invalid, and ESRCH (No such process) when the crc check fails. So it looks like sbc_decode() is being called with invalid data. |
@borine, the issue might be with the reading side being to slow, like you've said, the buffer is overwritten by incoming data. I've refactored decoding part as well (it's not yet on github), but I'm also working on mtu part (so the buffer can be read faster). My approach will be to get mtu from read() call with big buffer. Is seems that read() returns correct mtu for usb and uart chips. It will have some caveat, though. Before correct mtu usage, there will have to be capture connection for given chip for given voice type. What do you think about such approach? |
I'm not certain that the mtu depends on voice type. Have you seen that documented anywhere? My thought was that read() returns whatever data is available - the smallest of:
So I agree that bluealsa should always read as much data as is available by providing a "large" read buffer, and bluealsa needs to be careful not to let the write timing delay also delay a pending read. |
No. But my test app returns (on my USB Intel chip) 48 bytes for CVSD and 24 bytes for transparent during read() call with "big" buffer. And I'm sure that there is more than 48 bytes of data in the kernel/chip buffer, because after establishing connection with my headset I'm waiting 1s (but tcpdump shows hci incoming data all the time). So, I've seen such thing empirically. Anyway, I think that you are right, relying on read() is not the best idea... So, I think I will drop that "feature" :) |
I've just tried a test application, and I see the same packet sizes as you with USB - I have a very old CSR adapter . So that's a difference between USB and UART then, I guess. The kernel reports mtu of 64 bytes. With Broadcom UART I always see reads of 60 bytes with CVSD or mSBC. The mtu of that adapter is 64 bytes. Some other interesting results from this test:
|
I believe that mSBC should now work with both UART and USB adapters provided the you use the latest bluez-alsa code and kernels 5.12 and later, 5.10.18 and later, or 5.11.1 and later. See #400 for details. Closing, as much of the info here is now outdated. |
Audio playback using mSBC is garbled when playing to HF devices supporting HFP_HF_FEAT_CODEC and +BAC 2 (2 is mSBC, per HFP spec Table 3.3). I tried two completely different devices to make sure it wasn't a HF problem. I have not yet tried bluealsa to bluealsa. aplay correctly recognized the sample rate as 16000, so that's good.
The bluealsa source is at commit 740886b with the patch from #266 (comment) applied. Configure options:
--enable-debug --enable-msbc
.@kuikka Tagging for awareness.
The text was updated successfully, but these errors were encountered: