miércoles, 26 de mayo de 2010

Cómo medir la latencia real del sistema jack-hardware de audio

###################################
Notas de revisiones:
Revisión 0: 12 junio 2010. Publicado.
Revisión 1: 16 junio 2010. Añadidos créditos.
###################################


En la anterior entrada vimos cómo configurar el script rtirq para que nos levante automáticamente la prioridad de la tarjeta de audio (y de otros "kernel threads" importantes para el trabajo con MIDI). De esta forma, podemos conseguir en jack latencias teóricas asombrosamente bajas sin recibir xruns.

Con esto y M-audio Audiophile 2496 (PCI), consigo mantener jack (antes de lanzar ninguna aplicación, eso sí) funcionando durante un buen rato con 0 xruns a 96000 Hz, 16 cuadros por periodo y 2 periodos por buffer, con un consumo de CPU de alrededor de 20 % según marca el display de Jack Control. Sólo el conseguir que jack llegue a arrancar con estos parámetros es ya una señal de que tenemos un sistema al menos aceptablemente bien ajustado para el trabajo con audio en tiempo real, a baja latencia y libre de xruns.

El valor de latencia que aparece en el setup de qjackctl (abajo a la derecha) es la latencia de bucle completo (entrada + salida) calculada a partir de la fórmula:

(p·n / r) x 1000

Donde p es el valor de cuadros por periodo, n son los periodos por buffer y r la frecuencia de muestreo. Se multiplica por 1000 para que el resultado final salga en milisegundos.

Con p=16, n=2 y r = 96000, tenemos una latencia calculada de 0,333 ms.

¿Significa esto que si arrancamos jack con estos parámetros, conectamos virtualmente en jack el capture_1 al playback_1 y conectamos físicamente una fuente de sonido (por ejemplo, una guitarra) a la primera entrada analógica, habrá un "retardo" de 0,333 ms desde que pulsamos una cuerda hasta que escuchamos el sonido por los altavoces?

No. La indicación de jack no es una medida real de latencia. Para medir la latencia real de bucle completo podemos usar una herramienta llamada jack_delay o jdelay. Copio la descripción original:

"This is a small command line JACK app you can use to measure the latency of your sound card. It uses a phase measurements on a set of tones to measure the delay from the output to the input. Accuracy is about 1/1000 of a sample".

Instalación...

Está en los repositorios de Debian y ubuntu, con el nombre "jdelay".

Sin embargo, mejor compilar para obtener la última versión ya que en ésta aparece el valor de la latencia en milisegundos, no solamente en cuadros como probablemente encontraremos si instalamos el paquete. Tampoco pasa nada, pues podemos calcular la latencia en milisegundos diviendo los cuadros entre la frecuencia a la que esté trabajando jack.

Podemos descargar las fuentes desde:

http://www.kokkinizita.net/linuxaudio/downloads/index.html

La compilación es sencilla. Creo que sólo hacen falta las librerías de desarrollo de alsa y de jack, libasound2-dev y libjack-dev.


Uso

Advertencia: Hacer esto con el volumen de vuestro equipo de música completamente a cero. Si os confundís y conectáis la salida de jack_delay a los system_playbacks, el ruido es muy desagradable!

Si habéis compilado, el comando es "jack_delay". Si habéis instalado el paquete, "jdelay" (creo).

La salida de jack_delay se conecta a través de jack al system:playback_1 que representa la primera salida analógica. Desde ésta llevamos un cable a la primera entrada analógica que en jack aparece como system:capture_1, la cual conectamos a través de jack a la entrada de jack_delay, cerrando el bucle. En el pantallazo lo vemos más claro. La línea roja gruesa representa el cable físico.



En la salida de terminal de jack_delay, observamos que hay diferencia entre la latencia calculada y la latencia real del sistema hardware-software (tarjeta de audio + alsa-jack). Por ejemplo con:

p = 64
n = 2
r = 48000

tengo una latencia de 188,6 cuadros, que es mayor que la teórica 128 (64 x 2). En milisegundos, el valor resulta de 3,93 frente al 2,67 calculado. Esa latencia añadida es la que ocurre en los convertidores DA y AD.

No es buena idea llevar el sistema a la menor latencia posible en todas las circunstancias. De hecho, es muy mala idea. Cuando estemos editando, mezclando y masterizando subir los cuadros por periodo.

Utilizar un valor de cuadros por periodo bajo solamente cuando sea necesario. Por ejemplo, para capturar audio mientras hacemos monitorización por software.

