Audio Issues Qubes OS 4.2: Stuttering/Jerkiness

I keep spending hours and hours debugging. It always works afterward. But in the end, it’s not really a solution to the root cause. Sooner or later, in a few weeks, I’ll be stuck with another audio problem like this. It happened again yesterday. After hours of it, I’d had enough and put dom0 into suspend.
Today I noticed that, miraculously, the audio is working in all the AppVMs.

My system:
Qubes OS 4.2
Apps all debian 13
no sys-usb
no sys-audio

The current solution is probably to suspend dom0 and then resume it.

About the problem: It was even more complicated because the audio on YouTube in Firefox was working. Only espeak-ng was stuttering. But as soon as espeak-ng was running while YouTube was playing, YouTube started stuttering too.
So basically, the audio wasn’t completely broken, just partially.
And on top of that, it’s impossible to reproduce this problem.

A few weeks ago, this problem also occurred during a Zoom webinar (via Firefox).

So Firefox with YouTube must be doing something differently than Firefox with Zoom.

Summary:
On AppVM1:
espeak-ng: stutters

On AppVM2:
espeak-ng: stutters
Firefox YouTube: works fine
espeak-ng + Firefox YouTube: both stutter

On AppVM a few weeks earlier:
Firefox YouTube: I don’t remember
Firefox Zoom: stutters
Firefox YouTube + Firefox Zoom: I don’t remember
Zoom native app: stutters

The AI says it’s a “PulseAudio buffer latency creep.”
Can anyone confirm or deny this?

I don’t have any issues with it.

Qubes OS 4.2 is EOL, so you should upgrade to Qubes OS 4.3 first, maybe this will fix your audio issue as well.

The audio issue is so difficult to identify because several factors interact with each other.

#1 The underlying problem is this bug:

The workaround “suspending the system and then waking it up” always works with the apps I’ve tested: mpv, espeak-ng

I put marmarek’s code into a script (test.sh) and ran it in a loop.

test.sh
#!/usr/bin/env bash
# Measures how fast VLC can decode audio, to track the post-S3 slowdown from
# https://github.com/QubesOS/qubes-issues/issues/10616
#
#   X ms of audio decoded in Y ms  ->  Y << X is healthy, Y approaching X is the bug.
#   Reference (marmarek): 1018 ms in 7 ms fresh after S3, in 335 ms degraded,
#   4088 ms in 9062 ms while stuttering.
#
# Needs a 44.1 kHz compressed file: wav or 48 kHz opus/webm will not reproduce.

set -uo pipefail

if ! command -v cvlc >/dev/null 2>&1; then
    echo "vlc not found, installing..."
    sudo apt install -y vlc --no-install-recommends
fi

# Default sample lives next to this script, not in the caller's cwd.
DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
FILE="${1:-$DIR/melodigne-quiz-test-187137.mp3}"

if [ ! -r "$FILE" ]; then
    echo "error: cannot read '$FILE'" >&2
    echo "download the default sample from https://pixabay.com/music/beats-quiz-test-187137/" >&2
    exit 1
fi

VLC_VERBOSE=3 cvlc -A afile --audiofile-file=/dev/null \
    --no-loop --play-and-exit --run-time 10 "$FILE" 2>&1 |
awk -v ts="$(date '+%Y-%m-%d %H:%M:%S')" -v file="$FILE" '
    match($0, /samplerate:[0-9]+/) {
        rate = substr($0, RSTART + 11, RLENGTH - 11)
    }
    match($0, /buffering done \([^)]*\)/) {
        split(substr($0, RSTART, RLENGTH), paren, /[()]/)
        split(paren[2], f, " ")          # f = (X, ms, in, Y, ms)
        audio = f[1]; decode = f[4]
        if (decode > 0)
            speed = sprintf("%.1fx realtime", audio / decode)
        else
            speed = ">1000x realtime"
        printf "%s  %s Hz  %s ms audio decoded in %s ms  (%s)\n", \
            ts, rate, audio, decode, speed
        found = 1
    }
    END {
        if (!found) {
            print ts "  no measurement - VLC produced no buffering line; " \
                     "is \"" file "\" a decodable audio file?"
            exit 1
        }
    }
