domingo, 27 de noviembre de 2022

He de reconocer que el blog está un poco desatendido últimamente, pero es que la vida 1.0 tiene prioridad absoluta y apenas puedo hacer cosas retro. Así que, como a veces hago cosillas (soft y hard) que no se pueden definir como "retro" pero que aún así siguen siendo interesantes (o lo son al menos para mi), he decidido que también tienen cabida en el blog.

En esta ocasión os traigo unos scripts en bash para poder mutear/desmutear el micrófono en Linux. ¿Por qué los he hecho? Bueno, con la llegada de la pandemia y el confinamiento, las reuniones online se han multiplicado. Y no sé si os pasa también, pero yo tengo que usar diferentes plataformas como Teams, Jitsi, Skype, Zoom, Google Meet, etc. además de alguna propia de la universidad. ¡Y cada una de ellas tiene una forma distinta de mutear el micrófono! ¿A quién no le ha pasado tener el micro abierto mientras atendías al teléfono o estar hablando con el micro cerrado?

Así que me surgió la necesidad de tener una forma de poder mutear/desmutear el micro de manera sencilla. Empezando por el final, lo que quería era tener un script (que es micToggle) que pudiera asociar a una combinación de teclas y así poder silenciar o activar el micrófono directamente desde el teclado. Pero para eso necesitaba poder averiguar cuál era el micrófono que estaba definido como por defecto en el sistema, saber si estaba muteado o no y ver cómo cambiar su estado (e incluso avisar visualmente de alguna forma).

Vamos a empezar por el final y veremos cómo funciona el script micStatus, que es el más complicado y se encarga de averiguar el estado del micrófono por defecto e informar del mismo mediante una notificación de escritorio. Así pues, el primer paso será averiguar cuál es el micrófono por defecto en el sistema y ver qué información podemos obtener de él. Para ello, vamos a utilizar el comando pacmd. Este comando permite reconfigurar el servidor de sonido PulseAudio (que hoy por hoy es el más utilizado en Linux) durante su ejecución. También nos da información sobre el estado del servidor, por lo que si ejecutamos pacmd list-sources vamos a obtener un listado (enorme) de las fuentes de entrada de sonido a nuestro equipo.

De toda la información que nos proporciona pacmd, lo que nos interesa es el índice de la fuente, su estado (muteado o no) y, a modo de información, su descripción. Así que vamos a filtrar el resultado anterior utilizando el comando grep. Así pues ahora ejecutamos pacmd list-sources | grep -e 'index:' -e 'muted:' -e 'device.description' y obtenemos la siguiente información:

  index: 0
        muted: yes
                device.description = "Monitor of Audio Interno Estéreo analógico"
  index: 1
        muted: yes
                device.description = "Camera Mono"
* index: 2
        muted: yes
                device.description = "FIFINE K670 Microphone Estéreo analógico"
  index: 3
        muted: yes
                device.description = "Monitor of Logitech USB Headset Estéreo analógico"
  index: 4
        muted: yes
                device.description = "Logitech USB Headset Mono"

Como se puede ver, en mi ordenador hay instalados varios micrófonos y cada una de las expresiones que hemos utilizado en el comando grep nos ha proporcionado la información que necesitamos de todos ellos: el índice, estado y descripción de cada fuente de sonido, respectivamente. Pero también tenemos más información, ya que si nos fijamos en lo que está resaltado de color rojo, vemos que hay un asterisco delante de index, lo que significa que ese es el micrófono que está definido como por defecto en el sistema. ¡Hemos conseguido la información! Aunque ahora hay que quitar lo que sobra.

Si nos fijamos, tenemos tres líneas de información por cada una de las fuentes de audio, por lo que nos tendremos que quedar con las tres líneas que se refieren al micrófono por defecto, el cuál sabemos que está identificado por el asterisco delante de index. Así que echamos mano de uno de los comandos más potentes que nos ofrece Linux: sed. Este comando permite editar ficheros de texto de forma automática y con una flexibilidad increíble, aunque ahora sólo nos interesa cortar el texto que aparece antes de esa línea que tiene la cadena "* index:". Eso lo conseguiremos con sed -n -e '/* index:/,$p'. Por lo tanto, nuestro comando ahora quedaría como pacmd list-sources | grep -e 'index:' -e 'muted:' -e 'device.description' | sed -n -e '/* index:/,$p' y el resultado sería el siguiente:

* index: 2
        muted: yes
                device.description = "FIFINE K670 Microphone Estéreo analógico"
  index: 3
        muted: yes
                device.description = "Monitor of Logitech USB Headset Estéreo analógico"
  index: 4
        muted: yes
                device.description = "Logitech USB Headset Mono"

Con la opción -n le decimos a sed que no imprima la entrada, mientras que con -e le proporcionamos el script que queremos que ejecute, el cual va a buscar la cadena "* index:" y va a imprimir la entrada a partir de encontrar dicha cadena. Gracias a eso nos quitamos las líneas anteriores a la que buscábamos. Ahora sólo nos queda jugar con head y tail para quedarnos con las dos líneas siguientes a la que contiene "* index:". Por lo tanto, nuestro comando completo va a ser: pacmd list-sources | grep -e 'index:' -e 'muted:' -e 'device.description' | sed -n -e '/* index:/,$p' | head -n3 | tail -n2, el cual nos proporciona la siguiente salida:

        muted: yes
                device.description = "FIFINE K670 Microphone Estéreo analógico"

