1

Is it possible to set priorities for audio output devices, such that audio will be sent to the appropriate output, depending on what’s plugged in? (That is, hot pluggable: the priority list should be consulted whenever an output is added/removed, while running.)

For example, I’d like it to work like this:

  1. Headphones: If they’re plugged in, they override everything. (Bonus question: What about wireless/BT headphones, rather than with jack?)
  2. External HDMI: Otherwise, play sound through my external monitor.
  3. Onboard: If nothing’s plugged in, use the laptop speakers.

This sort-of-works at the moment, but I notice (anecdotally) that it can sometimes set the wrong output when devices change. (I’m not sure if I’ve established a pattern to its behaviour, though.)

  • Distro: NixOS 23.05 (with Pipewire)
  • Desktop: GNOME 4

1 Answer 1

0

Here's one approach which may work for you. Get a Python script to continuously monitor the output audio devices and then switch to the first available audio output device that you've defined in your own list of priorities. So, create config.py and define your prioritised list of devices (the names of which you can get from the "name" entries in the output of pacmd list-sinks, I've just stuck in some examples):

priority_list = [
    'alsa_output.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.iec958-stereo',
    'alsa_output.usb-Anker_Anker_PowerConf_B600_202012-00.iec958-stereo',
    'alsa_output.pci-0000_00_1f.3.analog-stereo'
]

Then create a Python script which you set running in the background (like in a tmux session or as a system daemon), or even just whenever you press a keyboard shortcut:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import subprocess
import time

from config import priority_list # priority list of output devices (names from `pacmd list-sinks`)

def main():
    print(f'current output device:\n{current_output_device()}\n')
    print(f"available devices:\n{chr(10).join(current_available_devices())}\n")
    print(f'device priority list:\n{chr(10).join(priority_list)}\n')
    print('start monitoring...\n')

    while True:
        available_devices = current_available_devices()
        current_device = current_output_device()

        # Identify the highest priority device available.
        highest_priority_device_available = None
        for device in priority_list:
            if device in available_devices:
                highest_priority_device_available = device
                break

        # Is a prioritised device available and not set as the current device?
        if highest_priority_device_available and current_device != highest_priority_device_available:
            # If yes, set the highest priority device as the current device.
            print(f'change from\n{current_device}\nto\n{highest_priority_device_available}')
            set_default_output_device(highest_priority_device_available)
        elif not highest_priority_device_available:
            print('no prioritised devices are available')
        else:
            pass

        time.sleep(3)

def current_output_device():
    result = subprocess.run(['pacmd', 'list-sinks'], stdout=subprocess.PIPE, text=True)
    output = result.stdout
    lines = output.split('\n')
    sink_name = None
    for line in lines:
        if '*' in line:
            for sub_line in lines[lines.index(line):]:
                if 'name:' in sub_line:
                    sink_name = sub_line.split('<')[1].split('>')[0]
                    break
    return sink_name

def current_available_devices():
    result = subprocess.run(['pacmd', 'list-sinks'], stdout=subprocess.PIPE, text=True)
    output = result.stdout
    lines = output.split('\n')
    _available_devices = []
    for line in lines:
        if 'name:' in line:
            device_name = line.split('<')[1].split('>')[0]
            _available_devices.append(device_name)
    return _available_devices

def set_default_output_device(device_name):
    try:
        subprocess.run(['pacmd', 'set-default-sink', device_name], stdout=subprocess.PIPE)
    except Exception as e:
        print(f'error changing to output device: {e}')

if __name__ == '__main__':
    main()

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.