'

As you can see from the numbers here, the “realtime” value gets worse and worse over time.
Slight stuttering starts when the value drops below 2 and after a suspend/wakeup, the sound quality is back to its best.

before suspend:

$ ./test.sh 
2026-09-02 11:34:59  44100 Hz  1018 ms audio decoded in 246 ms  (4.1x realtime)

dom0: systemctl suspend

then after waking up:

$ ./test.sh && sleep 1380 && while true; do ./test.sh; sleep 3592; done
2026-09-02 11:35:38  44100 Hz  1018 ms audio decoded in 3 ms  (339.3x realtime)
2026-09-02 12:00:00  44100 Hz  1018 ms audio decoded in 17 ms  (59.9x realtime)
2026-09-02 13:00:01  44100 Hz  1018 ms audio decoded in 56 ms  (18.2x realtime)
2026-09-02 14:00:02  44100 Hz  1018 ms audio decoded in 111 ms  (9.2x realtime)
2026-09-02 15:00:04  44100 Hz  1018 ms audio decoded in 138 ms  (7.4x realtime)
2026-09-02 16:00:05  44100 Hz  1018 ms audio decoded in 175 ms  (5.8x realtime)
2026-09-02 17:00:06  44100 Hz  1018 ms audio decoded in 217 ms  (4.7x realtime)
2026-09-02 18:00:09  44100 Hz  1018 ms audio decoded in 265 ms  (3.8x realtime)
2026-09-02 19:00:10  44100 Hz  1018 ms audio decoded in 306 ms  (3.3x realtime)
2026-09-02 20:00:11  44100 Hz  1018 ms audio decoded in 337 ms  (3.0x realtime)
2026-09-02 21:00:13  44100 Hz  1018 ms audio decoded in 385 ms  (2.6x realtime)
2026-09-02 22:00:14  44100 Hz  1018 ms audio decoded in 422 ms  (2.4x realtime)
2026-09-02 23:00:16  44100 Hz  1018 ms audio decoded in 446 ms  (2.3x realtime)
2026-09-03 00:00:17  44100 Hz  1018 ms audio decoded in 495 ms  (2.1x realtime)
2026-09-03 01:00:19  44100 Hz  1018 ms audio decoded in 527 ms  (1.9x realtime)
2026-09-03 02:00:21  44100 Hz  1018 ms audio decoded in 559 ms  (1.8x realtime)
2026-09-03 03:00:22  44100 Hz  1018 ms audio decoded in 595 ms  (1.7x realtime)
2026-09-03 04:00:24  44100 Hz  1018 ms audio decoded in 653 ms  (1.6x realtime)
2026-09-03 05:00:26  44100 Hz  1018 ms audio decoded in 683 ms  (1.5x realtime)
2026-09-03 06:00:28  44100 Hz  1018 ms audio decoded in 716 ms  (1.4x realtime)
2026-09-03 07:00:30  44100 Hz  1018 ms audio decoded in 780 ms  (1.3x realtime)
2026-09-03 08:00:31  44100 Hz  1018 ms audio decoded in 790 ms  (1.3x realtime)
2026-09-03 09:00:33  44100 Hz  1018 ms audio decoded in 865 ms  (1.2x realtime)
2026-09-03 10:00:35  44100 Hz  1018 ms audio decoded in 875 ms  (1.2x realtime)
2026-09-03 11:00:37  44100 Hz  1018 ms audio decoded in 921 ms  (1.1x realtime)
2026-09-03 12:00:40  44100 Hz  1018 ms audio decoded in 947 ms  (1.1x realtime)
2026-09-03 13:00:42  44100 Hz  1018 ms audio decoded in 991 ms  (1.0x realtime)
2026-09-03 14:00:44  44100 Hz  1018 ms audio decoded in 1023 ms  (1.0x realtime)
2026-09-03 15:00:46  44100 Hz  1018 ms audio decoded in 1105 ms  (0.9x realtime)
2026-09-03 16:00:49  44100 Hz  1018 ms audio decoded in 1103 ms  (0.9x realtime)
2026-09-03 17:00:52  44100 Hz  1018 ms audio decoded in 1146 ms  (0.9x realtime)
2026-09-03 17:00:52  44100 Hz  391 ms audio decoded in 435 ms  (0.9x realtime)

