pulsectl

raw JSON →
24.12.0 verified Mon Apr 27 auth: no python

Python high-level interface and ctypes-based bindings for PulseAudio (libpulse). Version 24.12.0 provides an object-oriented wrapper for controlling PulseAudio volume, sinks, sources, and more.

pip install pulsectl
error pulsectl.PulseError: Failed to create mainloop: No such file or directory
cause PulseAudio server not running or socket not accessible.
fix
Start PulseAudio with 'pulseaudio --start' or set PULSE_SERVER environment variable.
error AttributeError: module 'pulsectl' has no attribute 'Pulse'
cause Installed old version (<3.0) or import of wrong module.
fix
Run 'pip install --upgrade pulsectl' and use 'from pulsectl import Pulse'.
error pulsectl.PulseError: Connection refused
cause PulseAudio native protocol socket is not listening (e.g., in container without shared socket).
fix
Forward PulseAudio socket with '-v /run/user/$(id -u)/pulse:/run/user/$(id -u)/pulse' or set PULSE_SERVER=tcp:host:port.
breaking in version 24.12.0, the 'Pulse' context manager no longer uses pulseaudio server string; removed deprecated 'connect_to_server' method.
fix Use Pulse('app-name') with PulseAudio autodetection. Do not pass server string.
deprecated The 'sink_input_set_volume' method is deprecated; use 'volume_set_all_chans' on sink input objects.
fix Replace pulse.sink_input_set_volume(index, volume) with pulse.volume_set_all_chans(sink_input, volume).
gotcha Running without a running PulseAudio server or inside a Docker container will raise a PulseError.
fix Ensure PulseAudio is running (pulseaudio --start) or handle PulseError gracefully.
gotcha Volume values are floats between 0.0 and 1.0 (or greater for pa_volume_t range), not percentages or decibels.
fix Use values in [0.0, 1.0] scale, e.g., 0.5 for 50% volume.

Connect to PulseAudio, list sinks, and set volume.

from pulsectl import Pulse

with Pulse('my-app') as pulse:
    sink_list = pulse.sink_list()
    for sink in sink_list:
        print(sink.description, sink.volume)
    # Set volume of first sink
    if sink_list:
        pulse.volume_set_all_chans(sink_list[0], 0.5)