Créditos

Jack_delay es una utilidad escrita por Fons Adriaensen, ingeniero de sonido y desarrollador de excelentes herramientas de software para Linux.
http://www.kokkinizita.net/linuxaudio/

domingo, 9 de mayo de 2010

Configuración del script rtirq para muy baja latencia

###################################
Notas de revisiones:
Revisión 0: 8 mayo 2010. Publicado.
Revisión 1: 15 mayo 2010. Retocado.
Revisión 2: 26 mayo 2010. Añadida nota sobre los sirq-timers y la prioridad de jackd
Revisión 3: 28 mayo 2010. Actualizada la explicación sobre los sirq-timers, reordenado y modificado título
Revisión 4: 26 noviembre 2010. Añadida advertencia "sólo funciona con kernel rt"
Revisión 5: 8 enero 2011. Actualizado: Funciona con cualquier kernel >= 2.6.39 con threadirqs habilitado.
###################################


El script rtirq-init está empaquetado y disponible en la mayoría de las distros. En Debian/ubuntu el paquete se llama "rtirq-init".

A partir de la versión del kernel 2.6.39, el script funciona en kernels precompilados como "generic" o "low-latency", siempre y cuando tengan habilitada la opción de arranque "threadirqs". Es decir, ya no es necesario un kernel modificado con el parche realtime para que este script funcione.

Si usamos grub2 como gestor de arranque, una forma de habilitar "threadirqs" es editar el archivo /etc/default/grub.

$ sudo gedit /etc/default/grub

Buscamos la línea

GRUB_CMDLINE_LINUX=""

y la modificamos a:

GRUB_CMDLINE_LINUX="threadirqs"

Para que el cambio tenga efecto, debemos actualizar grub:

$ sudo update-grub

Y reiniciar



Este script levanta las prioridades de los "kernel threads" más importantes para el trabajo con audio y MIDI en tiempo real y a baja latencia, automáticamente al arrancar el ordenador. Este script no es necesario en todos los casos, pero a veces ayuda.

Si escribimos en la terminal:

$ sudo updatedb
$ locate rtirq

Entre otros debería dar:
/etc/default/rtirq
/etc/init.d/rtirq

/etc/init.d/rtirq es el script, que lee el archivo de configuración /etc/default/rtirq.
Es este el archivo que debemos editar para conseguir el objetivo.

El objetivo depende de cada caso, pero normalmente será levantar las prioridades de:

1º El reloj y los temporizadores del sistema (para precisión con MIDI)
2º La tarjeta de audio (para baja latencia de audio)

Todo lo indicado en esta entrada lo vamos a hacer en línea de comandos y vamos a usar htop como monitor del sistema. Propongo terminator como emulador de terminal.

$ sudo apt-get install terminator htop

Ahora vamos a lanzar terminator y lo vamos a maximizar. Botón derecho sobre la pantalla y dividimos verticalmente. En la ventana de la derecha, botón derecho y dividimos horizontalmente. Tenemos 3 terminales.

En la de la izquierda lanzamos:

$ htop

htop nos da un montón de información sobre los procesos y el consumo de recursos de nuestra máquina. Hacemos:

F2 --> Display options --> Desmarcar "hide kernel threads" --> F10
F6 --> Sort by: PRI

Así vemos los procesos, incluidos los "kernel threads", ordenados por prioridad. Al menos en mi caso, los kernel threads cuya prioridad no ha sido levantada automáticamente al iniciar el ordenador por rtirq, toman la prioridad (omitiendo el signo) 51 ó 50. Los que están por encima es que han sido levantados por rtirq.

En una terminal de la derecha vamos a ver los "interrupts" con el comando:

$ cat /proc/interrupts

En la columna de la derecha tendremos que adivinar cuál es nuestra tarjeta de audio. Con una m-audio Audiophile 2496 veo que ICE1712 está en el número 22. Esto será diferente para cada sistema hardware-software, pero lo pongo como ejemplo. Si tu tarjeta es usb, debes fijarte en el número de bus, por ejemplo ehci_hcd:usbx o uhci_hcd:usbx. Con el comando lsusb puedes ver el número de bus usb en la primera columna. Si nos fijamos ahora en htop, vemos en la columna "Command" el número de irq seguido por su identificación. En mi caso, en la prioridad 51 veo irq/22-ICE1712, y también el irq/8-rtc0. Precisamente los dos kernel threads que se pretendían levantar se han quedado en 51, mientras que los ehci_hcd y uhci_ehcd, así como el i8042 (para el ratón y teclado PS/2, en mi caso inexistente) tienen las prioridades levantadas. Esto no es lo que quería conseguir.

¿Cómo arreglarlo? Modificando el archivo de configuración de rtirq.
En la terminal libre de la derecha escribimos:

$ sudo cp /etc/default/rtirq /etc/default/rtirq.copia (copia por si acaso)
$ sudo nano /etc/default/rtirq

Hay una serie de variables escritas en mayúsculas que son las que debemos tener en cuenta. El resto son comentarios. En nano guardamos los cambios con [Control-O] y salimos con [Control-X]. Bueno, usar el editor que os parezca mejor.

La variable más importante es RTIRQ_NAME_LIST. La encontramos así:

RTIRQ_NAME_LIST="rtc snd usb i8042"

Esta me está fastidiando. Cambio a:

RTIRQ_NAME_LIST="rtc0 ICE1712"

Que es lo que leo en la columna derecha de cat /proc/interrupts. Creo que lo que Rui quiere dar a entender es: "1º reloj, 2º tarjeta PCI, 3º tarjeta usb, 4º teclado/ratón PS2". Las distribuciones tienen algunas diferencias, por eso es genérico.

(Me parece que en lucid funciona a la primera con las designaciones genéricas, pero habría que comprobarlo).

He dejado fuera de RTIRQ_NAME_LIST "usb" e "i8042" pues no tengo tarjetas usb ni teclado/ratón PS2.

Si tenemos una tarjeta firewire, habrá que levantar la prioridad de ohci1394 en lugar de la de la tarjeta PCI y posiblemente también la del controlador firewire. Comprobar con htop. Más información en el enlace de ffado de abajo.

Hay otra variable:

RTIRQ_NON_THREADED=

Habría que cambiarla de todas maneras (otra vez, rtc y snd que al menos en mi caso no significan nada) pero creo que la puedo deshabilitar tranquilamente, "comentándola" con una almohadilla delante.

Por probar, voy a dar 98 a RTIRQ_PRIO_HIGH y pongo un paso de 10
en RTIRQ_PRIO_DECR.

Por otro lado, en el lowlatency howto de alsa y en el blog de SounDebian hablan de levantar las prioridades de los soft irq timers. Esto es mucho más fácil con rtirq. Sólo tenemos que descomentar la línea:

# RTIRQ_HIGH_LIST="timer"
Es decir, dejarla en:
RTIRQ_HIGH_LIST="timer"
Y tendremos los sirq-timers con la máxima prioridad.

Los temporizadores no afectan a la tarjeta de audio, que tiene su propio reloj. Sin embargo, son importantes para la reproducción de MIDI.

Una vez modificado el archivo de configuración, vamos a lanzarlo manualmente sin tener que reiniciar. Desde otra terminal:

$ sudo /etc/init.d/rtirq start

Florian (tercer enlace, entrada "Linux Audio/MIDI System") y Soundebian explican cómo hacer todo esto de forma manual (con el comando chrt). Florian propone levantar las prioridades de los secuenciadores y sintetizadores de software por encima de la de la tarjeta de audio y jackd. Por eso elijo un paso grande entre rtc0 y la tarjeta, para tener sitio entre medias, por si acaso, aunque sinceramente nunca me preocupo de levantar manualmente prioridades de otros procesos.

Cuando arrancamos manualmente el script vemos que se levantan las prioridades de los "kernel threads" que hemos configurado pero las que estaban levantadas no "bajan". (A veces tengo que terminar htop con F10 y volver a lanzarlo para asegurarme, pues el efecto no se ve de inmediato). Cuando reiniciemos el ordenador volvemos a comprobar con htop si rtirq ha hecho su función. Los kernels threads no levantados los dejará con prioridad 51 ó 50.

Con esto se consiguen latencias bajísimas sin xruns, como vemos en la siguiente entrada, donde además realizamos algunas mediciones.

Créditos y para saber más, además de nuestro buscador favorito:

http://subversion.ffado.org/wiki/IrqPriorities
http://www.rncbc.org/drupal/node/107
http://tapas.affenbande.org/wordpress/ (linux audio pages)
http://bugtrack.alsa-project.org/main/index.php/Low_latency_howto
http://www.soundebian.com.ar/2009/10/configurando-verdaderamente-el-real-time-para-audio/