GNOME Per-Application Volume Control Explained

This article explores how the GNOME desktop environment dynamically manages audio stream volumes for individual applications. It covers the interaction between the GNOME user interface and underlying sound servers like PipeWire and PulseAudio, explaining how audio streams are created, identified, adjusted in real time, and persisted across sessions.

The Underlying Sound Server Architecture

GNOME does not process audio signals directly. Instead, it interfaces with a system sound server—traditionally PulseAudio, and in modern distributions, PipeWire operating via its PulseAudio-compatibility layer (pipewire-pulse).

When an application plays sound, it creates a playback client that requests an audio output stream. The sound server represents this stream as an independent entity—a "sink input" in PulseAudio or a "stream node" in PipeWire. Each stream is isolated from other active streams, maintaining its own volume, mute state, channel map, and routing target before the server mixes them together into the hardware master output (the sink).

Real-Time Communication via libgvc

GNOME communicates with the audio server through an internal library known as libgvc (GNOME Volume Control), which wraps the standard PulseAudio client API.

  1. Event Listening: When an application opens an audio stream, the sound server broadcasts an event over a local UNIX domain socket. libgvc listens for these events and instantiates an internal representation of the stream.
  2. Metadata Extraction: The sound server attaches metadata properties to the stream, such as application.name, application.id, application.icon_name, and process IDs (PID). GNOME uses this metadata to associate the stream with a visible desktop application and display the correct icon and label.
  3. Volume Modulation: When a user moves an application's volume slider in GNOME Settings or a panel extension, GNOME sends an asynchronous command back to the sound server via the API (e.g., pa_context_set_sink_input_volume). The sound server applies digital gain adjustments directly to that specific stream buffer in memory without modifying the global hardware master volume.

Dynamic Stream Lifecycle

Audio streams in GNOME are strictly dynamic. An application does not appear in GNOME’s volume settings merely by being open; it must actively register an output stream with the sound server.

Once an application ceases playback and closes its stream, the sound server destroys the sink input node. libgvc catches the removal event, causing GNOME to immediately remove the corresponding slider from the interface to prevent stale controls.

Volume Persistence with Session Managers

To prevent individual application volumes from resetting when an application closes or pauses playback, the sound server relies on a session manager:

  • PulseAudio: Uses module-stream-restore. This module stores volume levels in an internal database, keyed to the application identifier (such as the binary name or .desktop file name).
  • PipeWire: Employs WirePlumber or the legacy pipewire-media-session. WirePlumber maintains state files in the user's home directory that record routing preferences, stream volumes, and mute states mapped to application properties.

When an application starts a new stream, the session manager intercepts the creation request, checks its persistent database for a matching application key, and applies the saved volume offset before the audio begins playing.

Sandboxed Applications and Portals

For sandboxed applications, such as those running via Flatpak, dynamic stream control involves the XDG Desktop Portal system. The sandboxed client interacts with PipeWire either directly or through the portal, which validates permissions. PipeWire preserves the application's unique sandbox ID throughout the pipeline, enabling GNOME to accurately identify, label, and isolate the sandboxed process's audio stream just like a native host process.