¡Perfecto! Ya tenemos toda la información que necesitamos. El script almacena esta información en la variable $MICDATA para poder obtener el estado y el nombre del micrófono.

El estado del micrófono está en la primera línea, por lo que ejecutando STATUS=$(echo "$MICDATA" | head -n1) obtenemos esa primera línea en la variable $STATUS. ¡Ojo! Es importante no olvidar las comillas, puesto que $MICDATA contiene más de una línea de texto. Del mismo modo, obtenemos la segunda línea con echo "$MICDATA" | tail -n1 y ahora utilizamos el comando awk para obtener el texto que está entre comillas, por lo que el nombre del micrófono se almacena en la variable $NAME ejecutando: NAME=$(echo "$MICDATA" | tail -n1 | awk -F '"' '{print $2}'), ya que estamos definiendo el separador de campos como la comilla doble mediante -F '"' y nos quedamos con el segundo campo (el que se encuentra tras la primera comilla doble), que es el nombre del micrófono.

Ahora tenemos que saber si el micrófono está muteado o no, por lo que si al contenido de $STATUS le hacemos un grep 'yes', si el comando nos devuelve algo, significará que el micrófono está muteado, por lo que podremos mandar una notificación diciendo eso mismo. Si el comando no funciona, significará que la cadena "yes" no estará presente y el micrófono no estará muteado (en tal caso, aparecería "muted: no"). Eso lo podemos averiguar utilizando && y || de esta forma: grep 'yes' > /dev/null && --notificación micro muteado-- || --notificación micro abierto--.

Para mandar la notificación, vamos a hacer uso de notify-send, que nos permite mostrar notificaciones de escritorio. Normalmente viene instalado, pero igual hay que instalar el paquete libnotify-bin si no lo tuviéramos en nuestro sistema. Una vez que lo tengamos, podemos enviar una notificación de escritorio con un título obligatorio y opcionalmente un cuerpo de notificación. El script micStatus envía como título el estado del micrófono y como cuerpo el nombre del mismo. Podríamos hacer directamente notify-send "MICRÓFONO SILENCIADO" "$NAME" (sí, el cuerpo admite algunas etiquetas HTML de formato) y veríamos una notificación en nuestro escritorio similar a esta:
Pero podemos dejarlo un poco más bonito, ya que notify-send admite que insertemos un icono en la notificación y que indiquemos cuánto tiempo (en milisegundos) queremos que se muestre. Por lo tanto, nuestras notificaciones serán:

notify-send -t 2000 -i /usr/share/micStatus/mic-off.png "MICRÓFONO SILENCIADO" "$NAME"

y

notify-send -t 2000 -i /usr/share/micStatus/mic-off.png "MICRÓFONO ABIERTO" "$NAME"

Estas notificaciones están presentes un total de 2 segundos y muestran unos sencillos iconos de micrófono abierto y desactivado, los cuales se sitúan en un directorio que está dentro de /user/share y los podéis encontrar en el fichero zip que os he preparado.

Ya tenemos el script micStatus, pero ahora necesitamos poder cambiar estado del micrófono. Para ello vamos a utilizar el comando pactl. Este comando nos permite, entre otras cosas, controlar el estado de muteo de uno de los micrófonos, siempre que le proporcionemos el número de source que tiene en PulseAudio. En nuestro ejemplo, queremos ejecutar concretamente: pactl set-source-mute 2 toggle. Necesitamos cambiar ese 2 resaltado en rojo por el número de source de nuestro micrófono por defecto.

¡Pero eso ya sabemos cómo obtenerlo! Si ejecutamos "list-sources | grep "* index" | awk '{print $3}' obtendremos el número que buscamos. Ahora tendremos que pasárselo como argumento a pactl, para lo cual hacemos uso de xargs, por ejemplo así: xargs -I{} pactl set-source-mute {} toggle, lo que significa que cambie toda aparición de "{}" por lo que venga de la entrada estándar. De esta forma, el comando: pacmd list-sources | grep "* index" | awk '{print $3}' | xargs -I{} pactl set-source-mute {} toggle cambia el estado de nuestro micrófono por defecto. Si justo tras este comando llamamos a micStatus, tenemos el script micToggle.

Por completitud, podemos añadir los scripts micMute y micUnmute, sin más que cambiar toggle por true y false respectivamente.

Pues eso es todo por ahora. Echadle un vistazo a los scripts y a ver si os son de utilidad. Podéis cambiar lo que queráis para particularizarlo a vuestro sistema, por ejemplo notify-send también admite reproducir un audio, que podría indicaros si el micrófono está abierto o no sin mostrar nada en el escritorio. Esto es lo bueno del software libre, que con poco esfuerzo podemos adaptarlo a nuestras necesidades concretas.

Editado (29-Noviembre-2022): se ha actualizado el zip de descarga tras la actualización comentada en este otro post.