dom0: systemctl suspend

then after waking up:

$ ./test.sh
2026-09-03 17:07:40  44100 Hz  1018 ms audio decoded in 2 ms  (509.0x realtime)

#2 File 48000Hz/44100Hz/Opus/AAC or App mpv/espeak-ng

None of that matters. I tested these audio formats. Either they all worked, or they all stuttered.
Depending on what the “realtime” value was, as described in #1 above.

$ espeak-ng hello
$ mpv 139.m4a 
● Audio  --aid=1  --alang=eng  (aac 2ch 44100 Hz 48 kbps) [default]
AO: [pipewire] 44100Hz stereo 2ch floatp
A: 00:00:01 / 01:02:01 (0%)
Exiting... (Quit)
$ mpv 140.m4a 
● Audio  --aid=1  --alang=eng  (aac 2ch 44100 Hz 128 kbps) [default]
AO: [pipewire] 44100Hz stereo 2ch floatp
A: 00:00:02 / 01:02:01 (0%)
Exiting... (Quit)
$ mpv 233.mp4 
[ffmpeg/demuxer] aac: Estimating duration from bitrate, this may be inaccurate
● Audio  --aid=1  (aac 2ch 44100 Hz 51 kbps)
AO: [pipewire] 44100Hz stereo 2ch floatp
A: 00:00:02 / 00:59:32 (0%)
Exiting... (Quit)
$ mpv 234.mp4 
[ffmpeg/demuxer] aac: Estimating duration from bitrate, this may be inaccurate
● Audio  --aid=1  (aac 2ch 44100 Hz 133 kbps)
AO: [pipewire] 44100Hz stereo 2ch floatp
A: 00:00:02 / 01:00:50 (0%)
Exiting... (Quit)
$ mpv 249.webm 
client removed during hook handling
● Audio  --aid=1  --alang=eng  (opus 2ch 48000 Hz) [default]
AO: [pipewire] 48000Hz stereo 2ch floatp
A: 00:00:03 / 01:02:01 (0%)
Exiting... (Quit)
$ mpv 250.webm 
● Audio  --aid=1  --alang=eng  (opus 2ch 48000 Hz) [default]
AO: [pipewire] 48000Hz stereo 2ch floatp
A: 00:00:01 / 01:02:01 (0%)
Exiting... (Quit)
$ mpv 250.webm 
● Audio  --aid=1  --alang=eng  (opus 2ch 48000 Hz) [default]
AO: [pipewire] 48000Hz stereo 2ch floatp
A: 00:01:08 / 01:02:01 (2%)
Exiting... (Quit)
$ mpv 250.webm 
client removed during hook handling
● Audio  --aid=1  --alang=eng  (opus 2ch 48000 Hz) [default]
AO: [pipewire] 48000Hz stereo 2ch floatp
A: 00:00:05 / 01:02:01 (0%)
Exiting... (Quit)

#3 Restart the audio system

That doesn’t help either. Whether it’s AppVM or dom0! No improvement. The stuttering is still there.

user@appvm:~$ systemctl --user restart pipewire

user@dom0:~$ systemctl --user stop pipewire pipewire-pulse wireplumber pipewire.socket pipewire-pulse.socket
user@dom0:~$ systemctl --user start wireplumber
user@dom0:~$ systemctl --user start pipewire-pulse

#4 Firefox as a special candidate

In cases where the realtime value is below 2, mpv and espeak-ng cause stuttering when run individually.
When Firefox was playing YouTube, the sound was flawless despite a realtime value of 0.9.
But when I then started espeak-ng, Firefox also started stuttering.
And the opposite was true with mpv. When I ran mpv, it stuttered, but as soon as I ran YouTube in Firefox, the simultaneous audio from mpv was also flawless. Even though the realtime value was 0.9!

Conclusion

On Qubes OS 4.2, the only solution is to suspend and wake up the system.
On Qubes OS 4.3, the fix should already be included in the updates, so the problem shouldn’t occur.

P.S.:
All of my tests were conducted without the sys-audio VM and sys-usb VM, using a USB sound card connected directly to dom0.