Bug 439537

Summary: Kwave audio stutters when loading files or redrawing UI
Product: [Applications] kwave Reporter: nyanpasu64 <nyanpasu64>
Component: generalAssignee: Thomas Eschenbacher <Thomas.Eschenbacher>
Status: REPORTED ---    
Severity: normal    
Priority: NOR    
Version First Reported In: 21.04.2   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description nyanpasu64 2021-07-06 02:18:43 UTC
SUMMARY
If I begin Kwave audio playback, and load new files or perform UI operations, the audio stutters.

STEPS TO REPRODUCE
1. Open Kwave and open an audio file.
2. Begin playback.
3. Change the zoom level, or load a new audio file.

OBSERVED RESULT
Playback stutters while Kwave is working.

EXPECTED RESULT
Playback does not stutter.

SOFTWARE/OS VERSIONS
Operating System: Arch Linux
KDE Plasma Version: 5.22.2
KDE Frameworks Version: 5.83.0
Qt Version: 5.15.2
Kernel Version: 5.12.14-zen1-1-zen (64-bit)
Graphics Platform: X11
Processors: 12 × AMD Ryzen 5 5600X 6-Core Processor
Memory: 15.6 GiB of RAM
Graphics Processor: NVIDIA GeForce GT 730/PCIe/SSE2

ADDITIONAL INFORMATION
I'm looking into Kwave as an alternative to Audacity, given the recent Muse takeover and controversies, as well as reports of poor code quality. Is this stuttering due to Kwave's legacy code design, or due to maintainers who aren't experienced in wait-free audio? Looking at https://invent.kde.org/multimedia/kwave/-/commits/master/, it's been literally 6 months since Kwave's most recent non-metadata commit.

I'm open to contributing or maintaining Kwave, if it's possible to fix or work around whatever degree of technical debt is present, and if existing maintainers want more improvements.

I have some experience in developing audio apps. In order to play audio without stuttering, the general technique is to isolate audio playback on its own thread which progresses without the possibility of blocking on the UI, and communicate between the threads using wait-free constructs (which often require tailoring to a particular application or even restructuring an app around minimizing shared state), mutexes held for as short of a period as possible (easier to retrofit into a program, but less reliable at eliminating stuttering altogether), or performing data races and praying nothing bad happens (OpenMPT does that in some cases and it seems to work).

For background, see http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing. For a real-world example of how Firefox handles wait-free audio, see https://news.ycombinator.com/item?id=27128087.
Comment 1 nyanpasu64 2021-07-06 08:46:38 UTC
It appears the problem is that Qt Multimedia is the default audio backend on Linux: https://github.com/KDE/kwave/blob/master/libkwave/PlayBackParam.h#L31-L39 And Qt Multimedia is unacceptably flawed in that it drives audio from the UI thread, which causes audio to stop being processed when the UI is blocked on another operation.

Switching Kwave's Settings -> Playback to ALSA or "Pulse Audio" fixes the issue (the audio does not block when performing UI operations). However, ALSA results in the playback cursor being positioned around half a second ahead of the actual audio being played (as soon as I press Play with the cursor at the beginning, the playback point appears at ~590 ms), indicating a large amount of buffering far greater than the "1 kB" specified in the Playback settings dialog.

I suspect recording needs to be switched away from Qt Multimedia as well. (IDK if Qt 6 will come with Qt Multimedia APIs not tied to the UI thread, or if Kwave will be killed by the Qt 6 migration because of a lack of users and developers.)

Unfortunately the default playback backend can't easily be changed. The order of playback_method_t controls the order of default playback backends... but reordering the enum to switch default backends results in their options being swapped, because kwaverc's [plugin playback] saves each backend's options by enum index under last_device_N, rather than by name!

(Also I thought Settings -> Playback wasn't a settings dialog at all, but something else. I was confused that I couldn't find a Settings -> Settings menu item, and thought Kwave was completely unconfigurable. Turns out it actually is configurable.)