Jak utworzyć profil PulseAudio?

Ci, którzy znają mnie dobrze, wiedzą, że PulseAudio przez długi czas spędzało mi sen z powiek i korzystałem z każdej okazji, by się od niego uwolnić. Ale pod naporem różnych czynników i ponieważ niektóre funkcje jak choćby przerzucanie strumieni w locie między urządzenia są dla mnie bardzo przydatne, złamałem się i od jakiegoś czasu przestałem unikać nieuniknionego. Cóż, czasem płynięcie pod prąd przestaje być opłacalne.

W każdym razie, powodem tego postu jest pewna zmiana w konfiguracji PulseAudio, którą naniosłem na swoim laptopie. Domyślna konfiguracja zawierała wyjścia dla głośników/słuchawek i HDMI, ale na osobnych profilach, co uniemożliwiało banalne przerzucanie dźwięku między jednym a drugim, bo najpierw należało wejść w ustawienia dźwięku i zmienić profil. Problemu nie mam na stacjonarnym, bo tam zintegrowana dźwiękówka i HDMI audio od nvidii to osobne urządzenia, widoczne od razu. Cóż, rozwiązaniem jest utworzenie nowego profilu PulseAudio dla urządzenia i to będzie tematem tego wpisu.

Część pierwsza: jak stworzyć profil

Otóż, domyślny profil przechowywany jest w /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf i pod koniec zawiera nawet następujący przykład łączenia wyjść:

; An example for defining multiple-sink profiles
#[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
#description = Foobar
#output-mappings = analog-stereo iec958-stereo
#input-mappings = analog-stereo

Metodą prób i błędów doszedłem więc do następującego profilu łączącego analog-stereo oraz hdmi-stereo (nazwa i opis profilu w pierwszych dwóch linijkach są nieistotne):

[Profile output:analog-stereo+output:iec958-stereo+input:analog-stereo]
description = Combined
output-mappings = analog-stereo hdmi-stereo
input-mappings = analog-stereo

Po przeładowaniu PulseAudio za pomocą pulseaudio -k pojawiła się nowa lista urządzeń, zawierająca zarówno głośniki/słuchawki, jak i wyjście HDMI. Sukces!

Część druga: jak utrwalić nasze zmiany

Jak się jednak domyślacie, bezpośrednie edytowanie configów może być zgubne, bo zmiany mają szansę wylecieć przy najbliższej aktualizacji stosownego pakietu. Nie jest to więc koniec zabawy. Zapiszmy swój zmodyfikowany plik pod inną nazwą. W moim przypadku jest to /usr/share/pulseaudio/alsa-mixer/profile-sets/dell-analog-hdmi.conf.

EDIT: W nowszych wersjach PulseAudio i Alsy profile należy umieszczać w katalogu /usr/share/alsa-card-profile/mixer/profile-sets/

Cóż dalej? Musimy poinstruować PulseAudio, jakiego profilu ma używać dla naszej dźwiękówki, za pomocą odpowiedniej regułki udeva.

W tym celu będzie nam potrzebny jej device i vendor id, które sprawdzamy w następujący sposób:

# udevadm info -a -p /sys/bus/hdaudio/devices/hdaudioC0D0/ | grep subsystem
ATTR{subsystem_id}=="0x102808ca"
ATTRS{subsystem_device}=="0x08ca"
ATTRS{subsystem_vendor}=="0x1028"

Oparta na tych informacjach regułka udeva, zapisana u mnie w /etc/udev/rules.d/99-pulseaudio.conf (dokładna nazwa jest bez znaczenia, byle zaczynała się od 99 i kończyła na .conf) wygląda następująco:

SUBSYSTEM!="sound", GOTO="pulseaudio_end"
ACTION!="change", GOTO="pulseaudio_end"
KERNEL!="card*", GOTO="pulseaudio_end"

ATTRS{subsystem_device}=="0x08ca", ATTRS{subsystem_vendor}=="0x1028", ENV{PULSE_PROFILE_SET}="dell-analog-hdmi.conf"

LABEL="pulseaudio_end"

Jak widać, skopiowałem do subsystem_device i subsystem_vendor jako warunki, po czym ustawiłem zmienną z nazwą pliku z moim własnym profilem. I to powinno wystarczyć, aby nasze ustawienia pozostały na stałe, nawet po aktualizacji PulseAudio.

EDIT: vendor i device id można też podejrzeć za pomocą komendy pactl list cards | grep 'device.*\(\.name\|\.id\)'

Cześć trzecia: bonus

Postanowiłem podzielić się także dwoma skryptami, które w połączeniu z globalnym skrótem w KDE, pozwalają mi na szybkie przerzucanie dźwięku między urządzeniami bez potrzeby wchodzenia w jakiekolwiek gui.

