Esta pagina se ve mejor con JavaScript habilitado

Cambiar dispositivo de audio en caliente bajo FreeBSD

 ·  🎃 kr0m

FreeBSD nos permite cambiar de dispositivo de audio fácilmente mediante el comando “sysctl hw.snd.default_unit” tan solo debemos consultar los dispositivos disponibles:

Installed devices:
pcm0: <Intel Haswell (HDMI/DP 8ch)> (play)
pcm1: <Realtek ALC292 (Analog 2.0+HP/2.0)> (play/rec) default
pcm2: <Realtek ALC292 (Internal Analog Mic)> (rec)
pcm3: (play/rec)
Installed devices from userspace:

Y realizar la asignación pertinente:

sysctl hw.snd.default_unit=3

El problema de este método es que cada vez que cambiemos de dispositivo tendremos que reiniciar las aplicaciones abiertas para que relean el dispositivo de audio nuevo.

Para evitar dicho inconveniente podemos utilizar virtual_oss, un servicio que multiplexa y demultiplexa dispositivos de audio, la idea es que las aplicaciones siempre abran el dispositivo creado por virtual_oss pero que este realice el enrutado al dispositivo final.

Instalamos virtual_oss:

pkg install virtual_oss

Habilitamos el servicio:

sysrc virtual_oss_enable=YES

Configuramos los parámetros de virtual_oss, en mi caso se trata de un portátil donde los altavoces son el dispositivo pcm1 que equivale a dsp1:

sysrc virtual_oss_dsp="-T /dev/sndstat -C 2 -c 2 -S -i 0 -r 48000 -b 24 -s 8.0ms -f /dev/dsp1 -d dsp -t dsp.ctl"

-T devname      Install entry in /dev/sndstat
-C num          Set the maximum number of mix channels to num.
-c num          Set mix channels for the subsequent commands.
-S              Enable automatic DSP rate resampling.
-i priority     Set real-time priority to priority.  Refer to rtprio(1) for more information.
                Priority is an integer between 0 and RTP_PRIO_MAX (usually 31). 0 is the highest priority 31 the lowest.
-r rate         Set default sample-rate for the subsequent commands.
-b bits         Set sample depth to bits for the subsequent commands. Valid values are 8, 16, 24 and 32.
-s value        Set default buffer size to value. If the argument is suffixed by "ms" it is interpreted as milliseconds.
                Else the argument gives number of samples. The buffer size specified is per channel. If there are multiple
                channels, the total buffer size will be larger.
-f devname      Set both playback and recording DSP device
-d name         Create an OSS device by given name.
-t devname      Set control device name.

Los dispositivos de audio tanto de entrada como de salida se pueden definir con un solo parámetro:

-f devname      Set both playback and recording DSP device

O de forma independiente:

-P devname      Set playback DSP device only.  Specifying /dev/null is magic and
                means no playback device.  Specifying a sndio(7) device
                descriptor prefixed by "/dev/sndio/" is also magic, and will use
                a sndio backend rather than an OSS device.

-R devname      Set recording DSP device only.  Specifying /dev/null is magic and
                means no recording device.

Arrancamos el servicio:

service virtual_oss start

Consultamos los dispositivos disponibles y vemos que hay uno mas en la sección userspace:

cat /dev/sndstat

Installed devices:
pcm0: <Intel Haswell (HDMI/DP 8ch)> (play)
pcm1: <Realtek ALC292 (Analog 2.0+HP/2.0)> (play/rec) default
pcm2: <Realtek ALC292 (Internal Analog Mic)> (rec)
pcm3: <USB audio> (play/rec)
Installed devices from userspace:
dsp: <Virtual OSS> (play/rec)

Lo ideal llegados a este punto sería cerrar las X para asegurarnos de que todas las aplicaciones utilicen el dispositivo creado por virtual_oss.

Ahora si queremos hacer un cambio en caliente a los auriculares USB por ejemplo tan solo debemos indicar el dsp correcto:

virtual_oss_cmd /dev/dsp.ctl -f /dev/dsp3

Para revertir a los altavoces:

virtual_oss_cmd /dev/dsp.ctl -f /dev/dsp1

Un script que puede resultarnos útil es el siguiente:

#!/usr/bin/env bash

grep 'Virtual OSS' /dev/sndstat 1>/dev/null
if [ $? -eq 0 ]; then
    VIRTUAL_OSS_DETECTED=1
else
    VIRTUAL_OSS_DETECTED=0
fi

LAST_DEVICE=$(grep pcm /dev/sndstat | awk -F ":" '{print$1}' | awk -F "pcm" '{print$2}' | tail -n1)
#echo "LAST_DEVICE: $LAST_DEVICE"
for NEXT_DEVICE in $(grep pcm /dev/sndstat | awk -F ":" '{print$1}' | awk -F "pcm" '{print$2}'); do
    #echo "NEXT_DEVICE: $NEXT_DEVICE"

    if [ $VIRTUAL_OSS_DETECTED -eq 1 ]; then
        CURRENT_DEVICE=$(virtual_oss_cmd /dev/dsp.ctl|grep 'Output device name' | awk -F "/dev/dsp" '{print$2}')
    else
        CURRENT_DEVICE=$(grep default /dev/sndstat | awk -F ":" '{print$1}' | awk -F "pcm" '{print$2}')
    fi
    #echo "CURRENT_DEVICE: $CURRENT_DEVICE"

    if [ $NEXT_DEVICE -le $CURRENT_DEVICE ] && [ $NEXT_DEVICE -ne $LAST_DEVICE ]; then
        #echo "Continue"
        continue
    else
        if [ $CURRENT_DEVICE -eq $LAST_DEVICE ] && [ $NEXT_DEVICE -eq $LAST_DEVICE ]; then
            NEXT_DEVICE=0
            #echo "NEXT_DEVICE: $NEXT_DEVICE"
        fi
        DEVICE_NAME=$(grep pcm$NEXT_DEVICE /dev/sndstat | awk -F "<" '{print$2}' | awk -F ">" '{print$1}')
        if [ $VIRTUAL_OSS_DETECTED -eq 1 ]; then
            virtual_oss_cmd /dev/dsp.ctl -f /dev/dsp$NEXT_DEVICE
        else
            sysctl hw.snd.default_unit=$NEXT_DEVICE
        fi

        if [ $? -eq 0 ]; then
            notify-send "Audio device changed: PCM$NEXT_DEVICE: $DEVICE_NAME"
            break
        else
            notify-send "Error changing audio device, reverting"
            if [ $VIRTUAL_OSS_DETECTED -eq 1 ]; then
                virtual_oss_cmd /dev/dsp.ctl -f /dev/dsp$CURRENT_DEVICE
            else
                sysctl hw.snd.default_unit=$CURRENT_DEVICE
            fi
        fi
    fi
done
Si te ha gustado el artículo puedes invitarme a un RedBull aquí