Lidiando con los identificadores dinámicos de Pipewire

Escrito por Lyoneel el en Solución de problemas
 4 mins

Lidiando con los identificadores dinámicos de Pipewire

Configurando Deckboard descubrí que Pipewire puede alterar los IDs al reiniciar o suspender el dispositivo.

Motivación

Se me ocurrió una idea, utilizar una tabla Amazon Fire 8 como un "StreamDeck" . Configuraba Deckboard para multiples usos, uno de ellos es para cambiar entre dispositivos de audio. Estoy corriendo NixOS y usando de sound server Pipewire.

Cambiando de dispositivo por defecto

Miremos el uso básico de WirePlumber CLI:

web@lyoneel.dev : zsh — Konsole
  • $ wpctl
  • Usage:
  •   wpctl [OPTION…] COMMAND [COMMAND_OPTIONS] - WirePlumber Control CLI
  • Commands:
  •   status
  •   get-volume ID
  •   inspect ID
  •   set-default ID
  •   set-volume ID VOL[%][-/+]
  •   set-mute ID 1|0|toggle
  •   set-profile ID INDEX
  •   clear-default [ID]
  • Help Options:
  •   -h, --help       Show help options
  • Pass -h after a command to see command-specific options
 

En mi caso de uso el comando set-default para cambiar el dispositivo por defecto, pero también necesito saber cual es el ID.

Para obtener el ID necesitamos usar el comando status, status va a dar un montón de información, pero hay que hacer foco en Audio > Sinks.

web@lyoneel.dev : zsh — Konsole
  • $ wpctl status
  • [...]
  • Audio
  • ├─ Devices:
  • │      46. Renoir Radeon High Definition Audio Controller [alsa]
  • │      48. Family 17h/19h HD Audio Controller  [alsa]
  • │      80. Ditoo-audio                         [bluez5]
  • │     132. GSP 370                             [alsa]
  • │  
  • ├─ Sinks:
  • │  *   58. Family 17h/19h HD Audio Controller Analog Stereo [vol: 0.50]
  • │      68. Renoir Radeon High Definition Audio Controller Digital Stereo (HDMI) [vol: 0.40]
  • │      81. Ditoo-audio                         [vol: 0.70]
  • │     103. GSP 370 Analog Stereo               [vol: 0.55]
  • [...]
 

El dispositivo actual marcado por defecto tiene un asterisco *, usando esos IDs se puede cambiar el dispositivo por defecto, por ejemplo, si necesito por defecto GSP 370:

web@lyoneel.dev : zsh — Konsole
  • $ wpctl set-default 103
 

El problema: Los IDs dinámicos

Después de reiniciar o salir de suspensión los IDs potencialmente puede cambiar, para evitar esto vamos a procesar el texto de salida wpctl status de esta manera sabemos cuál es el ID necesario. El nombre con el que el dispositivo se muestra puede cambiar por muchas razones, por eso es preferible utilizar el comando status con --names, de esta manera la lista se va a mostrar con los nombres internos y quitan la posibilidad de tener dos valores iguales.

web@lyoneel.dev : zsh — Konsole
  • $ wpctl status --name
  • [...]
  • Audio
  • ├─ Devices:
  • │      46. alsa_card.pci-0000_06_00.1          [alsa]
  • │      48. alsa_card.pci-0000_06_00.6          [alsa]
  • │      80. bluez_card.XX_XX_XX_XX_XX_XX        [bluez5]
  • │     132. alsa_card.usb-Sennheiser_GSP_370-00 [alsa]
  • │  
  • ├─ Sinks:
  • │  *   58. alsa_output.pci-0000_06_00.6.analog-stereo [vol: 0.50]
  • │      68. alsa_output.pci-0000_06_00.1.hdmi-stereo [vol: 0.40]
  • │      81. bluez_output.XX_XX_XX_XX_XX_XX.1    [vol: 0.70]
  • │     103. alsa_output.usb-Sennheiser_GSP_370-00.analog-stereo [vol: 0.55]
  • [...]
 

Basándose en esta lista, es necesario los valores 58 and 103 para cambiar entre dispositivos, y obtendremos esos números via regex. Hay muchas maneras de lograr el objetivo, pero mi preferencia es usar el comando sed.

Obteniendo el valor

Ahora voy a mostrar los comandos por separado, para dar una explicación breve

1. Status

wpctl status --name

Devolverà el mismo output que tenemos más arriba, con todos los audio sinks.

2. Grep

Usando el output del comando anterior le aplicamos grep usando sinkname.

grep 'sinkname'

Por ejemplo sinkname sería:

grep 'alsa_output.pci-0000_06_00.6.analog-stereo'

Este comando devolverà la línea exácta donde se encuentra el valor:

│ 58. alsa_output.pci-0000_06_00.6.analog-stereo [vol: 0.50]

3. Sed

Usando el output del comando anterior se extrae el valor que se necesita, el comando sed tiene una estructura básica s/<input>/<output>/. El comando a ejecutar es sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/', lo explico por partes:

  • En el input buscamos desde el inicio ^
  • Cualquier caracter [^0-9]* que no sean números del 0 al 9 * quiere decir indeterminada cantidad de veces. En este caso ^ indica negado.
  • () indican captura, todo lo que está dentro del paréntesis es capturado y se utiliza el output usando \1, los paréntesis deben ser escapados \(\).
  • Dentro de la captura se requieren números [0-9]+, el signo + indica uno o más veces, seguido de un punto \. escapado.
  • El resto no importa, ya se tiene el número objetivo, entonces .* es cualquier caracter 0 o intederminadas veces.
  • Y por último en el output se utiliza el valor capturado \1.

El comando completo

wpctl status --name $(echo "$(wpctl status --name | grep 'alsa_output.pci-0000_06_00.6.analog-stereo')" | sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/')

wpctl status --name $(echo "$(wpctl status --name | grep 'alsa_output.usb-Sennheiser_GSP_370-00.analog-stereo')" | sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/')

Solo hay que adaptar el valor del dispositivo después de grep para adaptarlo a tus necesidades.


Gracias por leer!

Namaste.