Open qrp73 opened 1 month ago
I can't see this getting much attention anytime soon.
I believe this bug is a key factor contributing to choppy sound during audio and video playback, similar to what occurs on devices like the Raspberry Pi Zero 2W.
Therefore, addressing this issue is important to ensure smooth sound playback and avoid potential disruptions in audio performance. Which I believe is very important.
And how long has this problem existed?
it seems that it exists for a long time, just tested it on an old RaspiOS Bullseye and the issue is present, maybe a little bit less worse than on Bookworm. But does it means that it should not be fixed? The sound playback is very important part of computer and should works well.
Any idea why it works different than on other Linux distro?
My first thought is that it may be related to the USB host driver issues, but the issue present even with RPI4 onboard HDMI sound. So, it looks like issue in sound subsystem in general.
My first thought is that it may be related to the USB host driver issues, but the issue present even with RPI4 onboard HDMI sound. So, it looks like issue in sound subsystem in general.
I can't think of any pi specific sound subsystem code that is common to hdmi and usb audio (unless you could suggest where you think the problem lies?).
In which case, it's a problem with upstream code which we are not going to know a lot about.
Is your test app using alsa directly, or going thought pipewire (possibly using the pipewire-pulse shim)?
Is your test app using alsa directly, or going thought pipewire (possibly using the pipewire-pulse shim)?
My test app uses OpenAL (libopenal-dev), which uses backend "pulse". Here is the log:
$ ALSOFT_LOGLEVEL=3 ./test-al 4
AL lib: (II) alc_initconfig: Initializing library v1.19.1-unknown UNKNOWN
AL lib: (II) alc_initconfig: Supported backends: jack, pulse, alsa, sndio, oss, port, null, wave
AL lib: (II) ReadALConfig: Loading config /etc/openal/alsoft.conf...
AL lib: (II) LoadConfigFromFile: found 'decoder/hq-mode' = 'false'
AL lib: (II) LoadConfigFromFile: found 'decoder/distance-comp' = 'true'
AL lib: (II) LoadConfigFromFile: found 'decoder/nfc' = 'true'
AL lib: (II) LoadConfigFromFile: found 'decoder/nfc-ref-delay' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/quad' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround51' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround61' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround71' = ''
AL lib: (II) ReadALConfig: Loading config /etc/xdg/alsoft.conf...
AL lib: (II) ReadALConfig: Loading config /home/pi/.alsoftrc...
AL lib: (II) ReadALConfig: Loading config /home/pi/.config/alsoft.conf...
AL lib: (II) GetProcBinary: Got path: /home/pi/SRC-CS/TestOpenAL/CPP
AL lib: (II) ReadALConfig: Loading config /home/pi/SRC-CS/TestOpenAL/CPP/alsoft.conf...
AL lib: (II) GetConfigValue: Key disable-cpu-exts not found
AL lib: (II) FillCPUCaps: Got features string:fp asimd evtstrm crc32 cpuid
AL lib: (II) FillCPUCaps: Extensions: -NEON
AL lib: (II) GetConfigValue: Key rt-prio not found
AL lib: (II) GetConfigValue: Key resampler not found
AL lib: (II) GetConfigValue: Key trap-al-error not found
AL lib: (II) GetConfigValue: Key trap-alc-error not found
AL lib: (II) GetConfigValue: Key reverb/boost not found
AL lib: (II) GetConfigValue: Key drivers not found
AL lib: (II) GetConfigValue: Key jack/spawn-server not found
AL lib: (WW) jack_msg_handler: Cannot connect to server socket err = No such file or directory
AL lib: (WW) jack_msg_handler: Cannot connect to server request channel
AL lib: (WW) jack_msg_handler: jack server is not running or cannot be started
AL lib: (WW) jack_msg_handler: JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
AL lib: (WW) jack_msg_handler: JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
AL lib: (WW) ALCjackBackendFactory_init: jack_client_open() failed, 0x11
AL lib: (WW) alc_initconfig: Failed to initialize backend "jack"
AL lib: (II) GetConfigValue: Key pulse/spawn-server not found
AL lib: (II) GetProcBinary: Got filename: test-al
AL lib: (II) alc_initconfig: Initialized backend "pulse"
AL lib: (II) alc_initconfig: Added "pulse" for playback
AL lib: (II) alc_initconfig: Added "pulse" for capture
AL lib: (II) GetConfigValue: Key excludefx not found
AL lib: (II) GetConfigValue: Key default-reverb not found
AL lib: (II) GetConfigValue: Key channels not found
AL lib: (II) GetConfigValue: Key sample-type not found
AL lib: (II) GetConfigValue: Key frequency not found
AL lib: (II) GetConfigValue: Key periods not found
AL lib: (II) GetConfigValue: Key period_size not found
AL lib: (II) GetConfigValue: Key sources not found
AL lib: (II) GetConfigValue: Key slots not found
AL lib: (II) GetConfigValue: Key sends not found
AL lib: (II) GetProcBinary: Got filename: test-al
AL lib: (II) GetConfigValue: Key pulse/allow-moves not found
AL lib: (II) ALCpulsePlayback_open: Connecting to "(default)"
AL lib: (II) GetConfigValue: Key ambi-format not found
AL lib: (II) alcOpenDevice: Created device 0x55a8193cd0, "CX31993 384Khz HIFI AUDIO Analog Stereo"
AL lib: (II) UpdateDeviceParams: ALC_FREQUENCY = 48000
AL lib: (II) UpdateDeviceParams: 0x1008 = 0 (0x0)
AL lib: (II) UpdateDeviceParams: 0x1009 = 0 (0x0)
AL lib: (II) GetConfigValue: Key frequency not found
AL lib: (II) GetConfigValue: Key sources not found
AL lib: (II) GetConfigValue: Key sends not found
AL lib: (II) GetConfigValue: Key hrtf not found
AL lib: (II) UpdateDeviceParams: Pre-reset: Stereo, Float, *48000hz, 1114 update size x3
AL lib: (II) ALCpulsePlayback_sinkInfoCallback: Active port: analog-output (Analog Output)
AL lib: (II) GetConfigValue: Key pulse/fix-rate not found
AL lib: (II) GetConfigValue: Key pulse/allow-moves not found
AL lib: (II) ALCpulsePlayback_bufferAttrCallback: minreq=8912, tlength=22280, prebuf=0
AL lib: (II) UpdateDeviceParams: Post-reset: Stereo, Float, 48000hz, 1114 update size x3
AL lib: (II) GetConfigValue: Key stereo-mode not found
AL lib: (II) aluInitRenderer: HRTF disabled
AL lib: (II) GetConfigValue: Key cf_level not found
AL lib: (II) aluInitRenderer: BS2B disabled
AL lib: (II) GetConfigValue: Key stereo-encoding not found
AL lib: (II) aluInitRenderer: UHJ disabled
AL lib: (II) UpdateDeviceParams: Channel config, Dry: 2, FOA: 0, Real: 0
AL lib: (II) UpdateDeviceParams: Allocating 2 channels, 16384 bytes
AL lib: (II) UpdateDeviceParams: Max sources: 256 (255 + 1), effect slots: 64, sends: 2
AL lib: (II) GetConfigValue: Key dither not found
AL lib: (II) GetConfigValue: Key dither-depth not found
AL lib: (II) UpdateDeviceParams: Dithering disabled
AL lib: (II) GetConfigValue: Key output-limiter not found
AL lib: (II) UpdateDeviceParams: Output limiter enabled
AL lib: (II) UpdateDeviceParams: Fixed device latency: 1000000ns
AL lib: (II) GetConfigValue: Key volume-adjust not found
AL lib: (II) alcCreateContext: Created context 0x55a81a24b0
I'm a newbie in Linux audio, so I don't know exactly how it works under the hood. I'm trying to port code originally written for Windows DirectSound (DirectX). In DirectSound, it is possible to assign events that are automatically triggered by the driver during sound buffer playback. This allows user code to simply wait for the event, which is convenient in terms of minimizing CPU load. However, in OpenAL, I don't see such a mechanism - I have to poll the buffer in a loop, which increases CPU load. Perhaps you could suggest a more efficient way to set an event for thread synchronization as the sound buffer plays (I'm interested in a 1/50 second interval).
So audio will be going though: Your app->openal->pulse->pipewire->alsa->kernel driver
so there many places for it to go wrong. Might be worth trying to disable pipewire (and its pulseaudio shim) which will hopefully mean openal uses alsa directly.
The result of that will narrow down the problem somewhat.
so there many places for it to go wrong. Might be worth trying to disable pipewire (and its pulseaudio shim) which will hopefully mean openal uses alsa directly.
Just tried to configure OpenAL to use ALSA directly.
Now OpenAL uses backend "alsa", but the issue still present:
$ ALSOFT_LOGLEVEL=3 ALSOFT_DRIVERS=alsa ./test-al 500
SampleRate: 48000
BufferSize: 960
BufferCount: 500
AL lib: (II) alc_initconfig: Initializing library v1.19.1-unknown UNKNOWN
AL lib: (II) alc_initconfig: Supported backends: jack, pulse, alsa, sndio, oss, port, null, wave
AL lib: (II) ReadALConfig: Loading config /etc/openal/alsoft.conf...
AL lib: (II) LoadConfigFromFile: found 'decoder/hq-mode' = 'false'
AL lib: (II) LoadConfigFromFile: found 'decoder/distance-comp' = 'true'
AL lib: (II) LoadConfigFromFile: found 'decoder/nfc' = 'true'
AL lib: (II) LoadConfigFromFile: found 'decoder/nfc-ref-delay' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/quad' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround51' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround61' = ''
AL lib: (II) LoadConfigFromFile: found 'decoder/surround71' = ''
AL lib: (II) ReadALConfig: Loading config /etc/xdg/alsoft.conf...
AL lib: (II) ReadALConfig: Loading config /home/pi/.alsoftrc...
AL lib: (II) ReadALConfig: Loading config /home/pi/.config/alsoft.conf...
AL lib: (II) GetProcBinary: Got path: /home/pi/SRC-CS/TestOpenAL/CPP
AL lib: (II) ReadALConfig: Loading config /home/pi/SRC-CS/TestOpenAL/CPP/alsoft.conf...
AL lib: (II) GetConfigValue: Key disable-cpu-exts not found
AL lib: (II) FillCPUCaps: Got features string:fp asimd evtstrm crc32 cpuid
AL lib: (II) FillCPUCaps: Extensions: -NEON
AL lib: (II) GetConfigValue: Key rt-prio not found
AL lib: (II) GetConfigValue: Key resampler not found
AL lib: (II) GetConfigValue: Key trap-al-error not found
AL lib: (II) GetConfigValue: Key trap-alc-error not found
AL lib: (II) GetConfigValue: Key reverb/boost not found
AL lib: (II) alc_initconfig: Initialized backend "alsa"
AL lib: (II) alc_initconfig: Added "alsa" for playback
AL lib: (II) alc_initconfig: Added "alsa" for capture
AL lib: (II) GetConfigValue: Key excludefx not found
AL lib: (II) GetConfigValue: Key default-reverb not found
AL lib: (II) GetConfigValue: Key channels not found
AL lib: (II) GetConfigValue: Key sample-type not found
AL lib: (II) GetConfigValue: Key frequency not found
AL lib: (II) GetConfigValue: Key periods not found
AL lib: (II) GetConfigValue: Key period_size not found
AL lib: (II) GetConfigValue: Key sources not found
AL lib: (II) GetConfigValue: Key slots not found
AL lib: (II) GetConfigValue: Key sends not found
AL lib: (II) GetConfigValue: Key alsa/device not found
AL lib: (II) ALCplaybackAlsa_open: Opening device "default"
AL lib: (II) GetConfigValue: Key ambi-format not found
AL lib: (II) alcOpenDevice: Created device 0x55a603e5d0, "ALSA Default"
AL lib: (II) UpdateDeviceParams: ALC_FREQUENCY = 48000
AL lib: (II) UpdateDeviceParams: 0x1008 = 0 (0x0)
AL lib: (II) UpdateDeviceParams: 0x1009 = 0 (0x0)
AL lib: (II) GetConfigValue: Key frequency not found
AL lib: (II) GetConfigValue: Key sources not found
AL lib: (II) GetConfigValue: Key sends not found
AL lib: (II) GetConfigValue: Key hrtf not found
AL lib: (II) UpdateDeviceParams: Pre-reset: Stereo, Float, *48000hz, 1114 update size x3
AL lib: (II) GetConfigValue: Key alsa/mmap not found
AL lib: (II) GetConfigValue: Key alsa/allow-resampler not found
AL lib: (II) UpdateDeviceParams: Post-reset: Stereo, Float, 48000hz, 1114 update size x3
AL lib: (II) GetConfigValue: Key stereo-mode not found
AL lib: (II) aluInitRenderer: HRTF disabled
AL lib: (II) GetConfigValue: Key cf_level not found
AL lib: (II) aluInitRenderer: BS2B disabled
AL lib: (II) GetConfigValue: Key stereo-encoding not found
AL lib: (II) aluInitRenderer: UHJ disabled
AL lib: (II) UpdateDeviceParams: Channel config, Dry: 2, FOA: 0, Real: 0
AL lib: (II) UpdateDeviceParams: Allocating 2 channels, 16384 bytes
AL lib: (II) UpdateDeviceParams: Max sources: 256 (255 + 1), effect slots: 64, sends: 2
AL lib: (II) GetConfigValue: Key dither not found
AL lib: (II) GetConfigValue: Key dither-depth not found
AL lib: (II) UpdateDeviceParams: Dithering disabled
AL lib: (II) GetConfigValue: Key output-limiter not found
AL lib: (II) UpdateDeviceParams: Output limiter enabled
AL lib: (II) UpdateDeviceParams: Fixed device latency: 1000000ns
AL lib: (II) GetConfigValue: Key volume-adjust not found
AL lib: (II) alcCreateContext: Created context 0x55a607a740
0: time=0.0 ms, offset=0, buffers=0
1: time=0.1 ms, offset=0, buffers=0
2: time=0.3 ms, offset=0, buffers=0
<...>
314: time=35.7 ms, offset=0, buffers=0
315: time=35.8 ms, offset=1114, buffers=1 => offset_dt=35.81 ms, delta=1114 => buffer_dt=35.81 ms
316: time=35.9 ms, offset=1114, buffers=1
<...>
686: time=78.3 ms, offset=1114, buffers=1
687: time=78.5 ms, offset=2228, buffers=1 => offset_dt=42.67 ms, delta=1114
688: time=78.6 ms, offset=3342, buffers=3 => offset_dt=0.14 ms, delta=1114 => buffer_dt=42.81 ms
689: time=78.8 ms, offset=3342, buffers=3
<...>
1060: time=121.0 ms, offset=3342, buffers=3
1061: time=121.1 ms, offset=4456, buffers=4 => offset_dt=42.49 ms, delta=1114 => buffer_dt=42.49 ms
1062: time=121.5 ms, offset=5570, buffers=4 => offset_dt=0.36 ms, delta=1114
1063: time=121.6 ms, offset=5570, buffers=5 => buffer_dt=0.48 ms
1064: time=121.7 ms, offset=5570, buffers=5
<...>
Statistics: [sampleOffset] | [processedBuffers]
min dt [ms]: 0.12 | 0.12
max dt [ms]: 44.93 | 44.93
avg dt [ms]: 23.58 | 23.80
min delta samples: 1114
max delta samples: 2228
avg delta samples: 1129
As you can see, it shows the same 1114 sample step and is unable to notify the app about the buffer overrun. You can see that it notifies 2 buffers at once in the 121.0 - 121.6 ms time interval, and before that, there is no sign of a buffer overrun.
Other Linux distro has very stable 512 sample step in this case and it works without buffer overrun. I suspect this 1114 sample step is some specific to Raspberry Pi implementation issue.
Just noticed:
AL lib: (II) UpdateDeviceParams: Pre-reset: Stereo, Float, *48000hz, 1114 update size x3
Where did this 1114 update size
come from?
Is it configured somewhere or hard-coded?
just tried to set period_size = 512
in alsoft.conf
:
## drivers: (global)
# Sets the backend driver list order, comma-seperated. Unknown backends and
# duplicated names are ignored. Unlisted backends won't be considered for use
# unless the list is ended with a comma (e.g. 'oss,' will try OSS first before
# other backends, while 'oss' will try OSS only). Backends prepended with -
# won't be considered for use (e.g. '-oss,' will try all available backends
# except OSS). An empty list means to try all backends.
#drivers =
drivers = alsa
## period_size:
# Sets the update period size, in frames. This is the number of frames needed
# for each mixing update. Acceptable values range between 64 and 8192.
#period_size = 1024
period_size = 512
But it don't works. It uses alsa driver as specified in drivers
variable, but don't have effect on period_size
. On Raspberry Pi OS it still uses 1114.
Here is what OpenAL reports in the log when I set period size=512
:
AL lib: (II) UpdateDeviceParams: Pre-reset: Stereo, Float, *48000hz, 557 update size x3
It's not clear why 557 instead of configured 512???
But actual update size is still 1114, only first update size is 557, all other is 1114:
273: time=32.5 ms, offset=0, buffers=0
274: time=32.8 ms, offset=557, buffers=0 => offset_dt=32.81 ms, delta=557
275: time=32.9 ms, offset=557, buffers=0
<...>
466: time=53.9 ms, offset=557, buffers=0
467: time=54.2 ms, offset=1671, buffers=1 => offset_dt=21.40 ms, delta=1114 => buffer_dt=54.21 ms
468: time=54.3 ms, offset=1671, buffers=1
<...>
661: time=75.2 ms, offset=1671, buffers=1
662: time=75.5 ms, offset=2785, buffers=2 => offset_dt=21.32 ms, delta=1114 => buffer_dt=21.32 ms
663: time=75.6 ms, offset=2785, buffers=2
For comparison, on Arch Linux, changing period_size
has an effect. Furthermore, if it is not configured, OpenAL automatically selects the period_size
based on the requested buffer size. However, on Raspberry Pi OS, it always remains fixed at a value of 1114.
Any idea where it is hard-coded to 1114 on Raspberry Pi OS? Is it firmware code?
Any idea where it is hard-coded to 1114 on Raspberry Pi OS? Is it firmware code?
No. The firmware is not involved in anything like this. Using the kms driver means all audio/display code purely runs on the arm side (and will be open source).
But it don't works. It uses alsa driver as specified in
drivers
variable, but don't have effect onperiod_size
. On Raspberry Pi OS it still uses 1114.
Of note: Pipewire also hijacks alsa clients to make sure everything's under the same umbrella.
There's a difference between ALSA the kernel API (which to my knowledge is also used by Pipewire itself to actually talk to audio devices) and ALSA the userspace library. To check whether something's going through Pipewire, try looking at pw-top
while something is outputting audio. If it shows up there, it's still going through Pipewire.
The package responsible for this is pipewire-alsa
, which you could try uninstalling. Not sure if that will just work without more changes though, I haven't tried it myself in recent years.
Describe the bug
I am debugging my code, which is designed to synchronize precisely with the sound card to maintain a continuous audio stream without interruptions and with minimal latency. However, I have encountered an issue where my code fails to maintain stream synchronization with the sound card on Raspberry Pi OS, while it works fine on other systems (Windows, Linux Arch).
A detailed investigation revealed that on Raspberry Pi OS, the AL_SAMPLE_OFFSET position updates occur in excessively large steps with a long interval, and the update step is quite unusual (not a power of two).
For example, at a sample rate of 48 kHz, the step is typically 1114 samples with an update interval of 30 ms, which is too much to maintain synchronization with a buffer length of 1/50 or 1/60 seconds (to match the display refresh rate). In contrast, on Linux Arch with the same USB sound card, I see a stable update step of 512 samples and an update interval of 10 ms, which is sufficient for most use cases.
Could you please fix it to get 10 ms update and 512 sample steps at 48 kHz sample rate as it works on other OS?
Steps to reproduce the behaviour
1)
sudo apt install libopenal-dev
2) createtest-al.cpp
with the following test code:3) compile
g++ -o test-al test-al.cpp -lopenal
4) run the test for 500 buffers (500/50 = 10 seconds) to collect statistics:./test-al 500
Expected result:
max delta samples: 512
and max dt about 10 ms +- jitterActual result:
For comparison, here is results from Linux Arch with the same USB sound card:
As observed, Raspberry Pi OS can update the sample position by as much as 2228 samples in a single step (and even more!), while the buffer size is just 960 samples. Consequently, it occasionally updates the sample position only once per three buffers, resulting in significant lag and sound streaming breaks.
Device (s)
Raspberry Pi 4 Mod. B
System
Logs
Raspberry Pi OS:
Linux Arch on the same USB sound card:
Additional context
Tested with USB sound card CX31993. But with other sound cards it shows the same issue.