https://bugs.kde.org/show_bug.cgi?id=524550

            Bug ID: 524550
           Summary: Speaker mute is cleared without user action when an
                    AirPlay (RAOP) sink appears
    Classification: Plasma
           Product: plasmashell
      Version First 6.7.4
       Reported In:
          Platform: Fedora RPMs
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: Audio in general
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]
  Target Milestone: 1.0

Created attachment 195346
  --> https://bugs.kde.org/attachment.cgi?id=195346&action=edit
journalctl window around one occurrence (2026-08-21 10:49:13), showing the
raop_sink node creation one second before the mute cleared. Device name and LAN
IP redacted.

Hi! This report was generated with the help of an automated tool and manually
reviewed. It follows and matches the symptoms of the problem that I have been
having on this new ferora asahi install and provides insight that I think
should be valuable.

Thanks for all the work!
-- Romain

---
STEPS TO REPRODUCE

Not reproducible on demand; observed 7 times over ~2.5 days on one machine.
Conditions:

1. Have PipeWire's libpipewire-module-raop-discover enabled (it is by default
via /usr/share/pipewire/pipewire.conf.d/50-raop.conf) and an AirPlay receiver
on the LAN that advertises intermittently over mDNS -- here a laptop that comes
and goes, producing a raop_sink node roughly hourly.
2. Mute the default sink.
3. Wait. When the RAOP sink next appears, the mute clears by itself.

Note: creating a plain null sink (pactl load-module module-null-sink) does NOT
reproduce it -- 3 trials, mute held every time. So the trigger is not merely "a
new sink appears"; something specific to the RAOP sink matters (ports, or
briefly contending for default sink). Narrowing that is the main open question
below.

OBSERVED RESULT

The default sink's mute flips yes -> no with no user action, and the next
notification sound is audible. The volume level is unchanged across the event
(90% before and after); only mute changes. The volume OSD appears and the
audio-volume-change feedback sound plays, as if the volume had been adjusted.

EXPECTED RESULT

A deliberate mute survives an unrelated sink appearing or disappearing.

ATTRIBUTION (dbus-monitor --session, sender resolved to PID)

Captured at the instant of one occurrence, 2026-08-21 10:49:13 local:

    method call sender=kded6(pid 1903) -> destination=org.kde.plasmashell
        path=/org/kde/osdService; interface=org.kde.osdService;
member=volumeChanged
    signal      sender=plasmashell(pid 1930)
        path=/org/kde/osdService; interface=org.kde.osdService;
member=osdProgress

No globalShortcutPressed from kwin_wayland accompanies it, so no media key is
involved. (The capture does contain one globalShortcutPressed 388s earlier --
that is a deliberate self-test emitted by my tracing script, with no matching
Released. It is not part of the event.)

One second earlier, WirePlumber logs the new sink:

    mod.raop-sink: sess.latency.msec 250.000000 should be an integer multiple
of rtp.ptime
    create-item.lua: creating item for node -> si-audio-adapter <WpNode:122>
    state-stream.lua:80: restore values from Audio/Sink:media.name:RAOP to
<name>

SUSPECTED CODE PATH (src/kded/audioshortcutsservice.cpp, master)

osdService.volumeChanged is emitted from only two places: showMute() (line 363)
and showVolume() (line 371). The audio-volume-change sound comes from
playFeedback(). The only non-keyboard path that calls BOTH is
disableGlobalSinkMute() (line 559):

    for (int i = 0; i < m_sinkModel->rowCount(); ++i)
        applyGlobalSinkMute(m_sinkModel->index(i, 0), false,
globalMuteMutedDevices);
    ...
    showMute(volumePercent(sink->volume()));   // -> volumeChanged
    playFeedback(-1);                          // -> audio-volume-change

That matches every symptom: mute cleared on all sinks, volume untouched, OSD
shown, feedback sound played. It is reached from handleSinkDataChanged() line
498:

    } else if (globalMute && (volumeChanged || (mutedChanged && isUnmuted))) {
        // Changes to volume or unmuting break global mute
        disableGlobalSinkMute();
    }

OPEN QUESTION / WHERE I GOT STUCK: that branch is guarded by globalMute, but
GlobalMuteSinks defaults to false and this user's plasmaparc is empty, so the
guard should read false. Either the guard is satisfied some other way at
runtime, or there is a different path into disableGlobalSinkMute() / to a mute
write. I could not close this from static reading and would appreciate a
pointer.

TWO DEFECTS VISIBLE REGARDLESS OF WHICH PATH FIRES

1. changeVolumePercent() line 246 writes mute unconditionally on every volume
change:

       device->setMuted(newPercent == 0);
       device->setVolume(newVolume);

   With a zero or non-zero-crossing delta this still clears a deliberate mute.
It should only touch mute when the volume actually crosses zero.

2. disableGlobalSinkMute() and handleSinkDataChanged() write mute to EVERY sink
in response to ONE sink's property change. A newly appearing device's initial
property notifications are indistinguishable from deliberate user action, so
rows arriving via rowsInserted should not be treated as "the user unmuted
something".

WHAT THIS IS NOT

- Not WirePlumber. state-stream never logs "setting props on <the sink>"; it
only saves the node's props afterward, recording the change rather than causing
it. Distinct from WirePlumber issue #974 / commit 5941c9f4 (mute lost across
node recreation), which is a separate confirmed bug on this machine.
- Not the notification sound. A libcanberra stream with media.role=alert and
media.name=audio-volume-change accompanies every occurrence, but it is the OSD
feedback sound, i.e. an effect. Muting and then playing that exact sound does
not clear the mute (3 trials, held every time).
- Not a media key, per the DBus capture above.

CORRELATION

RAOP appears in the same second in 5 of 7 occurrences. The other two show only
the alert stream and no new sink, so a new RAOP sink is the main trigger but
likely not the only path in.

Setting module.raop = false in a PipeWire drop-in stops the RAOP sink appearing
and so far avoids the problem, but that is a workaround, not a fix.

SOFTWARE VERSIONS

Operating System:       Fedora Linux Asahi Remix 44 (KDE Plasma Desktop
Edition)
KDE Plasma Version:     6.7.4
KDE Frameworks Version: 6.28.0
Qt Version:             6.11.1
Kernel Version:         7.1.6-400.asahi.fc44.aarch64+16k (64-bit, aarch64)
Graphics Platform:      Wayland
plasma-pa:              6.7.4-1.fc44
PipeWire:               1.6.8-1.fc44
WirePlumber:            0.5.14-1.fc44

Hardware note: Apple MacBook Air (J313) under Asahi Linux. The default sink is
audio_effect.j313-convolver, asahi-audio's software DSP filter node. The
trigger side is stock, though -- libpipewire-module-raop-discover is a standard
PipeWire module, so this should not be Asahi-specific.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to