Pierwszy z nich to pa-switch i jest zaadaptowaną wersją rozwiązania znalezionego tutaj. Przenosi wszystkie aktywne strumienie do wybranego odpływu i zmienia domyślne wyjście (moja zmiana).

#!/bin/bash

if [ -z "$1" ]; then
echo "Usage: $0 <sinkId/sinkName>" >&2
echo "Valid sinks:" >&2
pactl list short sinks >&2
exit 1
fi

newSink="$1"

pactl list short sink-inputs|grep -v module|while read stream; do
streamId=$(echo $stream|cut '-d ' -f1)
echo "moving stream $streamId"
pactl move-sink-input "$streamId" "$newSink"
done

pactl set-default-sink $1

Pobierz

Drugi skrypt, hdmi-toggle, korzysta z powyższego, by przełączać się między urządzeniami. Logika mogłaby być bardziej rozbudowana, ale na moje potrzeby w zupełności wystarcza. Przełącza między głośnikami a HDMI lub głośnikami a urządzeniem BT, jeśli jest dostępne.

#!/bin/bash

function get_sink_index() {
pactl list short sinks | grep $1 | awk '{ print $1 }'
}

function get_current_sink() {
LC_ALL=C pactl info | grep 'Default Sink'
}

CURRENT_SINK="$(get_current_sink)"
ANALOG_SINK="$(get_sink_index analog)"
HDMI_SINK="$(get_sink_index hdmi)"
BLUEZ_SINK="$(get_sink_index bluez)"

ALT_SINK="hdmi"
ALT_SINK_INDEX="${HDMI_SINK}"

if [[ ! -z "${BLUEZ_SINK}" ]];
then
ALT_SINK="bluez"
ALT_SINK_INDEX="${BLUEZ_SINK}"
fi

if [[ "$CURRENT_SINK" =~ $ALT_SINK ]];
then
echo "Switching back to analog"
pa-switch $ANALOG_SINK
else
echo "Switching to ${ALT_SINK}"
pa-switch $ALT_SINK_INDEX
fi

Pobierz

EDIT: Zmieniłem jedyne wywołanie pacmd na analogiczne pactl (z wymuszonym angielskim locale, by uniknąć rozbieżności), ponieważ pierwsza z komend nie działała pod PipeWire.

Oba skrypty należy umieścić gdzieś w $PATH i przypisać drugi z nich do dogodnego skrótu klawiszowego. To rozwiązanie w moim przypadku działa znakomicie, a korzystam z niego na kilku maszynach, z odpowiednimi zmianami w nazwach wyjść. Powyższy poradnik nie wyczerpuje tematu i pomija opis mojego procesu dedukcji, ale mam nadzieję, że jest na tyle szczegółowy, że ktoś inny zdoła dostosować moje znalezisko do swoich potrzeb ;)

EDIT2: Przepisałem całkiem skrypt hdmi-toggle, ponieważ doszły mi kolejne urządzenia - słuchawki Bluetooth i headset USB. Postanowiłem więc, że prowizorkę należy zastąpić czymś ładniejszym i tak powstał skrypt audio-toggle. Pozwala on na przełączanie się między kilkoma urządzeniami wymienionych w tablicy SINKS, gdzie klucz to przyjazna dla użytkownika nazwa, a wartość to wyrażenie regularne, które pasuje do nazwy sinka. Nie przedłużając, oto on:

#!/bin/bash

function get_sink_index() {
pactl list short sinks | grep $1 | awk '{ print $1 }'
}

function get_current_sink() {
cat /tmp/cur-sink 2>/dev/null || echo 0
}

declare -A SINKS

SINKS=(
["USB"]="C-Media_Electronics"
["bluez"]="bluez"
["HDMI"]="hdmi"
["Analog"]="4.analog-stereo"
)

SINK_IDS=()

for sink in "${!SINKS[@]}";
do
sink_index="$(get_sink_index ${SINKS[$sink]})"

if [[ ! -z "${sink_index}" ]];
then
SINK_IDS+=("${sink}:${sink_index}")
fi
done

CURRENT_SINK="$(get_current_sink)"
CURRENT_SINK="$((${CURRENT_SINK} + 1))"

[[ ${CURRENT_SINK} -gt $((${#SINK_IDS[@]} - 1)) ]] && CURRENT_SINK=0

IFS=$':' read -r SINK_NAME SINK_ID <<< "${SINK_IDS[$CURRENT_SINK]}"
echo "Switching to ${SINK_NAME}"
pa-switch $SINK_ID
powiadom "Audio: ${SINK_NAME}"
echo ${CURRENT_SINK} >/tmp/cur-sink

Pobierz

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *