Configura y optimiza Linux para audio y MIDI

¿Qué tres cosas te llevarías a una isla desierta? Mi recomendación es; un ordenador con Linux, un teclado MIDI y este artículo.

Florian Paul Schmidt

Pensé en documentar la información que tengo sobre cómo utilizar un sistema Linux con un kernel apropiativo —N. del T.; Preemptive en el original— a tiempo real como sistema de trabajo con audio y MIDI. Así puedo referirme a esta documentación en lugar de repetirla una y otra vez en discusiones. :)

Todo lo que sigue se aplica a los kernels -rt con el modelo apropiativo completo —N. del T.; complete preemption en el original—. En mi opinión es la mejor forma de operar con un kernel -rt si se quiere en control más estricto sobre prioridades y latencias. Procede un aviso y es que los drivers binarios y los módulos para nVidia podrían ser incompatibles con este tipo de kernel. Utilícese el driver nv de las X11 en su lugar. Aún si el módulo binario fuese compatible, no habría garantías de que encaje en el modelo particular de este kernel y podría destruir las bajas latencias que el resto del código del kernel nos garantiza.

El sistema de audio y MIDI

En un sistema de audio y MIDI típico basado en Linux encontramos varios componentes clave:

  • JACK, el kit de conexiones de audio. Facilita servicios de audio y por supuesto utiliza la tarjeta de sonido. La IRQ de la tarjeta de sonido requiere un tratamiemto especial —es decir, una alta prioridad— para no ser interrumpida por otras cargas del sistema —el disco duro o la red—. JACK es la opción favorita para este trabajo dado que permite conectar las aplicaciones de audio las unas con las otras. Además está diseñado para el trabajo a tiempo real desde el primer día. Con un sistema correctamente configurado pueden lograrse latencias ridículamente bajas, p.ej. tamaños de buffer de 8 ó 16 muestras a 48 KHz.
  • ALSA, facilitando servicios de audio a JACK y el acceso al MIDI vía alsa-seq.
  • Algún secuenciador MIDI como rosegarden, muse o seq24 que utilice JACK y alsa-seq.
  • Algunos sintetizadores software —al final, clientes JACK— manejados por el secuenciador MIDI
  • Más hardware que precisa de una IRQ para funcionar —red, disco duro, etc.—
  • El resto del software, como el gestor de ventanas, el escritorio y otras utilidades.

Cualquier kernel -rt en combinación con realtime lsm o rt_limits facilita al usuario herramientas que le permiten un trabajo cómodo que asegura:

  • Operación de audio sin problemas, y con «sin problemas» me refiero a sin problemas. Por ejemplo, puedo fácilmente compilar un kernel mientras trabajo con audio a 32 muestras por buffer.
  • Sincronización MIDI exacta.

Claro está que habrá que ajustar el sistema para obtener el mejor resultado. Podría ser necesario llegar a modificar algunas aplicaciones.

Configuración de audio

Podemos simplificar y decir que cuanto más alta sea la prioridad de una tarea más posible es que se ejecute sin molestias. Con un kernel -rt se tiene la posibilidad de que ciertos manejadores de IRQ tengan menos prioridad que aplicaciones que corren en el espacio de usuario. Es posible, por ejemplo, correr jackd con una prioridad mayor que la de cualquier manejador de IRQ que podría interrumpir el trabajo con audio, como el disco duro o la red. Es necesario el parámetro -P de jackd para esto.

En un kernel -rt, por defecto todos los manejadores de IRQ tienen prioridades entre 40 y 60, así que hacer que jackd se ejecute con una prioridad de 70 parece una buena elección.

  jackd -R -P 70 -d alsa ...
  

Sólo que si queremos que el manejador de la IRQ de la tarjeta de sonido tenga una prioridad más alta que jackd, dado que la IRQ de la tarjeta de sonido es al final la que gobierna el funcionamiento de JACK. Utilizamos así la utilidad chrt para cambiar esto. En mi sistema la tarjeta de sonido tiene la IRQ 4, comprueba /proc/interrupts en tu máquina para averiguar la tuya.

  chrt -f -p 82 `pidof "IRQ-4"`
  

—N. del T.; Comprueba las distintas prioridades asignadas así:—

  top -b -H -n 1 | grep -i irq | sort -n -k3
  

Una prioridad de 82 está bien por encima de la concedida a jackd. Dado que jackd ejecuta hilos además de su bucle principal, es necesario vigilar esto. Por ejemplo, implementa un watchdog que se ejecuta a una prioridad 10 superior a la del bucle principal. Es decir, a 80 en nuestro caso. Los hilos de proceso de cliente, en cambio, corren con una prioridad 1 inferior a la del bucle principal, 69 en nuestro caso.

La parte audio de nuestro sistema queda cubierta. Con esta configuración consigo operar con tamaños de buffer ridículamente bajos con JACK y mi M-Audio Delta 66 —hasta 8 muestras por periodo, reconociendo algún problema con el cambio de contextos forzando tanto mi sistema— Y ahora, ¿qué pasa con MIDI? ¿Cómo lo hacemos funcionar aquí?

Configuración MIDI

Tenemos que saber una cosa o dos sobre cómo la planificación a tiempo real funciona para afinar esta parte. La clase de planificación utilizada por la mayor parte de aplicaciones de audio es SCHED_FIFO. Se trata de una clase especial con niveles de prioridad estáticos de 1 a 99 y que garantiza que las tareas que la utilizan pueden conservar la CPU tanto tiempo como requieran, siempre que otra tarea SCHED_FIFO de mayor prioridad no la reclame. Por supuesto esto significa que un programa lo suficientemente puñetero puede bloquear el sistema simplemente acaparando toda la CPU siempre que no haya otra tarea con una prioridad mayor que pase por encima. Así que las tareas que tienen más prioridad ahora en nuestro sistema son los hilos de jackd y el manejador de la IRQ de la tarjeta de sonido.

El procesado de audio se realiza por lotes y para ilustrarlo permítaseme asumir que utilizamos un tamaño de buffer muy grande, digamos de 1 segundo —no sé si alguna tarjeta de sonido permite un buffer de este tamaño, pero para ilustrar el argumento supongamos que sí—. Se trata de 48.000 muestras a 48 KHz. Asúmase también que los clientes JACK están cargando realmente el sistema. Por ejemplo que el indicador DSP de jackd muestra un 50%. Esto significa que cada segundo el sistema completo se detiene para que jackd y sus clientes se tomen ese medio segundo para realizar sus procesos. Es fácil adivinar que el efecto en la sincronización MIDI va a ser terrible.

En realidad MIDI necesita más «tiempo real» que el audio. El audio se procesa en lotes de tamaño respetable —entre 1 y 50 ms dependiendo del tamaño de buffer utilizado—. Pero cuando un secuenciador MIDI necesita enviar un evento a un puerto, debe hacerlo con garantías de que llegará a tiempo a su destino —hay una excepción derivada de la implementación del secuenciador MIDI en ALSA, el cual permite aplazar eventos hasta un tiempo posterior, y de la que se derivan los problemas de ALSA con el MIDI—. El procesado MIDI, en cambio, es ligero comparado con el de audio. El bucle principal de un secuenciador MIDI necesita ejecutarse muchas veces por segundo para ser capaz de procesar eventos MIDI con una resolución lo suficientemente detallada. Así que la parte MIDI de un secuenciador debería correr con una prioridad mayor que jackd. La interfaz alsa-seq permite que los eventos MIDI sean aplazados para ser entregados en un momento preciso, lo que aligera el requisito de «debo enviarlo ahora mismo». Pero se trata de una funcionalidad compleja de gestionar y no demasiados programadores la utilizan. Así que tiene mucho más sencillo configurar el sistema para que los datos MIDI tengan una entrega inmediata.

Hay distintas fuentes de sincronización utilizadas por los secuenciadores MIDI en un sistema Linux. Las más comunes son:

RTC

El reloj en tiempo real que todo PC tiene. Corre a una frecuencia programable, p.ej. de 1.024 Hz y permite a una aplicación ser ejecutada muy a menudo y muy regularmente. Para que la sincronización basada en RTC funcione bien, el manejador de la IRQ del RTC —la IRQ 8 en un sistema XT-PIC, consúltese /proc/interrupts— necesita una prioridad superior a la de jackd y a la del resto de aplicaciones del sistema. 98 es un buen valor.

  chrt -f -p 98 `pidof "IRQ-8"`
  

Aún si no se utiliza el RTC, esta configuración no hará ningún daño. Si se quiere que los usuarios ordinarios puedan hacer uso del RTC a una frecuencia razonablemente alta, hay que asegurarse de que:

  /proc/sys/dev/rtc/max-user-freq
  

Está configurado a 8192. Puede hacerse ejecutando como root:

  echo 8192 > /proc/sys/dev/rtc/max-user-freq
  

—N. del T.; Es posible llevar esta configuración a /etc/sysctl.conf, consúltese la ayuda.—

Es posible por supuesto visualizar el valor actual:

  cat /proc/sys/dev/rtc/max-user-freq
  

Temporizador basado en sleep()

En este caso la aplicación corre repetidamente poniéndose a sí misma a dormir por cortos intervalos. Luego mide cuánto tiempo durmió para ser capaz de sincronizar correctamente. No sé exactamente como sleep() o sus derivadas como nanosleep() o usleep() están exactamente implementadas en Linux, así que me permito una adivinanza.

Cuando una tarea echa a dormir en realidad le dice al planificador que libera la CPU y que no quiere volver a ser ejecutada hasta que el tiempo de sueño haya vencido. El temporizador del sistema es una interrupción especial de temporización en todo sistema PC que corre a una frecuencia de 250 Hz en la configuración por defecto de un kernel Linux. Este temporizador del sistema ejecuta el bucle principal de ejecución de un kernel Linux, a saber, el planificador. Así que cuando sólo hay un temporizador disponible en el sistema, el tiempo más breve que una aplicación puede dormir son 4 ms —1/250 seg—. La granularidad es grande, así que cabe aconsejar cambiar la frecuencia por defecto del temporizador del sistema de 250 Hz a 1.000 Hz, lo que facilita una resolución de milisegundo a sleep(). Presúmase que se trata de una configuración correcta, que sólo costará un mínimo de carga adicional en el sistema.

Dado que este temporizador del sistema siempre corre a la máxima prioridad SCHED_FIFO en los kernel -rt actuales, no es necesaria ninguna configuración extra.

¡BZZZZT ERROR! Podrías tener que dar a algunos hilos de temporización también una alta prioridad para que sleep() funcione correctamente. El más importante aquí es softirq-timer/0, así que dale también un 99.

¡BZZZZZZZT ERROR OTRA VEZ! Esto se aplicaría sólo para un kernel habiendo sido compilado sin el soporte al temporizador de alta resolución. Espero aclarar este punto en algún momento en más detalle preguntando directamente a Ingo Molnar. Si en tu configuración el temporizador de alta resolución ha sido activado, no debería ser necesario darle a softirq-timer/0 una prioridad tan alta.

En realidad, actualmente parece que establecer esa alta prioridad a softirq-timer/0 daña el rendimiento del sistema. Así que de momento recomiendo configurar el temporizador de alta resolución en el kernel y dejar en paz a softirq-timer/0.

Otras fuentes de sincronización

¿Los temporizadores POSIX de alta resolución?

Hilos MIDI en aplicaciones MIDI

Nuestras fuentes de sincronización ya han sido establecidas. ¿Qué pasa ahora con las aplicaciones que las utilizan? Establecer una alta prioridad para la interrupción del reloj no es todo lo que se necesita. Las aplicaciones corriendo en el espacio de usuario deben utilizar dichas interrupciones directa o indirectamente, y también necesitan correr con altas prioridades. Idealmente por encima de la de jackd y el resto de material de audio.

Sintetizadores software

Habitualmente tienen un hilo de ejecución que recibe eventos MIDI, les añade una marca temporal —p.ej. mediante jack_frametime()— y los mete en una cola —idealmente un ringbuffer sin lock— donde las llamadas a audio_process() los van consumiendo. La llamada a process() puede determinar exactamente dónde dentro del periodo deben generarse los datos de audio correspondientes utilizando el método jack_last_frametime() de JACK —un método sencillo; añádase un periodo a la marca temporal de los eventos MIDI y después réstese el jack_last_frametime de la cuenta. La diferencia resultante es el desplazamiento dentro de el periodo que se está procesando. El hilo de recepción y marca temporal MIDI necesita correr a una prioridad más alta que jackd y que todos los procesos de audio. 90 es por ejemplo una elección razonable.

He escrito un pequeño sintetizador software que evalúa numéricamente la ecuación de onda que utiliza este esquema, y que funciona realmente bien.

ughsynth-0.0.5.tgz

Téngase cuidado sin embargo con la cantidad de CPU que utiliza —hasta un 50% en mi Athlon a 1,2 GHz—.

Secuenciadores MIDI

Generalmente tienen al menos un hilo de ejecución para manejar su sincronización MIDI. Algunos usan el RTC, otros se basan en sleep(). El único que conozco que permite establecer su prioridad en tiempo real es seq24 pero, hasta donde sé, esta prioridad está a pelo en el código fuente. Así que para ajustarla tendrás que remangarte. :)

No sé demasiado sobre el resto de secuenciadores MIDI ni de cómo manejan esta cuestión. Rosegarden por ejemplo facilita dos opciones diferentes para su fuente de sincronización —RTC y reloj del sistema basado en sleep()— pero no he encontrado la forma de obtener más detalles. Debería ser posible acceder a esta configuración utilizando la interfaz de usuario, lo que sería lo ideal.

Probándolo todo

He escrito un pequeño conjunto de aplicaciones:

midi_timer-1.tgz

Incluye dos que intentan producir un flujo de mensajes MIDI note-on. Una está basada en el RTC ejecutándose a una frecuencia de 2.048 Hz, lo que debería ser suficiente. La otra está basada en sleep(). Las dos se ejecutan a una prioridad de 95, por encima de la de jackd, y deberían servir como casos de prueba.

Resumen de configuración de prioridades

  99 System timer IRQ
  98 RTC IRQ

  95
  .
  . Midi threads of softsynths/midi sequencers
  .
  85 -

  82 Soundcard IRQ

  80 Jackd watchdog thread
  70 Jackd main loop
  69 Jackd client (softsynths/midi sequencers) audio process callbacks

  60
  .
  . Other IRQ handlers (disk, network, USB, GFX)
  .
  40
  

Permitiendo a usuarios normales ejecutar procesos en tiempo real

Consúltense las páginas de ayuda de realtime-lsm y/o rtlimits.

Visto en Linux Audio/MIDI System Setup publicado en Ugh! – Software, Music and more…

¿Te pareció interesante? Sé tan amable de menear este artículo.

alsa amd Audio chrt Hardware irq jack kernel Linux midi muse realtime rosegarden rtlimits seq24

Bookmark and Share votar

4 Respuestas to “Configura y optimiza Linux para audio y MIDI”

  1. Información Bitacoras.com…

    Si lo deseas, puedes hacer click para valorar este post en Bitacoras.com. Gracias….

  2. ¡Uff! Me viene grande todo ésto. Apenas he logrado hacer sonar Ubuntu Studio hace un par de semanas, así que todo ésto me suena a chino…

    Pero de seguro que, más adelante, necesitaré volver aquí para configurar bien mi equipo, y optimizarlo para el audio…

    Muchas gracias por el esfuerzo…

  3. muy util, tu post muchas gracias por colgarlo, un saludo!

  4. gracias, voy a probarlo despues de quitar el polvo al teclado.

Deja una Respuesta

El enlace de tu firma no es válido para los buscadores. Cualquier intento por tu parte de hacer spam no tendrá el resultado esperado. Haz un comentario interesante y contribuye a la discusión.